diff --git a/modules/auth/scripts/auth_email_passwordless.ts b/modules/auth/scripts/auth_email_passwordless.ts index 217f320a..801f91d0 100644 --- a/modules/auth/scripts/auth_email_passwordless.ts +++ b/modules/auth/scripts/auth_email_passwordless.ts @@ -19,20 +19,24 @@ export async function run( if (!ctx.userConfig.email) throw new RuntimeError("provider_disabled"); + // Check if the email is already associated with an identity + const existingIdentity = await ctx.db.emailPasswordless.findFirst({ + where: { email: req.email }, + }); + // Fetch existing user if session token is provided - let userId: string | undefined; + let userId: string | undefined = existingIdentity?.userId; + if (req.userToken) { - const { userId } = await ctx.modules.users.authenticateUser({ + const authRes = await ctx.modules.users.authenticateUser({ userToken: req.userToken, }); - // Check if the email is already associated with an identity - const existingIdentity = await ctx.db.emailPasswordless.findFirst({ - where: { email: req.email }, - }); - if (existingIdentity && existingIdentity.userId !== userId) { + if (existingIdentity && existingIdentity.userId !== authRes.userId) { throw new RuntimeError("email_already_used"); } + + userId = authRes.userId; } // Create verification diff --git a/modules/auth/tests/e2e.ts b/modules/auth/tests/e2e.ts index d56b32e0..6d66eca0 100644 --- a/modules/auth/tests/e2e.ts +++ b/modules/auth/tests/e2e.ts @@ -3,20 +3,73 @@ import { assertEquals } from "https://deno.land/std@0.208.0/assert/mod.ts"; import { faker } from "https://deno.land/x/deno_faker@v1.0.3/mod.ts"; test("e2e", async (ctx: TestContext) => { - const authRes = await ctx.modules.auth.authEmailPasswordless({ - email: faker.internet.email(), - }); + // First we create a new user, and "register" into the auth + // using an authEmailPasswordless({ email, userToken }) + // call + const { user } = await ctx.modules.users.createUser({}); - // Look up correct code - const { code } = await ctx.db.emailPasswordlessVerification.findFirstOrThrow({ - where: { - id: authRes.verification.id, - }, + const { token: session } = await ctx.modules.users.createUserToken({ + userId: user.id }); - const verifyRes = await ctx.modules.auth.verifyEmailPasswordless({ - verificationId: authRes.verification.id, - code: code, - }); - assertEquals(verifyRes.token.type, "user"); + const fakeEmail = faker.internet.email(); + + // Now we test that post-signin, we get the same user + { + const authRes = await ctx.modules.auth.authEmailPasswordless({ + email: fakeEmail, + userToken: session.token + }); + + // Look up correct code + const { code } = await ctx.db.emailPasswordlessVerification.findFirstOrThrow({ + where: { + id: authRes.verification.id, + }, + }); + + // Now by verifying the email, we register, and can also use + // this to verify the token + const verifyRes = await ctx.modules.auth.verifyEmailPasswordless({ + verificationId: authRes.verification.id, + code: code, + }); + + assertEquals(verifyRes.token.type, "user"); + + + // Make sure we end up with the same user we started with + const verifyRes2 = await ctx.modules.users.authenticateUser({ + userToken: verifyRes.token.token + }); + + assertEquals(verifyRes2.userId, user.id); + } + + // Now we try logging back in with the same email, + // but without a token, expecting the same user + { + const authRes = await ctx.modules.auth.authEmailPasswordless({ + email: fakeEmail + }); + + // Look up correct code + const { code: code } = await ctx.db.emailPasswordlessVerification.findFirstOrThrow({ + where: { + id: authRes.verification.id, + }, + }); + + const verifyRes = await ctx.modules.auth.verifyEmailPasswordless({ + verificationId: authRes.verification.id, + code: code, + }); + + const verifyRes2 = await ctx.modules.users.authenticateUser({ + userToken: verifyRes.token.token + }); + + assertEquals(verifyRes2.userId, user.id); + } }); +