Skip to content

Commit 5f791bb

Browse files
committed
example: add working example app & cli tool
1 parent 965c438 commit 5f791bb

File tree

4 files changed

+254
-1
lines changed

4 files changed

+254
-1
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/bin
2+
/gopath

build

+7-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
11
#!/bin/bash -e
22

3-
go build ./...
3+
GOBUILD="go build -a -installsuffix netgo -ldflags '-s'"
4+
5+
echo "building bin/oidc-example-app..."
6+
${GOBUILD} -o bin/oidc-example-app github.com/coreos/go-oidc/example/app
7+
echo "building bin/oidc-example-cli..."
8+
${GOBUILD} -o bin/oidc-example-cli github.com/coreos/go-oidc/example/cli
9+
echo "done"

example/app/main.go

+162
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
package main
2+
3+
import (
4+
"encoding/json"
5+
"flag"
6+
"fmt"
7+
"log"
8+
"net"
9+
"net/http"
10+
"net/url"
11+
"os"
12+
"time"
13+
14+
"github.com/coreos/go-oidc/oidc"
15+
)
16+
17+
var (
18+
pathCallback = "/oauth2callback"
19+
defaultListenHost = "127.0.0.1:5555"
20+
)
21+
22+
func main() {
23+
log.SetOutput(os.Stderr)
24+
25+
fs := flag.NewFlagSet("oidc-example-app", flag.ExitOnError)
26+
listen := fs.String("listen", defaultListenHost, "serve traffic on this address (<host>:<port>)")
27+
redirectURL := fs.String("redirect-url", fmt.Sprintf("http://%s%s", defaultListenHost, pathCallback), "")
28+
clientID := fs.String("client-id", "", "")
29+
clientSecret := fs.String("client-secret", "", "")
30+
discovery := fs.String("discovery", "https://accounts.google.com", "")
31+
32+
if err := fs.Parse(os.Args[1:]); err != nil {
33+
log.Fatalf("failed parsing flags: %v", err)
34+
}
35+
36+
if *clientID == "" {
37+
log.Fatal("--client-id must be set")
38+
}
39+
40+
if *clientSecret == "" {
41+
log.Fatal("--client-secret must be set")
42+
}
43+
44+
_, _, err := net.SplitHostPort(*listen)
45+
if err != nil {
46+
log.Fatalf("unable to parse host:port from --listen flag: %v", err)
47+
}
48+
49+
cc := oidc.ClientCredentials{
50+
ID: *clientID,
51+
Secret: *clientSecret,
52+
}
53+
54+
log.Printf("fetching provider config from %s...", *discovery)
55+
56+
var cfg oidc.ProviderConfig
57+
for {
58+
cfg, err = oidc.FetchProviderConfig(http.DefaultClient, *discovery)
59+
if err == nil {
60+
break
61+
}
62+
63+
sleep := 3 * time.Second
64+
log.Printf("failed fetching provider config, trying again in %v: %v", sleep, err)
65+
time.Sleep(sleep)
66+
}
67+
68+
log.Printf("fetched provider config from %s: %#v", *discovery, cfg)
69+
70+
ccfg := oidc.ClientConfig{
71+
ProviderConfig: cfg,
72+
Credentials: cc,
73+
RedirectURL: *redirectURL,
74+
}
75+
76+
client, err := oidc.NewClient(ccfg)
77+
if err != nil {
78+
log.Fatalf("unable to create Client: %v", err)
79+
}
80+
81+
client.SyncProviderConfig(*discovery)
82+
83+
redirectURLParsed, err := url.Parse(*redirectURL)
84+
if err != nil {
85+
log.Fatalf("unable to parse url from --redirect-url flag: %v", err)
86+
}
87+
hdlr := NewClientHandler(client, *redirectURLParsed)
88+
httpsrv := &http.Server{
89+
Addr: fmt.Sprintf(*listen),
90+
Handler: hdlr,
91+
}
92+
93+
log.Printf("binding to %s...", httpsrv.Addr)
94+
log.Fatal(httpsrv.ListenAndServe())
95+
}
96+
97+
func NewClientHandler(c *oidc.Client, cbURL url.URL) http.Handler {
98+
mux := http.NewServeMux()
99+
mux.HandleFunc("/", handleIndex)
100+
mux.HandleFunc("/login", handleLoginFunc(c))
101+
mux.HandleFunc(pathCallback, handleCallbackFunc(c))
102+
return mux
103+
}
104+
105+
func handleIndex(w http.ResponseWriter, r *http.Request) {
106+
w.Write([]byte("<a href='/login'>login</a>"))
107+
}
108+
109+
func handleLoginFunc(c *oidc.Client) http.HandlerFunc {
110+
return func(w http.ResponseWriter, r *http.Request) {
111+
oac, err := c.OAuthClient()
112+
if err != nil {
113+
panic("unable to proceed")
114+
}
115+
116+
u, err := url.Parse(oac.AuthCodeURL("", "", ""))
117+
if err != nil {
118+
panic("unable to proceed")
119+
}
120+
http.Redirect(w, r, u.String(), http.StatusFound)
121+
}
122+
}
123+
124+
func handleCallbackFunc(c *oidc.Client) http.HandlerFunc {
125+
return func(w http.ResponseWriter, r *http.Request) {
126+
code := r.URL.Query().Get("code")
127+
if code == "" {
128+
writeError(w, http.StatusBadRequest, "code query param must be set")
129+
return
130+
}
131+
132+
tok, err := c.ExchangeAuthCode(code)
133+
if err != nil {
134+
writeError(w, http.StatusBadRequest, fmt.Sprintf("unable to verify auth code with issuer: %v", err))
135+
return
136+
}
137+
138+
claims, err := tok.Claims()
139+
if err != nil {
140+
writeError(w, http.StatusBadRequest, fmt.Sprintf("unable to construct claims: %v", err))
141+
return
142+
}
143+
144+
s := fmt.Sprintf("claims: %v", claims)
145+
w.Write([]byte(s))
146+
}
147+
}
148+
149+
func writeError(w http.ResponseWriter, code int, msg string) {
150+
e := struct {
151+
Error string `json:"error"`
152+
}{
153+
Error: msg,
154+
}
155+
b, err := json.Marshal(e)
156+
if err != nil {
157+
log.Printf("Failed marshaling %#v to JSON: %v", e, err)
158+
}
159+
w.Header().Set("Content-Type", "application/json")
160+
w.WriteHeader(code)
161+
w.Write(b)
162+
}

