Skip to content

Commit

Permalink
feat: Create verifications module to streamline auth_email_* and …
Browse files Browse the repository at this point in the history
…`auth_sms_*`
  • Loading branch information
Blckbrry-Pi committed Sep 10, 2024
1 parent 31ebc1e commit 8b5d6a5
Show file tree
Hide file tree
Showing 32 changed files with 751 additions and 476 deletions.

This file was deleted.

This file was deleted.

13 changes: 0 additions & 13 deletions modules/auth_email_password/db/migrations/meta/_journal.json

This file was deleted.

18 changes: 0 additions & 18 deletions modules/auth_email_password/db/schema.ts

This file was deleted.

3 changes: 2 additions & 1 deletion modules/auth_email_password/module.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
"users": {},
"tokens": {},
"user_passwords": {},
"rate_limit": {}
"rate_limit": {},
"verifications": {}
},
"defaultConfig": {
"fromEmail": "hello@test.com",
Expand Down
18 changes: 10 additions & 8 deletions modules/auth_email_password/scripts/send_verification.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,28 @@
import { ScriptContext } from "../module.gen.ts";
import { createVerification } from "../utils/code_management.ts";
import { Verification } from "../utils/types.ts";

export interface Request {
email: string;
userToken?: string;
}

export interface Response {
verification: Verification;
token: string;
}

const HOUR_MS = 60 * 60 * 1000 * 1000;
const ATTEMPTS = 3;

export async function run(
ctx: ScriptContext,
req: Request,
): Promise<Response> {
await ctx.modules.rateLimit.throttlePublic({});

const { code, verification } = await createVerification(
ctx,
req.email,
);
const { code, token } = await ctx.modules.verifications.create({
data: { email: req.email },
expireAt: new Date(Date.now() + HOUR_MS).toISOString(),
maxAttempts: ATTEMPTS,
});

// Send email
await ctx.modules.email.sendEmail({
Expand All @@ -34,5 +36,5 @@ export async function run(
html: `Your verification code is: <b>${code}</b>`,
});

return { verification };
return { token };
}
20 changes: 14 additions & 6 deletions modules/auth_email_password/scripts/verify_add_email_pass.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { Empty, RuntimeError, ScriptContext } from "../module.gen.ts";
import { verifyCode } from "../utils/code_management.ts";
import { IDENTITY_INFO_PASSWORD } from "../utils/provider.ts";
import { ensureNotAssociatedAll } from "../utils/link_assertions.ts";

Expand All @@ -10,7 +9,7 @@ export interface Request {
password: string;
oldPassword: string | null;

verificationToken: string;
token: string;
code: string;
}

Expand All @@ -24,16 +23,25 @@ export async function run(

// Check the verification code. If it is valid, but for the wrong email, say
// the verification failed.
const { email } = await verifyCode(ctx, req.verificationToken, req.code);
if (!compareConstantTime(req.email, email)) {
const { data, succeeded } = await ctx.modules.verifications.attempt({ token: req.token, code: req.code });
if (!succeeded) throw new RuntimeError("invalid_code");

if (
typeof data !== "object" ||
data === null ||
!("email" in data) ||
typeof data.email !== "string"
) throw new RuntimeError("unknown_err");

if (!compareConstantTime(req.email, data.email)) {
throw new RuntimeError("verification_failed");
}

// Ensure that the email is not associated with ANY accounts in ANY way.
const providedUser = await ctx.modules.users.authenticateToken({
userToken: req.userToken,
});
await ensureNotAssociatedAll(ctx, email, new Set([providedUser.userId]));
await ensureNotAssociatedAll(ctx, data.email, new Set([providedUser.userId]));

// If an old password was provided, ensure it was correct and update it.
// If one was not, register the user with the `userPasswords` module.
Expand All @@ -58,7 +66,7 @@ export async function run(
userToken: req.userToken,
info: IDENTITY_INFO_PASSWORD,
uniqueData: {
identifier: email,
identifier: data.email,
},
additionalData: {},
});
Expand Down
20 changes: 14 additions & 6 deletions modules/auth_email_password/scripts/verify_sign_up_email_pass.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import { RuntimeError, ScriptContext } from "../module.gen.ts";
import { verifyCode } from "../utils/code_management.ts";
import { IDENTITY_INFO_PASSWORD } from "../utils/provider.ts";
import { ensureNotAssociatedAll } from "../utils/link_assertions.ts";

export interface Request {
email: string;
password: string;

verificationToken: string;
token: string;
code: string;
}

Expand All @@ -23,19 +22,28 @@ export async function run(

// Check the verification code. If it is valid, but for the wrong email, say
// the verification failed.
const { email } = await verifyCode(ctx, req.verificationToken, req.code);
if (!compareConstantTime(req.email, email)) {
const { data, succeeded } = await ctx.modules.verifications.attempt({ token: req.token, code: req.code });
if (!succeeded) throw new RuntimeError("invalid_code");

if (
typeof data !== "object" ||
data === null ||
!("email" in data) ||
typeof data.email !== "string"
) throw new RuntimeError("unknown_err");

if (!compareConstantTime(req.email, data.email)) {
throw new RuntimeError("verification_failed");
}

// Ensure that the email is not associated with ANY accounts in ANY way.
await ensureNotAssociatedAll(ctx, email, new Set());
await ensureNotAssociatedAll(ctx, data.email, new Set());

// Sign up the user with the passwordless email identity
const { userToken, userId } = await ctx.modules.identities.signUp({
info: IDENTITY_INFO_PASSWORD,
uniqueData: {
identifier: email,
identifier: data.email,
},
additionalData: {},
});
Expand Down
76 changes: 0 additions & 76 deletions modules/auth_email_password/utils/code_management.ts

This file was deleted.

This file was deleted.

Loading

0 comments on commit 8b5d6a5

Please sign in to comment.