| /* |
| Package oidc implements OpenID Connect client logic for the golang.org/x/oauth2 package. |
| |
| provider, err := oidc.NewProvider(ctx, "https://accounts.example.com") |
| if err != nil { |
| return err |
| } |
| |
| // Configure an OpenID Connect aware OAuth2 client. |
| oauth2Config := oauth2.Config{ |
| ClientID: clientID, |
| ClientSecret: clientSecret, |
| RedirectURL: redirectURL, |
| Endpoint: provider.Endpoint(), |
| Scopes: []string{oidc.ScopeOpenID, "profile", "email"}, |
| } |
| |
| OAuth2 redirects are unchanged. |
| |
| func handleRedirect(w http.ResponseWriter, r *http.Request) { |
| http.Redirect(w, r, oauth2Config.AuthCodeURL(state), http.StatusFound) |
| }) |
| |
| For callbacks the provider can be used to query for user information such as email. |
| |
| func handleOAuth2Callback(w http.ResponseWriter, r *http.Request) { |
| // Verify state... |
| |
| oauth2Token, err := oauth2Config.Exchange(ctx, r.URL.Query().Get("code")) |
| if err != nil { |
| http.Error(w, "Failed to exchange token: "+err.Error(), http.StatusInternalServerError) |
| return |
| } |
| |
| userinfo, err := provider.UserInfo(ctx, oauth2.StaticTokenSource(oauth2Token)) |
| if err != nil { |
| http.Error(w, "Failed to get userinfo: "+err.Error(), http.StatusInternalServerError) |
| return |
| } |
| |
| // ... |
| }) |
| |
| The provider also has the ability to verify ID Tokens. |
| |
| verifier := provider.Verifier() |
| |
| The returned verifier can be used to perform basic validation on ID Token issued by the provider, |
| including verifying the JWT signature. It then returns the payload. |
| |
| func handleOAuth2Callback(w http.ResponseWriter, r *http.Request) { |
| // Verify state... |
| |
| oauth2Token, err := oauth2Config.Exchange(ctx, r.URL.Query().Get("code")) |
| if err != nil { |
| http.Error(w, "Failed to exchange token: "+err.Error(), http.StatusInternalServerError) |
| return |
| } |
| |
| // Extract the ID Token from oauth2 token. |
| rawIDToken, ok := oauth2Token.Extra("id_token").(string) |
| if !ok { |
| http.Error(w, "No ID Token found", http.StatusInternalServerError) |
| return |
| } |
| |
| // Verify that the ID Token is signed by the provider. |
| idToken, err := verifier.Verify(rawIDToken) |
| if err != nil { |
| http.Error(w, "Failed to verify ID Token: "+err.Error(), http.StatusInternalServerError) |
| return |
| } |
| |
| // Unmarshal ID Token for expected custom claims. |
| var claims struct { |
| Email string `json:"email"` |
| EmailVerified bool `json:"email_verified"` |
| } |
| if err := idToken.Claims(&claims); err != nil { |
| http.Error(w, "Failed to unmarshal ID Token custom claims: "+err.Error(), http.StatusInternalServerError) |
| return |
| } |
| |
| // ... |
| }) |
| |
| ID Token nonces are supported. |
| |
| First, provide a nonce source for nonce validation. This will then be used to wrap the existing |
| provider ID Token verifier. |
| |
| // A verifier which boths verifies the ID Token signature and nonce. |
| nonceEnabledVerifier := provider.Verifier(oidc.VerifyNonce(nonceSource)) |
| |
| For the redirect provide a nonce auth code option. This will be placed as a URL parameter during |
| the client redirect. |
| |
| func handleRedirect(w http.ResponseWriter, r *http.Request) { |
| nonce, err := newNonce() |
| if err != nil { |
| // ... |
| } |
| // Provide a nonce for the OpenID Connect ID Token. |
| http.Redirect(w, r, oauth2Config.AuthCodeURL(state, oidc.Nonce(nonce)), http.StatusFound) |
| }) |
| |
| The nonce enabled verifier can then be used to verify the nonce while unpacking the ID Token. |
| |
| func handleOAuth2Callback(w http.ResponseWriter, r *http.Request) { |
| // Verify state... |
| |
| oauth2Token, err := oauth2Config.Exchange(ctx, r.URL.Query().Get("code")) |
| if err != nil { |
| http.Error(w, "Failed to exchange token: "+err.Error(), http.StatusInternalServerError) |
| return |
| } |
| |
| // Extract the ID Token from oauth2 token. |
| rawIDToken, ok := oauth2Token.Extra("id_token").(string) |
| if !ok { |
| http.Error(w, "No ID Token found", http.StatusInternalServerError) |
| return |
| } |
| |
| // Verify that the ID Token is signed by the provider and verify the nonce. |
| idToken, err := nonceEnabledVerifier.Verify(rawIDToken) |
| if err != nil { |
| http.Error(w, "Failed to verify ID Token: "+err.Error(), http.StatusInternalServerError) |
| return |
| } |
| |
| // Continue as above... |
| }) |
| |
| This package uses contexts to derive HTTP clients in the same way as the oauth2 package. To configure |
| a custom client, use the oauth2 packages HTTPClient context key when constructing the context. |
| |
| myClient := &http.Client{} |
| |
| myCtx := context.WithValue(parentCtx, oauth2.HTTPClient, myClient) |
| |
| // NewProvider will use myClient to make the request. |
| provider, err := oidc.NewProvider(myCtx, "https://accounts.example.com") |
| */ |
| package oidc |