-
Notifications
You must be signed in to change notification settings - Fork 63
Expand file tree
/
Copy pathcreateVerificationCode.ts
More file actions
111 lines (105 loc) · 3.18 KB
/
Copy pathcreateVerificationCode.ts
File metadata and controls
111 lines (105 loc) · 3.18 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
import { GenericId, Infer, v } from "convex/values";
import { ActionCtx, MutationCtx } from "../types.js";
import * as Provider from "../provider.js";
import { EmailConfig, PhoneConfig } from "../../types.js";
import { getAccountOrThrow, upsertUserAndAccount } from "../users.js";
import { getAuthSessionId } from "../sessions.js";
import { LOG_LEVELS, logWithLevel, sha256 } from "../utils.js";
import { createTriggeredCtx } from "../triggeredDb.js";
export const createVerificationCodeArgs = v.object({
accountId: v.optional(v.id("authAccounts")),
provider: v.string(),
email: v.optional(v.string()),
phone: v.optional(v.string()),
code: v.string(),
expirationTime: v.number(),
allowExtraProviders: v.boolean(),
});
type ReturnType = string;
export async function createVerificationCodeImpl(
originalCtx: MutationCtx,
args: Infer<typeof createVerificationCodeArgs>,
getProviderOrThrow: Provider.GetProviderOrThrowFunc,
config: Provider.Config,
): Promise<ReturnType> {
const ctx = createTriggeredCtx(originalCtx, config);
logWithLevel(LOG_LEVELS.DEBUG, "createVerificationCodeImpl args:", args);
const {
email,
phone,
code,
expirationTime,
provider: providerId,
accountId: existingAccountId,
allowExtraProviders,
} = args;
const existingAccount =
existingAccountId !== undefined
? await getAccountOrThrow(originalCtx, existingAccountId)
: await ctx.db
.query("authAccounts")
.withIndex("providerAndAccountId", (q) =>
q
.eq("provider", providerId)
.eq("providerAccountId", email ?? phone!),
)
.unique();
const provider = getProviderOrThrow(providerId, allowExtraProviders) as
| EmailConfig
| PhoneConfig;
const { accountId } = await upsertUserAndAccount(
originalCtx,
await getAuthSessionId(originalCtx),
existingAccount !== null
? { existingAccount }
: { providerAccountId: email ?? phone! },
provider.type === "email"
? { type: "email", provider, profile: { email: email! } }
: { type: "phone", provider, profile: { phone: phone! } },
config,
);
await generateUniqueVerificationCode(
ctx,
accountId,
providerId,
code,
expirationTime,
{ email, phone },
);
return email ?? phone!;
}
export const callCreateVerificationCode = async (
ctx: ActionCtx,
args: Infer<typeof createVerificationCodeArgs>,
): Promise<ReturnType> => {
return ctx.runMutation("auth:store" as any, {
args: {
type: "createVerificationCode",
...args,
},
});
};
async function generateUniqueVerificationCode(
ctx: MutationCtx,
accountId: GenericId<"authAccounts">,
provider: string,
code: string,
expirationTime: number,
{ email, phone }: { email?: string; phone?: string },
) {
const existingCode = await ctx.db
.query("authVerificationCodes")
.withIndex("accountId", (q) => q.eq("accountId", accountId))
.unique();
if (existingCode !== null) {
await ctx.db.delete(existingCode._id);
}
await ctx.db.insert("authVerificationCodes", {
accountId,
provider,
code: await sha256(code),
expirationTime,
emailVerified: email,
phoneVerified: phone,
});
}