Skip to content

Commit 74a8b3b

Browse files
feat: use dynamic signing key switching
1 parent 0eff984 commit 74a8b3b

File tree

4 files changed

+91
-4
lines changed

4 files changed

+91
-4
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
88
## [unreleased]
99

1010
- `session.CreateNewSession` now defaults to the value of the `st-auth-mode` header (if available) if the configured `config.GetTokenTransferMethod` returns `any`.
11+
- Enable smooth switching between `useDynamicAccessTokenSigningKey` settings by allowing refresh calls to change the signing key type of a session.
1112

1213
## [0.17.5] - 2024-03-14
1314
- Adds a type uint64 to the `accessTokenCookiesExpiryDurationMillis` local variable in `recipe/session/utils.go`. It also removes the redundant `uint64` type forcing needed because of the untyped variable.

recipe/session/recipeImplementation.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,7 @@ func MakeRecipeImplementation(querier supertokens.Querier, config sessmodels.Typ
294294

295295
supertokens.LogDebugMessage("refreshSession: Started")
296296

297-
response, err := refreshSessionHelper(config, querier, refreshToken, antiCsrfToken, disableAntiCsrf, userContext)
297+
response, err := refreshSessionHelper(config, querier, refreshToken, antiCsrfToken, disableAntiCsrf, config.UseDynamicAccessTokenSigningKey, userContext)
298298
if err != nil {
299299
return nil, err
300300
}

recipe/session/sessionFunctions.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -224,10 +224,11 @@ func getSessionInformationHelper(querier supertokens.Querier, sessionHandle stri
224224
return nil, nil
225225
}
226226

227-
func refreshSessionHelper(config sessmodels.TypeNormalisedInput, querier supertokens.Querier, refreshToken string, antiCsrfToken *string, disableAntiCsrf bool, userContext supertokens.UserContext) (sessmodels.CreateOrRefreshAPIResponse, error) {
227+
func refreshSessionHelper(config sessmodels.TypeNormalisedInput, querier supertokens.Querier, refreshToken string, antiCsrfToken *string, disableAntiCsrf bool, useDynamicAccessTokenSigningKey bool, userContext supertokens.UserContext) (sessmodels.CreateOrRefreshAPIResponse, error) {
228228
requestBody := map[string]interface{}{
229-
"refreshToken": refreshToken,
230-
"enableAntiCsrf": !disableAntiCsrf && config.AntiCsrfFunctionOrString.StrValue == AntiCSRF_VIA_TOKEN,
229+
"refreshToken": refreshToken,
230+
"enableAntiCsrf": !disableAntiCsrf && config.AntiCsrfFunctionOrString.StrValue == AntiCSRF_VIA_TOKEN,
231+
"useDynamicSigningKey": useDynamicAccessTokenSigningKey,
231232
}
232233
if antiCsrfToken != nil {
233234
requestBody["antiCsrfToken"] = *antiCsrfToken

recipe/session/sessionHandlingFuncsWithoutReq_test.go

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package session
22

33
import (
44
"errors"
5+
"strings"
56
"testing"
67

78
"github.com/stretchr/testify/assert"
@@ -472,3 +473,87 @@ func TestRefreshShouldReturnErrorForNonTokens(t *testing.T) {
472473
assert.NotNil(t, err2)
473474
assert.True(t, errors.As(err2, &sessionError.UnauthorizedError{}))
474475
}
476+
477+
func TestUseDynamicAccessTokenSigningKey(t *testing.T) {
478+
useDynamicAccessTokenSigningKey := true
479+
configValue := supertokens.TypeInput{
480+
Supertokens: &supertokens.ConnectionInfo{
481+
ConnectionURI: "http://localhost:8080",
482+
},
483+
AppInfo: supertokens.AppInfo{
484+
AppName: "SuperTokens",
485+
WebsiteDomain: "supertokens.io",
486+
APIDomain: "api.supertokens.io",
487+
},
488+
RecipeList: []supertokens.Recipe{
489+
Init(&sessmodels.TypeInput{
490+
UseDynamicAccessTokenSigningKey: &useDynamicAccessTokenSigningKey,
491+
}),
492+
},
493+
}
494+
BeforeEach()
495+
unittesting.StartUpST("localhost", "8080")
496+
defer AfterEach()
497+
498+
checkAccessTokenSigningKeyType := func(t *testing.T, tokens sessmodels.SessionTokens, isDynamic bool) {
499+
t.Helper()
500+
501+
info, err := ParseJWTWithoutSignatureVerification(tokens.AccessToken)
502+
assert.NoError(t, err)
503+
504+
if isDynamic {
505+
assert.True(t, strings.HasPrefix(*info.KID, "d-"))
506+
} else {
507+
assert.True(t, strings.HasPrefix(*info.KID, "s-"))
508+
}
509+
}
510+
511+
err := supertokens.Init(configValue)
512+
assert.NoError(t, err)
513+
514+
res, err := CreateNewSessionWithoutRequestResponse("public", "test-user-id", map[string]interface{}{
515+
"tokenProp": true,
516+
}, map[string]interface{}{
517+
"dbProp": true,
518+
}, nil)
519+
520+
assert.NoError(t, err)
521+
522+
tokens := res.GetAllSessionTokensDangerously()
523+
checkAccessTokenSigningKeyType(t, tokens, true)
524+
525+
resetAll()
526+
527+
// here we change to false
528+
useDynamicAccessTokenSigningKey = false
529+
err = supertokens.Init(configValue)
530+
assert.NoError(t, err)
531+
532+
t.Run("should throw when verifying", func(t *testing.T) {
533+
_, err = GetSessionWithoutRequestResponse(tokens.AccessToken, tokens.AntiCsrfToken, nil)
534+
assert.Equal(t, err.Error(), "The access token doesn't match the useDynamicAccessTokenSigningKey setting")
535+
})
536+
537+
t.Run("should work after refresh", func(t *testing.T) {
538+
disableAntiCsrf := true
539+
refreshedSession, err := RefreshSessionWithoutRequestResponse(*tokens.RefreshToken, &disableAntiCsrf, tokens.AntiCsrfToken)
540+
assert.NoError(t, err)
541+
542+
tokensAfterRefresh := refreshedSession.GetAllSessionTokensDangerously()
543+
assert.True(t, tokensAfterRefresh.AccessAndFrontendTokenUpdated)
544+
checkAccessTokenSigningKeyType(t, tokensAfterRefresh, false)
545+
546+
verifiedSession, err := GetSessionWithoutRequestResponse(tokensAfterRefresh.AccessToken, tokensAfterRefresh.AntiCsrfToken, nil)
547+
assert.NoError(t, err)
548+
549+
tokensAfterVerify := verifiedSession.GetAllSessionTokensDangerously()
550+
assert.True(t, tokensAfterVerify.AccessAndFrontendTokenUpdated)
551+
checkAccessTokenSigningKeyType(t, tokensAfterVerify, false)
552+
553+
verifiedSession2, err := GetSessionWithoutRequestResponse(tokensAfterVerify.AccessToken, tokensAfterVerify.AntiCsrfToken, nil)
554+
assert.NoError(t, err)
555+
556+
tokensAfterVerify2 := verifiedSession2.GetAllSessionTokensDangerously()
557+
assert.False(t, tokensAfterVerify2.AccessAndFrontendTokenUpdated)
558+
})
559+
}

0 commit comments

Comments
 (0)