Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: emailverified in tp & pless #774

Merged
merged 4 commits into from
Aug 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 66 additions & 1 deletion src/main/java/io/supertokens/passwordless/Passwordless.java
Original file line number Diff line number Diff line change
Expand Up @@ -251,19 +251,31 @@ public static ConsumeCodeResponse consumeCode(Main main,
Storage storage = StorageLayer.getStorage(main);
return consumeCode(
new TenantIdentifierWithStorage(null, null, null, storage),
main, deviceId, deviceIdHashFromUser, userInputCode, linkCode);
main, deviceId, deviceIdHashFromUser, userInputCode, linkCode, false);
} catch (TenantOrAppNotFoundException | BadPermissionException e) {
throw new IllegalStateException(e);
}
}

@TestOnly
public static ConsumeCodeResponse consumeCode(TenantIdentifierWithStorage tenantIdentifierWithStorage, Main main,
String deviceId, String deviceIdHashFromUser,
String userInputCode, String linkCode)
throws RestartFlowException, ExpiredUserInputCodeException,
IncorrectUserInputCodeException, DeviceIdHashMismatchException, StorageTransactionLogicException,
StorageQueryException, NoSuchAlgorithmException, InvalidKeyException, IOException, Base64EncodingException,
TenantOrAppNotFoundException, BadPermissionException {
return consumeCode(tenantIdentifierWithStorage, main, deviceId, deviceIdHashFromUser, userInputCode, linkCode,
false);
}

