diff --git a/CHANGELOG.md b/CHANGELOG.md index 4cb47ade0..d3fd7016f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,9 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - Fixes userIdMapping queries - Fixes issue with session creation for users with userIdMapping and accounts linked - Fixes active users tracking while linking accounts +- Adds a new required `useDynamicSigningKey` into the request body of `RefreshSessionAPI` + - This enables smooth switching between `useDynamicAccessTokenSigningKey` settings by allowing refresh calls to + change the signing key type of a session ## [8.0.1] - 2024-03-11 diff --git a/src/main/java/io/supertokens/inmemorydb/Start.java b/src/main/java/io/supertokens/inmemorydb/Start.java index 7d58c9c07..4ba263eff 100644 --- a/src/main/java/io/supertokens/inmemorydb/Start.java +++ b/src/main/java/io/supertokens/inmemorydb/Start.java @@ -520,11 +520,11 @@ public SessionInfo getSessionInfo_Transaction(TenantIdentifier tenantIdentifier, @Override public void updateSessionInfo_Transaction(TenantIdentifier tenantIdentifier, TransactionConnection con, String sessionHandle, String refreshTokenHash2, - long expiry) throws StorageQueryException { + long expiry, boolean useStaticKey) throws StorageQueryException { Connection sqlCon = (Connection) con.getConnection(); try { SessionQueries.updateSessionInfo_Transaction(this, sqlCon, tenantIdentifier, sessionHandle, - refreshTokenHash2, expiry); + refreshTokenHash2, expiry, useStaticKey); } catch (SQLException e) { throw new StorageQueryException(e); } diff --git a/src/main/java/io/supertokens/inmemorydb/queries/SessionQueries.java b/src/main/java/io/supertokens/inmemorydb/queries/SessionQueries.java index 65fa18c1a..dbb23bbac 100644 --- a/src/main/java/io/supertokens/inmemorydb/queries/SessionQueries.java +++ b/src/main/java/io/supertokens/inmemorydb/queries/SessionQueries.java @@ -147,18 +147,19 @@ public static SessionInfo getSessionInfo_Transaction(Start start, Connection con public static void updateSessionInfo_Transaction(Start start, Connection con, TenantIdentifier tenantIdentifier, String sessionHandle, - String refreshTokenHash2, long expiry) + String refreshTokenHash2, long expiry, boolean useStaticKey) throws SQLException, StorageQueryException { String QUERY = "UPDATE " + getConfig(start).getSessionInfoTable() - + " SET refresh_token_hash_2 = ?, expires_at = ?" + + " SET refresh_token_hash_2 = ?, expires_at = ?, use_static_key = ?" + " WHERE app_id = ? AND tenant_id = ? AND session_handle = ?"; update(con, QUERY, pst -> { pst.setString(1, refreshTokenHash2); pst.setLong(2, expiry); - pst.setString(3, tenantIdentifier.getAppId()); - pst.setString(4, tenantIdentifier.getTenantId()); - pst.setString(5, sessionHandle); + pst.setBoolean(3, useStaticKey); + pst.setString(4, tenantIdentifier.getAppId()); + pst.setString(5, tenantIdentifier.getTenantId()); + pst.setString(6, sessionHandle); }); } diff --git a/src/main/java/io/supertokens/session/Session.java b/src/main/java/io/supertokens/session/Session.java index 406feb9e6..a2d51d27c 100644 --- a/src/main/java/io/supertokens/session/Session.java +++ b/src/main/java/io/supertokens/session/Session.java @@ -398,7 +398,7 @@ public static SessionInformationHolder getSession(AppIdentifier appIdentifier, M accessToken.sessionHandle, Utils.hashSHA256(accessToken.refreshTokenHash1), System.currentTimeMillis() + - config.getRefreshTokenValidity()); + config.getRefreshTokenValidity(), sessionInfo.useStaticKey); } sessionStorage.commitTransaction(con); @@ -475,7 +475,7 @@ public static SessionInformationHolder getSession(AppIdentifier appIdentifier, M Utils.hashSHA256(accessToken.refreshTokenHash1), System.currentTimeMillis() + Config.getConfig(tenantIdentifier, main) .getRefreshTokenValidity(), - sessionInfo.lastUpdatedSign); + sessionInfo.lastUpdatedSign, sessionInfo.useStaticKey); if (!success) { continue; } @@ -530,7 +530,7 @@ public static SessionInformationHolder refreshSession(Main main, @Nonnull String UnsupportedJWTSigningAlgorithmException, AccessTokenPayloadError { try { return refreshSession(new AppIdentifier(null, null), main, refreshToken, antiCsrfToken, - enableAntiCsrf, accessTokenVersion); + enableAntiCsrf, accessTokenVersion, null); } catch (TenantOrAppNotFoundException e) { throw new IllegalStateException(e); } @@ -539,7 +539,8 @@ public static SessionInformationHolder refreshSession(Main main, @Nonnull String public static SessionInformationHolder refreshSession(AppIdentifier appIdentifier, Main main, @Nonnull String refreshToken, @Nullable String antiCsrfToken, boolean enableAntiCsrf, - AccessToken.VERSION accessTokenVersion) + AccessToken.VERSION accessTokenVersion, + Boolean shouldUseStaticKey) throws StorageTransactionLogicException, UnauthorisedException, StorageQueryException, TokenTheftDetectedException, UnsupportedJWTSigningAlgorithmException, AccessTokenPayloadError, TenantOrAppNotFoundException { @@ -556,14 +557,15 @@ public static SessionInformationHolder refreshSession(AppIdentifier appIdentifie TenantIdentifier tenantIdentifier = refreshTokenInfo.tenantIdentifier; Storage storage = StorageLayer.getStorage(refreshTokenInfo.tenantIdentifier, main); return refreshSessionHelper( - tenantIdentifier, storage, main, refreshToken, refreshTokenInfo, enableAntiCsrf, accessTokenVersion); + tenantIdentifier, storage, main, refreshToken, refreshTokenInfo, enableAntiCsrf, accessTokenVersion, shouldUseStaticKey); } private static SessionInformationHolder refreshSessionHelper( TenantIdentifier tenantIdentifier, Storage storage, Main main, String refreshToken, RefreshToken.RefreshTokenInfo refreshTokenInfo, boolean enableAntiCsrf, - AccessToken.VERSION accessTokenVersion) + AccessToken.VERSION accessTokenVersion, + Boolean shouldUseStaticKey) throws StorageTransactionLogicException, UnauthorisedException, StorageQueryException, TokenTheftDetectedException, UnsupportedJWTSigningAlgorithmException, AccessTokenPayloadError, TenantOrAppNotFoundException { @@ -588,7 +590,16 @@ private static SessionInformationHolder refreshSessionHelper( throw new UnauthorisedException("Session missing in db or has expired"); } + boolean useStaticKey = shouldUseStaticKey != null ? shouldUseStaticKey : sessionInfo.useStaticKey; + if (sessionInfo.refreshTokenHash2.equals(Utils.hashSHA256(Utils.hashSHA256(refreshToken)))) { + if (useStaticKey != sessionInfo.useStaticKey) { + // We do not update anything except the static key status + sessionStorage.updateSessionInfo_Transaction(tenantIdentifier, con, sessionHandle, + sessionInfo.refreshTokenHash2, sessionInfo.expiry, + useStaticKey); + } + // at this point, the input refresh token is the parent one. sessionStorage.commitTransaction(con); @@ -622,13 +633,13 @@ private static SessionInformationHolder refreshSessionHelper( .equals(sessionInfo.refreshTokenHash2))) { sessionStorage.updateSessionInfo_Transaction(tenantIdentifier, con, sessionHandle, Utils.hashSHA256(Utils.hashSHA256(refreshToken)), - System.currentTimeMillis() + config.getRefreshTokenValidity()); + System.currentTimeMillis() + config.getRefreshTokenValidity(), useStaticKey); sessionStorage.commitTransaction(con); return refreshSessionHelper(tenantIdentifier, storage, main, refreshToken, refreshTokenInfo, enableAntiCsrf, - accessTokenVersion); + accessTokenVersion, useStaticKey); } sessionStorage.commitTransaction(con); @@ -677,7 +688,18 @@ private static SessionInformationHolder refreshSessionHelper( throw new UnauthorisedException("Session missing in db or has expired"); } + boolean useStaticKey = shouldUseStaticKey != null ? shouldUseStaticKey : sessionInfo.useStaticKey; + if (sessionInfo.refreshTokenHash2.equals(Utils.hashSHA256(Utils.hashSHA256(refreshToken)))) { + if (sessionInfo.useStaticKey != useStaticKey) { + // We do not update anything except the static key status + boolean success = sessionStorage.updateSessionInfo_Transaction(sessionHandle, + sessionInfo.refreshTokenHash2, sessionInfo.expiry, + sessionInfo.lastUpdatedSign, useStaticKey); + if (!success) { + continue; + } + } // at this point, the input refresh token is the parent one. String antiCsrfToken = enableAntiCsrf ? UUID.randomUUID().toString() : null; @@ -710,13 +732,13 @@ private static SessionInformationHolder refreshSessionHelper( Utils.hashSHA256(Utils.hashSHA256(refreshToken)), System.currentTimeMillis() + Config.getConfig(tenantIdentifier, main).getRefreshTokenValidity(), - sessionInfo.lastUpdatedSign); + sessionInfo.lastUpdatedSign, useStaticKey); if (!success) { continue; } return refreshSessionHelper(tenantIdentifier, storage, main, refreshToken, refreshTokenInfo, enableAntiCsrf, - accessTokenVersion); + accessTokenVersion, shouldUseStaticKey); } throw new TokenTheftDetectedException(sessionHandle, sessionInfo.recipeUserId, sessionInfo.userId); diff --git a/src/main/java/io/supertokens/webserver/api/session/RefreshSessionAPI.java b/src/main/java/io/supertokens/webserver/api/session/RefreshSessionAPI.java index dca193196..25a8e9ed4 100644 --- a/src/main/java/io/supertokens/webserver/api/session/RefreshSessionAPI.java +++ b/src/main/java/io/supertokens/webserver/api/session/RefreshSessionAPI.java @@ -63,15 +63,17 @@ public String getPath() { @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException { // API is app specific, but session is updated based on tenantId obtained from the refreshToken + SemVer version = super.getVersionFromRequest(req); + JsonObject input = InputParser.parseJsonObjectOrThrowError(req); String refreshToken = InputParser.parseStringOrThrowError(input, "refreshToken", false); String antiCsrfToken = InputParser.parseStringOrThrowError(input, "antiCsrfToken", true); Boolean enableAntiCsrf = InputParser.parseBooleanOrThrowError(input, "enableAntiCsrf", false); + Boolean useDynamicSigningKey = version.greaterThanOrEqualTo(SemVer.v3_0) ? + InputParser.parseBooleanOrThrowError(input, "useDynamicSigningKey", true) : null; assert enableAntiCsrf != null; assert refreshToken != null; - - SemVer version = super.getVersionFromRequest(req); TenantIdentifier tenantIdentifierForLogging = null; try { tenantIdentifierForLogging = getTenantIdentifier(req); @@ -85,7 +87,8 @@ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws I SessionInformationHolder sessionInfo = Session.refreshSession(appIdentifier, main, refreshToken, antiCsrfToken, - enableAntiCsrf, accessTokenVersion); + enableAntiCsrf, accessTokenVersion, + useDynamicSigningKey == null ? null : Boolean.FALSE.equals(useDynamicSigningKey)); TenantIdentifier tenantIdentifier = new TenantIdentifier(appIdentifier.getConnectionUriDomain(), appIdentifier.getAppId(), sessionInfo.session.tenantId); Storage storage = StorageLayer.getStorage(tenantIdentifier, main);