Add support for IDPs that do not send ID tokens in the reply when using a refresh grant. Add tests for the aforementioned functionality.

Signed-off-by: Anthony Brandelli <abrandel@cisco.com>
This commit is contained in:
Anthony Brandelli
2022-05-19 22:13:10 -06:00
parent 9cd29bdee0
commit 7c335e9337
2 changed files with 129 additions and 24 deletions

View File

@@ -226,6 +226,13 @@ func (e *oauth2Error) Error() string {
return e.error + ": " + e.errorDescription
}
type caller uint
const (
createCaller caller = iota
refreshCaller
)
func (c *oidcConnector) HandleCallback(s connector.Scopes, r *http.Request) (identity connector.Identity, err error) {
q := r.URL.Query()
if errType := q.Get("error"); errType != "" {
@@ -235,8 +242,7 @@ func (c *oidcConnector) HandleCallback(s connector.Scopes, r *http.Request) (ide
if err != nil {
return identity, fmt.Errorf("oidc: failed to get token: %v", err)
}
return c.createIdentity(r.Context(), identity, token)
return c.createIdentity(r.Context(), identity, token, createCaller)
}
// Refresh is used to refresh a session with the refresh token provided by the IdP
@@ -255,23 +261,25 @@ func (c *oidcConnector) Refresh(ctx context.Context, s connector.Scopes, identit
if err != nil {
return identity, fmt.Errorf("oidc: failed to get refresh token: %v", err)
}
return c.createIdentity(ctx, identity, token)
return c.createIdentity(ctx, identity, token, refreshCaller)
}
func (c *oidcConnector) createIdentity(ctx context.Context, identity connector.Identity, token *oauth2.Token) (connector.Identity, error) {
rawIDToken, ok := token.Extra("id_token").(string)
if !ok {
return identity, errors.New("oidc: no id_token in token response")
}
idToken, err := c.verifier.Verify(ctx, rawIDToken)
if err != nil {
return identity, fmt.Errorf("oidc: failed to verify ID Token: %v", err)
}
func (c *oidcConnector) createIdentity(ctx context.Context, identity connector.Identity, token *oauth2.Token, caller caller) (connector.Identity, error) {
var claims map[string]interface{}
if err := idToken.Claims(&claims); err != nil {
return identity, fmt.Errorf("oidc: failed to decode claims: %v", err)
rawIDToken, ok := token.Extra("id_token").(string)
if ok {
idToken, err := c.verifier.Verify(ctx, rawIDToken)
if err != nil {
return identity, fmt.Errorf("oidc: failed to verify ID Token: %v", err)
}
if err := idToken.Claims(&claims); err != nil {
return identity, fmt.Errorf("oidc: failed to decode claims: %v", err)
}
} else if caller != refreshCaller {
// ID tokens aren't mandatory in the reply when using a refresh_token grant
return identity, errors.New("oidc: no id_token in token response")
}
// We immediately want to run getUserInfo if configured before we validate the claims
@@ -285,6 +293,12 @@ func (c *oidcConnector) createIdentity(ctx context.Context, identity connector.I
}
}
const subjectClaimKey = "sub"
subject, found := claims[subjectClaimKey].(string)
if !found {
return identity, fmt.Errorf("missing \"%s\" claim", subjectClaimKey)
}
userNameKey := "name"
if c.userNameKey != "" {
userNameKey = c.userNameKey
@@ -358,7 +372,7 @@ func (c *oidcConnector) createIdentity(ctx context.Context, identity connector.I
}
identity = connector.Identity{
UserID: idToken.Subject,
UserID: subject,
Username: name,
PreferredUsername: preferredUsername,
Email: email,