1/* 2This is an example application to demonstrate verifying an ID Token with a nonce. 3*/ 4package main 5 6import ( 7 "encoding/json" 8 "log" 9 "net/http" 10 "os" 11 12 oidc "github.com/coreos/go-oidc" 13 14 "golang.org/x/net/context" 15 "golang.org/x/oauth2" 16) 17 18var ( 19 clientID = os.Getenv("GOOGLE_OAUTH2_CLIENT_ID") 20 clientSecret = os.Getenv("GOOGLE_OAUTH2_CLIENT_SECRET") 21) 22 23const appNonce = "a super secret nonce" 24 25func main() { 26 ctx := context.Background() 27 28 provider, err := oidc.NewProvider(ctx, "https://accounts.google.com") 29 if err != nil { 30 log.Fatal(err) 31 } 32 33 oidcConfig := &oidc.Config{ 34 ClientID: clientID, 35 } 36 // Use the nonce source to create a custom ID Token verifier. 37 nonceEnabledVerifier := provider.Verifier(oidcConfig) 38 39 config := oauth2.Config{ 40 ClientID: clientID, 41 ClientSecret: clientSecret, 42 Endpoint: provider.Endpoint(), 43 RedirectURL: "http://127.0.0.1:5556/auth/google/callback", 44 Scopes: []string{oidc.ScopeOpenID, "profile", "email"}, 45 } 46 47 state := "foobar" // Don't do this in production. 48 49 http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 50 http.Redirect(w, r, config.AuthCodeURL(state, oidc.Nonce(appNonce)), http.StatusFound) 51 }) 52 53 http.HandleFunc("/auth/google/callback", func(w http.ResponseWriter, r *http.Request) { 54 if r.URL.Query().Get("state") != state { 55 http.Error(w, "state did not match", http.StatusBadRequest) 56 return 57 } 58 59 oauth2Token, err := config.Exchange(ctx, r.URL.Query().Get("code")) 60 if err != nil { 61 http.Error(w, "Failed to exchange token: "+err.Error(), http.StatusInternalServerError) 62 return 63 } 64 65 rawIDToken, ok := oauth2Token.Extra("id_token").(string) 66 if !ok { 67 http.Error(w, "No id_token field in oauth2 token.", http.StatusInternalServerError) 68 return 69 } 70 // Verify the ID Token signature and nonce. 71 idToken, err := nonceEnabledVerifier.Verify(ctx, rawIDToken) 72 if err != nil { 73 http.Error(w, "Failed to verify ID Token: "+err.Error(), http.StatusInternalServerError) 74 return 75 } 76 if idToken.Nonce != appNonce { 77 http.Error(w, "Invalid ID Token nonce", http.StatusInternalServerError) 78 return 79 } 80 81 resp := struct { 82 OAuth2Token *oauth2.Token 83 IDTokenClaims *json.RawMessage // ID Token payload is just JSON. 84 }{oauth2Token, new(json.RawMessage)} 85 86 if err := idToken.Claims(&resp.IDTokenClaims); err != nil { 87 http.Error(w, err.Error(), http.StatusInternalServerError) 88 return 89 } 90 data, err := json.MarshalIndent(resp, "", " ") 91 if err != nil { 92 http.Error(w, err.Error(), http.StatusInternalServerError) 93 return 94 } 95 w.Write(data) 96 }) 97 98 log.Printf("listening on http://%s/", "127.0.0.1:5556") 99 log.Fatal(http.ListenAndServe("127.0.0.1:5556", nil)) 100} 101