example/cli/main.go

+83
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
package main
2+
3+
import (
4+
"flag"
5+
"fmt"
6+
"net/http"
7+
"os"
8+
"time"
9+
10+
"github.com/coreos/go-oidc/oidc"
11+
)
12+
13+
func main() {
14+
fs := flag.NewFlagSet("oidc-example-cli", flag.ExitOnError)
15+
clientID := fs.String("client-id", "", "")
16+
clientSecret := fs.String("client-secret", "", "")
17+
discovery := fs.String("discovery", "https://accounts.google.com", "")
18+
19+
if err := fs.Parse(os.Args[1:]); err != nil {
20+
fmt.Fprintln(os.Stderr, err.Error())
21+
os.Exit(1)
22+
}
23+
24+
if *clientID == "" {
25+
fmt.Println("--client-id must be set")
26+
os.Exit(2)
27+
}
28+
29+
if *clientSecret == "" {
30+
fmt.Println("--client-secret must be set")
31+
os.Exit(2)
32+
}
33+
34+
cc := oidc.ClientCredentials{
35+
ID: *clientID,
36+
Secret: *clientSecret,
37+
}
38+
39+
log.Printf("fetching provider config from %s...", *discovery)
40+
41+
// NOTE: A real CLI would cache this config, or provide it via flags/config file.
42+
var cfg oidc.ProviderConfig
43+
var err error
44+
for {
45+
cfg, err = oidc.FetchProviderConfig(http.DefaultClient, *discovery)
46+
if err == nil {
47+
break
48+
}
49+
50+
sleep := 1 * time.Second
51+
fmt.Printf("failed fetching provider config, trying again in %v: %v\n", sleep, err)
52+
time.Sleep(sleep)
53+
}
54+
55+
fmt.Printf("fetched provider config from %s: %#v\n\n", *discovery, cfg)
56+
57+
ccfg := oidc.ClientConfig{
58+
ProviderConfig: cfg,
59+
Credentials: cc,
60+
}
61+
62+
client, err := oidc.NewClient(ccfg)
63+
if err != nil {
64+
fmt.Printf("unable to create Client: %v\n", err)
65+
os.Exit(1)
66+
}
67+
68+
tok, err := client.ClientCredsToken([]string{"openid"})
69+
if err != nil {
70+
fmt.Printf("unable to verify auth code with issuer: %v\n", err)
71+
os.Exit(1)
72+
}
73+
74+
fmt.Printf("got jwt: %v\n\n", tok.Encode())
75+
76+
claims, err := tok.Claims()
77+
if err != nil {
78+
fmt.Printf("unable to construct claims: %v\n", err)
79+
os.Exit(1)
80+
}
81+
82+
fmt.Printf("got claims %#v...\n", claims)
83+
}

0 commit comments

Comments
 (0)