Example #1
0
// 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)
}
Example #2
0
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
}
Example #3
0
// 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)
}
Example #4
0
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
	}
}
Example #5
0
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
	}
}