diff --git a/clerk/middleware.go b/clerk/middleware.go index 2bb8f074..76ef8ebc 100644 --- a/clerk/middleware.go +++ b/clerk/middleware.go @@ -51,7 +51,7 @@ func isAuthV2Request(r *http.Request, client Client) (string, bool) { claims, err := client.DecodeToken(headerToken) if err == nil { - return headerToken, isValidIssuer(claims.Issuer) + return headerToken, isValidIssuer(claims.Issuer, nil) } // Verification from header token failed, try with token from cookie @@ -65,5 +65,5 @@ func isAuthV2Request(r *http.Request, client Client) (string, bool) { return "", false } - return cookieSession.Value, isValidIssuer(claims.Issuer) + return cookieSession.Value, isValidIssuer(claims.Issuer, nil) } diff --git a/clerk/tokens.go b/clerk/tokens.go index 7f238aa4..49fb4e44 100644 --- a/clerk/tokens.go +++ b/clerk/tokens.go @@ -52,6 +52,8 @@ type verifyTokenOptions struct { leeway time.Duration jwk *jose.JSONWebKey customClaims interface{} + isSatellite bool + proxyURL string } // VerifyToken verifies the session jwt token. @@ -99,7 +101,7 @@ func (c *client) VerifyToken(token string, opts ...VerifyTokenOption) (*SessionC return nil, err } - if !isValidIssuer(claims.Issuer) { + if !isValidIssuer(claims.Issuer, options) { return nil, fmt.Errorf("invalid issuer %s", claims.Issuer) } @@ -132,6 +134,16 @@ func verifyTokenParseClaims(parsedToken *jwt.JSONWebToken, key interface{}, sess return parsedToken.Claims(key, sessionClaims, options.customClaims) } -func isValidIssuer(issuer string) bool { +func isValidIssuer(issuer string, options *verifyTokenOptions) bool { + if options != nil { + if options.isSatellite { + return true + } + + if options.proxyURL != "" { + return issuer == options.proxyURL + } + } + return strings.HasPrefix(issuer, "https://clerk.") || strings.Contains(issuer, ".clerk.accounts") } diff --git a/clerk/tokens_options.go b/clerk/tokens_options.go index 136bb07e..c2692f6e 100644 --- a/clerk/tokens_options.go +++ b/clerk/tokens_options.go @@ -63,6 +63,20 @@ func WithCustomClaims(customClaims interface{}) VerifyTokenOption { } } +func WithSatelliteDomain(isSatellite bool) VerifyTokenOption { + return func(o *verifyTokenOptions) error { + o.isSatellite = isSatellite + return nil + } +} + +func WithProxyURL(proxyURL string) VerifyTokenOption { + return func(o *verifyTokenOptions) error { + o.proxyURL = proxyURL + return nil + } +} + func pemToJWK(key string) (*jose.JSONWebKey, error) { block, _ := pem.Decode([]byte(key)) if block == nil { diff --git a/clerk/tokens_options_test.go b/clerk/tokens_options_test.go index fb4bd2c6..66d91472 100644 --- a/clerk/tokens_options_test.go +++ b/clerk/tokens_options_test.go @@ -76,6 +76,28 @@ BQIDAQAB } } +func TestWithSatelliteDomain(t *testing.T) { + isSatellite := true + + opts := &verifyTokenOptions{} + err := WithSatelliteDomain(isSatellite)(opts) + + if assert.NoError(t, err) { + assert.Equal(t, isSatellite, opts.isSatellite) + } +} + +func TestWithProxyURL(t *testing.T) { + proxyURL := "url" + + opts := &verifyTokenOptions{} + err := WithProxyURL(proxyURL)(opts) + + if assert.NoError(t, err) { + assert.Equal(t, proxyURL, opts.proxyURL) + } +} + func arrayToMap(t *testing.T, input []string) map[string]struct{} { t.Helper() diff --git a/clerk/tokens_test.go b/clerk/tokens_test.go index d1005ddf..763529fa 100644 --- a/clerk/tokens_test.go +++ b/clerk/tokens_test.go @@ -152,6 +152,54 @@ func TestClient_VerifyToken_InvalidIssuer(t *testing.T) { } } +func TestClient_VerifyToken_IssuerSatelliteDomain(t *testing.T) { + c, _ := NewClient("token") + + token, pubKey := testGenerateTokenJWT(t, dummySessionClaims, "kid") + + client := c.(*client) + client.jwksCache.set(testBuildJWKS(t, pubKey, jose.RS256, "kid")) + + got, _ := c.VerifyToken(token, WithSatelliteDomain(true)) + if !reflect.DeepEqual(got, &dummySessionClaims) { + t.Errorf("Expected %+v, but got %+v", dummySessionClaims, got) + } +} + +func TestClient_VerifyToken_InvalidIssuerProxyURL(t *testing.T) { + c, _ := NewClient("token") + + claims := dummySessionClaims + claims.Issuer = "invalid" + + token, pubKey := testGenerateTokenJWT(t, claims, "kid") + + client := c.(*client) + client.jwksCache.set(testBuildJWKS(t, pubKey, jose.RS256, "kid")) + + _, err := c.VerifyToken(token, WithProxyURL("issuer")) + if err == nil { + t.Errorf("Expected error to be returned") + } +} + +func TestClient_VerifyToken_ValidIssuerProxyURL(t *testing.T) { + c, _ := NewClient("token") + + claims := dummySessionClaims + claims.Issuer = "issuer" + + token, pubKey := testGenerateTokenJWT(t, claims, "kid") + + client := c.(*client) + client.jwksCache.set(testBuildJWKS(t, pubKey, jose.RS256, "kid")) + + got, _ := c.VerifyToken(token, WithProxyURL("issuer")) + if !reflect.DeepEqual(got, &claims) { + t.Errorf("Expected %+v, but got %+v", claims, got) + } +} + func TestClient_VerifyToken_ExpiredToken(t *testing.T) { c, _ := NewClient("token")