Exemple #1
0
func authToken(oidc *Client, jwt jose.JWT, mustBeAdmin bool) (string, bool, error) {
	claims, err := jwt.Claims()
	if err != nil {
		return "", false, fmt.Errorf("auth.go: failed to get claims %v", err)
	}

	aud, ok, err := claims.StringClaim("aud")
	if err != nil || !ok || aud == "" {
		return "", false, fmt.Errorf("auth.go: failed to parse 'aud' claim: %v", err)
	}

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

	err = oidc.VerifyJWT(jwt, aud)
	if err != nil {
		return sub, false, fmt.Errorf("auth.go: Failed to verify signature: %v", err)
	}

	typ, _, _ := claims.StringClaim(OtsimoUserTypeClaim)

	if mustBeAdmin {
		if typ != OtsimoAdminUserType {
			return sub, false, fmt.Errorf("auth.go: user must be admin")
		}
	}
	return sub, typ == OtsimoAdminUserType, nil
}
Exemple #2
0
func getUserFromJWT(jwt jose.JWT) (*models.User, error) {
	claims, err := jwt.Claims()
	if err != nil {
		return nil, fmt.Errorf("auth.go: failed to get claims %v", err)
	}

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

	aud, _, _ := claims.StringClaim("aud")
	typ, _, _ := claims.StringClaim(OtsimoUserTypeClaim)
	isadmin := typ == OtsimoAdminUserType

	return &models.User{
		ID:       sub,
		IsAdmin:  isadmin,
		Token:    jwt,
		ClientID: aud,
	}, nil
}
Exemple #3
0
func compareJWTs(a, b jose.JWT) string {
	if a.Encode() == b.Encode() {
		return ""
	}

	var aClaims, bClaims jose.Claims
	for _, j := range []struct {
		claims *jose.Claims
		jwt    jose.JWT
	}{
		{&aClaims, a},
		{&bClaims, b},
	} {
		var err error
		*j.claims, err = j.jwt.Claims()
		if err != nil {
			*j.claims = jose.Claims(map[string]interface{}{
				"msg": "bad claims",
				"err": err,
			})
		}
	}

	return diff.ObjectDiff(aClaims, bClaims)
}
Exemple #4
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)
}
Exemple #5
0
// Verify claims in accordance with OIDC spec
// http://openid.net/specs/openid-connect-basic-1_0.html#IDTokenValidation
func VerifyClaims(jwt jose.JWT, issuer, clientID string) error {
	now := time.Now().UTC()

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

	ident, err := IdentityFromClaims(claims)
	if err != nil {
		return err
	}

	if ident.ExpiresAt.Before(now) {
		return errors.New("token is expired")
	}

	// iss REQUIRED. Issuer Identifier for the Issuer of the response.
	// The iss value is a case sensitive URL using the https scheme that contains scheme,
	// host, and optionally, port number and path components and no query or fragment components.
	if iss, exists := claims["iss"].(string); exists {
		if !urlEqual(iss, issuer) {
			return fmt.Errorf("invalid claim value: 'iss'. expected=%s, found=%s.", issuer, iss)
		}
	} else {
		return errors.New("missing claim: 'iss'")
	}

	// iat REQUIRED. Time at which the JWT was issued.
	// Its value is a JSON number representing the number of seconds from 1970-01-01T0:0:0Z
	// as measured in UTC until the date/time.
	if _, exists := claims["iat"].(float64); !exists {
		return errors.New("missing claim: 'iat'")
	}

	// aud REQUIRED. Audience(s) that this ID Token is intended for.
	// It MUST contain the OAuth 2.0 client_id of the Relying Party as an audience value.
	// It MAY also contain identifiers for other audiences. In the general case, the aud
	// value is an array of case sensitive strings. In the common special case when there
	// is one audience, the aud value MAY be a single case sensitive string.
	if aud, ok, err := claims.StringClaim("aud"); err == nil && ok {
		if aud != clientID {
			return fmt.Errorf("invalid claims, 'aud' claim and 'client_id' do not match, aud=%s, client_id=%s", aud, clientID)
		}
	} else if aud, ok, err := claims.StringsClaim("aud"); err == nil && ok {
		if !containsString(clientID, aud) {
			return fmt.Errorf("invalid claims, cannot find 'client_id' in 'aud' claim, aud=%v, client_id=%s", aud, clientID)
		}
	} else {
		return errors.New("invalid claim value: 'aud' is required, and should be either string or string array")
	}

	return nil
}
Exemple #6
0
func (o *testOIDCClient) verifyJWT(jwt jose.JWT) error {
	claims, err := jwt.Claims()
	if err != nil {
		return err
	}
	claim, _, _ := claims.StringClaim("test")
	if claim != "good" {
		return errors.New("bad token")
	}
	return nil
}
Exemple #7
0
func VerifySignature(jwt jose.JWT, keys []key.PublicKey) (bool, error) {
	jwtBytes := []byte(jwt.Data())
	for _, k := range keys {
		v, err := k.Verifier()
		if err != nil {
			return false, err
		}
		if v.Verify(jwt.Signature, jwtBytes) == nil {
			return true, nil
		}
	}
	return false, nil
}
Exemple #8
0
func validateJWTSignature(jwt *jose.JWT, jwkSet *jwkSet) (bool, error) {
	for _, jwk := range jwkSet.Keys {
		v, err := jose.NewVerifier(jwk)
		if err != nil {
			return false, err
		}

		if err := v.Verify(jwt.Signature, []byte(jwt.Data())); err == nil {
			return true, nil
		}
	}

	return false, nil
}
Exemple #9
0
func (c *Client) VerifyJWTForClientID(jwt jose.JWT, clientID string) error {
	var keysFunc func() []key.PublicKey
	if kID, ok := jwt.KeyID(); ok {
		keysFunc = c.keysFuncWithID(kID)
	} else {
		keysFunc = c.keysFuncAll()
	}

	v := oidc.NewJWTVerifier(
		c.providerConfig.Get().Issuer.String(),
		clientID,
		c.maybeSyncKeys, keysFunc)

	return v.Verify(jwt)
}
Exemple #10
0
func (c *Client) VerifyJWT(jwt jose.JWT) error {
	var keysFunc func() []key.PublicKey
	if kID, ok := jwt.KeyID(); ok {
		keysFunc = c.keysFuncWithID(kID)
	} else {
		keysFunc = c.keysFuncAll()
	}

	v := NewJWTVerifier(
		c.providerConfig.Issuer,
		c.credentials.ID,
		c.maybeSyncKeys, keysFunc)

	return v.Verify(jwt)
}
Exemple #11
0
// VerifyClientClaims verifies all the required claims are valid for a "client credentials" JWT.
// Returns the client ID if valid, or an error if invalid.
func VerifyClientClaims(jwt jose.JWT, issuer string) (string, error) {
	claims, err := jwt.Claims()
	if err != nil {
		return "", fmt.Errorf("failed to parse JWT claims: %v", err)
	}

	iss, ok, err := claims.StringClaim("iss")
	if err != nil {
		return "", fmt.Errorf("failed to parse 'iss' claim: %v", err)
	} else if !ok {
		return "", errors.New("missing required 'iss' claim")
	} else if !urlEqual(iss, issuer) {
		return "", fmt.Errorf("'iss' claim does not match expected issuer, iss=%s", iss)
	}

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

	if aud, ok, err := claims.StringClaim("aud"); err == nil && ok {
		if aud != sub {
			return "", fmt.Errorf("invalid claims, 'aud' claim and 'sub' claim do not match, aud=%s, sub=%s", aud, sub)
		}
	} else if aud, ok, err := claims.StringsClaim("aud"); err == nil && ok {
		if !containsString(sub, aud) {
			return "", fmt.Errorf("invalid claims, cannot find 'sud' in 'aud' claim, aud=%v, sub=%s", aud, sub)
		}
	} else {
		return "", errors.New("invalid claim value: 'aud' is required, and should be either string or string array")
	}

	now := time.Now().UTC()
	exp, ok, err := claims.TimeClaim("exp")
	if err != nil {
		return "", fmt.Errorf("failed to parse 'exp' claim: %v", err)
	} else if !ok {
		return "", errors.New("missing required 'exp' claim")
	} else if exp.Before(now) {
		return "", fmt.Errorf("token already expired at: %v", exp)
	}

	return sub, nil
}
Exemple #12
0
// VerifyClientClaims verifies all the required claims are valid for a "client credentials" JWT.
// Returns the client ID if valid, or an error if invalid.
func VerifyClientClaims(jwt jose.JWT, issuer string) (string, error) {
	claims, err := jwt.Claims()
	if err != nil {
		return "", fmt.Errorf("failed to parse JWT claims: %v", err)
	}

	iss, ok, err := claims.StringClaim("iss")
	if err != nil {
		return "", fmt.Errorf("failed to parse 'iss' claim: %v", err)
	} else if !ok {
		return "", errors.New("missing required 'iss' claim")
	} else if !urlEqual(iss, issuer) {
		return "", fmt.Errorf("'iss' claim does not match expected issuer, iss=%s", iss)
	}

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

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

	if sub != aud {
		return "", fmt.Errorf("invalid claims, 'aud' claim and 'sub' claim do not match, aud=%s, sub=%s", aud, sub)
	}

	now := time.Now().UTC()
	exp, ok, err := claims.TimeClaim("exp")
	if err != nil {
		return "", fmt.Errorf("failed to parse 'exp' claim: %v", err)
	} else if !ok {
		return "", errors.New("missing required 'exp' claim")
	} else if exp.Before(now) {
		return "", fmt.Errorf("token already expired at: %v", exp)
	}

	return sub, nil
}
Exemple #13
0
func (s *grpcServer) authToken(jwt jose.JWT, mustBeAdmin bool) (string, string, error) {
	claims, err := jwt.Claims()
	if err != nil {
		return "", "", fmt.Errorf("auth.go: failed to get claims %v", err)
	}

	aud, ok, err := claims.StringClaim("aud")
	if err != nil || !ok || aud == "" {
		return "", "", fmt.Errorf("auth.go: failed to parse 'aud' claim: %v", err)
	}

	err = s.server.Oidc.VerifyJWT(jwt, aud)
	if err != nil {
		return "", "", fmt.Errorf("auth.go: Failed to verify signature: %v", err)
	}

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

	email, ok, err := claims.StringClaim("email")
	if err != nil || !ok || email == "" {
		return "", "", fmt.Errorf("auth.go: failed to parse 'email' claim: %v", err)
	}

	if mustBeAdmin {
		typ, ok, err := claims.StringClaim(OtsimoUserTypeClaim)
		if err != nil {
			return "", "", fmt.Errorf("auth.go: failed to parse '%s' claim: %v", OtsimoUserTypeClaim, err)
		}
		if !ok || typ == "" {
			return "", "", fmt.Errorf("auth.go: missing required '%s' claim", OtsimoUserTypeClaim)
		}
		if typ != OtsimoAdminUserType {
			return "", "", fmt.Errorf("auth.go: user must be admin")
		}
	}
	return sub, email, nil
}
Exemple #14
0
func (r *idTokenRefresher) Verify(jwt jose.JWT) error {
	claims, err := jwt.Claims()
	if err != nil {
		return err
	}

	now := time.Now()
	exp, ok, err := claims.TimeClaim("exp")
	switch {
	case err != nil:
		return fmt.Errorf("failed to parse 'exp' claim: %v", err)
	case !ok:
		return errors.New("missing required 'exp' claim")
	case exp.Before(now):
		return fmt.Errorf("token already expired at: %v", exp)
	}

	return nil
}
Exemple #15
0
func getUserIdAndEmail(jwt jose.JWT) (string, string, error) {
	claims, err := jwt.Claims()

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

	return sub, email, nil

}
Exemple #16
0
func GetJWTIssuer(jwt jose.JWT) (issuer string, audiences []string, err error) {

	claims, err := jwt.Claims()
	if err != nil {
		return "", audiences, fmt.Errorf("failed to parse JWT claims: %v", err)
	}

	iss, ok, err := claims.StringClaim("iss")
	if err != nil {
		return "", audiences, fmt.Errorf("Failed to parse 'iss' claim: %v", err)
	} else if !ok {
		return "", audiences, errors.New("Missing required 'iss' claim")
	}

	if aud, ok, err := claims.StringClaim("aud"); err == nil && ok {
		audiences = append(audiences, aud)
	} else if aud, ok, err := claims.StringsClaim("aud"); err == nil && ok {
		audiences = aud
	} else {
		return "", audiences, errors.New("Missing required 'aud' claim.")
	}

	return iss, audiences, nil
}
Exemple #17
0
func handleTokenFunc(srv OIDCServer) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		if r.Method != "POST" {
			w.Header().Set("Allow", "POST")
			phttp.WriteError(w, http.StatusMethodNotAllowed, fmt.Sprintf("POST only acceptable method"))
			return
		}

		err := r.ParseForm()
		if err != nil {
			log.Errorf("error parsing request: %v", err)
			writeTokenError(w, oauth2.NewError(oauth2.ErrorInvalidRequest), "")
			return
		}

		state := r.PostForm.Get("state")

		user, password, ok := r.BasicAuth()
		if !ok {
			log.Errorf("error parsing basic auth")
			writeTokenError(w, oauth2.NewError(oauth2.ErrorInvalidClient), state)
			return
		}

		creds := oidc.ClientCredentials{ID: user, Secret: password}

		var jwt *jose.JWT
		var refreshToken string
		grantType := r.PostForm.Get("grant_type")

		switch grantType {
		case oauth2.GrantTypeAuthCode:
			code := r.PostForm.Get("code")
			if code == "" {
				log.Errorf("missing code param")
				writeTokenError(w, oauth2.NewError(oauth2.ErrorInvalidRequest), state)
				return
			}
			jwt, refreshToken, err = srv.CodeToken(creds, code)
			if err != nil {
				log.Errorf("couldn't exchange code for token: %v", err)
				writeTokenError(w, err, state)
				return
			}
		case oauth2.GrantTypeClientCreds:
			jwt, err = srv.ClientCredsToken(creds)
			if err != nil {
				log.Errorf("couldn't creds for token: %v", err)
				writeTokenError(w, err, state)
				return
			}
		case oauth2.GrantTypeRefreshToken:
			token := r.PostForm.Get("refresh_token")
			if token == "" {
				writeTokenError(w, oauth2.NewError(oauth2.ErrorInvalidRequest), state)
				return
			}
			jwt, err = srv.RefreshToken(creds, token)
			if err != nil {
				writeTokenError(w, err, state)
				return
			}
		default:
			log.Errorf("unsupported grant: %v", grantType)
			writeTokenError(w, oauth2.NewError(oauth2.ErrorUnsupportedGrantType), state)
			return
		}

		t := oAuth2Token{
			AccessToken:  jwt.Encode(),
			IDToken:      jwt.Encode(),
			TokenType:    "bearer",
			RefreshToken: refreshToken,
		}

		b, err := json.Marshal(t)
		if err != nil {
			log.Errorf("Failed marshaling %#v to JSON: %v", t, err)
			writeTokenError(w, oauth2.NewError(oauth2.ErrorServerError), state)
			return
		}

		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusOK)
		w.Write(b)
	}
}