// Reads the OIDC JWT passed in the context and verifies it using the given OIDC client. // Returns the verified identity on success, error otherwise. func VerifiedIdentityFromContext(client *gooidc.Client, ctx context.Context) (*gooidc.Identity, error) { md, ok := metadata.FromContext(ctx) if !ok { return nil, errors.New("missing RPC credentials") } rawJWT, ok := md["jwt"] if !ok { return nil, errors.New("missing OIDC credentials") } if len(rawJWT) != 1 { return nil, errors.New("incorrect JWT data sent") } jwt, err := jose.ParseJWT(rawJWT[0]) if err != nil { return nil, err } if err := client.VerifyJWT(jwt); err != nil { return nil, err } claims, err := jwt.Claims() if err != nil { return nil, err } return gooidc.IdentityFromClaims(claims) }
func (tm *ClientCredsTokenManager) fetchToken() (time.Duration, error) { if err := tm.client.Healthy(); err != nil { return 0, fmt.Errorf("unable to authenticate, unhealthy OIDC client: %v", err) } jwt, err := tm.client.ClientCredsToken(oidc.DefaultScope) if err != nil { return 0, err } claims, err := jwt.Claims() if err != nil { return 0, err } ident, err := oidc.IdentityFromClaims(claims) if err != nil { return 0, err } tm.Token = jwt if !tm.initialSyncDone { tm.initialSyncWait.Done() tm.initialSyncDone = true } return nextSyncAfter(ident.ExpiresAt, tm.clock), nil }
// Extracts the JWT Identity Claims (includes ID, Email, Expiry) from a JWT. func GetJWTIdentity(jwt jose.JWT) (identity *oidc.Identity, err error) { claims, err := jwt.Claims() if err != nil { return identity, err } return oidc.IdentityFromClaims(claims) }
func (c *OIDCConnector) handleCallbackFunc(lf oidc.LoginFunc, errorURL url.URL) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { q := r.URL.Query() e := q.Get("error") if e != "" { redirectError(w, errorURL, q) return } code := q.Get("code") if code == "" { q.Set("error", oauth2.ErrorInvalidRequest) q.Set("error_description", "code query param must be set") redirectError(w, errorURL, q) return } tok, err := c.client.ExchangeAuthCode(code) if err != nil { log.Errorf("Unable to verify auth code with issuer: %v", err) q.Set("error", oauth2.ErrorUnsupportedResponseType) q.Set("error_description", "unable to verify auth code with issuer") redirectError(w, errorURL, q) return } claims, err := tok.Claims() if err != nil { log.Errorf("Unable to construct claims: %v", err) q.Set("error", oauth2.ErrorUnsupportedResponseType) q.Set("error_description", "unable to construct claims") redirectError(w, errorURL, q) return } // Override the email claim by using the value of the specified claim. // This is used for the provider (e.g., Azure AD) which returns // the ID token that does not include a email claim. if c.emailClaim != "" && c.emailClaim != defaultEmailClaim { email, ok, err := claims.StringClaim(c.emailClaim) if err != nil { log.Errorf("Unable to get value of alternative email claim: %v", err) q.Set("error", oauth2.ErrorUnsupportedResponseType) q.Set("error_description", "unable to get value of alternative email claim") redirectError(w, errorURL, q) return } if !ok { log.Errorf("Failed parsing alternative email claim from remote provider: %v", err) q.Set("error", oauth2.ErrorUnsupportedResponseType) q.Set("error_description", "failed parsing alternative email claim") redirectError(w, errorURL, q) return } claims.Add(defaultEmailClaim, email) } ident, err := oidc.IdentityFromClaims(claims) if err != nil { log.Errorf("Failed parsing claims from remote provider: %v", err) q.Set("error", oauth2.ErrorUnsupportedResponseType) q.Set("error_description", "unable to convert claims to identity") redirectError(w, errorURL, q) return } sessionKey := q.Get("state") if sessionKey == "" { q.Set("error", oauth2.ErrorInvalidRequest) q.Set("error_description", "missing state query param") redirectError(w, errorURL, q) return } redirectURL, err := lf(*ident, sessionKey) if err != nil { log.Errorf("Unable to log in %#v: %v", *ident, err) q.Set("error", oauth2.ErrorAccessDenied) q.Set("error_description", "login failed") redirectError(w, errorURL, q) return } w.Header().Set("Location", redirectURL) w.WriteHeader(http.StatusFound) return } }
func (c *OIDCConnector) handleCallbackFunc(lf oidc.LoginFunc, errorURL url.URL) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { q := r.URL.Query() e := q.Get("error") if e != "" { redirectError(w, errorURL, q) return } code := q.Get("code") if code == "" { q.Set("error", oauth2.ErrorInvalidRequest) q.Set("error_description", "code query param must be set") redirectError(w, errorURL, q) return } tok, err := c.client.ExchangeAuthCode(code) if err != nil { log.Errorf("Unable to verify auth code with issuer: %v", err) q.Set("error", oauth2.ErrorUnsupportedResponseType) q.Set("error_description", "unable to verify auth code with issuer") redirectError(w, errorURL, q) return } claims, err := tok.Claims() if err != nil { log.Errorf("Unable to construct claims: %v", err) q.Set("error", oauth2.ErrorUnsupportedResponseType) q.Set("error_description", "unable to construct claims") redirectError(w, errorURL, q) return } ident, err := oidc.IdentityFromClaims(claims) if err != nil { log.Errorf("Failed parsing claims from remote provider: %v", err) q.Set("error", oauth2.ErrorUnsupportedResponseType) q.Set("error_description", "unable to convert claims to identity") redirectError(w, errorURL, q) return } sessionKey := q.Get("state") if sessionKey == "" { q.Set("error", oauth2.ErrorInvalidRequest) q.Set("error_description", "missing state query param") redirectError(w, errorURL, q) return } redirectURL, err := lf(*ident, sessionKey) if err != nil { log.Errorf("Unable to log in %#v: %v", *ident, err) q.Set("error", oauth2.ErrorAccessDenied) q.Set("error_description", "login failed") redirectError(w, errorURL, q) return } w.Header().Set("Location", redirectURL) w.WriteHeader(http.StatusTemporaryRedirect) return } }