Skip to content

Commit

Permalink
feat: Add userinfo validation (#18)
Browse files Browse the repository at this point in the history
  • Loading branch information
vdbulcke committed May 20, 2022
1 parent 98805e9 commit 54f875d
Show file tree
Hide file tree
Showing 4 changed files with 107 additions and 66 deletions.
2 changes: 1 addition & 1 deletion oidc-client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,7 @@ func (c *OIDCClient) OIDCAuthorizationCodeFlow() error {
} else {

// validate signature agains the JWK
idToken, err := c.processIdToken(c.ctx, idTokenRaw)
idToken, err := c.processIdToken(idTokenRaw)
if err != nil {
c.logger.Error("ID Token validation failed", "err", err)
http.Error(w, "Failed to verify ID Token: "+err.Error(), http.StatusInternalServerError)
Expand Down
48 changes: 48 additions & 0 deletions oidc-client/id_token.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package oidcclient

import (
"context"
"encoding/json"

"github.com/coreos/go-oidc/v3/oidc"
)

// processIdToken Handle idToken call
func (c *OIDCClient) processIdToken(idTokenRaw string) (*oidc.IDToken, error) {

// validate signature agains the JWK
idToken, err := c.verifier.Verify(c.ctx, idTokenRaw)
if err != nil {
c.logger.Error("ID Token validation failed", "err", err)

return nil, err
}

// validate AMR Values
if !c.validateAMR(idToken) {
c.logger.Error("Amr not valid", "amrs", c.config.AMRWhitelist)
}

// Print IDToken
var idTokenClaims *json.RawMessage

// format id Token Claims
if err := idToken.Claims(&idTokenClaims); err != nil {
c.logger.Error("Error Parsing ID Token Claims", "err", err)
return nil, err
}

// Print ID Token Claims
idTokenClaimsByte, err := json.MarshalIndent(idTokenClaims, "", " ")
if err != nil {
c.logger.Error("Could not parse idTokenClaims", "err", err)
}
c.logger.Info("IDToken Claims", "IDTokenClaims", string(idTokenClaimsByte))

// Save sub from ID Token into context
// for Userinfo validation
sub := idToken.Subject
c.ctx = context.WithValue(c.ctx, "sub", sub)

return idToken, nil
}
66 changes: 1 addition & 65 deletions oidc-client/refresh_token.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ func (c *OIDCClient) RefreshTokenFlow(refreshToken string, skipIdTokenVerificati
c.logger.Error("no ID Token Found")
} else if !skipIdTokenVerification {
// verify and print idToken
_, err = c.processIdToken(c.ctx, idTokenRaw)
_, err = c.processIdToken( idTokenRaw)
if err != nil {
return err
}
Expand Down Expand Up @@ -101,40 +101,6 @@ func (c *OIDCClient) RefreshTokenFlow(refreshToken string, skipIdTokenVerificati

}

// processIdToken Handle idToken call
func (c *OIDCClient) processIdToken(ctx context.Context, idTokenRaw string) (*oidc.IDToken, error) {

// validate signature agains the JWK
idToken, err := c.verifier.Verify(c.ctx, idTokenRaw)
if err != nil {
c.logger.Error("ID Token validation failed", "err", err)

return nil, err
}

// validate AMR Values
if !c.validateAMR(idToken) {
c.logger.Error("Amr not valid", "amrs", c.config.AMRWhitelist)
}

// Print IDToken
var idTokenClaims *json.RawMessage

// format id Token Claims
if err := idToken.Claims(&idTokenClaims); err != nil {
c.logger.Error("Error Parsing ID Token Claims", "err", err)
return nil, err
}

// Print ID Token Claims, and User Info
idTokenClaimsByte, err := json.MarshalIndent(idTokenClaims, "", " ")
if err != nil {
c.logger.Error("Could not parse idTokenClaims", "err", err)
}
c.logger.Info("IDToken Claims", "IDTokenClaims", string(idTokenClaimsByte))

return idToken, nil
}

// processAccessToken Handle accessToken JWT validation
func (c *OIDCClient) processAccessToken(ctx context.Context, accessTokenRaw string) (*oidc.IDToken, error) {
Expand Down Expand Up @@ -173,33 +139,3 @@ func (c *OIDCClient) processGenericToken(ctx context.Context, tokenRaw string, t

return jwtToken, nil
}

// userinfo Handle userinfo call
func (c *OIDCClient) userinfo(oauth2Token *oauth2.Token) error {
// Fetch Userinfo
if !c.config.SkipUserinfo {
// NOTE: this will detects based on the Content-Type if the userinfo is application/jwt
// and if it is JWT it will validate signature agains JWK for the provider
userInfo, err := c.provider.UserInfo(c.ctx, oauth2.StaticTokenSource(oauth2Token))
if err != nil {
return err
}

var userInfoClaims *json.RawMessage
// format userinfo Claims
if err := userInfo.Claims(&userInfoClaims); err != nil {
c.logger.Error("Error Parsing USerinfo Claims", "err", err)
return err
}

userInfoClaimsByte, err := json.MarshalIndent(userInfoClaims, "", " ")
if err != nil {
c.logger.Error("Could not parse idTokenClaims", "err", err)
}

c.logger.Info("Userinfo Claims", "UserInfoClaims", string(userInfoClaimsByte))

}

return nil
}
57 changes: 57 additions & 0 deletions oidc-client/userinfo.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package oidcclient

import (
"encoding/json"

"golang.org/x/oauth2"
)

// userinfo Handle userinfo call
func (c *OIDCClient) userinfo(oauth2Token *oauth2.Token) error {
// Fetch Userinfo
if !c.config.SkipUserinfo {
// NOTE: this will detects based on the Content-Type if the userinfo is application/jwt
// and if it is JWT it will validate signature agains JWK for the provider
userInfo, err := c.provider.UserInfo(c.ctx, oauth2.StaticTokenSource(oauth2Token))
if err != nil {
return err
}

// validation 'sub'
// https://openid.net/specs/openid-connect-core-1_0.html#UserInfoResponse
sub := userInfo.Subject
if sub == "" {
c.logger.Error("Missing mandatory 'sub' field")
}

// fetch id_token 'sub' from context
idTokenSub := c.ctx.Value("sub")
if idTokenSub != nil {

// userinfo 'sub' must match id_token 'sub'
if sub != idTokenSub.(string) {
c.logger.Error("'sub' fields do not match", "idTokenSub", idTokenSub, "userinfoSub", sub)
}

} else {
c.logger.Error("Could not retrieve id_token 'sub' field from context")
}

var userInfoClaims *json.RawMessage
// format userinfo Claims
if err := userInfo.Claims(&userInfoClaims); err != nil {
c.logger.Error("Error Parsing USerinfo Claims", "err", err)
return err
}

userInfoClaimsByte, err := json.MarshalIndent(userInfoClaims, "", " ")
if err != nil {
c.logger.Error("Could not parse idTokenClaims", "err", err)
}

c.logger.Info("Userinfo Claims", "UserInfoClaims", string(userInfoClaimsByte))

}

return nil
}

0 comments on commit 54f875d

Please sign in to comment.