github.com/clerkinc/clerk-sdk-go@v1.49.1/clerk/tokens.go (about) 1 package clerk 2 3 import ( 4 "fmt" 5 "time" 6 7 "github.com/go-jose/go-jose/v3" 8 "github.com/go-jose/go-jose/v3/jwt" 9 ) 10 11 var standardClaimsKeys = []string{"iss", "sub", "aud", "exp", "nbf", "iat", "jti"} 12 13 type TokenClaims struct { 14 jwt.Claims 15 Extra map[string]interface{} 16 } 17 18 // DecodeToken decodes a jwt token without verifying it. 19 func (c *client) DecodeToken(token string) (*TokenClaims, error) { 20 parsedToken, err := jwt.ParseSigned(token) 21 if err != nil { 22 return nil, err 23 } 24 25 standardClaims := jwt.Claims{} 26 extraClaims := make(map[string]interface{}) 27 28 if err = parsedToken.UnsafeClaimsWithoutVerification(&standardClaims, &extraClaims); err != nil { 29 return nil, err 30 } 31 32 // Delete any standard claims included in the extra claims 33 for _, key := range standardClaimsKeys { 34 delete(extraClaims, key) 35 } 36 37 return &TokenClaims{Claims: standardClaims, Extra: extraClaims}, nil 38 } 39 40 type verifyTokenOptions struct { 41 authorizedParties map[string]struct{} 42 leeway time.Duration 43 jwk *jose.JSONWebKey 44 customClaims interface{} 45 isSatellite bool 46 proxyURL string 47 } 48 49 // VerifyToken verifies the session jwt token. 50 func (c *client) VerifyToken(token string, opts ...VerifyTokenOption) (*SessionClaims, error) { 51 options := &verifyTokenOptions{} 52 53 for _, opt := range opts { 54 if err := opt(options); err != nil { 55 return nil, err 56 } 57 } 58 59 parsedToken, err := jwt.ParseSigned(token) 60 if err != nil { 61 return nil, err 62 } 63 64 if len(parsedToken.Headers) == 0 { 65 return nil, fmt.Errorf("missing jwt headers") 66 } 67 68 kid := parsedToken.Headers[0].KeyID 69 if kid == "" { 70 return nil, fmt.Errorf("missing jwt kid header claim") 71 } 72 73 jwk := options.jwk 74 if jwk == nil { 75 jwk, err = c.getJWK(kid) 76 if err != nil { 77 return nil, err 78 } 79 } 80 81 if parsedToken.Headers[0].Algorithm != jwk.Algorithm { 82 return nil, fmt.Errorf("invalid signing algorithm %s", jwk.Algorithm) 83 } 84 85 claims := SessionClaims{} 86 if err = verifyTokenParseClaims(parsedToken, jwk.Key, &claims, options); err != nil { 87 return nil, err 88 } 89 90 if err = claims.Claims.ValidateWithLeeway(jwt.Expected{Time: time.Now()}, options.leeway); err != nil { 91 return nil, err 92 } 93 94 issuer := newIssuer(claims.Issuer). 95 WithSatelliteDomain(options.isSatellite). 96 WithProxyURL(options.proxyURL) 97 98 if !issuer.IsValid() { 99 return nil, fmt.Errorf("invalid issuer %s", claims.Issuer) 100 } 101 102 if claims.AuthorizedParty != "" && len(options.authorizedParties) > 0 { 103 if _, ok := options.authorizedParties[claims.AuthorizedParty]; !ok { 104 return nil, fmt.Errorf("invalid authorized party %s", claims.AuthorizedParty) 105 } 106 } 107 108 return &claims, nil 109 } 110 111 func (c *client) getJWK(kid string) (*jose.JSONWebKey, error) { 112 if c.jwksCache.isInvalid() { 113 jwks, err := c.jwks.ListAll() 114 if err != nil { 115 return nil, err 116 } 117 118 c.jwksCache.set(jwks) 119 } 120 121 return c.jwksCache.get(kid) 122 } 123 124 func verifyTokenParseClaims(parsedToken *jwt.JSONWebToken, key interface{}, sessionClaims *SessionClaims, options *verifyTokenOptions) error { 125 if options.customClaims == nil { 126 return parsedToken.Claims(key, sessionClaims) 127 } 128 return parsedToken.Claims(key, sessionClaims, options.customClaims) 129 }