Skip to content

Commit 08079bc

Browse files
authored
fix: backport to core 6.0 (#972)
1 parent e1b37b0 commit 08079bc

15 files changed

+561
-34
lines changed

CHANGELOG.md

+7
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,13 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

88
## [unreleased]
99

10+
## [6.0.19] - 2024-03-29
11+
12+
- Fixes userIdMapping queries
13+
- Adds a new required `useDynamicSigningKey` into the request body of `RefreshSessionAPI`
14+
- This enables smooth switching between `useDynamicAccessTokenSigningKey` settings by allowing refresh calls to
15+
change the signing key type of a session
16+
1017
## [6.0.18] - 2024-02-20
1118

1219
- Fixes vulnerabilities in dependencies

build.gradle

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ compileTestJava { options.encoding = "UTF-8" }
1919
// }
2020
//}
2121

22-
version = "6.0.18"
22+
version = "6.0.19"
2323

2424

2525
repositories {

src/main/java/io/supertokens/inmemorydb/Start.java

+6-4
Original file line numberDiff line numberDiff line change
@@ -511,10 +511,11 @@ public SessionInfo getSessionInfo_Transaction(TenantIdentifier tenantIdentifier,
511511
@Override
512512
public void updateSessionInfo_Transaction(TenantIdentifier tenantIdentifier, TransactionConnection con,
513513
String sessionHandle, String refreshTokenHash2,
514-
long expiry) throws StorageQueryException {
514+
long expiry, boolean useStaticKey) throws StorageQueryException {
515515
Connection sqlCon = (Connection) con.getConnection();
516516
try {
517-
SessionQueries.updateSessionInfo_Transaction(this, sqlCon, tenantIdentifier, sessionHandle, refreshTokenHash2, expiry);
517+
SessionQueries.updateSessionInfo_Transaction(this, sqlCon, tenantIdentifier, sessionHandle,
518+
refreshTokenHash2, expiry, useStaticKey);
518519
} catch (SQLException e) {
519520
throw new StorageQueryException(e);
520521
}
@@ -2154,10 +2155,11 @@ public boolean updateOrDeleteExternalUserIdInfo(AppIdentifier appIdentifier, Str
21542155
}
21552156

21562157
@Override
2157-
public HashMap<String, String> getUserIdMappingForSuperTokensIds(ArrayList<String> userIds)
2158+
public HashMap<String, String> getUserIdMappingForSuperTokensIds(AppIdentifier appIdentifier,
2159+
ArrayList<String> userIds)
21582160
throws StorageQueryException {
21592161
try {
2160-
return UserIdMappingQueries.getUserIdMappingWithUserIds(this, userIds);
2162+
return UserIdMappingQueries.getUserIdMappingWithUserIds(this, appIdentifier, userIds);
21612163
} catch (SQLException e) {
21622164
throw new StorageQueryException(e);
21632165
}

src/main/java/io/supertokens/inmemorydb/queries/SessionQueries.java

+6-5
Original file line numberDiff line numberDiff line change
@@ -124,18 +124,19 @@ public static SessionInfo getSessionInfo_Transaction(Start start, Connection con
124124

125125
public static void updateSessionInfo_Transaction(Start start, Connection con, TenantIdentifier tenantIdentifier,
126126
String sessionHandle,
127-
String refreshTokenHash2, long expiry)
127+
String refreshTokenHash2, long expiry, boolean useStaticKey)
128128
throws SQLException, StorageQueryException {
129129
String QUERY = "UPDATE " + getConfig(start).getSessionInfoTable()
130-
+ " SET refresh_token_hash_2 = ?, expires_at = ?"
130+
+ " SET refresh_token_hash_2 = ?, expires_at = ?, use_static_key = ?"
131131
+ " WHERE app_id = ? AND tenant_id = ? AND session_handle = ?";
132132

133133
update(con, QUERY, pst -> {
134134
pst.setString(1, refreshTokenHash2);
135135
pst.setLong(2, expiry);
136-
pst.setString(3, tenantIdentifier.getAppId());
137-
pst.setString(4, tenantIdentifier.getTenantId());
138-
pst.setString(5, sessionHandle);
136+
pst.setBoolean(3, useStaticKey);
137+
pst.setString(4, tenantIdentifier.getAppId());
138+
pst.setString(5, tenantIdentifier.getTenantId());
139+
pst.setString(6, sessionHandle);
139140
});
140141
}
141142

src/main/java/io/supertokens/inmemorydb/queries/UserIdMappingQueries.java

+8-4
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,9 @@ public static UserIdMapping[] getUserIdMappingWithEitherSuperTokensUserIdOrExter
115115

116116
}
117117

118-
public static HashMap<String, String> getUserIdMappingWithUserIds(Start start, ArrayList<String> userIds)
118+
public static HashMap<String, String> getUserIdMappingWithUserIds(Start start,
119+
AppIdentifier appIdentifier,
120+
ArrayList<String> userIds)
119121
throws SQLException, StorageQueryException {
120122

121123
if (userIds.size() == 0) {
@@ -124,7 +126,8 @@ public static HashMap<String, String> getUserIdMappingWithUserIds(Start start, A
124126

125127
// No need to filter based on tenantId because the id list is already filtered for a tenant
126128
StringBuilder QUERY = new StringBuilder(
127-
"SELECT * FROM " + Config.getConfig(start).getUserIdMappingTable() + " WHERE supertokens_user_id IN (");
129+
"SELECT * FROM " + Config.getConfig(start).getUserIdMappingTable() + " WHERE app_id = ? AND " +
130+
"supertokens_user_id IN (");
128131
for (int i = 0; i < userIds.size(); i++) {
129132
QUERY.append("?");
130133
if (i != userIds.size() - 1) {
@@ -134,9 +137,10 @@ public static HashMap<String, String> getUserIdMappingWithUserIds(Start start, A
134137
}
135138
QUERY.append(")");
136139
return execute(start, QUERY.toString(), pst -> {
140+
pst.setString(1, appIdentifier.getAppId());
137141
for (int i = 0; i < userIds.size(); i++) {
138-
// i+1 cause this starts with 1 and not 0
139-
pst.setString(i + 1, userIds.get(i));
142+
// i+2 cause this starts with 1 and not 0, and 1 is the app_id
143+
pst.setString(i + 2, userIds.get(i));
140144
}
141145
}, result -> {
142146
HashMap<String, String> userIdMappings = new HashMap<>();

src/main/java/io/supertokens/session/Session.java

+35-12
Original file line numberDiff line numberDiff line change
@@ -351,7 +351,7 @@ public static SessionInformationHolder getSession(AppIdentifier appIdentifier, M
351351
accessToken.sessionHandle,
352352
Utils.hashSHA256(accessToken.refreshTokenHash1),
353353
System.currentTimeMillis() +
354-
config.getRefreshTokenValidity());
354+
config.getRefreshTokenValidity(), sessionInfo.useStaticKey);
355355
}
356356
storage.commitTransaction(con);
357357

@@ -423,7 +423,7 @@ public static SessionInformationHolder getSession(AppIdentifier appIdentifier, M
423423
Utils.hashSHA256(accessToken.refreshTokenHash1),
424424
System.currentTimeMillis() + Config.getConfig(tenantIdentifierWithStorage, main)
425425
.getRefreshTokenValidity(),
426-
sessionInfo.lastUpdatedSign);
426+
sessionInfo.lastUpdatedSign, sessionInfo.useStaticKey);
427427
if (!success) {
428428
continue;
429429
}
@@ -473,7 +473,7 @@ public static SessionInformationHolder refreshSession(Main main, @Nonnull String
473473
UnsupportedJWTSigningAlgorithmException, AccessTokenPayloadError {
474474
try {
475475
return refreshSession(new AppIdentifier(null, null), main, refreshToken, antiCsrfToken,
476-
enableAntiCsrf, accessTokenVersion);
476+
enableAntiCsrf, accessTokenVersion, null);
477477
} catch (TenantOrAppNotFoundException e) {
478478
throw new IllegalStateException(e);
479479
}
@@ -482,7 +482,8 @@ public static SessionInformationHolder refreshSession(Main main, @Nonnull String
482482
public static SessionInformationHolder refreshSession(AppIdentifier appIdentifier, Main main,
483483
@Nonnull String refreshToken,
484484
@Nullable String antiCsrfToken, boolean enableAntiCsrf,
485-
AccessToken.VERSION accessTokenVersion)
485+
AccessToken.VERSION accessTokenVersion,
486+
Boolean shouldUseStaticKey)
486487
throws StorageTransactionLogicException,
487488
UnauthorisedException, StorageQueryException, TokenTheftDetectedException,
488489
UnsupportedJWTSigningAlgorithmException, AccessTokenPayloadError, TenantOrAppNotFoundException {
@@ -498,14 +499,15 @@ public static SessionInformationHolder refreshSession(AppIdentifier appIdentifie
498499

499500
return refreshSessionHelper(refreshTokenInfo.tenantIdentifier.withStorage(
500501
StorageLayer.getStorage(refreshTokenInfo.tenantIdentifier, main)),
501-
main, refreshToken, refreshTokenInfo, enableAntiCsrf, accessTokenVersion);
502+
main, refreshToken, refreshTokenInfo, enableAntiCsrf, accessTokenVersion, shouldUseStaticKey);
502503
}
503504

504505
private static SessionInformationHolder refreshSessionHelper(
505506
TenantIdentifierWithStorage tenantIdentifierWithStorage, Main main, String refreshToken,
506507
RefreshToken.RefreshTokenInfo refreshTokenInfo,
507508
boolean enableAntiCsrf,
508-
AccessToken.VERSION accessTokenVersion)
509+
AccessToken.VERSION accessTokenVersion,
510+
Boolean shouldUseStaticKey)
509511
throws StorageTransactionLogicException, UnauthorisedException, StorageQueryException,
510512
TokenTheftDetectedException, UnsupportedJWTSigningAlgorithmException, AccessTokenPayloadError,
511513
TenantOrAppNotFoundException {
@@ -530,7 +532,16 @@ private static SessionInformationHolder refreshSessionHelper(
530532
throw new UnauthorisedException("Session missing in db or has expired");
531533
}
532534

535+
boolean useStaticKey = shouldUseStaticKey != null ? shouldUseStaticKey : sessionInfo.useStaticKey;
536+
533537
if (sessionInfo.refreshTokenHash2.equals(Utils.hashSHA256(Utils.hashSHA256(refreshToken)))) {
538+
if (useStaticKey != sessionInfo.useStaticKey) {
539+
// We do not update anything except the static key status
540+
storage.updateSessionInfo_Transaction(tenantIdentifierWithStorage, con, sessionHandle,
541+
sessionInfo.refreshTokenHash2, sessionInfo.expiry,
542+
useStaticKey);
543+
}
544+
534545
// at this point, the input refresh token is the parent one.
535546
storage.commitTransaction(con);
536547
String antiCsrfToken = enableAntiCsrf ? UUID.randomUUID().toString() : null;
@@ -542,7 +553,7 @@ private static SessionInformationHolder refreshSessionHelper(
542553
main, sessionHandle,
543554
sessionInfo.userId, Utils.hashSHA256(newRefreshToken.token),
544555
Utils.hashSHA256(refreshToken), sessionInfo.userDataInJWT, antiCsrfToken,
545-
null, accessTokenVersion, sessionInfo.useStaticKey);
556+
null, accessTokenVersion, useStaticKey);
546557

547558
TokenInfo idRefreshToken = new TokenInfo(UUID.randomUUID().toString(),
548559
newRefreshToken.expiry, newRefreshToken.createdTime);
@@ -560,13 +571,13 @@ private static SessionInformationHolder refreshSessionHelper(
560571
.equals(sessionInfo.refreshTokenHash2))) {
561572
storage.updateSessionInfo_Transaction(tenantIdentifierWithStorage, con, sessionHandle,
562573
Utils.hashSHA256(Utils.hashSHA256(refreshToken)),
563-
System.currentTimeMillis() + config.getRefreshTokenValidity());
574+
System.currentTimeMillis() + config.getRefreshTokenValidity(), useStaticKey);
564575

565576
storage.commitTransaction(con);
566577

567578
return refreshSessionHelper(tenantIdentifierWithStorage, main, refreshToken,
568579
refreshTokenInfo, enableAntiCsrf,
569-
accessTokenVersion);
580+
accessTokenVersion, shouldUseStaticKey);
570581
}
571582

572583
storage.commitTransaction(con);
@@ -613,7 +624,19 @@ private static SessionInformationHolder refreshSessionHelper(
613624
throw new UnauthorisedException("Session missing in db or has expired");
614625
}
615626

627+
boolean useStaticKey = shouldUseStaticKey != null ? shouldUseStaticKey : sessionInfo.useStaticKey;
628+
616629
if (sessionInfo.refreshTokenHash2.equals(Utils.hashSHA256(Utils.hashSHA256(refreshToken)))) {
630+
if (sessionInfo.useStaticKey != useStaticKey) {
631+
// We do not update anything except the static key status
632+
boolean success = storage.updateSessionInfo_Transaction(sessionHandle,
633+
sessionInfo.refreshTokenHash2, sessionInfo.expiry,
634+
sessionInfo.lastUpdatedSign, useStaticKey);
635+
if (!success) {
636+
continue;
637+
}
638+
}
639+
617640
// at this point, the input refresh token is the parent one.
618641
String antiCsrfToken = enableAntiCsrf ? UUID.randomUUID().toString() : null;
619642

@@ -624,7 +647,7 @@ private static SessionInformationHolder refreshSessionHelper(
624647
sessionHandle,
625648
sessionInfo.userId, Utils.hashSHA256(newRefreshToken.token),
626649
Utils.hashSHA256(refreshToken), sessionInfo.userDataInJWT, antiCsrfToken,
627-
null, accessTokenVersion, sessionInfo.useStaticKey);
650+
null, accessTokenVersion, useStaticKey);
628651

629652
TokenInfo idRefreshToken = new TokenInfo(UUID.randomUUID().toString(), newRefreshToken.expiry,
630653
newRefreshToken.createdTime);
@@ -644,13 +667,13 @@ private static SessionInformationHolder refreshSessionHelper(
644667
Utils.hashSHA256(Utils.hashSHA256(refreshToken)),
645668
System.currentTimeMillis() +
646669
Config.getConfig(tenantIdentifierWithStorage, main).getRefreshTokenValidity(),
647-
sessionInfo.lastUpdatedSign);
670+
sessionInfo.lastUpdatedSign, useStaticKey);
648671
if (!success) {
649672
continue;
650673
}
651674
return refreshSessionHelper(tenantIdentifierWithStorage, main, refreshToken, refreshTokenInfo,
652675
enableAntiCsrf,
653-
accessTokenVersion);
676+
accessTokenVersion, shouldUseStaticKey);
654677
}
655678

656679
throw new TokenTheftDetectedException(sessionHandle, sessionInfo.userId);

src/main/java/io/supertokens/useridmapping/UserIdMapping.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,8 @@ public static HashMap<String, String> getUserIdMappingForSuperTokensUserIds(
258258
ArrayList<String> userIds)
259259
throws StorageQueryException {
260260
// userIds are already filtered for a tenant, so this becomes a tenant specific operation.
261-
return tenantIdentifierWithStorage.getUserIdMappingStorage().getUserIdMappingForSuperTokensIds(userIds);
261+
return tenantIdentifierWithStorage.getUserIdMappingStorage().getUserIdMappingForSuperTokensIds(
262+
tenantIdentifierWithStorage.toAppIdentifier(), userIds);
262263
}
263264

264265
@TestOnly

src/main/java/io/supertokens/webserver/api/session/RefreshSessionAPI.java

+5-2
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,13 @@ public String getPath() {
6161
@Override
6262
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException {
6363
// API is app specific, but session is updated based on tenantId obtained from the refreshToken
64+
SemVer version = super.getVersionFromRequest(req);
6465
JsonObject input = InputParser.parseJsonObjectOrThrowError(req);
6566
String refreshToken = InputParser.parseStringOrThrowError(input, "refreshToken", false);
6667
String antiCsrfToken = InputParser.parseStringOrThrowError(input, "antiCsrfToken", true);
6768
Boolean enableAntiCsrf = InputParser.parseBooleanOrThrowError(input, "enableAntiCsrf", false);
69+
Boolean useDynamicSigningKey = version.greaterThanOrEqualTo(SemVer.v3_0) ?
70+
InputParser.parseBooleanOrThrowError(input, "useDynamicSigningKey", true) : null;
6871
assert enableAntiCsrf != null;
6972
assert refreshToken != null;
7073

@@ -75,13 +78,13 @@ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws I
7578
throw new ServletException(e);
7679
}
7780

78-
SemVer version = super.getVersionFromRequest(req);
7981
try {
8082
AccessToken.VERSION accessTokenVersion = AccessToken.getAccessTokenVersionForCDI(version);
8183

8284
SessionInformationHolder sessionInfo = Session.refreshSession(appIdentifierWithStorage, main,
8385
refreshToken, antiCsrfToken,
84-
enableAntiCsrf, accessTokenVersion);
86+
enableAntiCsrf, accessTokenVersion,
87+
useDynamicSigningKey == null ? null : Boolean.FALSE.equals(useDynamicSigningKey));
8588

8689
if (StorageLayer.getStorage(this.getTenantIdentifierWithStorageFromRequest(req), main).getType() ==
8790
STORAGE_TYPE.SQL) {

src/test/java/io/supertokens/test/AuthRecipeTest.java

+6
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import io.supertokens.storageLayer.StorageLayer;
3434
import io.supertokens.thirdparty.ThirdParty;
3535
import io.supertokens.usermetadata.UserMetadata;
36+
import io.supertokens.version.Version;
3637
import org.junit.AfterClass;
3738
import org.junit.Before;
3839
import org.junit.Rule;
@@ -488,6 +489,8 @@ public void randomPaginationTest() throws Exception {
488489
fail();
489490
}
490491

492+
boolean isMySQL = Version.getVersion(process.getProcess()).getPluginName().equals("mysql");
493+
491494
for (int limit : limits) {
492495

493496
// now we paginate in asc order
@@ -497,6 +500,9 @@ public void randomPaginationTest() throws Exception {
497500
if (o1.timeJoined != o2.timeJoined) {
498501
return (int) (o1.timeJoined - o2.timeJoined);
499502
}
503+
if (isMySQL) {
504+
return o1.id.compareTo(o2.id);
505+
}
500506
return o2.id.compareTo(o1.id);
501507
});
502508

0 commit comments

Comments
 (0)