@robelest/convex-auth 0.0.2-preview.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin.cjs +466 -63
- package/dist/client/index.d.ts +211 -30
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +673 -59
- package/dist/client/index.js.map +1 -1
- package/dist/component/_generated/api.d.ts +56 -1
- package/dist/component/_generated/api.d.ts.map +1 -1
- package/dist/component/_generated/api.js.map +1 -1
- package/dist/component/_generated/component.d.ts +93 -3
- package/dist/component/_generated/component.d.ts.map +1 -1
- package/dist/component/convex.config.d.ts.map +1 -1
- package/dist/component/convex.config.js +2 -0
- package/dist/component/convex.config.js.map +1 -1
- package/dist/component/index.d.ts +5 -3
- package/dist/component/index.d.ts.map +1 -1
- package/dist/component/index.js +5 -3
- package/dist/component/index.js.map +1 -1
- package/dist/component/portalBridge.d.ts +80 -0
- package/dist/component/portalBridge.d.ts.map +1 -0
- package/dist/component/portalBridge.js +102 -0
- package/dist/component/portalBridge.js.map +1 -0
- package/dist/component/public.d.ts +193 -9
- package/dist/component/public.d.ts.map +1 -1
- package/dist/component/public.js +204 -33
- package/dist/component/public.js.map +1 -1
- package/dist/component/schema.d.ts +89 -9
- package/dist/component/schema.d.ts.map +1 -1
- package/dist/component/schema.js +68 -7
- package/dist/component/schema.js.map +1 -1
- package/dist/providers/{Anonymous.d.ts → anonymous.d.ts} +8 -8
- package/dist/providers/{Anonymous.d.ts.map → anonymous.d.ts.map} +1 -1
- package/dist/providers/{Anonymous.js → anonymous.js} +9 -10
- package/dist/providers/anonymous.js.map +1 -0
- package/dist/providers/{ConvexCredentials.d.ts → credentials.d.ts} +11 -11
- package/dist/providers/credentials.d.ts.map +1 -0
- package/dist/providers/{ConvexCredentials.js → credentials.js} +8 -8
- package/dist/providers/credentials.js.map +1 -0
- package/dist/providers/{Email.d.ts → email.d.ts} +6 -6
- package/dist/providers/email.d.ts.map +1 -0
- package/dist/providers/{Email.js → email.js} +6 -6
- package/dist/providers/email.js.map +1 -0
- package/dist/providers/passkey.d.ts +20 -0
- package/dist/providers/passkey.d.ts.map +1 -0
- package/dist/providers/passkey.js +32 -0
- package/dist/providers/passkey.js.map +1 -0
- package/dist/providers/{Password.d.ts → password.d.ts} +10 -10
- package/dist/providers/{Password.d.ts.map → password.d.ts.map} +1 -1
- package/dist/providers/{Password.js → password.js} +19 -20
- package/dist/providers/password.js.map +1 -0
- package/dist/providers/{Phone.d.ts → phone.d.ts} +3 -3
- package/dist/providers/{Phone.d.ts.map → phone.d.ts.map} +1 -1
- package/dist/providers/{Phone.js → phone.js} +3 -3
- package/dist/providers/{Phone.js.map → phone.js.map} +1 -1
- package/dist/providers/totp.d.ts +14 -0
- package/dist/providers/totp.d.ts.map +1 -0
- package/dist/providers/totp.js +23 -0
- package/dist/providers/totp.js.map +1 -0
- package/dist/server/convex-auth.d.ts +243 -0
- package/dist/server/convex-auth.d.ts.map +1 -0
- package/dist/server/convex-auth.js +365 -0
- package/dist/server/convex-auth.js.map +1 -0
- package/dist/server/implementation/index.d.ts +153 -166
- package/dist/server/implementation/index.d.ts.map +1 -1
- package/dist/server/implementation/index.js +162 -105
- package/dist/server/implementation/index.js.map +1 -1
- package/dist/server/implementation/passkey.d.ts +33 -0
- package/dist/server/implementation/passkey.d.ts.map +1 -0
- package/dist/server/implementation/passkey.js +450 -0
- package/dist/server/implementation/passkey.js.map +1 -0
- package/dist/server/implementation/redirects.d.ts.map +1 -1
- package/dist/server/implementation/redirects.js +4 -9
- package/dist/server/implementation/redirects.js.map +1 -1
- package/dist/server/implementation/sessions.d.ts +2 -20
- package/dist/server/implementation/sessions.d.ts.map +1 -1
- package/dist/server/implementation/sessions.js +2 -20
- package/dist/server/implementation/sessions.js.map +1 -1
- package/dist/server/implementation/signIn.d.ts +13 -0
- package/dist/server/implementation/signIn.d.ts.map +1 -1
- package/dist/server/implementation/signIn.js +26 -1
- package/dist/server/implementation/signIn.js.map +1 -1
- package/dist/server/implementation/totp.d.ts +40 -0
- package/dist/server/implementation/totp.d.ts.map +1 -0
- package/dist/server/implementation/totp.js +211 -0
- package/dist/server/implementation/totp.js.map +1 -0
- package/dist/server/index.d.ts +18 -0
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +255 -0
- package/dist/server/index.js.map +1 -1
- package/dist/server/portal-email.d.ts +19 -0
- package/dist/server/portal-email.d.ts.map +1 -0
- package/dist/server/portal-email.js +89 -0
- package/dist/server/portal-email.js.map +1 -0
- package/dist/server/portal.d.ts +116 -0
- package/dist/server/portal.d.ts.map +1 -0
- package/dist/server/portal.js +294 -0
- package/dist/server/portal.js.map +1 -0
- package/dist/server/provider_utils.d.ts +1 -1
- package/dist/server/provider_utils.d.ts.map +1 -1
- package/dist/server/provider_utils.js +39 -1
- package/dist/server/provider_utils.js.map +1 -1
- package/dist/server/types.d.ts +128 -11
- package/dist/server/types.d.ts.map +1 -1
- package/package.json +7 -7
- package/src/cli/index.ts +48 -6
- package/src/cli/portal-link.ts +112 -0
- package/src/cli/portal-upload.ts +411 -0
- package/src/client/index.ts +823 -109
- package/src/component/_generated/api.ts +72 -1
- package/src/component/_generated/component.ts +180 -4
- package/src/component/convex.config.ts +3 -0
- package/src/component/index.ts +5 -10
- package/src/component/portalBridge.ts +116 -0
- package/src/component/public.ts +231 -37
- package/src/component/schema.ts +70 -7
- package/src/providers/{Anonymous.ts → anonymous.ts} +10 -11
- package/src/providers/{ConvexCredentials.ts → credentials.ts} +11 -11
- package/src/providers/{Email.ts → email.ts} +5 -5
- package/src/providers/passkey.ts +35 -0
- package/src/providers/{Password.ts → password.ts} +22 -27
- package/src/providers/{Phone.ts → phone.ts} +2 -2
- package/src/providers/totp.ts +26 -0
- package/src/server/convex-auth.ts +470 -0
- package/src/server/implementation/index.ts +228 -239
- package/src/server/implementation/passkey.ts +650 -0
- package/src/server/implementation/redirects.ts +4 -11
- package/src/server/implementation/sessions.ts +2 -20
- package/src/server/implementation/signIn.ts +39 -1
- package/src/server/implementation/totp.ts +366 -0
- package/src/server/index.ts +373 -0
- package/src/server/portal-email.ts +95 -0
- package/src/server/portal.ts +375 -0
- package/src/server/provider_utils.ts +42 -1
- package/src/server/types.ts +161 -10
- package/dist/providers/Anonymous.js.map +0 -1
- package/dist/providers/ConvexCredentials.d.ts.map +0 -1
- package/dist/providers/ConvexCredentials.js.map +0 -1
- package/dist/providers/Email.d.ts.map +0 -1
- package/dist/providers/Email.js.map +0 -1
- package/dist/providers/Password.js.map +0 -1
- package/providers/Anonymous/package.json +0 -6
- package/providers/ConvexCredentials/package.json +0 -6
- package/providers/Email/package.json +0 -6
- package/providers/Password/package.json +0 -6
- package/providers/Phone/package.json +0 -6
- package/server/package.json +0 -6
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import { callCreateVerificationCode, callRefreshSession, callSignIn, callVerifier, callVerifyCodeAndSignIn, } from "./mutations/index.js";
|
|
1
|
+
import { callCreateVerificationCode, callRefreshSession, callSignIn, callVerifier, callVerifierSignature, callVerifyCodeAndSignIn, } from "./mutations/index.js";
|
|
2
2
|
import { redirectAbsoluteUrl, setURLSearchParam } from "./redirects.js";
|
|
3
3
|
import { requireEnv } from "../utils.js";
|
|
4
4
|
import { generateRandomString } from "./utils.js";
|
|
5
|
+
import { handlePasskey } from "./passkey.js";
|
|
6
|
+
import { handleTotp, checkTotpRequired } from "./totp.js";
|
|
5
7
|
const DEFAULT_EMAIL_VERIFICATION_CODE_DURATION_S = 60 * 60 * 24; // 24 hours
|
|
6
8
|
export async function signInImpl(ctx, provider, args, options) {
|
|
7
9
|
if (provider === null && args.refreshToken) {
|
|
@@ -34,6 +36,12 @@ export async function signInImpl(ctx, provider, args, options) {
|
|
|
34
36
|
if (provider.type === "oauth" || provider.type === "oidc") {
|
|
35
37
|
return handleOAuthProvider(ctx, provider, args, options);
|
|
36
38
|
}
|
|
39
|
+
if (provider.type === "passkey") {
|
|
40
|
+
return handlePasskey(ctx, provider, args);
|
|
41
|
+
}
|
|
42
|
+
if (provider.type === "totp") {
|
|
43
|
+
return handleTotp(ctx, provider, args);
|
|
44
|
+
}
|
|
37
45
|
const _typecheck = provider;
|
|
38
46
|
throw new Error(`Provider type ${provider.type} is not supported yet`);
|
|
39
47
|
}
|
|
@@ -104,6 +112,23 @@ async function handleCredentials(ctx, provider, args, options) {
|
|
|
104
112
|
if (result === null) {
|
|
105
113
|
return { kind: "signedIn", signedIn: null };
|
|
106
114
|
}
|
|
115
|
+
// Check if user has TOTP 2FA enrolled before issuing tokens
|
|
116
|
+
const hasTotpEnrolled = await checkTotpRequired(ctx, result.userId);
|
|
117
|
+
if (hasTotpEnrolled) {
|
|
118
|
+
// Create session but withhold tokens — TOTP verification needed
|
|
119
|
+
const idsWithoutTokens = await callSignIn(ctx, {
|
|
120
|
+
userId: result.userId,
|
|
121
|
+
sessionId: result.sessionId,
|
|
122
|
+
generateTokens: false,
|
|
123
|
+
});
|
|
124
|
+
// Store userId in verifier so the TOTP verify flow can complete sign-in
|
|
125
|
+
const verifier = await callVerifier(ctx);
|
|
126
|
+
await callVerifierSignature(ctx, {
|
|
127
|
+
verifier,
|
|
128
|
+
signature: JSON.stringify({ userId: result.userId }),
|
|
129
|
+
});
|
|
130
|
+
return { kind: "totpRequired", verifier };
|
|
131
|
+
}
|
|
107
132
|
const idsAndTokens = await callSignIn(ctx, {
|
|
108
133
|
userId: result.userId,
|
|
109
134
|
sessionId: result.sessionId,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"signIn.js","sourceRoot":"","sources":["../../../src/server/implementation/signIn.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"signIn.js","sourceRoot":"","sources":["../../../src/server/implementation/signIn.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Server-side TOTP ceremony logic for two-factor authentication.
|
|
3
|
+
*
|
|
4
|
+
* Handles the three phases of the TOTP flow:
|
|
5
|
+
* 1. setup — generate a TOTP secret and `otpauth://` URI for enrollment
|
|
6
|
+
* 2. confirm — verify the first code from the authenticator app and mark
|
|
7
|
+
* the enrollment as verified
|
|
8
|
+
* 3. verify — verify a TOTP code during sign-in (2FA challenge)
|
|
9
|
+
*
|
|
10
|
+
* Uses `@oslojs/otp` for TOTP generation / verification and
|
|
11
|
+
* `@oslojs/encoding` for base-32 secret encoding.
|
|
12
|
+
*/
|
|
13
|
+
import { TotpProviderConfig, GenericActionCtxWithAuthConfig } from "../types.js";
|
|
14
|
+
import { AuthDataModel, SessionInfo } from "./types.js";
|
|
15
|
+
type EnrichedActionCtx = GenericActionCtxWithAuthConfig<AuthDataModel>;
|
|
16
|
+
/**
|
|
17
|
+
* Main TOTP handler dispatched from signIn.ts.
|
|
18
|
+
*
|
|
19
|
+
* Routes to the appropriate phase based on `params.flow`.
|
|
20
|
+
*/
|
|
21
|
+
export declare function handleTotp(ctx: EnrichedActionCtx, provider: TotpProviderConfig, args: {
|
|
22
|
+
params?: Record<string, any>;
|
|
23
|
+
verifier?: string;
|
|
24
|
+
}): Promise<{
|
|
25
|
+
kind: "signedIn";
|
|
26
|
+
signedIn: SessionInfo | null;
|
|
27
|
+
} | {
|
|
28
|
+
kind: "totpSetup";
|
|
29
|
+
uri: string;
|
|
30
|
+
secret: string;
|
|
31
|
+
verifier: string;
|
|
32
|
+
totpId: string;
|
|
33
|
+
}>;
|
|
34
|
+
/**
|
|
35
|
+
* Check if a user has a verified TOTP enrollment.
|
|
36
|
+
* Called after credentials sign-in to determine if 2FA is needed.
|
|
37
|
+
*/
|
|
38
|
+
export declare function checkTotpRequired(ctx: EnrichedActionCtx, userId: string): Promise<boolean>;
|
|
39
|
+
export {};
|
|
40
|
+
//# sourceMappingURL=totp.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"totp.d.ts","sourceRoot":"","sources":["../../../src/server/implementation/totp.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AASH,OAAO,EACL,kBAAkB,EAClB,8BAA8B,EAC/B,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAIxD,KAAK,iBAAiB,GAAG,8BAA8B,CAAC,aAAa,CAAC,CAAC;AA2QvE;;;;GAIG;AACH,wBAAsB,UAAU,CAC9B,GAAG,EAAE,iBAAiB,EACtB,QAAQ,EAAE,kBAAkB,EAC5B,IAAI,EAAE;IACJ,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC7B,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GACA,OAAO,CACN;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,QAAQ,EAAE,WAAW,GAAG,IAAI,CAAA;CAAE,GAClD;IACE,IAAI,EAAE,WAAW,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAChB,CACJ,CA8BA;AAMD;;;GAGG;AACH,wBAAsB,iBAAiB,CACrC,GAAG,EAAE,iBAAiB,EACtB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,OAAO,CAAC,CAMlB"}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Server-side TOTP ceremony logic for two-factor authentication.
|
|
3
|
+
*
|
|
4
|
+
* Handles the three phases of the TOTP flow:
|
|
5
|
+
* 1. setup — generate a TOTP secret and `otpauth://` URI for enrollment
|
|
6
|
+
* 2. confirm — verify the first code from the authenticator app and mark
|
|
7
|
+
* the enrollment as verified
|
|
8
|
+
* 3. verify — verify a TOTP code during sign-in (2FA challenge)
|
|
9
|
+
*
|
|
10
|
+
* Uses `@oslojs/otp` for TOTP generation / verification and
|
|
11
|
+
* `@oslojs/encoding` for base-32 secret encoding.
|
|
12
|
+
*/
|
|
13
|
+
import { verifyTOTPWithGracePeriod, createTOTPKeyURI, } from "@oslojs/otp";
|
|
14
|
+
import { encodeBase32LowerCaseNoPadding } from "@oslojs/encoding";
|
|
15
|
+
import { callSignIn, callVerifier } from "./mutations/index.js";
|
|
16
|
+
import { callVerifierSignature } from "./mutations/verifierSignature.js";
|
|
17
|
+
// ============================================================================
|
|
18
|
+
// Setup flow
|
|
19
|
+
// ============================================================================
|
|
20
|
+
/**
|
|
21
|
+
* Phase 1: Generate a TOTP secret and enrollment URI.
|
|
22
|
+
*
|
|
23
|
+
* Requires an authenticated user — TOTP enrollment always adds a second
|
|
24
|
+
* factor to an existing account. The userId is taken from the current
|
|
25
|
+
* session identity.
|
|
26
|
+
*/
|
|
27
|
+
async function handleSetup(ctx, provider, params) {
|
|
28
|
+
// TOTP enrollment requires an authenticated user
|
|
29
|
+
const identity = await ctx.auth.getUserIdentity();
|
|
30
|
+
if (identity === null) {
|
|
31
|
+
throw new Error("TOTP enrollment requires an authenticated user. " +
|
|
32
|
+
"Sign in first, then add TOTP to your account.");
|
|
33
|
+
}
|
|
34
|
+
const [userId] = identity.subject.split("|");
|
|
35
|
+
// Generate a 20-byte random secret (160 bits, per RFC 4226 recommendation)
|
|
36
|
+
const secret = new Uint8Array(20);
|
|
37
|
+
crypto.getRandomValues(secret);
|
|
38
|
+
// Resolve the account name for the otpauth:// URI
|
|
39
|
+
let accountName = params.accountName;
|
|
40
|
+
if (!accountName) {
|
|
41
|
+
const user = await ctx.runQuery(ctx.auth.config.component.public.userGetById, { userId: userId });
|
|
42
|
+
accountName = user?.email ?? "user";
|
|
43
|
+
}
|
|
44
|
+
// Build the otpauth:// URI for QR code scanning
|
|
45
|
+
const uri = createTOTPKeyURI(provider.options.issuer, accountName, secret, provider.options.period, provider.options.digits);
|
|
46
|
+
// Encode the secret as base-32 for manual entry
|
|
47
|
+
const base32Secret = encodeBase32LowerCaseNoPadding(secret);
|
|
48
|
+
// Store enrolment metadata in a verifier so we can correlate the confirm step
|
|
49
|
+
const verifier = await callVerifier(ctx);
|
|
50
|
+
await callVerifierSignature(ctx, {
|
|
51
|
+
verifier,
|
|
52
|
+
signature: JSON.stringify({
|
|
53
|
+
secret: Array.from(secret),
|
|
54
|
+
userId,
|
|
55
|
+
digits: provider.options.digits,
|
|
56
|
+
period: provider.options.period,
|
|
57
|
+
}),
|
|
58
|
+
});
|
|
59
|
+
// Insert an UNVERIFIED TOTP record in the DB
|
|
60
|
+
const totpId = await ctx.runMutation(ctx.auth.config.component.public.totpInsert, {
|
|
61
|
+
userId: userId,
|
|
62
|
+
secret: secret.buffer.slice(secret.byteOffset, secret.byteOffset + secret.byteLength),
|
|
63
|
+
digits: provider.options.digits,
|
|
64
|
+
period: provider.options.period,
|
|
65
|
+
verified: false,
|
|
66
|
+
name: params.name,
|
|
67
|
+
createdAt: Date.now(),
|
|
68
|
+
});
|
|
69
|
+
return {
|
|
70
|
+
kind: "totpSetup",
|
|
71
|
+
uri,
|
|
72
|
+
secret: base32Secret,
|
|
73
|
+
verifier,
|
|
74
|
+
totpId: totpId,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
// ============================================================================
|
|
78
|
+
// Confirm flow
|
|
79
|
+
// ============================================================================
|
|
80
|
+
/**
|
|
81
|
+
* Phase 2: Verify the first code from the authenticator app.
|
|
82
|
+
*
|
|
83
|
+
* Requires an authenticated user. Marks the TOTP enrollment as verified
|
|
84
|
+
* after confirming the code is correct.
|
|
85
|
+
*/
|
|
86
|
+
async function handleConfirm(ctx, provider, params, verifierValue) {
|
|
87
|
+
// TOTP confirmation requires an authenticated user
|
|
88
|
+
const identity = await ctx.auth.getUserIdentity();
|
|
89
|
+
if (identity === null) {
|
|
90
|
+
throw new Error("TOTP confirmation requires an authenticated user. " +
|
|
91
|
+
"Sign in first, then confirm your TOTP enrollment.");
|
|
92
|
+
}
|
|
93
|
+
const [userId] = identity.subject.split("|");
|
|
94
|
+
if (!verifierValue) {
|
|
95
|
+
throw new Error("Missing verifier");
|
|
96
|
+
}
|
|
97
|
+
if (!params.code) {
|
|
98
|
+
throw new Error("Missing `code` parameter");
|
|
99
|
+
}
|
|
100
|
+
if (!params.totpId) {
|
|
101
|
+
throw new Error("Missing `totpId` parameter");
|
|
102
|
+
}
|
|
103
|
+
// Look up the TOTP record
|
|
104
|
+
const totpDoc = await ctx.runQuery(ctx.auth.config.component.public.totpGetById, { totpId: params.totpId });
|
|
105
|
+
if (!totpDoc) {
|
|
106
|
+
throw new Error("TOTP enrollment not found");
|
|
107
|
+
}
|
|
108
|
+
if (totpDoc.verified) {
|
|
109
|
+
throw new Error("TOTP enrollment is already verified");
|
|
110
|
+
}
|
|
111
|
+
// Extract the secret from the TOTP record
|
|
112
|
+
const secret = new Uint8Array(totpDoc.secret);
|
|
113
|
+
// Verify the code with a 30-second grace period
|
|
114
|
+
const valid = verifyTOTPWithGracePeriod(secret, provider.options.period, provider.options.digits, params.code, 30);
|
|
115
|
+
if (!valid) {
|
|
116
|
+
throw new Error("Invalid TOTP code");
|
|
117
|
+
}
|
|
118
|
+
// Mark the enrollment as verified
|
|
119
|
+
await ctx.runMutation(ctx.auth.config.component.public.totpMarkVerified, { totpId: params.totpId, lastUsedAt: Date.now() });
|
|
120
|
+
// Clean up the verifier
|
|
121
|
+
await ctx.runMutation(ctx.auth.config.component.public.verifierDelete, { verifierId: verifierValue });
|
|
122
|
+
// Return tokens for the existing session
|
|
123
|
+
const signInResult = await callSignIn(ctx, {
|
|
124
|
+
userId: userId,
|
|
125
|
+
generateTokens: true,
|
|
126
|
+
});
|
|
127
|
+
return { kind: "signedIn", signedIn: signInResult };
|
|
128
|
+
}
|
|
129
|
+
// ============================================================================
|
|
130
|
+
// Verify flow (2FA during sign-in)
|
|
131
|
+
// ============================================================================
|
|
132
|
+
/**
|
|
133
|
+
* Phase 3: Verify a TOTP code during sign-in.
|
|
134
|
+
*
|
|
135
|
+
* Does NOT require an authenticated user — this runs mid-sign-in as a
|
|
136
|
+
* second-factor challenge. The userId is retrieved from the stored verifier.
|
|
137
|
+
*/
|
|
138
|
+
async function handleVerify(ctx, provider, params, verifierValue) {
|
|
139
|
+
if (!verifierValue) {
|
|
140
|
+
throw new Error("Missing verifier");
|
|
141
|
+
}
|
|
142
|
+
if (!params.code) {
|
|
143
|
+
throw new Error("Missing `code` parameter");
|
|
144
|
+
}
|
|
145
|
+
// Look up the verifier to retrieve the stored userId
|
|
146
|
+
const verifierDoc = await ctx.runQuery(ctx.auth.config.component.public.verifierGetById, { verifierId: verifierValue });
|
|
147
|
+
if (!verifierDoc) {
|
|
148
|
+
throw new Error("Invalid or expired verifier");
|
|
149
|
+
}
|
|
150
|
+
// Parse the signature to extract userId
|
|
151
|
+
const signatureData = JSON.parse(verifierDoc.signature);
|
|
152
|
+
const userId = signatureData.userId;
|
|
153
|
+
// Look up the user's verified TOTP enrollment
|
|
154
|
+
const totpDoc = await ctx.runQuery(ctx.auth.config.component.public.totpGetVerifiedByUserId, { userId: userId });
|
|
155
|
+
if (!totpDoc) {
|
|
156
|
+
throw new Error("No TOTP enrollment found");
|
|
157
|
+
}
|
|
158
|
+
// Extract the secret from the TOTP record
|
|
159
|
+
const secret = new Uint8Array(totpDoc.secret);
|
|
160
|
+
// Verify the code with a 30-second grace period
|
|
161
|
+
const valid = verifyTOTPWithGracePeriod(secret, totpDoc.period, totpDoc.digits, params.code, 30);
|
|
162
|
+
if (!valid) {
|
|
163
|
+
throw new Error("Invalid TOTP code");
|
|
164
|
+
}
|
|
165
|
+
// Update last used timestamp
|
|
166
|
+
await ctx.runMutation(ctx.auth.config.component.public.totpUpdateLastUsed, { totpId: totpDoc._id, lastUsedAt: Date.now() });
|
|
167
|
+
// Clean up the verifier
|
|
168
|
+
await ctx.runMutation(ctx.auth.config.component.public.verifierDelete, { verifierId: verifierValue });
|
|
169
|
+
// Sign in the user with tokens
|
|
170
|
+
const signInResult = await callSignIn(ctx, {
|
|
171
|
+
userId,
|
|
172
|
+
generateTokens: true,
|
|
173
|
+
});
|
|
174
|
+
return { kind: "signedIn", signedIn: signInResult };
|
|
175
|
+
}
|
|
176
|
+
// ============================================================================
|
|
177
|
+
// Main dispatch
|
|
178
|
+
// ============================================================================
|
|
179
|
+
/**
|
|
180
|
+
* Main TOTP handler dispatched from signIn.ts.
|
|
181
|
+
*
|
|
182
|
+
* Routes to the appropriate phase based on `params.flow`.
|
|
183
|
+
*/
|
|
184
|
+
export async function handleTotp(ctx, provider, args) {
|
|
185
|
+
const flow = args.params?.flow;
|
|
186
|
+
if (!flow) {
|
|
187
|
+
throw new Error("Missing `flow` parameter. Expected one of: setup, confirm, verify");
|
|
188
|
+
}
|
|
189
|
+
switch (flow) {
|
|
190
|
+
case "setup":
|
|
191
|
+
return handleSetup(ctx, provider, args.params ?? {});
|
|
192
|
+
case "confirm":
|
|
193
|
+
return handleConfirm(ctx, provider, args.params ?? {}, args.verifier);
|
|
194
|
+
case "verify":
|
|
195
|
+
return handleVerify(ctx, provider, args.params ?? {}, args.verifier);
|
|
196
|
+
default:
|
|
197
|
+
throw new Error(`Unknown TOTP flow: ${flow}. Expected one of: setup, confirm, verify`);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
// ============================================================================
|
|
201
|
+
// Helpers
|
|
202
|
+
// ============================================================================
|
|
203
|
+
/**
|
|
204
|
+
* Check if a user has a verified TOTP enrollment.
|
|
205
|
+
* Called after credentials sign-in to determine if 2FA is needed.
|
|
206
|
+
*/
|
|
207
|
+
export async function checkTotpRequired(ctx, userId) {
|
|
208
|
+
const totpDoc = await ctx.runQuery(ctx.auth.config.component.public.totpGetVerifiedByUserId, { userId: userId });
|
|
209
|
+
return totpDoc !== null;
|
|
210
|
+
}
|
|
211
|
+
//# sourceMappingURL=totp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"totp.js","sourceRoot":"","sources":["../../../src/server/implementation/totp.ts"],"names":[],"mappings":"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"}
|
package/dist/server/index.d.ts
CHANGED
|
@@ -6,6 +6,18 @@ export type AuthCookies = {
|
|
|
6
6
|
refreshToken: string | null;
|
|
7
7
|
verifier: string | null;
|
|
8
8
|
};
|
|
9
|
+
export type ServerOptions = {
|
|
10
|
+
/** Convex deployment URL. */
|
|
11
|
+
url: string;
|
|
12
|
+
apiRoute?: string;
|
|
13
|
+
cookieMaxAge?: number | null;
|
|
14
|
+
verbose?: boolean;
|
|
15
|
+
shouldHandleCode?: ((request: Request) => boolean | Promise<boolean>) | boolean;
|
|
16
|
+
};
|
|
17
|
+
export type RefreshResult = {
|
|
18
|
+
response?: Response;
|
|
19
|
+
cookies?: string[];
|
|
20
|
+
};
|
|
9
21
|
export declare function authCookieNames(host?: string): {
|
|
10
22
|
token: string;
|
|
11
23
|
refreshToken: string;
|
|
@@ -14,4 +26,10 @@ export declare function authCookieNames(host?: string): {
|
|
|
14
26
|
export declare function parseAuthCookies(cookieHeader: string | null | undefined, host?: string): AuthCookies;
|
|
15
27
|
export declare function serializeAuthCookies(cookies: AuthCookies, host?: string, config?: AuthCookieConfig): string[];
|
|
16
28
|
export declare function shouldProxyAuthAction(pathname: string, apiRoute: string): boolean;
|
|
29
|
+
export declare function server(options: ServerOptions): {
|
|
30
|
+
token(request: Request): string | null;
|
|
31
|
+
verify(request: Request): Promise<boolean>;
|
|
32
|
+
proxy(request: Request): Promise<Response>;
|
|
33
|
+
refresh(request: Request): Promise<RefreshResult>;
|
|
34
|
+
};
|
|
17
35
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AASA,MAAM,MAAM,gBAAgB,GAAG;IAC7B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,6BAA6B;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,gBAAgB,CAAC,EAAE,CAAC,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,GAAG,OAAO,CAAC;CACjF,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB,CAAC;AAEF,wBAAgB,eAAe,CAAC,IAAI,CAAC,EAAE,MAAM;;;;EAO5C;AAED,wBAAgB,gBAAgB,CAC9B,YAAY,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EACvC,IAAI,CAAC,EAAE,MAAM,GACZ,WAAW,CAQb;AAED,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,WAAW,EACpB,IAAI,CAAC,EAAE,MAAM,EACb,MAAM,GAAE,gBAAmC,YA4B5C;AAED,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,WAKvE;AAOD,wBAAgB,MAAM,CAAC,OAAO,EAAE,aAAa;mBAsH1B,OAAO,GAAG,MAAM,GAAG,IAAI;oBAIhB,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;mBAY3B,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC;qBAkHzB,OAAO,GAAG,OAAO,CAAC,aAAa,CAAC;EAkG1D"}
|
package/dist/server/index.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { ConvexHttpClient } from "convex/browser";
|
|
2
|
+
import { jwtDecode } from "jwt-decode";
|
|
1
3
|
import { parse, serialize } from "cookie";
|
|
2
4
|
import { isLocalHost } from "./utils.js";
|
|
3
5
|
export function authCookieNames(host) {
|
|
@@ -51,4 +53,257 @@ export function shouldProxyAuthAction(pathname, apiRoute) {
|
|
|
51
53
|
}
|
|
52
54
|
return pathname === apiRoute || pathname === `${apiRoute}/`;
|
|
53
55
|
}
|
|
56
|
+
const REQUIRED_TOKEN_LIFETIME_MS = 60_000;
|
|
57
|
+
const MINIMUM_REQUIRED_TOKEN_LIFETIME_MS = 10_000;
|
|
58
|
+
export function server(options) {
|
|
59
|
+
const convexUrl = options.url;
|
|
60
|
+
const apiRoute = options.apiRoute ?? "/api/auth";
|
|
61
|
+
const cookieConfig = { maxAge: options.cookieMaxAge ?? null };
|
|
62
|
+
const verbose = options.verbose ?? false;
|
|
63
|
+
const logVerbose = (message) => {
|
|
64
|
+
if (!verbose) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
console.debug(`${new Date().toISOString()} [convex-auth/server] ${message}`);
|
|
68
|
+
};
|
|
69
|
+
const cookieHost = (request) => {
|
|
70
|
+
return request.headers.get("host") ?? new URL(request.url).host;
|
|
71
|
+
};
|
|
72
|
+
const parseRequestCookies = (request) => {
|
|
73
|
+
return parseAuthCookies(request.headers.get("cookie"), cookieHost(request));
|
|
74
|
+
};
|
|
75
|
+
const attachCookies = (response, cookies) => {
|
|
76
|
+
for (const value of cookies) {
|
|
77
|
+
response.headers.append("Set-Cookie", value);
|
|
78
|
+
}
|
|
79
|
+
return response;
|
|
80
|
+
};
|
|
81
|
+
const jsonResponse = (body, status = 200) => {
|
|
82
|
+
return new Response(JSON.stringify(body), {
|
|
83
|
+
status,
|
|
84
|
+
headers: {
|
|
85
|
+
"Content-Type": "application/json",
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
};
|
|
89
|
+
const isCorsRequest = (request) => {
|
|
90
|
+
const originHeader = request.headers.get("origin");
|
|
91
|
+
if (originHeader === null) {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
const requestUrl = new URL(request.url);
|
|
95
|
+
const originUrl = new URL(originHeader);
|
|
96
|
+
return (originUrl.host !== requestUrl.host ||
|
|
97
|
+
originUrl.protocol !== requestUrl.protocol);
|
|
98
|
+
};
|
|
99
|
+
const decodeToken = (token) => {
|
|
100
|
+
try {
|
|
101
|
+
return jwtDecode(token);
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
const convexClient = (token) => {
|
|
108
|
+
const client = new ConvexHttpClient(convexUrl);
|
|
109
|
+
if (token !== undefined && token !== null) {
|
|
110
|
+
client.setAuth(token);
|
|
111
|
+
}
|
|
112
|
+
return client;
|
|
113
|
+
};
|
|
114
|
+
const refreshTokens = async (request) => {
|
|
115
|
+
const cookies = parseRequestCookies(request);
|
|
116
|
+
const { token, refreshToken } = cookies;
|
|
117
|
+
if (refreshToken === null && token === null) {
|
|
118
|
+
logVerbose("No auth cookies found, skipping refresh");
|
|
119
|
+
return undefined;
|
|
120
|
+
}
|
|
121
|
+
if (refreshToken === null || token === null) {
|
|
122
|
+
logVerbose("Only one auth cookie present, clearing auth cookies");
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
const decodedToken = decodeToken(token);
|
|
126
|
+
if (decodedToken?.exp === undefined || decodedToken.iat === undefined) {
|
|
127
|
+
logVerbose("Failed to decode token, clearing auth cookies");
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
const totalTokenLifetimeMs = decodedToken.exp * 1000 - decodedToken.iat * 1000;
|
|
131
|
+
const minimumExpiration = Date.now() +
|
|
132
|
+
Math.min(REQUIRED_TOKEN_LIFETIME_MS, Math.max(MINIMUM_REQUIRED_TOKEN_LIFETIME_MS, totalTokenLifetimeMs / 10));
|
|
133
|
+
if (decodedToken.exp * 1000 > minimumExpiration) {
|
|
134
|
+
logVerbose("Token valid long enough, skipping refresh");
|
|
135
|
+
return undefined;
|
|
136
|
+
}
|
|
137
|
+
try {
|
|
138
|
+
const result = await convexClient().action("auth:signIn", {
|
|
139
|
+
refreshToken,
|
|
140
|
+
});
|
|
141
|
+
if (result.tokens === undefined) {
|
|
142
|
+
throw new Error("Invalid `auth:signIn` result for token refresh");
|
|
143
|
+
}
|
|
144
|
+
logVerbose(`Refreshed tokens, null=${result.tokens === null}`);
|
|
145
|
+
return result.tokens;
|
|
146
|
+
}
|
|
147
|
+
catch (error) {
|
|
148
|
+
console.error(error);
|
|
149
|
+
logVerbose("Token refresh failed, clearing auth cookies");
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
return {
|
|
154
|
+
token(request) {
|
|
155
|
+
return parseRequestCookies(request).token;
|
|
156
|
+
},
|
|
157
|
+
async verify(request) {
|
|
158
|
+
const token = parseRequestCookies(request).token;
|
|
159
|
+
if (token === null) {
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
const decodedToken = decodeToken(token);
|
|
163
|
+
if (decodedToken?.exp === undefined) {
|
|
164
|
+
return false;
|
|
165
|
+
}
|
|
166
|
+
return decodedToken.exp * 1000 > Date.now();
|
|
167
|
+
},
|
|
168
|
+
async proxy(request) {
|
|
169
|
+
const requestUrl = new URL(request.url);
|
|
170
|
+
if (!shouldProxyAuthAction(requestUrl.pathname, apiRoute)) {
|
|
171
|
+
return new Response("Invalid route", { status: 404 });
|
|
172
|
+
}
|
|
173
|
+
if (request.method !== "POST") {
|
|
174
|
+
return new Response("Invalid method", { status: 405 });
|
|
175
|
+
}
|
|
176
|
+
if (isCorsRequest(request)) {
|
|
177
|
+
return new Response("Invalid origin", { status: 403 });
|
|
178
|
+
}
|
|
179
|
+
const body = await request.json();
|
|
180
|
+
const action = body.action;
|
|
181
|
+
const args = (body.args ?? {});
|
|
182
|
+
if (action !== "auth:signIn" && action !== "auth:signOut") {
|
|
183
|
+
return new Response("Invalid action", { status: 400 });
|
|
184
|
+
}
|
|
185
|
+
const currentCookies = parseRequestCookies(request);
|
|
186
|
+
const host = cookieHost(request);
|
|
187
|
+
if (action === "auth:signIn") {
|
|
188
|
+
if (args.refreshToken !== undefined) {
|
|
189
|
+
if (currentCookies.refreshToken === null) {
|
|
190
|
+
return jsonResponse({ tokens: null });
|
|
191
|
+
}
|
|
192
|
+
args.refreshToken = currentCookies.refreshToken;
|
|
193
|
+
}
|
|
194
|
+
const client = convexClient(args.refreshToken !== undefined || args.params?.code !== undefined
|
|
195
|
+
? null
|
|
196
|
+
: currentCookies.token);
|
|
197
|
+
try {
|
|
198
|
+
const result = await client.action("auth:signIn", args);
|
|
199
|
+
if (result.redirect !== undefined) {
|
|
200
|
+
const response = jsonResponse({ redirect: result.redirect });
|
|
201
|
+
return attachCookies(response, serializeAuthCookies({
|
|
202
|
+
...currentCookies,
|
|
203
|
+
verifier: result.verifier ?? null,
|
|
204
|
+
}, host, cookieConfig));
|
|
205
|
+
}
|
|
206
|
+
if (result.tokens !== undefined) {
|
|
207
|
+
const response = jsonResponse({
|
|
208
|
+
tokens: result.tokens === null
|
|
209
|
+
? null
|
|
210
|
+
: { token: result.tokens.token, refreshToken: "dummy" },
|
|
211
|
+
});
|
|
212
|
+
return attachCookies(response, serializeAuthCookies({
|
|
213
|
+
token: result.tokens?.token ?? null,
|
|
214
|
+
refreshToken: result.tokens?.refreshToken ?? null,
|
|
215
|
+
verifier: null,
|
|
216
|
+
}, host, cookieConfig));
|
|
217
|
+
}
|
|
218
|
+
return jsonResponse(result);
|
|
219
|
+
}
|
|
220
|
+
catch (error) {
|
|
221
|
+
const response = jsonResponse({ error: error.message }, 400);
|
|
222
|
+
return attachCookies(response, serializeAuthCookies({
|
|
223
|
+
token: null,
|
|
224
|
+
refreshToken: null,
|
|
225
|
+
verifier: null,
|
|
226
|
+
}, host, cookieConfig));
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
try {
|
|
230
|
+
await convexClient(currentCookies.token).action("auth:signOut");
|
|
231
|
+
}
|
|
232
|
+
catch (error) {
|
|
233
|
+
console.error(error);
|
|
234
|
+
}
|
|
235
|
+
return attachCookies(jsonResponse(null), serializeAuthCookies({
|
|
236
|
+
token: null,
|
|
237
|
+
refreshToken: null,
|
|
238
|
+
verifier: null,
|
|
239
|
+
}, host, cookieConfig));
|
|
240
|
+
},
|
|
241
|
+
async refresh(request) {
|
|
242
|
+
const host = cookieHost(request);
|
|
243
|
+
if (isCorsRequest(request)) {
|
|
244
|
+
return {
|
|
245
|
+
cookies: serializeAuthCookies({
|
|
246
|
+
token: null,
|
|
247
|
+
refreshToken: null,
|
|
248
|
+
verifier: null,
|
|
249
|
+
}, host, cookieConfig),
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
const requestUrl = new URL(request.url);
|
|
253
|
+
const code = requestUrl.searchParams.get("code");
|
|
254
|
+
const shouldHandleCode = options.shouldHandleCode === undefined
|
|
255
|
+
? true
|
|
256
|
+
: typeof options.shouldHandleCode === "function"
|
|
257
|
+
? await options.shouldHandleCode(request)
|
|
258
|
+
: options.shouldHandleCode;
|
|
259
|
+
if (code !== null &&
|
|
260
|
+
request.method === "GET" &&
|
|
261
|
+
request.headers.get("accept")?.includes("text/html") &&
|
|
262
|
+
shouldHandleCode) {
|
|
263
|
+
const requestCookies = parseRequestCookies(request);
|
|
264
|
+
const redirectUrl = new URL(requestUrl);
|
|
265
|
+
redirectUrl.searchParams.delete("code");
|
|
266
|
+
try {
|
|
267
|
+
const result = await convexClient().action("auth:signIn", {
|
|
268
|
+
params: { code },
|
|
269
|
+
verifier: requestCookies.verifier ?? undefined,
|
|
270
|
+
});
|
|
271
|
+
if (result.tokens === undefined) {
|
|
272
|
+
throw new Error("Invalid `auth:signIn` result for code exchange");
|
|
273
|
+
}
|
|
274
|
+
const response = Response.redirect(redirectUrl.toString(), 302);
|
|
275
|
+
return {
|
|
276
|
+
response: attachCookies(response, serializeAuthCookies({
|
|
277
|
+
token: result.tokens?.token ?? null,
|
|
278
|
+
refreshToken: result.tokens?.refreshToken ?? null,
|
|
279
|
+
verifier: null,
|
|
280
|
+
}, host, cookieConfig)),
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
catch (error) {
|
|
284
|
+
console.error(error);
|
|
285
|
+
const response = Response.redirect(redirectUrl.toString(), 302);
|
|
286
|
+
return {
|
|
287
|
+
response: attachCookies(response, serializeAuthCookies({
|
|
288
|
+
token: null,
|
|
289
|
+
refreshToken: null,
|
|
290
|
+
verifier: null,
|
|
291
|
+
}, host, cookieConfig)),
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
const tokens = await refreshTokens(request);
|
|
296
|
+
if (tokens === undefined) {
|
|
297
|
+
return {};
|
|
298
|
+
}
|
|
299
|
+
return {
|
|
300
|
+
cookies: serializeAuthCookies({
|
|
301
|
+
token: tokens?.token ?? null,
|
|
302
|
+
refreshToken: tokens?.refreshToken ?? null,
|
|
303
|
+
verifier: null,
|
|
304
|
+
}, host, cookieConfig),
|
|
305
|
+
};
|
|
306
|
+
},
|
|
307
|
+
};
|
|
308
|
+
}
|
|
54
309
|
//# sourceMappingURL=index.js.map
|