Skip to content

Commit 43a5a68

Browse files
authored
fix: simplifying email verification token creation (#1016)
* #334 - simplify email verification token creation * Update tests.yml updating github action `checkout` to version 4 * reverting workflow modifications * #334 - simplify email verification token creation tests * chore: bumping version number, writing changelog
1 parent 2f99bf9 commit 43a5a68

File tree

3 files changed

+70
-14
lines changed

3 files changed

+70
-14
lines changed

CHANGELOG.md

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

1414
- Account linking now properly checks if the login methods of the primary user can be shared with the tenants of the
1515
recipe user we are trying to link
16+
- Simplifying email verification token creation
1617

1718
## [9.1.0] - 2024-05-24
1819

src/main/java/io/supertokens/emailverification/EmailVerification.java

Lines changed: 38 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -82,22 +82,11 @@ public static String generateEmailVerificationToken(TenantIdentifier tenantIdent
8282
while (true) {
8383

8484
// we first generate a email verification token
85-
byte[] random = new byte[64];
86-
byte[] salt = new byte[64];
85+
byte[] random = new byte[48];
8786

8887
new SecureRandom().nextBytes(random);
89-
new SecureRandom().nextBytes(salt);
90-
91-
int iterations = 1000;
92-
String token = Utils
93-
.toHex(Utils.pbkdf2(Utils.bytesToString(random).toCharArray(), salt, iterations, 64 * 6));
94-
95-
// we make it URL safe:
96-
token = Utils.convertToBase64(token);
97-
token = token.replace("=", "");
98-
token = token.replace("/", "");
99-
token = token.replace("+", "");
10088

89+
String token = Utils.convertToBase64Url(Utils.bytesToString(random));
10190
String hashedToken = getHashedToken(token);
10291

10392
try {
@@ -234,6 +223,42 @@ public static void unverifyEmail(AppIdentifier appIdentifier, Storage storage, S
234223
.unverifyEmail(appIdentifier, userId, email);
235224
}
236225

226+
@TestOnly
227+
public static String generateEmailVerificationTokenTheOldWay(Main main, String userId, String email)
228+
throws NoSuchAlgorithmException, InvalidKeySpecException, StorageQueryException,
229+
TenantOrAppNotFoundException {
230+
while(true) {
231+
// we first generate a email verification token
232+
byte[] random = new byte[64];
233+
byte[] salt = new byte[64];
234+
235+
new SecureRandom().nextBytes(random);
236+
new SecureRandom().nextBytes(salt);
237+
238+
int iterations = 1000;
239+
String token = io.supertokens.utils.Utils
240+
.toHex(io.supertokens.utils.Utils.pbkdf2(io.supertokens.utils.Utils.bytesToString(random).toCharArray(), salt, iterations, 64 * 6));
241+
242+
// we make it URL safe:
243+
token = io.supertokens.utils.Utils.convertToBase64(token);
244+
token = token.replace("=", "");
245+
token = token.replace("/", "");
246+
token = token.replace("+", "");
247+
248+
String hashedToken = EmailVerification.getHashedToken(token);
249+
250+
try {
251+
StorageUtils.getEmailVerificationStorage(StorageLayer.getStorage(main))
252+
.addEmailVerificationToken(new TenantIdentifier(null, null, null),
253+
new EmailVerificationTokenInfo(userId, hashedToken,
254+
System.currentTimeMillis() +
255+
EmailVerification.getEmailVerificationTokenLifetimeForTests(main), email));
256+
return token;
257+
} catch (DuplicateEmailVerificationTokenException ignored) {
258+
}
259+
}
260+
}
261+
237262
private static String getHashedToken(String token) throws NoSuchAlgorithmException {
238263
return Utils.hashSHA256(token);
239264
}

src/test/java/io/supertokens/test/emailverification/EmailVerificationTest.java

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ public void testFormatOfEmailVerificationToken() throws Exception {
238238
String verifyToken = EmailVerification.generateEmailVerificationToken(process.getProcess(),
239239
user.getSupertokensUserId(),
240240
user.loginMethods[0].email);
241-
assertEquals(verifyToken.length(), 128);
241+
assertEquals(128, verifyToken.length());
242242
assertFalse(verifyToken.contains("+"));
243243
assertFalse(verifyToken.contains("="));
244244
assertFalse(verifyToken.contains("/"));
@@ -316,6 +316,36 @@ public void verifyEmail() throws Exception {
316316
assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED));
317317
}
318318

319+
@Test
320+
public void verifyEmailWithOldTokenAfterTokenGenerationChanged() throws Exception {
321+
String[] args = {"../"};
322+
323+
TestingProcessManager.TestingProcess process = TestingProcessManager.start(args);
324+
assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED));
325+
326+
if (StorageLayer.getStorage(process.getProcess()).getType() != STORAGE_TYPE.SQL) {
327+
return;
328+
}
329+
330+
AuthRecipeUserInfo user = EmailPassword.signUp(process.getProcess(), "[email protected]", "password");
331+
332+
assert (!EmailVerification.isEmailVerified(process.getProcess(), user.getSupertokensUserId(),
333+
user.loginMethods[0].email));
334+
335+
String token = EmailVerification.generateEmailVerificationTokenTheOldWay(process.getProcess(),
336+
user.getSupertokensUserId(), user.loginMethods[0].email);
337+
338+
assert (token != null);
339+
340+
EmailVerification.verifyEmail(process.getProcess(), token);
341+
342+
assert (EmailVerification.isEmailVerified(process.getProcess(), user.getSupertokensUserId(),
343+
user.loginMethods[0].email));
344+
345+
process.kill();
346+
assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED));
347+
}
348+
319349
// Verify the email successfully, then unverify and check that its unverified
320350
@Test
321351
public void testVerifyingEmailAndThenUnverify() throws Exception {

0 commit comments

Comments
 (0)