mirror of https://github.com/dexidp/dex.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
145 lines
4.5 KiB
145 lines
4.5 KiB
/* |
|
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.NewVerifier(ctx) |
|
|
|
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.NewVerifier(ctx, 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
|
|
|