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  }