public static ConsumeCodeResponse consumeCode(TenantIdentifierWithStorage tenantIdentifierWithStorage, Main main,
String deviceId, String deviceIdHashFromUser,
String userInputCode, String linkCode, boolean setEmailVerified)
throws RestartFlowException, ExpiredUserInputCodeException,
IncorrectUserInputCodeException, DeviceIdHashMismatchException, StorageTransactionLogicException,
StorageQueryException, NoSuchAlgorithmException, InvalidKeyException, IOException, Base64EncodingException,
TenantOrAppNotFoundException, BadPermissionException {

TenantConfig config = Multitenancy.getTenantInfo(main, tenantIdentifierWithStorage);
if (config == null) {
Expand Down Expand Up @@ -417,6 +429,33 @@ public static ConsumeCodeResponse consumeCode(TenantIdentifierWithStorage tenant
long timeJoined = System.currentTimeMillis();
user = passwordlessStorage.createUser(tenantIdentifierWithStorage, userId, consumedDevice.email,
consumedDevice.phoneNumber, timeJoined);

// Set email as verified, if using email
if (setEmailVerified && consumedDevice.email != null) {
try {
AuthRecipeUserInfo finalUser = user;
tenantIdentifierWithStorage.getEmailVerificationStorage().startTransaction(con -> {
try {
tenantIdentifierWithStorage.getEmailVerificationStorage()
.updateIsEmailVerified_Transaction(tenantIdentifierWithStorage.toAppIdentifier(), con,
finalUser.getSupertokensUserId(), consumedDevice.email, true);
tenantIdentifierWithStorage.getEmailVerificationStorage()
.commitTransaction(con);

return null;
} catch (TenantOrAppNotFoundException e) {
throw new StorageTransactionLogicException(e);
}
});
user.loginMethods[0].setVerified(); // newly created user has only one loginMethod
} catch (StorageTransactionLogicException e) {
if (e.actualException instanceof TenantOrAppNotFoundException) {
throw (TenantOrAppNotFoundException) e.actualException;
}
throw new StorageQueryException(e);
}
}

return new ConsumeCodeResponse(true, user, consumedDevice.email, consumedDevice.phoneNumber);
} catch (DuplicateEmailException | DuplicatePhoneNumberException e) {
// Getting these would mean that between getting the user and trying creating it:
Expand All @@ -433,6 +472,32 @@ public static ConsumeCodeResponse consumeCode(TenantIdentifierWithStorage tenant
} else {
// We do not need this cleanup if we are creating the user, since it uses the email/phoneNumber of the
// device, which has already been cleaned up
if (setEmailVerified && consumedDevice.email != null) {
// Set email verification
try {
LoginMethod finalLoginMethod = loginMethod;
tenantIdentifierWithStorage.getEmailVerificationStorage().startTransaction(con -> {
try {
tenantIdentifierWithStorage.getEmailVerificationStorage()
.updateIsEmailVerified_Transaction(tenantIdentifierWithStorage.toAppIdentifier(), con,
finalLoginMethod.getSupertokensUserId(), consumedDevice.email, true);
tenantIdentifierWithStorage.getEmailVerificationStorage()
.commitTransaction(con);

return null;
} catch (TenantOrAppNotFoundException e) {
throw new StorageTransactionLogicException(e);
}
});
loginMethod.setVerified();
} catch (StorageTransactionLogicException e) {
if (e.actualException instanceof TenantOrAppNotFoundException) {
throw (TenantOrAppNotFoundException) e.actualException;
}
throw new StorageQueryException(e);
}
}

if (loginMethod.email != null && !loginMethod.email.equals(consumedDevice.email)) {
removeCodesByEmail(tenantIdentifierWithStorage, loginMethod.email);
}
Expand Down
45 changes: 43 additions & 2 deletions src/main/java/io/supertokens/thirdparty/ThirdParty.java
Original file line number Diff line number Diff line change
Expand Up @@ -120,17 +120,26 @@ public static SignInUpResponse signInUp(Main main, String thirdPartyId, String t
Storage storage = StorageLayer.getStorage(main);
return signInUp(
new TenantIdentifierWithStorage(null, null, null, storage), main,
thirdPartyId, thirdPartyUserId, email);
thirdPartyId, thirdPartyUserId, email, false);
} catch (TenantOrAppNotFoundException | BadPermissionException e) {
throw new IllegalStateException(e);
}
}

@TestOnly
public static SignInUpResponse signInUp(TenantIdentifierWithStorage tenantIdentifierWithStorage, Main main,
String thirdPartyId,
String thirdPartyUserId, String email)
throws StorageQueryException, TenantOrAppNotFoundException, BadPermissionException,
EmailChangeNotAllowedException {
return signInUp(tenantIdentifierWithStorage, main, thirdPartyId, thirdPartyUserId, email, false);
}

public static SignInUpResponse signInUp(TenantIdentifierWithStorage tenantIdentifierWithStorage, Main main,
String thirdPartyId,
String thirdPartyUserId, String email, boolean isEmailVerified)
throws StorageQueryException, TenantOrAppNotFoundException, BadPermissionException,
EmailChangeNotAllowedException {

TenantConfig config = Multitenancy.getTenantInfo(main, tenantIdentifierWithStorage);
if (config == null) {
Expand All @@ -140,7 +149,39 @@ public static SignInUpResponse signInUp(TenantIdentifierWithStorage tenantIdenti
throw new BadPermissionException("Third Party login not enabled for tenant");
}

return signInUpHelper(tenantIdentifierWithStorage, main, thirdPartyId, thirdPartyUserId, email);
SignInUpResponse response = signInUpHelper(tenantIdentifierWithStorage, main, thirdPartyId, thirdPartyUserId,
email);

if (isEmailVerified) {
for (LoginMethod lM : response.user.loginMethods) {
if (lM.thirdParty != null && lM.thirdParty.id.equals(thirdPartyId) && lM.thirdParty.userId.equals(thirdPartyUserId)) {
try {
tenantIdentifierWithStorage.getEmailVerificationStorage().startTransaction(con -> {
try {
tenantIdentifierWithStorage.getEmailVerificationStorage()
.updateIsEmailVerified_Transaction(tenantIdentifierWithStorage.toAppIdentifier(), con,
lM.getSupertokensUserId(), lM.email, true);
tenantIdentifierWithStorage.getEmailVerificationStorage()
.commitTransaction(con);

return null;
} catch (TenantOrAppNotFoundException e) {
throw new StorageTransactionLogicException(e);
}
});
lM.setVerified();
} catch (StorageTransactionLogicException e) {
if (e.actualException instanceof TenantOrAppNotFoundException) {
throw (TenantOrAppNotFoundException) e.actualException;
}
throw new StorageQueryException(e);
}
break;
}
}
}

return response;
}

private static SignInUpResponse signInUpHelper(TenantIdentifierWithStorage tenantIdentifierWithStorage,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,9 @@ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws I
ConsumeCodeResponse consumeCodeResponse = Passwordless.consumeCode(
this.getTenantIdentifierWithStorageFromRequest(req), main,
deviceId, deviceIdHash,
userInputCode, linkCode);
userInputCode, linkCode,
// From CDI version 4.0 onwards, the email verification will be set
getVersionFromRequest(req).greaterThanOrEqualTo(SemVer.v4_0));
io.supertokens.useridmapping.UserIdMapping.populateExternalUserIdForUsers(this.getTenantIdentifierWithStorageFromRequest(req), new AuthRecipeUserInfo[]{consumeCodeResponse.user});

ActiveUsers.updateLastActive(this.getAppIdentifierWithStorage(req), main, consumeCodeResponse.user.getSupertokensUserId());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,13 @@ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws I
JsonObject emailObject = InputParser.parseJsonObjectOrThrowError(input, "email", false);
String email = InputParser.parseStringOrThrowError(emailObject, "id", false);

// setting email verified behaviour is to be done only for CDI 4.0 onwards. version 3.0 and earlier
// do not have this field
Boolean isEmailVerified = false;
if (getVersionFromRequest(req).greaterThanOrEqualTo(SemVer.v4_0)) {
isEmailVerified = InputParser.parseBooleanOrThrowError(emailObject, "isVerified", false);
}

assert thirdPartyId != null;
assert thirdPartyUserId != null;
assert email != null;
Expand All @@ -129,7 +136,7 @@ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws I
try {
ThirdParty.SignInUpResponse response = ThirdParty.signInUp(
this.getTenantIdentifierWithStorageFromRequest(req), super.main, thirdPartyId, thirdPartyUserId,
email);
email, isEmailVerified);
UserIdMapping.populateExternalUserIdForUsers(this.getTenantIdentifierWithStorageFromRequest(req), new AuthRecipeUserInfo[]{response.user});

ActiveUsers.updateLastActive(this.getAppIdentifierWithStorage(req), main, response.user.getSupertokensUserId());
Expand Down
Loading
Loading