Skip to content

Commit 1b56172

Browse files
committed
example: use real cookies for state and nonce tokens
This also consolidates the nonce example into the "idtoken" app.
1 parent fbec1bd commit 1b56172

File tree

3 files changed

+89
-106
lines changed

3 files changed

+89
-106
lines changed

example/idtoken/app.go

Lines changed: 53 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,14 @@ This is an example application to demonstrate parsing an ID Token.
44
package main
55

66
import (
7+
"crypto/rand"
8+
"encoding/base64"
79
"encoding/json"
10+
"io"
811
"log"
912
"net/http"
1013
"os"
14+
"time"
1115

1216
"github.com/coreos/go-oidc/v3/oidc"
1317
"golang.org/x/net/context"
@@ -19,6 +23,25 @@ var (
1923
clientSecret = os.Getenv("GOOGLE_OAUTH2_CLIENT_SECRET")
2024
)
2125

26+
func randString(nByte int) (string, error) {
27+
b := make([]byte, nByte)
28+
if _, err := io.ReadFull(rand.Reader, b); err != nil {
29+
return "", err
30+
}
31+
return base64.RawURLEncoding.EncodeToString(b), nil
32+
}
33+
34+
func setCallbackCookie(w http.ResponseWriter, r *http.Request, name, value string) {
35+
c := &http.Cookie{
36+
Name: name,
37+
Value: value,
38+
MaxAge: int(time.Hour.Seconds()),
39+
Secure: r.TLS != nil,
40+
HttpOnly: true,
41+
}
42+
http.SetCookie(w, c)
43+
}
44+
2245
func main() {
2346
ctx := context.Background()
2447

@@ -39,14 +62,30 @@ func main() {
3962
Scopes: []string{oidc.ScopeOpenID, "profile", "email"},
4063
}
4164

42-
state := "foobar" // Don't do this in production.
43-
4465
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
45-
http.Redirect(w, r, config.AuthCodeURL(state), http.StatusFound)
66+
state, err := randString(16)
67+
if err != nil {
68+
http.Error(w, "Internal error", http.StatusInternalServerError)
69+
return
70+
}
71+
nonce, err := randString(16)
72+
if err != nil {
73+
http.Error(w, "Internal error", http.StatusInternalServerError)
74+
return
75+
}
76+
setCallbackCookie(w, r, "state", state)
77+
setCallbackCookie(w, r, "nonce", nonce)
78+
79+
http.Redirect(w, r, config.AuthCodeURL(state, oidc.Nonce(nonce)), http.StatusFound)
4680
})
4781

4882
http.HandleFunc("/auth/google/callback", func(w http.ResponseWriter, r *http.Request) {
49-
if r.URL.Query().Get("state") != state {
83+
state, err := r.Cookie("state")
84+
if err != nil {
85+
http.Error(w, "state not found", http.StatusBadRequest)
86+
return
87+
}
88+
if r.URL.Query().Get("state") != state.Value {
5089
http.Error(w, "state did not match", http.StatusBadRequest)
5190
return
5291
}
@@ -67,6 +106,16 @@ func main() {
67106
return
68107
}
69108

109+
nonce, err := r.Cookie("nonce")
110+
if err != nil {
111+
http.Error(w, "nonce not found", http.StatusBadRequest)
112+
return
113+
}
114+
if idToken.Nonce != nonce.Value {
115+
http.Error(w, "nonce did not match", http.StatusBadRequest)
116+
return
117+
}
118+
70119
oauth2Token.AccessToken = "*REDACTED*"
71120

72121
resp := struct {

example/nonce/app.go

Lines changed: 0 additions & 99 deletions
This file was deleted.

example/userinfo/app.go

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,14 @@ This is an example application to demonstrate querying the user info endpoint.
44
package main
55

66
import (
7+
"crypto/rand"
8+
"encoding/base64"
79
"encoding/json"
10+
"io"
811
"log"
912
"net/http"
1013
"os"
14+
"time"
1115

1216
"github.com/coreos/go-oidc/v3/oidc"
1317
"golang.org/x/net/context"
@@ -19,6 +23,25 @@ var (
1923
clientSecret = os.Getenv("GOOGLE_OAUTH2_CLIENT_SECRET")
2024
)
2125

26+
func randString(nByte int) (string, error) {
27+
b := make([]byte, nByte)
28+
if _, err := io.ReadFull(rand.Reader, b); err != nil {
29+
return "", err
30+
}
31+
return base64.RawURLEncoding.EncodeToString(b), nil
32+
}
33+
34+
func setCallbackCookie(w http.ResponseWriter, r *http.Request, name, value string) {
35+
c := &http.Cookie{
36+
Name: name,
37+
Value: value,
38+
MaxAge: int(time.Hour.Seconds()),
39+
Secure: r.TLS != nil,
40+
HttpOnly: true,
41+
}
42+
http.SetCookie(w, c)
43+
}
44+
2245
func main() {
2346
ctx := context.Background()
2447

@@ -34,14 +57,24 @@ func main() {
3457
Scopes: []string{oidc.ScopeOpenID, "profile", "email"},
3558
}
3659

37-
state := "foobar" // Don't do this in production.
38-
3960
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
61+
state, err := randString(16)
62+
if err != nil {
63+
http.Error(w, "Internal error", http.StatusInternalServerError)
64+
return
65+
}
66+
setCallbackCookie(w, r, "state", state)
67+
4068
http.Redirect(w, r, config.AuthCodeURL(state), http.StatusFound)
4169
})
4270

4371
http.HandleFunc("/auth/google/callback", func(w http.ResponseWriter, r *http.Request) {
44-
if r.URL.Query().Get("state") != state {
72+
state, err := r.Cookie("state")
73+
if err != nil {
74+
http.Error(w, "state not found", http.StatusBadRequest)
75+
return
76+
}
77+
if r.URL.Query().Get("state") != state.Value {
4578
http.Error(w, "state did not match", http.StatusBadRequest)
4679
return
4780
}

0 commit comments

Comments
 (0)