コード例 #1
0
ファイル: http.go プロジェクト: otsimo/distribution
func middleAuth(server *Server) func(h echo.HandlerFunc) echo.HandlerFunc {
	return func(h echo.HandlerFunc) echo.HandlerFunc {
		return func(c *echo.Context) error {
			ah := c.Request().Header.Get(echo.Authorization)

			if len(ah) <= 6 || strings.ToUpper(ah[0:6]) != "BEARER" {
				return errors.New("should be a bearer token")
			}
			val := ah[7:]
			if len(val) == 0 {
				return errors.New("bearer token is empty")
			}
			jwt, err := jose.ParseJWT(val)
			if err != nil {
				return errors.New("failed to parse token")
			}
			usr, err := getUserFromJWT(jwt)
			if err != nil {
				return err
			}
			err = server.oidc.VerifyJWT(jwt, usr.ClientID)
			if err != nil {
				return err
			}
			c.Set("User", usr)
			return h(c)
		}
	}
}
コード例 #2
0
ファイル: auth_middleware.go プロジェクト: jbagel2/dex
// getClientIDFromAuthorizedRequest will extract the clientID from the bearer token.
func getClientIDFromAuthorizedRequest(r *http.Request) (string, error) {
	rawToken, err := oidc.ExtractBearerToken(r)
	if err != nil {
		return "", err
	}

	jwt, err := jose.ParseJWT(rawToken)
	if err != nil {
		return "", err
	}

	claims, err := jwt.Claims()
	if err != nil {
		return "", err
	}

	sub, ok, err := claims.StringClaim("sub")
	if err != nil {
		return "", fmt.Errorf("failed to parse 'sub' claim: %v", err)
	} else if !ok || sub == "" {
		return "", errors.New("missing required 'sub' claim")
	}

	return sub, nil
}
コード例 #3
0
ファイル: oidc.go プロジェクト: polvi/grpc-credentials
// 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)
}
コード例 #4
0
ファイル: oidc.go プロジェクト: Q-Lee/kubernetes
func (r *idTokenRefresher) Refresh() (jose.JWT, error) {
	rt, ok := r.cfg[cfgRefreshToken]
	if !ok {
		return jose.JWT{}, errors.New("No valid id-token, and cannot refresh without refresh-token")
	}

	tokens, err := r.client.refreshToken(rt)
	if err != nil {
		return jose.JWT{}, fmt.Errorf("could not refresh token: %v", err)
	}
	jwt, err := jose.ParseJWT(tokens.IDToken)
	if err != nil {
		return jose.JWT{}, err
	}

	if tokens.RefreshToken != "" && tokens.RefreshToken != rt {
		r.cfg[cfgRefreshToken] = tokens.RefreshToken
	}
	r.cfg[cfgIDToken] = jwt.Encode()

	err = r.persister.Persist(r.cfg)
	if err != nil {
		return jose.JWT{}, fmt.Errorf("could not perist new tokens: %v", err)
	}

	return jwt, r.client.verifyJWT(jwt)
}
コード例 #5
0
ファイル: jwt.go プロジェクト: paulharter/sync_gateway
// Parses and validates a JWT token, based on the client definition provided.
func ValidateJWT(idToken string, client *oidc.Client) (jose.JWT, error) {

	jwt, err := jose.ParseJWT(idToken)
	if err != nil {
		return jose.JWT{}, err
	}

	return jwt, client.VerifyJWT(jwt)
}
コード例 #6
0
// AuthenticateToken decodes and verifies an ID Token using the OIDC client, if the verification succeeds,
// then it will extract the user info from the JWT claims.
func (a *OIDCAuthenticator) AuthenticateToken(value string) (user.Info, bool, error) {
	jwt, err := jose.ParseJWT(value)
	if err != nil {
		return nil, false, err
	}

	client, err := a.client()
	if err != nil {
		return nil, false, err
	}
	if err := client.VerifyJWT(jwt); err != nil {
		return nil, false, err
	}

	claims, err := jwt.Claims()
	if err != nil {
		return nil, false, err
	}

	claim, ok, err := claims.StringClaim(a.usernameClaim)
	if err != nil {
		return nil, false, err
	}
	if !ok {
		return nil, false, fmt.Errorf("cannot find %q in JWT claims", a.usernameClaim)
	}

	var username string
	switch a.usernameClaim {
	case "email":
		// TODO(yifan): Check 'email_verified' to make sure the email is valid.
		username = claim
	default:
		// For all other cases, use issuerURL + claim as the user name.
		username = fmt.Sprintf("%s#%s", a.issuerURL, claim)
	}

	// TODO(yifan): Add UID, also populate the issuer to upper layer.
	info := &user.DefaultInfo{Name: username}

	if a.groupsClaim != "" {
		groups, found, err := claims.StringsClaim(a.groupsClaim)
		if err != nil {
			// Groups type is present but is not an array of strings, try to decode as a string.
			group, _, err := claims.StringClaim(a.groupsClaim)
			if err != nil {
				// Custom claim is present, but isn't an array of strings or a string.
				return nil, false, fmt.Errorf("custom group claim contains invalid type: %T", claims[a.groupsClaim])
			}
			info.Groups = []string{group}
		} else if found {
			info.Groups = groups
		}
	}
	return info, true, nil
}
コード例 #7
0
ファイル: auth.go プロジェクト: otsimo/listener
func (s *grpcServer) parseGrpcMetadata(ctx context.Context, mustBeAdmin bool) (*GrpcClientInfo, error) {
	info := NewClientInfo()
	md, ok := metadata.FromContext(ctx)
	if !ok {
		return nil, fmt.Errorf("missing metadata")
	}
	//GET JWT
	var auth []string
	auth, ok = md["authorization"]

	if !ok || len(auth) == 0 {
		return nil, fmt.Errorf("missing authorization header")
	}

	ah := auth[0]
	if len(ah) <= 6 || strings.ToUpper(ah[0:6]) != "BEARER" {
		return nil, errors.New("should be a bearer token")
	}
	val := ah[7:]
	if len(val) == 0 {
		return nil, errors.New("bearer token is empty")
	}
	jwt, err := jose.ParseJWT(val)
	if err != nil {
		return nil, err
	}
	info.JWT = jwt

	//APP ID
	var devices []string
	devices, ok = md["device"]
	if !ok || len(devices) == 0 {
		return nil, fmt.Errorf("missing 'device' header")
	}
	dbytes, err := base64.StdEncoding.DecodeString(devices[0])
	if err != nil {
		return nil, err
	}
	device := &apipb.DeviceInfo{}
	err = device.Unmarshal(dbytes)
	if err != nil {
		return nil, err
	}
	info.Device = device

	//AUTH TOKEN
	id, email, err := s.authToken(jwt, mustBeAdmin)
	if err != nil {
		return nil, err
	}
	info.UserID = id
	info.Email = email
	info.State = GrpcClientInfoStateUnknown
	return info, nil
}
コード例 #8
0
ファイル: oidc.go プロジェクト: kubernetes/kubernetes
func (p *oidcAuthProvider) idToken() (string, error) {
	p.mu.Lock()
	defer p.mu.Unlock()

	if idToken, ok := p.cfg[cfgIDToken]; ok && len(idToken) > 0 {
		valid, err := verifyJWTExpiry(p.now(), idToken)
		if err != nil {
			return "", err
		}
		if valid {
			// If the cached id token is still valid use it.
			return idToken, nil
		}
	}

	// Try to request a new token using the refresh token.
	rt, ok := p.cfg[cfgRefreshToken]
	if !ok || len(rt) == 0 {
		return "", errors.New("No valid id-token, and cannot refresh without refresh-token")
	}

	tokens, err := p.client.refreshToken(rt)
	if err != nil {
		return "", fmt.Errorf("could not refresh token: %v", err)
	}
	jwt, err := jose.ParseJWT(tokens.IDToken)
	if err != nil {
		return "", err
	}

	if err := p.client.verifyJWT(&jwt); err != nil {
		return "", err
	}

	// Create a new config to persist.
	newCfg := make(map[string]string)
	for key, val := range p.cfg {
		newCfg[key] = val
	}

	if tokens.RefreshToken != "" && tokens.RefreshToken != rt {
		newCfg[cfgRefreshToken] = tokens.RefreshToken
	}

	newCfg[cfgIDToken] = tokens.IDToken
	if err = p.persister.Persist(newCfg); err != nil {
		return "", fmt.Errorf("could not perist new tokens: %v", err)
	}

	// Update the in memory config to reflect the on disk one.
	p.cfg = newCfg

	return tokens.IDToken, nil
}
コード例 #9
0
ファイル: util.go プロジェクト: jboeuf/go-oidc
func ParseTokenFromRequest(r *http.Request) (token jose.JWT, err error) {
	ah := r.Header.Get("Authorization")
	if ah == "" {
		err = errors.New("missing Authorization header")
		return
	}

	if len(ah) <= 6 || strings.ToUpper(ah[0:6]) != "BEARER" {
		err = errors.New("should be a bearer token")
		return
	}

	return jose.ParseJWT(ah[7:])
}
コード例 #10
0
ファイル: auth.go プロジェクト: paulharter/sync_gateway
// Authenticates a user based on a JWT token obtained directly from a provider (auth code flow, refresh flow).
// Verifies the token claims, but doesn't require signature verification.
// If the token is validated but the user for the username defined in the subject claim doesn't exist,
// creates the user when autoRegister=true.
func (auth *Authenticator) AuthenticateTrustedJWT(token string, provider *OIDCProvider, callbackURLFunc OIDCCallbackURLFunc) (User, jose.JWT, error) {

	// Parse JWT
	jwt, err := jose.ParseJWT(token)
	if err != nil {
		base.LogTo("OIDC+", "Error parsing JWT in AuthenticateTrustedJWT: %v", err)
		return nil, jose.JWT{}, err
	}

	// Verify claims - ensures that the token we received from the provider is valid for Sync Gateway
	if err := oidc.VerifyClaims(jwt, provider.Issuer, *provider.ClientID); err != nil {
		return nil, jose.JWT{}, err
	}
	return auth.authenticateJWT(jwt, provider)
}
コード例 #11
0
ファイル: jwt.go プロジェクト: jzelinskie/chihaya
func (tkr *Tracker) validateJWT(jwtStr, infohash string) error {
	jwkSet := tkr.jwkSet
	if time.Now().After(jwkSet.validUntil) {
		return fmt.Errorf("Failed verify JWT due to stale JWK Set")
	}

	jwt, err := jose.ParseJWT(jwtStr)
	if err != nil {
		return err
	}

	validated, err := validateJWTSignature(&jwt, &jwkSet)
	if err != nil {
		return err
	} else if !validated {
		return errors.New("Failed to verify JWT with all available verifiers")
	}

	claims, err := jwt.Claims()
	if err != nil {
		return err
	}

	if claimedIssuer, ok, err := claims.StringClaim("iss"); claimedIssuer != jwkSet.Issuer || err != nil || !ok {
		return errors.New("Failed to validate JWT issuer claim")
	}

	if claimedAudience, ok, err := claims.StringClaim("aud"); claimedAudience != tkr.Config.JWTAudience || err != nil || !ok {
		return errors.New("Failed to validate JWT audience claim")
	}

	claimedInfohash, ok, err := claims.StringClaim("infohash")
	if err != nil || !ok {
		return errors.New("Failed to validate JWT infohash claim")
	}

	unescapedInfohash, err := url.QueryUnescape(claimedInfohash)
	if err != nil {
		return errors.New("Failed to unescape JWT infohash claim")
	}

	if unescapedInfohash != infohash {
		return errors.New("Failed to match infohash claim with requested infohash")
	}

	return nil
}
コード例 #12
0
ファイル: client.go プロジェクト: ryanj/dex
// RefreshToken uses a refresh token to exchange for a new OIDC JWT ID Token.
func (c *Client) RefreshToken(refreshToken string) (jose.JWT, error) {
	oac, err := c.OAuthClient()
	if err != nil {
		return jose.JWT{}, err
	}

	t, err := oac.RequestToken(oauth2.GrantTypeRefreshToken, refreshToken)
	if err != nil {
		return jose.JWT{}, err
	}

	jwt, err := jose.ParseJWT(t.IDToken)
	if err != nil {
		return jose.JWT{}, err
	}

	return jwt, c.VerifyJWT(jwt)
}
コード例 #13
0
ファイル: client.go プロジェクト: sym3tri/go-oidc
// Exchange an OAuth2 auth code for an OIDC JWT
func (c *Client) ExchangeAuthCode(code string) (jose.JWT, error) {
	oac, err := c.OAuthClient()
	if err != nil {
		return jose.JWT{}, err
	}

	t, err := oac.Exchange(code)
	if err != nil {
		return jose.JWT{}, err
	}

	jwt, err := jose.ParseJWT(t.IDToken)
	if err != nil {
		return jose.JWT{}, err
	}

	return jwt, c.VerifyJWT(jwt)
}
コード例 #14
0
ファイル: token.go プロジェクト: otsimo/accounts
func (l *TokenValidator) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {
	log.Info("validating")
	rawToken, err := oidc.ExtractBearerToken(r)
	if err != nil {
		log.Error("token.go: failed to get jwt from header")
		writeError(rw, http.StatusUnauthorized, "missing or invalid token")
		return
	}

	jwt, err := jose.ParseJWT(rawToken)
	if err != nil {
		log.Error("token.go: failed to parse jwt")
		writeError(rw, http.StatusUnauthorized, "missing or invalid token")
		return
	}

	err = l.accounts.Oidc.VerifyJWT(jwt)
	if err != nil {
		log.Errorf("token.go: Failed to verify signature: %v", err)
		writeError(rw, http.StatusUnauthorized, "invalid token")
	}

	claims, err := jwt.Claims()
	if err != nil {
		log.Error("token.go: failed to get claims", err)
		writeError(rw, http.StatusUnauthorized, "missing or invalid token")
		return
	}

	sub, ok, err := claims.StringClaim("sub")
	if err != nil {
		log.Errorf("token.go: failed to parse 'sub' claim: %v", err)
		writeError(rw, http.StatusUnauthorized, "missing or invalid token")
		return
	}
	if !ok || sub == "" {
		log.Error("token.go: missing required 'sub' claim")
		writeError(rw, http.StatusUnauthorized, "missing or invalid token")
		return
	}
	fmt.Println("token.go: verified token for", sub)
	r.Header.Set("sub", sub)
	next(rw, r)
}
コード例 #15
0
ファイル: oidc_test.go プロジェクト: Tecsisa/dex
func handleCallbackFunc(c *oidc.Client, claims *jose.Claims, refresh *string) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		code := r.URL.Query().Get("code")
		if code == "" {
			phttp.WriteError(w, http.StatusBadRequest, "code query param must be set")
			return
		}

		oac, err := c.OAuthClient()
		if err != nil {
			phttp.WriteError(w, http.StatusInternalServerError, fmt.Sprintf("unable to create oauth client: %v", err))
			return
		}

		t, err := oac.RequestToken(oauth2.GrantTypeAuthCode, code)
		if err != nil {
			phttp.WriteError(w, http.StatusBadRequest, fmt.Sprintf("unable to verify auth code with issuer: %v", err))
			return
		}

		// Get id token and claims.
		tok, err := jose.ParseJWT(t.IDToken)
		if err != nil {
			phttp.WriteError(w, http.StatusBadRequest, fmt.Sprintf("unable to parse id_token: %v", err))
			return
		}

		if err := c.VerifyJWT(tok); err != nil {
			phttp.WriteError(w, http.StatusBadRequest, fmt.Sprintf("unable to verify the JWT: %v", err))
			return
		}

		if *claims, err = tok.Claims(); err != nil {
			phttp.WriteError(w, http.StatusBadRequest, fmt.Sprintf("unable to construct claims: %v", err))
			return
		}

		// Get refresh token.
		*refresh = t.RefreshToken

		w.WriteHeader(http.StatusOK)
	}
}
コード例 #16
0
ファイル: user.go プロジェクト: Tecsisa/dex
// Returns TokenClaims if and only if
// - the given token string is an appropriately formatted JWT
// - the JWT contains nonempty "aud" and "sub" claims
// - the JWT can be verified for the client associated with the "aud" claim
//   using the given keys
func parseAndVerifyTokenClaims(token string, issuer url.URL, keys []key.PublicKey) (TokenClaims, error) {
	jwt, err := jose.ParseJWT(token)
	if err != nil {
		return TokenClaims{}, err
	}

	claims, err := jwt.Claims()
	if err != nil {
		return TokenClaims{}, err
	}

	clientID, ok, err := claims.StringClaim("aud")
	if err != nil {
		return TokenClaims{}, err
	}
	if !ok || clientID == "" {
		return TokenClaims{}, errors.New("no aud(client ID) claim")
	}

	sub, ok, err := claims.StringClaim("sub")
	if err != nil {
		return TokenClaims{}, err
	}
	if !ok || sub == "" {
		return TokenClaims{}, errors.New("no sub claim")
	}

	noop := func() error { return nil }

	keysFunc := func() []key.PublicKey {
		return keys
	}

	verifier := oidc.NewJWTVerifier(issuer.String(), clientID, noop, keysFunc)
	if err := verifier.Verify(jwt); err != nil {
		return TokenClaims{}, err
	}

	timeClaimsToInt(claims)

	return TokenClaims{claims}, nil
}
コード例 #17
0
ファイル: main.go プロジェクト: GamerockSA/dex
func handleCallbackFunc(c *oidc.Client) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		code := r.URL.Query().Get("code")
		if code == "" {
			phttp.WriteError(w, http.StatusBadRequest, "code query param must be set")
			return
		}

		tokens, err := exchangeAuthCode(c, code)
		if err != nil {
			phttp.WriteError(w, http.StatusBadRequest,
				fmt.Sprintf("unable to verify auth code with issuer: %v", err))
			return
		}

		tok, err := jose.ParseJWT(tokens.IDToken)
		if err != nil {
			phttp.WriteError(w, http.StatusBadRequest,
				fmt.Sprintf("unable to parse JWT: %v", err))
			return
		}

		claims, err := tok.Claims()
		if err != nil {
			phttp.WriteError(w, http.StatusBadRequest,
				fmt.Sprintf("unable to construct claims: %v", err))
			return
		}

		s := fmt.Sprintf(`
<html>
  <body>
    <p> Token: %v</p>
    <p> Claims: %v </p>
        <a href="/resend?jwt=%s">Resend Verification Email</a>
    <p> Refresh Token: %v </p>
  </body>
</html>`, tok.Encode(), claims, tok.Encode(), tokens.RefreshToken)
		w.Write([]byte(s))
	}
}
コード例 #18
0
ファイル: client.go プロジェクト: sym3tri/go-oidc
func (c *Client) ClientCredsToken(scope []string) (jose.JWT, error) {
	if !c.providerConfig.SupportsGrantType(oauth2.GrantTypeClientCreds) {
		return jose.JWT{}, fmt.Errorf("%v grant type is not supported", oauth2.GrantTypeClientCreds)
	}

	oac, err := c.OAuthClient()
	if err != nil {
		return jose.JWT{}, err
	}

	t, err := oac.ClientCredsToken(scope)
	if err != nil {
		return jose.JWT{}, err
	}

	jwt, err := jose.ParseJWT(t.IDToken)
	if err != nil {
		return jose.JWT{}, err
	}

	return jwt, c.VerifyJWT(jwt)
}
コード例 #19
0
ファイル: oidc.go プロジェクト: kubernetes/kubernetes
func verifyJWTExpiry(now time.Time, s string) (valid bool, err error) {
	jwt, err := jose.ParseJWT(s)
	if err != nil {
		return false, fmt.Errorf("invalid %q", cfgIDToken)
	}
	claims, err := jwt.Claims()
	if err != nil {
		return false, err
	}

	exp, ok, err := claims.TimeClaim("exp")
	switch {
	case err != nil:
		return false, fmt.Errorf("failed to parse 'exp' claim: %v", err)
	case !ok:
		return false, errors.New("missing required 'exp' claim")
	case exp.After(now.Add(expiryDelta)):
		return true, nil
	}

	return false, nil
}
コード例 #20
0
ファイル: grpc.go プロジェクト: otsimo/accounts
func getJWTToken(ctx context.Context) (jose.JWT, error) {
	md, ok := metadata.FromContext(ctx)
	if !ok {
		return jose.JWT{}, fmt.Errorf("missing metadata")
	}
	var auth []string
	auth, ok = md["Authorization"]
	if !ok || len(auth) == 0 {
		return jose.JWT{}, fmt.Errorf("missing authorization header")
	}
	if len(auth) > 1 {
		return jose.JWT{}, fmt.Errorf("too many authorization header")
	}
	ah := auth[0]
	if len(ah) <= 6 || strings.ToUpper(ah[0:6]) != "BEARER" {
		return jose.JWT{}, errors.New("should be a bearer token")
	}
	val := ah[7:]
	if len(val) == 0 {
		return jose.JWT{}, errors.New("bearer token is empty")
	}
	return jose.ParseJWT(val)
}
コード例 #21
0
ファイル: auth.go プロジェクト: paulharter/sync_gateway
// Authenticates a user based on a JWT token string and a set of providers.  Attempts to match the
// issuer in the token with a provider.
// Used to authenticate a JWT token coming from an insecure source (e.g. client request)
// If the token is validated but the user for the username defined in the subject claim doesn't exist,
// creates the user when autoRegister=true.
func (auth *Authenticator) AuthenticateUntrustedJWT(token string, providers OIDCProviderMap, callbackURLFunc OIDCCallbackURLFunc) (User, jose.JWT, error) {

	base.LogTo("OIDC+", "AuthenticateJWT called with token: %s", token)

	// Parse JWT (needed to determine issuer/provider)
	jwt, err := jose.ParseJWT(token)
	if err != nil {
		base.LogTo("OIDC+", "Error parsing JWT in AuthenticateJWT: %v", err)
		return nil, jose.JWT{}, err
	}

	// Get client for issuer
	issuer, audiences, err := GetJWTIssuer(jwt)
	base.LogTo("OIDC+", "JWT issuer: %v, audiences: %v", issuer, audiences)
	if err != nil {
		base.LogTo("OIDC+", "Error getting JWT issuer: %v", err)
		return nil, jose.JWT{}, err
	}

	base.LogTo("OIDC+", "Call GetProviderForIssuer w/ providers: %+v", providers)
	provider := providers.GetProviderForIssuer(issuer, audiences)
	base.LogTo("OIDC+", "Provider for issuer: %+v", provider)

	if provider == nil {
		return nil, jose.JWT{}, fmt.Errorf("No provider found for issuer %v", issuer)
	}

	// VerifyJWT validates the claims and signature on the JWT
	client := provider.GetClient(callbackURLFunc)
	err = client.VerifyJWT(jwt)
	if err != nil {
		base.LogTo("OIDC+", "Client %v could not verify JWT. Error: %v", client, err)
		return nil, jwt, err
	}

	return auth.authenticateJWT(jwt, provider)
}
コード例 #22
0
ファイル: oidc.go プロジェクト: qinguoan/vulcan
// AuthenticateToken decodes and verifies a JWT using the OIDC client, if the verification succeeds,
// then it will extract the user info from the JWT claims.
func (a *OIDCAuthenticator) AuthenticateToken(value string) (user.Info, bool, error) {
	jwt, err := jose.ParseJWT(value)
	if err != nil {
		return nil, false, err
	}

	if err := a.client.VerifyJWT(jwt); err != nil {
		return nil, false, err
	}

	claims, err := jwt.Claims()
	if err != nil {
		return nil, false, err
	}

	claim, ok, err := claims.StringClaim(a.usernameClaim)
	if err != nil {
		return nil, false, err
	}
	if !ok {
		return nil, false, fmt.Errorf("cannot find %q in JWT claims", a.usernameClaim)
	}

	var username string
	switch a.usernameClaim {
	case "email":
		// TODO(yifan): Check 'email_verified' to make sure the email is valid.
		username = claim
	default:
		// For all other cases, use issuerURL + claim as the user name.
		username = fmt.Sprintf("%s#%s", a.clientConfig.ProviderConfig.Issuer, claim)
	}

	// TODO(yifan): Add UID and Group, also populate the issuer to upper layer.
	return &user.DefaultInfo{Name: username}, true, nil
}
コード例 #23
0
ファイル: password.go プロジェクト: no2key/dex
// ParseAndVerifyPasswordResetToken parses a string into a an PasswordReset, verifies the signature, and ensures that required claims are present.
// In addition to the usual claims required by the OIDC spec, "aud" and "sub" must be present as well as ClaimPasswordResetCallback, ClaimPasswordResetEmail and ClaimPasswordResetPassword.
func ParseAndVerifyPasswordResetToken(token string, issuer url.URL, keys []key.PublicKey) (PasswordReset, error) {
	jwt, err := jose.ParseJWT(token)
	if err != nil {
		return PasswordReset{}, err
	}

	claims, err := jwt.Claims()
	if err != nil {
		return PasswordReset{}, err
	}

	cb, ok, err := claims.StringClaim(ClaimPasswordResetCallback)
	if err != nil {
		return PasswordReset{}, err
	}
	var clientID string
	if ok && cb != "" {
		clientID, ok, err = claims.StringClaim("aud")
		if err != nil {
			return PasswordReset{}, err
		}
		if !ok || clientID == "" {
			return PasswordReset{}, errors.New("no aud(client ID) claim")
		}
	}
	if _, err := url.Parse(cb); err != nil {
		return PasswordReset{}, fmt.Errorf("callback URL not parseable: %v", cb)
	}

	pw, ok, err := claims.StringClaim(ClaimPasswordResetPassword)
	if err != nil {
		return PasswordReset{}, err
	}
	if pw == "" {
		return PasswordReset{}, fmt.Errorf("no %q claim", ClaimPasswordResetPassword)
	}

	sub, ok, err := claims.StringClaim("sub")
	if err != nil {
		return PasswordReset{}, err
	}
	if sub == "" {
		return PasswordReset{}, errors.New("no sub claim")
	}

	noop := func() error { return nil }

	keysFunc := func() []key.PublicKey {
		return keys
	}

	verifier := oidc.NewJWTVerifier(issuer.String(), clientID, noop, keysFunc)
	if err := verifier.Verify(jwt); err != nil {
		return PasswordReset{}, err
	}

	return PasswordReset{
		claims: claims,
	}, nil

}
コード例 #24
0
ファイル: email_verification.go プロジェクト: no2key/dex
// ParseAndVerifyEmailVerificationToken parses a string into a an EmailVerification, verifies the signature, and ensures that required claims are present.
// In addition to the usual claims required by the OIDC spec, "aud" and "sub" must be present as well as ClaimEmailVerificationCallback and ClaimEmailVerificationEmail.
func ParseAndVerifyEmailVerificationToken(token string, issuer url.URL, keys []key.PublicKey) (EmailVerification, error) {
	jwt, err := jose.ParseJWT(token)
	if err != nil {
		return EmailVerification{}, err
	}

	claims, err := jwt.Claims()
	if err != nil {
		return EmailVerification{}, err
	}

	clientID, ok, err := claims.StringClaim("aud")
	if err != nil {
		return EmailVerification{}, err
	}
	if !ok {
		return EmailVerification{}, errors.New("no aud(client ID) claim")
	}

	cb, ok, err := claims.StringClaim(ClaimEmailVerificationCallback)
	if err != nil {
		return EmailVerification{}, err
	}
	if cb == "" {
		return EmailVerification{}, fmt.Errorf("no %q claim", ClaimEmailVerificationCallback)
	}
	if _, err := url.Parse(cb); err != nil {
		return EmailVerification{}, fmt.Errorf("callback URL not parseable: %v", cb)
	}

	email, ok, err := claims.StringClaim(ClaimEmailVerificationEmail)
	if err != nil {
		return EmailVerification{}, err
	}
	if email == "" {
		return EmailVerification{}, fmt.Errorf("no %q claim", ClaimEmailVerificationEmail)
	}

	sub, ok, err := claims.StringClaim("sub")
	if err != nil {
		return EmailVerification{}, err
	}
	if sub == "" {
		return EmailVerification{}, errors.New("no sub claim")
	}

	noop := func() error { return nil }

	keysFunc := func() []key.PublicKey {
		return keys
	}

	verifier := oidc.NewJWTVerifier(issuer.String(), clientID, noop, keysFunc)
	if err := verifier.Verify(jwt); err != nil {
		return EmailVerification{}, err
	}

	return EmailVerification{
		claims: claims,
	}, nil

}
コード例 #25
0
ファイル: user.go プロジェクト: ryanj/dex
func (s *UserMgmtServer) getCreds(r *http.Request) (api.Creds, error) {
	token, err := oidc.ExtractBearerToken(r)
	if err != nil {
		log.Errorf("userMgmtServer: GetCreds err: %q", err)
		return api.Creds{}, api.ErrorUnauthorized
	}

	jwt, err := jose.ParseJWT(token)
	if err != nil {
		log.Errorf("userMgmtServer: GetCreds err: %q", err)
		return api.Creds{}, api.ErrorUnauthorized
	}

	claims, err := jwt.Claims()
	if err != nil {
		log.Errorf("userMgmtServer: GetCreds err: %q", err)
		return api.Creds{}, api.ErrorUnauthorized
	}

	clientID, ok, err := claims.StringClaim("aud")
	if err != nil {
		log.Errorf("userMgmtServer: GetCreds err: %q", err)
		return api.Creds{}, err
	}
	if !ok || clientID == "" {
		return api.Creds{}, errors.New("no aud(client ID) claim")
	}

	verifier := s.jwtvFactory(clientID)
	if err := verifier.Verify(jwt); err != nil {
		log.Errorf("userMgmtServer: GetCreds err: %q", err)
		return api.Creds{}, api.ErrorUnauthorized
	}

	sub, ok, err := claims.StringClaim("sub")
	if err != nil {
		log.Errorf("userMgmtServer: GetCreds err: %q", err)
		return api.Creds{}, err
	}
	if !ok || sub == "" {
		return api.Creds{}, api.ErrorUnauthorized
	}

	usr, err := s.um.Get(sub)
	if err != nil {
		if err == user.ErrorNotFound {
			return api.Creds{}, api.ErrorUnauthorized
		}
		log.Errorf("userMgmtServer: GetCreds err: %q", err)
		return api.Creds{}, err
	}

	isAdmin, err := s.cir.IsDexAdmin(clientID)
	if err != nil {
		log.Errorf("userMgmtServer: GetCreds err: %q", err)
		return api.Creds{}, err
	}
	if !isAdmin {
		return api.Creds{}, api.ErrorForbidden
	}

	return api.Creds{
		ClientID: clientID,
		User:     usr,
	}, nil
}
コード例 #26
0
ファイル: oidc.go プロジェクト: Q-Lee/kubernetes
func newOIDCAuthProvider(_ string, cfg map[string]string, persister rest.AuthProviderConfigPersister) (rest.AuthProvider, error) {
	issuer := cfg[cfgIssuerUrl]
	if issuer == "" {
		return nil, fmt.Errorf("Must provide %s", cfgIssuerUrl)
	}

	clientID := cfg[cfgClientID]
	if clientID == "" {
		return nil, fmt.Errorf("Must provide %s", cfgClientID)
	}

	clientSecret := cfg[cfgClientSecret]
	if clientSecret == "" {
		return nil, fmt.Errorf("Must provide %s", cfgClientSecret)
	}

	var certAuthData []byte
	var err error
	if cfg[cfgCertificateAuthorityData] != "" {
		certAuthData, err = base64.StdEncoding.DecodeString(cfg[cfgCertificateAuthorityData])
		if err != nil {
			return nil, err
		}
	}

	clientConfig := rest.Config{
		TLSClientConfig: rest.TLSClientConfig{
			CAFile: cfg[cfgCertificateAuthority],
			CAData: certAuthData,
		},
	}

	trans, err := rest.TransportFor(&clientConfig)
	if err != nil {
		return nil, err
	}
	hc := &http.Client{Transport: trans}

	providerCfg, err := oidc.FetchProviderConfig(hc, issuer)
	if err != nil {
		return nil, fmt.Errorf("error fetching provider config: %v", err)
	}

	scopes := strings.Split(cfg[cfgExtraScopes], ",")
	oidcCfg := oidc.ClientConfig{
		HTTPClient: hc,
		Credentials: oidc.ClientCredentials{
			ID:     clientID,
			Secret: clientSecret,
		},
		ProviderConfig: providerCfg,
		Scope:          append(scopes, oidc.DefaultScope...),
	}

	client, err := oidc.NewClient(oidcCfg)
	if err != nil {
		return nil, fmt.Errorf("error creating OIDC Client: %v", err)
	}

	oClient := &oidcClient{client}

	var initialIDToken jose.JWT
	if cfg[cfgIDToken] != "" {
		initialIDToken, err = jose.ParseJWT(cfg[cfgIDToken])
		if err != nil {
			return nil, err
		}
	}

	return &oidcAuthProvider{
		initialIDToken: initialIDToken,
		refresher: &idTokenRefresher{
			client:    oClient,
			cfg:       cfg,
			persister: persister,
		},
	}, nil
}
コード例 #27
0
ファイル: email_verification.go プロジェクト: no2key/dex
// handleVerifyEmailResendFunc will resend an email-verification email given a valid JWT for the user and a redirect URL.
// This handler is meant to be wrapped in clientTokenMiddleware, so a valid
// bearer token for the client is expected to be present.
// The user's JWT should be in the "token" parameter and the redirect URL should
// be in the "redirect_uri" param. Note that this re
func handleVerifyEmailResendFunc(
	issuerURL url.URL,
	srvKeysFunc func() ([]key.PublicKey, error),
	emailer *useremail.UserEmailer,
	userRepo user.UserRepo,
	clientIdentityRepo client.ClientIdentityRepo) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		decoder := json.NewDecoder(r.Body)
		var params struct {
			Token       string `json:"token"`
			RedirectURI string `json:"redirectURI"`
		}
		err := decoder.Decode(&params)
		if err != nil {
			writeAPIError(w, http.StatusBadRequest, newAPIError(errorInvalidRequest,
				"unable to parse body as JSON"))
			return
		}

		token := params.Token
		if token == "" {
			writeAPIError(w, http.StatusBadRequest,
				newAPIError(errorInvalidRequest, "missing valid JWT"))
			return
		}

		clientID, err := getClientIDFromAuthorizedRequest(r)
		if err != nil {
			log.Errorf("Failed to extract clientID: %v", err)
			writeAPIError(w, http.StatusUnauthorized,
				newAPIError(errorInvalidRequest, "cilent could not be extracted from bearer token."))
			return
		}

		cm, err := clientIdentityRepo.Metadata(clientID)
		if err == client.ErrorNotFound {
			log.Errorf("No such client: %v", err)
			writeAPIError(w, http.StatusBadRequest,
				newAPIError(errorInvalidRequest, "invalid client_id"))
			return

		}
		if err != nil {
			log.Errorf("Error getting ClientMetadata: %v", err)
			writeAPIError(w, http.StatusInternalServerError,
				newAPIError(errorServerError, "could not send email at this time"))
			return
		}

		noop := func() error { return nil }
		keysFunc := func() []key.PublicKey {
			keys, err := srvKeysFunc()
			if err != nil {
				log.Errorf("Error getting keys: %v", err)
			}
			return keys
		}

		jwt, err := jose.ParseJWT(token)
		if err != nil {
			log.Errorf("Failed to Parse JWT: %v", err)
			writeAPIError(w, http.StatusBadRequest,
				newAPIError(errorInvalidRequest, "token could not be parsed"))
			return
		}

		verifier := oidc.NewJWTVerifier(issuerURL.String(), clientID, noop, keysFunc)
		if err := verifier.Verify(jwt); err != nil {
			log.Errorf("Failed to Verify JWT: %v", err)
			writeAPIError(w, http.StatusUnauthorized,
				newAPIError(errorAccessDenied, "invalid token could not be verified"))
			return
		}

		claims, err := jwt.Claims()
		if err != nil {
			log.Errorf("Failed to extract claims from JWT: %v", err)
			writeAPIError(w, http.StatusBadRequest,
				newAPIError(errorInvalidRequest, "invalid token could not be parsed"))
			return
		}

		sub, ok, err := claims.StringClaim("sub")
		if err != nil || !ok || sub == "" {
			log.Errorf("Failed to extract sub claim from JWT: err:%q ok:%v", err, ok)
			writeAPIError(w, http.StatusBadRequest,
				newAPIError(errorInvalidRequest, "could not extract sub claim from token"))
			return
		}

		usr, err := userRepo.Get(nil, sub)
		if err != nil {
			if err == user.ErrorNotFound {
				log.Errorf("Failed to find user specified by token: %v", err)
				writeAPIError(w, http.StatusBadRequest,
					newAPIError(errorInvalidRequest, "could not find user"))
				return
			}
			log.Errorf("Failed to fetch user: %v", err)
			writeAPIError(w, http.StatusInternalServerError,
				newAPIError(errorServerError, "could not send email at this time"))
			return
		}

		if usr.EmailVerified {
			log.Errorf("User's email already verified")
			writeAPIError(w, http.StatusBadRequest,
				newAPIError(errorInvalidRequest, "email already verified"))
			return
		}

		aud, _, _ := claims.StringClaim("aud")
		if aud != clientID {
			log.Errorf("aud of token and sub of bearer token must match: %v", err)
			writeAPIError(w, http.StatusForbidden,
				newAPIError(errorAccessDenied, "JWT is from another client."))
			return
		}

		redirectURLStr := params.RedirectURI
		if redirectURLStr == "" {
			log.Errorf("No redirect URL: %v", err)
			writeAPIError(w, http.StatusBadRequest,
				newAPIError(errorInvalidRequest, "must provide a redirect_uri"))
			return
		}

		redirectURL, err := url.Parse(redirectURLStr)
		if err != nil {
			log.Errorf("Unparsable URL: %v", err)
			writeAPIError(w, http.StatusBadRequest,
				newAPIError(errorInvalidRequest, "invalid redirect_uri"))
			return
		}

		*redirectURL, err = client.ValidRedirectURL(redirectURL, cm.RedirectURLs)
		if err != nil {
			switch err {
			case (client.ErrorInvalidRedirectURL):
				log.Errorf("Request provided unregistered redirect URL: %s", redirectURLStr)
				writeAPIError(w, http.StatusBadRequest,
					newAPIError(errorInvalidRequest, "invalid redirect_uri"))
				return
			case (client.ErrorNoValidRedirectURLs):
				log.Errorf("There are no registered URLs for the requested client: %s", redirectURL)
				writeAPIError(w, http.StatusBadRequest,
					newAPIError(errorInvalidRequest, "invalid redirect_uri"))
				return
			}
		}

		_, err = emailer.SendEmailVerification(usr.ID, clientID, *redirectURL)
		if err != nil {
			log.Errorf("Failed to send email verification email: %v", err)
			writeAPIError(w, http.StatusInternalServerError,
				newAPIError(errorServerError, "could not send email at this time"))
			return
		}
		writeResponseWithBody(w, http.StatusOK, struct{}{})
	}
}
コード例 #28
0
ファイル: oidc.go プロジェクト: kubernetes/kubernetes
// AuthenticateToken decodes and verifies an ID Token using the OIDC client, if the verification succeeds,
// then it will extract the user info from the JWT claims.
func (a *OIDCAuthenticator) AuthenticateToken(value string) (user.Info, bool, error) {
	jwt, err := jose.ParseJWT(value)
	if err != nil {
		return nil, false, err
	}

	client, err := a.client()
	if err != nil {
		return nil, false, err
	}
	if err := client.VerifyJWT(jwt); err != nil {
		return nil, false, err
	}

	claims, err := jwt.Claims()
	if err != nil {
		return nil, false, err
	}

	claim, ok, err := claims.StringClaim(a.usernameClaim)
	if err != nil {
		return nil, false, err
	}
	if !ok {
		return nil, false, fmt.Errorf("cannot find %q in JWT claims", a.usernameClaim)
	}

	var username string
	switch a.usernameClaim {
	case "email":
		verified, ok := claims["email_verified"]
		if !ok {
			return nil, false, errors.New("'email_verified' claim not present")
		}

		emailVerified, ok := verified.(bool)
		if !ok {
			// OpenID Connect spec defines 'email_verified' as a boolean. For now, be a pain and error if
			// it's a different type. If there are enough misbehaving providers we can relax this latter.
			//
			// See: https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims
			return nil, false, fmt.Errorf("malformed claim 'email_verified', expected boolean got %T", verified)
		}

		if !emailVerified {
			return nil, false, errors.New("email not verified")
		}
		username = claim
	default:
		// For all other cases, use issuerURL + claim as the user name.
		username = fmt.Sprintf("%s#%s", a.issuerURL, claim)
	}

	// TODO(yifan): Add UID, also populate the issuer to upper layer.
	info := &user.DefaultInfo{Name: username}

	if a.groupsClaim != "" {
		groups, found, err := claims.StringsClaim(a.groupsClaim)
		if err != nil {
			// Groups type is present but is not an array of strings, try to decode as a string.
			group, _, err := claims.StringClaim(a.groupsClaim)
			if err != nil {
				// Custom claim is present, but isn't an array of strings or a string.
				return nil, false, fmt.Errorf("custom group claim contains invalid type: %T", claims[a.groupsClaim])
			}
			info.Groups = []string{group}
		} else if found {
			info.Groups = groups
		}
	}
	return info, true, nil
}
コード例 #29
0
ファイル: auth_middleware.go プロジェクト: jbagel2/dex
func (c *clientTokenMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	respondError := func() {
		writeAPIError(w, http.StatusUnauthorized, newAPIError(errorAccessDenied, "missing or invalid token"))
	}

	if c.keysFunc == nil {
		log.Errorf("Misconfigured clientTokenMiddleware, keysFunc is not set")
		respondError()
		return
	}

	if c.ciRepo == nil {
		log.Errorf("Misconfigured clientTokenMiddleware, ClientIdentityRepo is not set")
		respondError()
		return
	}

	rawToken, err := oidc.ExtractBearerToken(r)
	if err != nil {
		log.Errorf("Failed to extract token from request: %v", err)
		respondError()
		return
	}

	jwt, err := jose.ParseJWT(rawToken)
	if err != nil {
		log.Errorf("Failed to parse JWT from token: %v", err)
		respondError()
		return
	}

	keys, err := c.keysFunc()
	if err != nil {
		log.Errorf("Failed to get keys: %v", err)
		writeAPIError(w, http.StatusUnauthorized, newAPIError(errorAccessDenied, ""))
		respondError()
		return
	} else if len(keys) == 0 {
		log.Error("No keys available for verification in client token middleware")
		writeAPIError(w, http.StatusUnauthorized, newAPIError(errorAccessDenied, ""))
		respondError()
		return
	}

	ok, err := oidc.VerifySignature(jwt, keys)
	if err != nil {
		log.Errorf("Failed to verify signature: %v", err)
		respondError()
		return
	} else if !ok {
		log.Info("Invalid token")
		respondError()
		return
	}

	clientID, err := oidc.VerifyClientClaims(jwt, c.issuerURL)
	if err != nil {
		log.Errorf("Failed to verify JWT claims: %v", err)
		respondError()
		return
	}

	md, err := c.ciRepo.Metadata(clientID)
	if md == nil || err != nil {
		log.Errorf("Failed to find clientID: %s, error=%v", clientID, err)
		respondError()
		return
	}

	log.Infof("Authenticated token for client ID %s", clientID)
	c.next.ServeHTTP(w, r)
}
コード例 #30
0
ファイル: main.go プロジェクト: Tecsisa/dex
func handleCallbackFunc(c *oidc.Client) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		refreshToken := r.URL.Query().Get("refresh_token")
		code := r.URL.Query().Get("code")

		oac, err := c.OAuthClient()
		if err != nil {
			phttp.WriteError(w, http.StatusBadRequest, fmt.Sprintf("unable to create OAuth2 client: %v", err))
			return
		}

		var token oauth2.TokenResponse

		switch {
		case code != "":
			if token, err = oac.RequestToken(oauth2.GrantTypeAuthCode, code); err != nil {
				phttp.WriteError(w, http.StatusBadRequest, fmt.Sprintf("unable to verify auth code with issuer: %v", err))
				return
			}
		case refreshToken != "":
			if token, err = oac.RequestToken(oauth2.GrantTypeRefreshToken, refreshToken); err != nil {
				phttp.WriteError(w, http.StatusBadRequest, fmt.Sprintf("unable to refresh token: %v", err))
				return
			}
			if token.RefreshToken == "" {
				token.RefreshToken = refreshToken
			}
		default:
			phttp.WriteError(w, http.StatusBadRequest, "code query param must be set")
			return
		}

		tok, err := jose.ParseJWT(token.IDToken)
		if err != nil {
			phttp.WriteError(w, http.StatusBadRequest, fmt.Sprintf("unable to parse JWT: %v", err))
			return
		}

		claims := new(bytes.Buffer)
		if err := json.Indent(claims, tok.Payload, "", "  "); err != nil {
			phttp.WriteError(w, http.StatusBadRequest, fmt.Sprintf("unable to construct claims: %v", err))
			return
		}
		s := fmt.Sprintf(`
<html>
  <head>
    <style>
/* make pre wrap */
pre {
 white-space: pre-wrap;       /* css-3 */
 white-space: -moz-pre-wrap;  /* Mozilla, since 1999 */
 white-space: -pre-wrap;      /* Opera 4-6 */
 white-space: -o-pre-wrap;    /* Opera 7 */
 word-wrap: break-word;       /* Internet Explorer 5.5+ */
} 
    </style>
  </head>
  <body>
    <p> Token: <pre><code>%v</code></pre></p>
    <p> Claims: <pre><code>%v</code></pre></p>
    <p> Refresh Token: <pre><code>%v</code></pre></p>
    <p><a href="%s?refresh_token=%s">Redeem refresh token</a><p>
    <p><a href="/resend?jwt=%s">Resend Verification Email</a></p>
  </body>
</html>`, tok.Encode(), claims.String(), token.RefreshToken, r.URL.Path, token.RefreshToken, tok.Encode())
		w.Write([]byte(s))
	}
}