Add option in oidc to hit the optional userinfo endpoint

Some oauth providers return "thin tokens" which won't include all of the
claims requested. This simply adds an option which will make the oidc
connector use the userinfo endpoint to fetch all the claims.
This commit is contained in:
Thomas Jackson 2019-04-24 13:58:35 -07:00
parent cd3c6983da
commit 52d09a2dfa
2 changed files with 25 additions and 0 deletions

View file

@ -60,6 +60,12 @@ connectors:
# or if they are acting as a proxy for another IDP etc AWS Cognito with an upstream SAML IDP # or if they are acting as a proxy for another IDP etc AWS Cognito with an upstream SAML IDP
# This can be overridden with the below option # This can be overridden with the below option
# insecureSkipEmailVerified: true # insecureSkipEmailVerified: true
# When enabled, the OpenID Connector will query the UserInfo endpoint for additional claims. UserInfo claims
# take priority over claims returned by the IDToken. This option should be used when the IDToken doesn't contain
# all the claims requested.
# https://openid.net/specs/openid-connect-core-1_0.html#UserInfo
# getUserInfo: true
``` ```
[oidc-doc]: openid-connect.md [oidc-doc]: openid-connect.md

View file

@ -39,6 +39,11 @@ type Config struct {
// Override the value of email_verifed to true in the returned claims // Override the value of email_verifed to true in the returned claims
InsecureSkipEmailVerified bool `json:"insecureSkipEmailVerified"` InsecureSkipEmailVerified bool `json:"insecureSkipEmailVerified"`
// GetUserInfo uses the userinfo endpoint to get additional claims for
// the token. This is especially useful where upstreams return "thin"
// id tokens
GetUserInfo bool `json:"getUserInfo"`
} }
// Domains that don't support basic auth. golang.org/x/oauth2 has an internal // Domains that don't support basic auth. golang.org/x/oauth2 has an internal
@ -105,6 +110,7 @@ func (c *Config) Open(id string, logger log.Logger) (conn connector.Connector, e
clientID := c.ClientID clientID := c.ClientID
return &oidcConnector{ return &oidcConnector{
provider: provider,
redirectURI: c.RedirectURI, redirectURI: c.RedirectURI,
oauth2Config: &oauth2.Config{ oauth2Config: &oauth2.Config{
ClientID: clientID, ClientID: clientID,
@ -120,6 +126,7 @@ func (c *Config) Open(id string, logger log.Logger) (conn connector.Connector, e
cancel: cancel, cancel: cancel,
hostedDomains: c.HostedDomains, hostedDomains: c.HostedDomains,
insecureSkipEmailVerified: c.InsecureSkipEmailVerified, insecureSkipEmailVerified: c.InsecureSkipEmailVerified,
getUserInfo: c.GetUserInfo,
}, nil }, nil
} }
@ -129,6 +136,7 @@ var (
) )
type oidcConnector struct { type oidcConnector struct {
provider *oidc.Provider
redirectURI string redirectURI string
oauth2Config *oauth2.Config oauth2Config *oauth2.Config
verifier *oidc.IDTokenVerifier verifier *oidc.IDTokenVerifier
@ -137,6 +145,7 @@ type oidcConnector struct {
logger log.Logger logger log.Logger
hostedDomains []string hostedDomains []string
insecureSkipEmailVerified bool insecureSkipEmailVerified bool
getUserInfo bool
} }
func (c *oidcConnector) Close() error { func (c *oidcConnector) Close() error {
@ -219,6 +228,16 @@ func (c *oidcConnector) HandleCallback(s connector.Scopes, r *http.Request) (ide
} }
if c.getUserInfo {
userInfo, err := c.provider.UserInfo(r.Context(), oauth2.StaticTokenSource(token))
if err != nil {
return identity, fmt.Errorf("oidc: error loading userinfo: %v", err)
}
if err := userInfo.Claims(&claims); err != nil {
return identity, fmt.Errorf("oidc: failed to decode userinfo claims: %v", err)
}
}
identity = connector.Identity{ identity = connector.Identity{
UserID: idToken.Subject, UserID: idToken.Subject,
Username: claims.Username, Username: claims.Username,