Skip to content

Commit a90148d

Browse files
authored
fix: getUserByAccountInfo fix to consider tenantId instead of appId (#1153)
1 parent 6871192 commit a90148d

File tree

6 files changed

+153
-14
lines changed

6 files changed

+153
-14
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

88
## [Unreleased]
99

10+
## [11.0.2]
11+
12+
- Fixes `AuthRecipe#getUserByAccountInfo` to consider the tenantId instead of the appId when fetching the webauthn user
13+
1014
## [11.0.1]
1115

1216
- Upgrades the embedded tomcat 11.0.6 and logback classic to 1.5.13 because of security vulnerabilities

build.gradle

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
plugins {
1010
id 'application'
11+
id 'java-library'
1112
}
1213
compileJava { options.encoding = "UTF-8" }
1314
compileTestJava { options.encoding = "UTF-8" }
@@ -19,7 +20,7 @@ compileTestJava { options.encoding = "UTF-8" }
1920
// }
2021
//}
2122

22-
version = "11.0.1"
23+
version = "11.0.2"
2324

2425
repositories {
2526
mavenCentral()
@@ -45,7 +46,7 @@ dependencies {
4546
implementation group: 'ch.qos.logback', name: 'logback-classic', version: '1.5.13'
4647

4748
// https://mvnrepository.com/artifact/org.apache.tomcat.embed/tomcat-embed-core
48-
implementation group: 'org.apache.tomcat.embed', name: 'tomcat-embed-core', version: '11.0.6'
49+
api group: 'org.apache.tomcat.embed', name: 'tomcat-embed-core', version: '11.0.6'
4950

5051
// https://mvnrepository.com/artifact/com.google.code.findbugs/jsr305
5152
implementation group: 'com.google.code.findbugs', name: 'jsr305', version: '3.0.2'

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1275,7 +1275,8 @@ public static AuthRecipeUserInfo[] listPrimaryUsersByEmail_Transaction(Start sta
12751275

12761276
userIds.addAll(ThirdPartyQueries.getPrimaryUserIdUsingEmail_Transaction(start, sqlCon, appIdentifier, email));
12771277

1278-
String webauthnUserId = WebAuthNQueries.getPrimaryUserIdUsingEmail_Transaction(start, sqlCon, appIdentifier, email);
1278+
String webauthnUserId = WebAuthNQueries.getPrimaryUserIdForAppUsingEmail_Transaction(start, sqlCon,
1279+
appIdentifier, email);
12791280
if(webauthnUserId != null) {
12801281
userIds.add(webauthnUserId);
12811282
}
@@ -1312,7 +1313,7 @@ public static AuthRecipeUserInfo[] listPrimaryUsersByEmail(Start start, TenantId
13121313

13131314
userIds.addAll(ThirdPartyQueries.getPrimaryUserIdUsingEmail(start, tenantIdentifier, email));
13141315

1315-
String webauthnUserId = WebAuthNQueries.getPrimaryUserIdUsingEmail(start, tenantIdentifier.toAppIdentifier(), email);
1316+
String webauthnUserId = WebAuthNQueries.getPrimaryUserIdForTenantUsingEmail(start, tenantIdentifier, email);
13161317
if(webauthnUserId != null) {
13171318
userIds.add(webauthnUserId);
13181319
}

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

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -376,13 +376,15 @@ private static AuthRecipeUserInfo getAuthRecipeUserInfo(Start start, Connection
376376
return userInfo;
377377
}
378378

379-
public static String getPrimaryUserIdUsingEmail(Start start, AppIdentifier appIdentifier, String email)
379+
public static String getPrimaryUserIdForTenantUsingEmail(Start start, TenantIdentifier tenantIdentifier,
380+
String email)
380381
throws StorageQueryException {
381382
try {
382383
return start.startTransaction(con -> {
383384
try {
384385
Connection sqlConnection = (Connection) con.getConnection();
385-
return getPrimaryUserIdUsingEmail_Transaction(start, sqlConnection, appIdentifier, email);
386+
return getPrimaryUserIdForTenantUsingEmail_Transaction(start, sqlConnection, tenantIdentifier,
387+
email);
386388
} catch (SQLException e) {
387389
throw new StorageQueryException(e);
388390
}
@@ -392,7 +394,30 @@ public static String getPrimaryUserIdUsingEmail(Start start, AppIdentifier appId
392394
}
393395
}
394396

395-
public static String getPrimaryUserIdUsingEmail_Transaction(Start start, Connection sqlConnection, AppIdentifier appIdentifier, String email)
397+
public static String getPrimaryUserIdForTenantUsingEmail_Transaction(Start start, Connection sqlConnection,
398+
TenantIdentifier tenantIdentifier,
399+
String email)
400+
throws SQLException, StorageQueryException {
401+
String QUERY = "SELECT DISTINCT all_users.primary_or_recipe_user_id AS user_id "
402+
+ "FROM " + getConfig(start).getWebAuthNUserToTenantTable() + " AS ep" +
403+
" JOIN " + getConfig(start).getUsersTable() + " AS all_users" +
404+
" ON ep.app_id = all_users.app_id AND ep.user_id = all_users.user_id" +
405+
" WHERE ep.app_id = ? AND ep.email = ? AND ep.tenant_id = ?";
406+
407+
return execute(sqlConnection, QUERY, pst -> {
408+
pst.setString(1, tenantIdentifier.getAppId());
409+
pst.setString(2, email);
410+
pst.setString(3, tenantIdentifier.getTenantId());
411+
}, result -> {
412+
if (result.next()) {
413+
return result.getString("user_id");
414+
}
415+
return null;
416+
});
417+
}
418+
419+
public static String getPrimaryUserIdForAppUsingEmail_Transaction(Start start, Connection sqlConnection,
420+
AppIdentifier appIdentifier, String email)
396421
throws SQLException, StorageQueryException {
397422
String QUERY = "SELECT DISTINCT all_users.primary_or_recipe_user_id AS user_id "
398423
+ "FROM " + getConfig(start).getWebAuthNUserToTenantTable() + " AS ep" +
@@ -405,7 +430,7 @@ public static String getPrimaryUserIdUsingEmail_Transaction(Start start, Connect
405430
pst.setString(2, email);
406431
}, result -> {
407432
if (result.next()) {
408-
return result.getString("user_id");
433+
return result.getString("user_id");
409434
}
410435
return null;
411436
});

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

Lines changed: 114 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,41 @@
1717
package io.supertokens.test;
1818

1919
import com.google.gson.JsonObject;
20+
import io.supertokens.Main;
2021
import io.supertokens.ProcessState;
22+
import io.supertokens.ResourceDistributor;
2123
import io.supertokens.authRecipe.AuthRecipe;
2224
import io.supertokens.authRecipe.UserPaginationContainer;
25+
import io.supertokens.authRecipe.exception.AccountInfoAlreadyAssociatedWithAnotherPrimaryUserIdException;
26+
import io.supertokens.authRecipe.exception.InputUserIdIsNotAPrimaryUserException;
27+
import io.supertokens.authRecipe.exception.RecipeUserIdAlreadyLinkedWithAnotherPrimaryUserIdException;
28+
import io.supertokens.authRecipe.exception.RecipeUserIdAlreadyLinkedWithPrimaryUserIdException;
2329
import io.supertokens.emailpassword.EmailPassword;
2430
import io.supertokens.emailverification.EmailVerification;
31+
import io.supertokens.emailverification.exception.EmailAlreadyVerifiedException;
2532
import io.supertokens.emailverification.exception.EmailVerificationInvalidTokenException;
33+
import io.supertokens.featureflag.EE_FEATURES;
34+
import io.supertokens.featureflag.FeatureFlagTestContent;
35+
import io.supertokens.featureflag.exceptions.FeatureNotEnabledException;
36+
import io.supertokens.multitenancy.Multitenancy;
37+
import io.supertokens.multitenancy.exception.BadPermissionException;
38+
import io.supertokens.multitenancy.exception.CannotModifyBaseConfigException;
2639
import io.supertokens.passwordless.Passwordless;
2740
import io.supertokens.passwordless.Passwordless.CreateCodeResponse;
2841
import io.supertokens.pluginInterface.RECIPE_ID;
2942
import io.supertokens.pluginInterface.STORAGE_TYPE;
3043
import io.supertokens.pluginInterface.authRecipe.AuthRecipeUserInfo;
44+
import io.supertokens.pluginInterface.emailpassword.exceptions.DuplicateEmailException;
45+
import io.supertokens.pluginInterface.emailpassword.exceptions.UnknownUserIdException;
46+
import io.supertokens.pluginInterface.exceptions.InvalidConfigException;
47+
import io.supertokens.pluginInterface.exceptions.StorageQueryException;
48+
import io.supertokens.pluginInterface.exceptions.StorageTransactionLogicException;
49+
import io.supertokens.pluginInterface.multitenancy.*;
50+
import io.supertokens.pluginInterface.multitenancy.exceptions.TenantOrAppNotFoundException;
3151
import io.supertokens.session.Session;
3252
import io.supertokens.storageLayer.StorageLayer;
53+
import io.supertokens.test.httpRequest.HttpResponseException;
54+
import io.supertokens.thirdparty.InvalidProviderConfigException;
3355
import io.supertokens.thirdparty.ThirdParty;
3456
import io.supertokens.usermetadata.UserMetadata;
3557
import io.supertokens.version.Version;
@@ -38,15 +60,16 @@
3860
import org.junit.Rule;
3961
import org.junit.Test;
4062
import org.junit.rules.TestRule;
41-
import org.reflections.Reflections;
4263

64+
import java.io.IOException;
65+
import java.security.NoSuchAlgorithmException;
66+
import java.security.spec.InvalidKeySpecException;
4367
import java.util.*;
4468
import java.util.concurrent.ExecutorService;
4569
import java.util.concurrent.Executors;
4670
import java.util.concurrent.TimeUnit;
4771
import java.util.concurrent.atomic.AtomicInteger;
4872
import java.util.function.Function;
49-
import java.util.stream.Collectors;
5073

5174
import static org.junit.Assert.*;
5275

@@ -660,6 +683,95 @@ public void deleteUserTest() throws Exception {
660683
assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED));
661684
}
662685

686+
@Test
687+
public void loadUsersByAccountInfoReturnsUsersForTenantOnly()
688+
throws InterruptedException, TenantOrAppNotFoundException, InvalidProviderConfigException,
689+
StorageQueryException, FeatureNotEnabledException, IOException, InvalidConfigException,
690+
CannotModifyBaseConfigException, BadPermissionException, DuplicateEmailException, HttpResponseException,
691+
AccountInfoAlreadyAssociatedWithAnotherPrimaryUserIdException, InputUserIdIsNotAPrimaryUserException,
692+
RecipeUserIdAlreadyLinkedWithAnotherPrimaryUserIdException, UnknownUserIdException,
693+
RecipeUserIdAlreadyLinkedWithPrimaryUserIdException, EmailVerificationInvalidTokenException,
694+
StorageTransactionLogicException, NoSuchAlgorithmException, EmailAlreadyVerifiedException,
695+
InvalidKeySpecException {
696+
String[] args = {"../"};
697+
698+
TestingProcessManager.TestingProcess process = TestingProcessManager.start(args);
699+
assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED));
700+
701+
if (StorageLayer.getStorage(process.getProcess()).getType() != STORAGE_TYPE.SQL) {
702+
return;
703+
}
704+
705+
Main main = process.getProcess();
706+
707+
FeatureFlagTestContent.getInstance(process.getProcess())
708+
.setKeyValue(FeatureFlagTestContent.ENABLED_FEATURES, new EE_FEATURES[]{
709+
EE_FEATURES.MULTI_TENANCY, EE_FEATURES.ACCOUNT_LINKING});
710+
711+
// tenant 1
712+
TenantIdentifier tenantIdentifier = ResourceDistributor.getAppForTesting();
713+
714+
// tenant 2
715+
JsonObject config2 = new JsonObject();
716+
TenantIdentifier tenantIdentifier2 = new TenantIdentifier(tenantIdentifier.getConnectionUriDomain(),
717+
tenantIdentifier.getAppId(), "t1");
718+
719+
Multitenancy.addNewOrUpdateAppOrTenant(
720+
main,
721+
tenantIdentifier,
722+
new TenantConfig(
723+
tenantIdentifier2,
724+
new EmailPasswordConfig(true),
725+
new ThirdPartyConfig(true, null),
726+
new PasswordlessConfig(true),
727+
null, null, config2
728+
)
729+
);
730+
731+
AuthRecipeUserInfo publicTenantUser = EmailPassword.signUp(tenantIdentifier,
732+
StorageLayer.getStorage(main), main,
733+
"[email protected]", "password");
734+
735+
EmailPassword.signUp(tenantIdentifier2, StorageLayer.getStorage(main), main,
736+
"[email protected]", "password");
737+
738+
List<JsonObject> webauthnUsers = io.supertokens.test.webauthn.Utils.registerUsers(main, 1);
739+
740+
String token = EmailVerification.generateEmailVerificationToken(main,
741+
publicTenantUser.getSupertokensUserId(), "[email protected]");
742+
EmailVerification.verifyEmail(main, token);
743+
744+
AuthRecipe.CreatePrimaryUserResult result = AuthRecipe.createPrimaryUser(main,
745+
tenantIdentifier.toAppIdentifier(), StorageLayer.getStorage(main),
746+
publicTenantUser.getSupertokensUserId());
747+
748+
publicTenantUser = AuthRecipe.getUserById(tenantIdentifier.toAppIdentifier(), StorageLayer.getStorage(main),
749+
result.user.getSupertokensUserId());
750+
751+
AuthRecipe.linkAccounts(main,
752+
webauthnUsers.getFirst().getAsJsonObject("user").get("id").getAsString(),
753+
publicTenantUser.getSupertokensUserId());
754+
755+
AuthRecipeUserInfo[] usersByEmail = AuthRecipe.getUsersByAccountInfo(tenantIdentifier2,
756+
StorageLayer.getStorage(tenantIdentifier2, main), false,
757+
"[email protected]", null, null, null, null);
758+
759+
assertEquals(1, usersByEmail.length);
760+
assertEquals(1, usersByEmail[0].loginMethods.length);
761+
assertEquals(1, usersByEmail[0].tenantIds.size());
762+
assertEquals("t1", usersByEmail[0].tenantIds.toArray()[0]);
763+
764+
765+
usersByEmail = AuthRecipe.getUsersByAccountInfo(tenantIdentifier,
766+
StorageLayer.getStorage(tenantIdentifier, main), false,
767+
"[email protected]", null, null, null, null);
768+
769+
assertEquals(1, usersByEmail.length);
770+
assertEquals(2, usersByEmail[0].loginMethods.length);
771+
assertEquals(1, usersByEmail[0].tenantIds.size());
772+
assertEquals("public", usersByEmail[0].tenantIds.toArray()[0]);
773+
}
774+
663775
private static List<String> getAuthRecipes() {
664776
return Arrays.asList("emailpassword", "thirdparty", "passwordless");
665777
}

src/test/java/io/supertokens/test/TestServiceUtils.java

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,6 @@ public static void startServices() {
3434
try {
3535
OAuthProviderService.startService();
3636
PostgresqlService.startService();
37-
MysqlService.startService();
38-
MongodbService.startService();
3937
} catch (Exception e) {
4038
throw new RuntimeException(e);
4139
}
@@ -44,8 +42,6 @@ public static void startServices() {
4442
public static void killServices() throws InterruptedException, IOException {
4543
OAuthProviderService.killService();
4644
PostgresqlService.killService();
47-
MysqlService.killService();
48-
MongodbService.killService();
4945
}
5046

5147
private static class CmdHelper {

0 commit comments

Comments
 (0)