@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,19 +1,17 @@
|
|
|
1
1
|
import { OAuth2Config, OAuthConfig } from "@auth/core/providers";
|
|
2
2
|
import {
|
|
3
3
|
Auth,
|
|
4
|
-
DocumentByName,
|
|
5
4
|
GenericActionCtx,
|
|
6
5
|
GenericDataModel,
|
|
7
6
|
HttpRouter,
|
|
8
|
-
WithoutSystemFields,
|
|
9
7
|
actionGeneric,
|
|
10
8
|
httpActionGeneric,
|
|
11
9
|
internalMutationGeneric,
|
|
12
10
|
} from "convex/server";
|
|
13
|
-
import { ConvexError, GenericId,
|
|
11
|
+
import { ConvexError, GenericId, v } from "convex/values";
|
|
14
12
|
import { parse as parseCookies, serialize as serializeCookie } from "cookie";
|
|
15
13
|
import { redirectToParamCookie, useRedirectToParam } from "../cookies.js";
|
|
16
|
-
import { FunctionReferenceFromExport
|
|
14
|
+
import { FunctionReferenceFromExport } from "../convex_types.js";
|
|
17
15
|
import {
|
|
18
16
|
configDefaults,
|
|
19
17
|
listAvailableProviders,
|
|
@@ -22,7 +20,6 @@ import {
|
|
|
22
20
|
import {
|
|
23
21
|
AuthProviderConfig,
|
|
24
22
|
ConvexAuthConfig,
|
|
25
|
-
GenericActionCtxWithAuthConfig,
|
|
26
23
|
} from "../types.js";
|
|
27
24
|
import { requireEnv } from "../utils.js";
|
|
28
25
|
import { ActionCtx, MutationCtx, Tokens } from "./types.js";
|
|
@@ -53,7 +50,6 @@ import {
|
|
|
53
50
|
oAuthConfigToInternalProvider,
|
|
54
51
|
} from "../oauth/convexAuth.js";
|
|
55
52
|
import { handleOAuth } from "../oauth/callback.js";
|
|
56
|
-
export { getAuthSessionId } from "./sessions.js";
|
|
57
53
|
|
|
58
54
|
/**
|
|
59
55
|
* The type of the signIn Convex Action returned from the auth() helper.
|
|
@@ -118,15 +114,25 @@ export function Auth(config_: ConvexAuthConfig) {
|
|
|
118
114
|
}
|
|
119
115
|
return provider;
|
|
120
116
|
};
|
|
121
|
-
const enrichCtx = <DataModel extends GenericDataModel>(
|
|
122
|
-
ctx: GenericActionCtx<DataModel>,
|
|
123
|
-
) => ({ ...ctx, auth: { ...ctx.auth, config } });
|
|
124
117
|
type ComponentCtx = Pick<
|
|
125
118
|
GenericActionCtx<GenericDataModel>,
|
|
126
119
|
"runQuery" | "runMutation"
|
|
127
120
|
>;
|
|
128
121
|
type ComponentReadCtx = Pick<GenericActionCtx<GenericDataModel>, "runQuery">;
|
|
129
122
|
type ComponentAuthReadCtx = ComponentReadCtx & { auth: Auth };
|
|
123
|
+
type AccountCredentials = { id: string; secret?: string };
|
|
124
|
+
type CreateAccountArgs = {
|
|
125
|
+
provider: string;
|
|
126
|
+
account: AccountCredentials;
|
|
127
|
+
profile: Record<string, unknown>;
|
|
128
|
+
shouldLinkViaEmail?: boolean;
|
|
129
|
+
shouldLinkViaPhone?: boolean;
|
|
130
|
+
};
|
|
131
|
+
type RetrieveAccountArgs = { provider: string; account: AccountCredentials };
|
|
132
|
+
type UpdateAccountCredentialsArgs = {
|
|
133
|
+
provider: string;
|
|
134
|
+
account: { id: string; secret: string };
|
|
135
|
+
};
|
|
130
136
|
|
|
131
137
|
const auth = {
|
|
132
138
|
user: {
|
|
@@ -198,6 +204,97 @@ export function Auth(config_: ConvexAuthConfig) {
|
|
|
198
204
|
},
|
|
199
205
|
},
|
|
200
206
|
},
|
|
207
|
+
session: {
|
|
208
|
+
/**
|
|
209
|
+
* Get the current session ID from the auth context, or `null` if
|
|
210
|
+
* not signed in.
|
|
211
|
+
*/
|
|
212
|
+
current: async (ctx: { auth: Auth }) => {
|
|
213
|
+
const identity = await ctx.auth.getUserIdentity();
|
|
214
|
+
if (identity === null) {
|
|
215
|
+
return null;
|
|
216
|
+
}
|
|
217
|
+
const [, sessionId] = identity.subject.split(TOKEN_SUB_CLAIM_DIVIDER);
|
|
218
|
+
return sessionId as GenericId<"session">;
|
|
219
|
+
},
|
|
220
|
+
/**
|
|
221
|
+
* Invalidate sessions for a user, optionally preserving specific sessions.
|
|
222
|
+
*/
|
|
223
|
+
invalidate: async <DataModel extends GenericDataModel>(
|
|
224
|
+
ctx: GenericActionCtx<DataModel>,
|
|
225
|
+
args: {
|
|
226
|
+
userId: GenericId<"user">;
|
|
227
|
+
except?: GenericId<"session">[];
|
|
228
|
+
},
|
|
229
|
+
): Promise<void> => {
|
|
230
|
+
const actionCtx = ctx as unknown as ActionCtx;
|
|
231
|
+
return await callInvalidateSessions(actionCtx, args);
|
|
232
|
+
},
|
|
233
|
+
},
|
|
234
|
+
account: {
|
|
235
|
+
/**
|
|
236
|
+
* Create an account and user for a credentials provider.
|
|
237
|
+
*/
|
|
238
|
+
create: async <DataModel extends GenericDataModel>(
|
|
239
|
+
ctx: GenericActionCtx<DataModel>,
|
|
240
|
+
args: CreateAccountArgs,
|
|
241
|
+
) => {
|
|
242
|
+
const actionCtx = ctx as unknown as ActionCtx;
|
|
243
|
+
return await callCreateAccountFromCredentials(actionCtx, args as any);
|
|
244
|
+
},
|
|
245
|
+
/**
|
|
246
|
+
* Retrieve an account and user for a credentials provider.
|
|
247
|
+
*/
|
|
248
|
+
get: async <DataModel extends GenericDataModel>(
|
|
249
|
+
ctx: GenericActionCtx<DataModel>,
|
|
250
|
+
args: RetrieveAccountArgs,
|
|
251
|
+
) => {
|
|
252
|
+
const actionCtx = ctx as unknown as ActionCtx;
|
|
253
|
+
const result = await callRetreiveAccountWithCredentials(actionCtx, args);
|
|
254
|
+
if (typeof result === "string") {
|
|
255
|
+
throw new Error(result);
|
|
256
|
+
}
|
|
257
|
+
return result;
|
|
258
|
+
},
|
|
259
|
+
/**
|
|
260
|
+
* Update credentials for an existing account.
|
|
261
|
+
*/
|
|
262
|
+
updateCredentials: async <DataModel extends GenericDataModel>(
|
|
263
|
+
ctx: GenericActionCtx<DataModel>,
|
|
264
|
+
args: UpdateAccountCredentialsArgs,
|
|
265
|
+
): Promise<void> => {
|
|
266
|
+
const actionCtx = ctx as unknown as ActionCtx;
|
|
267
|
+
return await callModifyAccount(actionCtx, args);
|
|
268
|
+
},
|
|
269
|
+
},
|
|
270
|
+
provider: {
|
|
271
|
+
/**
|
|
272
|
+
* Sign in via another provider, typically from a credentials flow.
|
|
273
|
+
*/
|
|
274
|
+
signIn: async <DataModel extends GenericDataModel>(
|
|
275
|
+
ctx: GenericActionCtx<DataModel>,
|
|
276
|
+
provider: AuthProviderConfig,
|
|
277
|
+
args: {
|
|
278
|
+
accountId?: GenericId<"account">;
|
|
279
|
+
params?: Record<string, unknown>;
|
|
280
|
+
},
|
|
281
|
+
) => {
|
|
282
|
+
const result = await signInImpl(
|
|
283
|
+
enrichCtx(ctx),
|
|
284
|
+
materializeProvider(provider),
|
|
285
|
+
args as any,
|
|
286
|
+
{
|
|
287
|
+
generateTokens: false,
|
|
288
|
+
allowExtraProviders: true,
|
|
289
|
+
},
|
|
290
|
+
);
|
|
291
|
+
return result.kind === "signedIn"
|
|
292
|
+
? result.signedIn !== null
|
|
293
|
+
? { userId: result.signedIn.userId, sessionId: result.signedIn.sessionId }
|
|
294
|
+
: null
|
|
295
|
+
: null;
|
|
296
|
+
},
|
|
297
|
+
},
|
|
201
298
|
/**
|
|
202
299
|
* Hierarchical group management. Groups can nest arbitrarily deep
|
|
203
300
|
* via `parentGroupId`. A root group has no parent.
|
|
@@ -349,12 +446,15 @@ export function Auth(config_: ConvexAuthConfig) {
|
|
|
349
446
|
* Create a new invitation.
|
|
350
447
|
*
|
|
351
448
|
* @param data.groupId - Optional group to invite the user into.
|
|
352
|
-
* @param data.invitedByUserId -
|
|
353
|
-
*
|
|
449
|
+
* @param data.invitedByUserId - Optional user sending the invitation
|
|
450
|
+
* (omit for CLI-generated invites).
|
|
451
|
+
* @param data.email - Optional email of the invitee (omit for
|
|
452
|
+
* CLI-generated invite links where the email is unknown upfront).
|
|
354
453
|
* @param data.tokenHash - Hashed token for secure acceptance.
|
|
355
454
|
* @param data.role - Optional role to assign on acceptance.
|
|
356
455
|
* @param data.status - Initial status (typically "pending").
|
|
357
|
-
* @param data.expiresTime -
|
|
456
|
+
* @param data.expiresTime - Optional expiration timestamp (omit for
|
|
457
|
+
* single-use, non-expiring invites).
|
|
358
458
|
* @param data.extend - Optional arbitrary JSON extension data.
|
|
359
459
|
* @throws ConvexError with code `DUPLICATE_INVITE` if a pending invite
|
|
360
460
|
* already exists for this email and scope.
|
|
@@ -364,12 +464,12 @@ export function Auth(config_: ConvexAuthConfig) {
|
|
|
364
464
|
ctx: ComponentCtx,
|
|
365
465
|
data: {
|
|
366
466
|
groupId?: string;
|
|
367
|
-
invitedByUserId
|
|
368
|
-
email
|
|
467
|
+
invitedByUserId?: string;
|
|
468
|
+
email?: string;
|
|
369
469
|
tokenHash: string;
|
|
370
470
|
role?: string;
|
|
371
471
|
status: "pending" | "accepted" | "revoked" | "expired";
|
|
372
|
-
expiresTime
|
|
472
|
+
expiresTime?: number;
|
|
373
473
|
extend?: Record<string, unknown>;
|
|
374
474
|
},
|
|
375
475
|
): Promise<string> => {
|
|
@@ -381,6 +481,12 @@ export function Auth(config_: ConvexAuthConfig) {
|
|
|
381
481
|
get: async (ctx: ComponentReadCtx, inviteId: string) => {
|
|
382
482
|
return await ctx.runQuery(config.component.public.inviteGet, { inviteId });
|
|
383
483
|
},
|
|
484
|
+
/**
|
|
485
|
+
* Retrieve an invite by its token hash. Returns `null` if not found.
|
|
486
|
+
*/
|
|
487
|
+
getByTokenHash: async (ctx: ComponentReadCtx, tokenHash: string) => {
|
|
488
|
+
return await ctx.runQuery(config.component.public.inviteGetByTokenHash, { tokenHash });
|
|
489
|
+
},
|
|
384
490
|
/**
|
|
385
491
|
* List invites, optionally filtered by group and/or status.
|
|
386
492
|
*/
|
|
@@ -427,8 +533,11 @@ export function Auth(config_: ConvexAuthConfig) {
|
|
|
427
533
|
* });
|
|
428
534
|
* ```
|
|
429
535
|
*/
|
|
430
|
-
accept: async (ctx: ComponentCtx, inviteId: string) => {
|
|
431
|
-
await ctx.runMutation(config.component.public.inviteAccept, {
|
|
536
|
+
accept: async (ctx: ComponentCtx, inviteId: string, acceptedByUserId?: string) => {
|
|
537
|
+
await ctx.runMutation(config.component.public.inviteAccept, {
|
|
538
|
+
inviteId,
|
|
539
|
+
...(acceptedByUserId ? { acceptedByUserId } : {}),
|
|
540
|
+
});
|
|
432
541
|
},
|
|
433
542
|
/**
|
|
434
543
|
* Revoke a pending invitation.
|
|
@@ -442,6 +551,86 @@ export function Auth(config_: ConvexAuthConfig) {
|
|
|
442
551
|
await ctx.runMutation(config.component.public.inviteRevoke, { inviteId });
|
|
443
552
|
},
|
|
444
553
|
},
|
|
554
|
+
/**
|
|
555
|
+
* Manage passkey credentials for users.
|
|
556
|
+
*
|
|
557
|
+
* ```ts
|
|
558
|
+
* const passkeys = await auth.passkey.list(ctx, { userId });
|
|
559
|
+
* await auth.passkey.rename(ctx, passkeyId, "MacBook Touch ID");
|
|
560
|
+
* await auth.passkey.remove(ctx, passkeyId);
|
|
561
|
+
* ```
|
|
562
|
+
*/
|
|
563
|
+
passkey: {
|
|
564
|
+
/**
|
|
565
|
+
* List all passkeys for a user.
|
|
566
|
+
*
|
|
567
|
+
* @param opts.userId - The user whose passkeys to list.
|
|
568
|
+
* @returns Array of passkey records with credentialId, name, deviceType,
|
|
569
|
+
* backedUp, createdAt, and lastUsedAt.
|
|
570
|
+
*/
|
|
571
|
+
list: async (ctx: ComponentReadCtx, opts: { userId: string }) => {
|
|
572
|
+
return await ctx.runQuery(
|
|
573
|
+
config.component.public.passkeyListByUserId,
|
|
574
|
+
opts,
|
|
575
|
+
);
|
|
576
|
+
},
|
|
577
|
+
/**
|
|
578
|
+
* Rename a passkey (set a user-friendly display name).
|
|
579
|
+
*
|
|
580
|
+
* @param passkeyId - The passkey document ID.
|
|
581
|
+
* @param name - New display name (e.g. "MacBook Touch ID").
|
|
582
|
+
*/
|
|
583
|
+
rename: async (ctx: ComponentCtx, passkeyId: string, name: string) => {
|
|
584
|
+
await ctx.runMutation(
|
|
585
|
+
config.component.public.passkeyUpdateMeta,
|
|
586
|
+
{ passkeyId, data: { name } },
|
|
587
|
+
);
|
|
588
|
+
},
|
|
589
|
+
/**
|
|
590
|
+
* Delete a passkey credential.
|
|
591
|
+
*
|
|
592
|
+
* @param passkeyId - The passkey document ID to remove.
|
|
593
|
+
*/
|
|
594
|
+
remove: async (ctx: ComponentCtx, passkeyId: string) => {
|
|
595
|
+
await ctx.runMutation(
|
|
596
|
+
config.component.public.passkeyDelete,
|
|
597
|
+
{ passkeyId },
|
|
598
|
+
);
|
|
599
|
+
},
|
|
600
|
+
},
|
|
601
|
+
/**
|
|
602
|
+
* Manage TOTP two-factor authentication enrollments for users.
|
|
603
|
+
*
|
|
604
|
+
* ```ts
|
|
605
|
+
* const enrollments = await auth.totp.list(ctx, { userId });
|
|
606
|
+
* await auth.totp.remove(ctx, totpId);
|
|
607
|
+
* ```
|
|
608
|
+
*/
|
|
609
|
+
totp: {
|
|
610
|
+
/**
|
|
611
|
+
* List all TOTP enrollments for a user.
|
|
612
|
+
*
|
|
613
|
+
* @param opts.userId - The user whose enrollments to list.
|
|
614
|
+
* @returns Array of TOTP enrollment records.
|
|
615
|
+
*/
|
|
616
|
+
list: async (ctx: ComponentReadCtx, opts: { userId: string }) => {
|
|
617
|
+
return await ctx.runQuery(
|
|
618
|
+
config.component.public.totpListByUserId,
|
|
619
|
+
opts,
|
|
620
|
+
);
|
|
621
|
+
},
|
|
622
|
+
/**
|
|
623
|
+
* Delete a TOTP enrollment.
|
|
624
|
+
*
|
|
625
|
+
* @param totpId - The TOTP document ID to remove.
|
|
626
|
+
*/
|
|
627
|
+
remove: async (ctx: ComponentCtx, totpId: string) => {
|
|
628
|
+
await ctx.runMutation(
|
|
629
|
+
config.component.public.totpDelete,
|
|
630
|
+
{ totpId },
|
|
631
|
+
);
|
|
632
|
+
},
|
|
633
|
+
},
|
|
445
634
|
/**
|
|
446
635
|
* Add HTTP actions for JWT verification and OAuth sign-in.
|
|
447
636
|
*
|
|
@@ -655,6 +844,19 @@ export function Auth(config_: ConvexAuthConfig) {
|
|
|
655
844
|
}
|
|
656
845
|
},
|
|
657
846
|
};
|
|
847
|
+
const enrichCtx = <DataModel extends GenericDataModel>(
|
|
848
|
+
ctx: GenericActionCtx<DataModel>,
|
|
849
|
+
) => ({
|
|
850
|
+
...ctx,
|
|
851
|
+
auth: {
|
|
852
|
+
...ctx.auth,
|
|
853
|
+
config,
|
|
854
|
+
account: auth.account,
|
|
855
|
+
session: auth.session,
|
|
856
|
+
provider: auth.provider,
|
|
857
|
+
},
|
|
858
|
+
});
|
|
859
|
+
|
|
658
860
|
return {
|
|
659
861
|
/**
|
|
660
862
|
* Helper for configuring HTTP actions.
|
|
@@ -681,6 +883,9 @@ export function Auth(config_: ConvexAuthConfig) {
|
|
|
681
883
|
verifier?: string;
|
|
682
884
|
tokens?: Tokens | null;
|
|
683
885
|
started?: boolean;
|
|
886
|
+
options?: Record<string, any>;
|
|
887
|
+
totpRequired?: boolean;
|
|
888
|
+
totpSetup?: { uri: string; secret: string; totpId: string };
|
|
684
889
|
}> => {
|
|
685
890
|
if (args.calledBy !== undefined) {
|
|
686
891
|
logWithLevel("INFO", `\`auth:signIn\` called by ${args.calledBy}`);
|
|
@@ -701,6 +906,12 @@ export function Auth(config_: ConvexAuthConfig) {
|
|
|
701
906
|
return { tokens: result.signedIn?.tokens ?? null };
|
|
702
907
|
case "started":
|
|
703
908
|
return { started: true };
|
|
909
|
+
case "passkeyOptions":
|
|
910
|
+
return { options: result.options, verifier: result.verifier };
|
|
911
|
+
case "totpRequired":
|
|
912
|
+
return { totpRequired: true, verifier: result.verifier };
|
|
913
|
+
case "totpSetup":
|
|
914
|
+
return { totpSetup: { uri: result.uri, secret: result.secret, totpId: result.totpId }, verifier: result.verifier };
|
|
704
915
|
default: {
|
|
705
916
|
const _typecheck: never = result;
|
|
706
917
|
throw new Error(`Unexpected result from signIn, ${result as any}`);
|
|
@@ -732,228 +943,6 @@ export function Auth(config_: ConvexAuthConfig) {
|
|
|
732
943
|
};
|
|
733
944
|
}
|
|
734
945
|
|
|
735
|
-
/**
|
|
736
|
-
* Return the currently signed-in user's ID.
|
|
737
|
-
*
|
|
738
|
-
* ```ts filename="convex/myFunctions.tsx"
|
|
739
|
-
* import { mutation } from "./_generated/server";
|
|
740
|
-
* import { getAuthUserId } from "@robelest/convex-auth/component";
|
|
741
|
-
*
|
|
742
|
-
* export const doSomething = mutation({
|
|
743
|
-
* args: {/* ... *\/},
|
|
744
|
-
* handler: async (ctx, args) => {
|
|
745
|
-
* const userId = await getAuthUserId(ctx);
|
|
746
|
-
* if (userId === null) {
|
|
747
|
-
* throw new Error("Client is not authenticated!")
|
|
748
|
-
* }
|
|
749
|
-
* const user = await ctx.db.get(userId);
|
|
750
|
-
* // ...
|
|
751
|
-
* },
|
|
752
|
-
* });
|
|
753
|
-
* ```
|
|
754
|
-
*
|
|
755
|
-
* @param ctx query, mutation or action `ctx`
|
|
756
|
-
* @returns the user ID or `null` if the client isn't authenticated
|
|
757
|
-
*/
|
|
758
|
-
export async function getAuthUserId(ctx: { auth: Auth }) {
|
|
759
|
-
const identity = await ctx.auth.getUserIdentity();
|
|
760
|
-
if (identity === null) {
|
|
761
|
-
return null;
|
|
762
|
-
}
|
|
763
|
-
const [userId] = identity.subject.split(TOKEN_SUB_CLAIM_DIVIDER);
|
|
764
|
-
return userId as GenericId<"user">;
|
|
765
|
-
}
|
|
766
|
-
|
|
767
|
-
/**
|
|
768
|
-
* Use this function from a
|
|
769
|
-
* [`ConvexCredentials`](https://labs.convex.dev/auth/api_reference/providers/ConvexCredentials)
|
|
770
|
-
* provider to create an account and a user with a unique account "id" (OAuth
|
|
771
|
-
* provider ID, email address, phone number, username etc.).
|
|
772
|
-
*
|
|
773
|
-
* @returns user ID if it successfully creates the account
|
|
774
|
-
* or throws an error.
|
|
775
|
-
*/
|
|
776
|
-
export async function createAccount<
|
|
777
|
-
DataModel extends GenericDataModel = GenericDataModel,
|
|
778
|
-
>(
|
|
779
|
-
ctx: GenericActionCtx<DataModel>,
|
|
780
|
-
args: {
|
|
781
|
-
/**
|
|
782
|
-
* The provider ID (like "password"), used to disambiguate accounts.
|
|
783
|
-
*
|
|
784
|
-
* It is also used to configure account secret hashing via the provider's
|
|
785
|
-
* `crypto` option.
|
|
786
|
-
*/
|
|
787
|
-
provider: string;
|
|
788
|
-
account: {
|
|
789
|
-
/**
|
|
790
|
-
* The unique external ID for the account, for example email address.
|
|
791
|
-
*/
|
|
792
|
-
id: string;
|
|
793
|
-
/**
|
|
794
|
-
* The secret credential to store for this account, if given.
|
|
795
|
-
*/
|
|
796
|
-
secret?: string;
|
|
797
|
-
};
|
|
798
|
-
/**
|
|
799
|
-
* The profile data to store for the user.
|
|
800
|
-
* These must fit the `users` table schema.
|
|
801
|
-
*/
|
|
802
|
-
profile: WithoutSystemFields<DocumentByName<DataModel, "user">>;
|
|
803
|
-
/**
|
|
804
|
-
* If `true`, the account will be linked to an existing user
|
|
805
|
-
* with the same verified email address.
|
|
806
|
-
* This is only safe if the returned account's email is verified
|
|
807
|
-
* before the user is allowed to sign in with it.
|
|
808
|
-
*/
|
|
809
|
-
shouldLinkViaEmail?: boolean;
|
|
810
|
-
/**
|
|
811
|
-
* If `true`, the account will be linked to an existing user
|
|
812
|
-
* with the same verified phone number.
|
|
813
|
-
* This is only safe if the returned account's phone is verified
|
|
814
|
-
* before the user is allowed to sign in with it.
|
|
815
|
-
*/
|
|
816
|
-
shouldLinkViaPhone?: boolean;
|
|
817
|
-
},
|
|
818
|
-
): Promise<{
|
|
819
|
-
account: GenericDoc<DataModel, "account">;
|
|
820
|
-
user: GenericDoc<DataModel, "user">;
|
|
821
|
-
}> {
|
|
822
|
-
const actionCtx = ctx as unknown as ActionCtx;
|
|
823
|
-
return (await callCreateAccountFromCredentials(
|
|
824
|
-
actionCtx,
|
|
825
|
-
args as any,
|
|
826
|
-
)) as any;
|
|
827
|
-
}
|
|
828
|
-
|
|
829
|
-
/**
|
|
830
|
-
* Use this function from a
|
|
831
|
-
* [`ConvexCredentials`](https://labs.convex.dev/auth/api_reference/providers/ConvexCredentials)
|
|
832
|
-
* provider to retrieve a user given the account provider ID and
|
|
833
|
-
* the provider-specific account ID.
|
|
834
|
-
*
|
|
835
|
-
* @returns the retrieved user document, or `null` if there is no account
|
|
836
|
-
* for given account ID or throws if the provided
|
|
837
|
-
* secret does not match.
|
|
838
|
-
*/
|
|
839
|
-
export async function retrieveAccount<
|
|
840
|
-
DataModel extends GenericDataModel = GenericDataModel,
|
|
841
|
-
>(
|
|
842
|
-
ctx: GenericActionCtx<DataModel>,
|
|
843
|
-
args: {
|
|
844
|
-
/**
|
|
845
|
-
* The provider ID (like "password"), used to disambiguate accounts.
|
|
846
|
-
*
|
|
847
|
-
* It is also used to configure account secret hashing via the provider's
|
|
848
|
-
* `crypto` option.
|
|
849
|
-
*/
|
|
850
|
-
provider: string;
|
|
851
|
-
account: {
|
|
852
|
-
/**
|
|
853
|
-
* The unique external ID for the account, for example email address.
|
|
854
|
-
*/
|
|
855
|
-
id: string;
|
|
856
|
-
/**
|
|
857
|
-
* The secret that should match the stored credential, if given.
|
|
858
|
-
*/
|
|
859
|
-
secret?: string;
|
|
860
|
-
};
|
|
861
|
-
},
|
|
862
|
-
): Promise<{
|
|
863
|
-
account: GenericDoc<DataModel, "account">;
|
|
864
|
-
user: GenericDoc<DataModel, "user">;
|
|
865
|
-
}> {
|
|
866
|
-
const actionCtx = ctx as unknown as ActionCtx;
|
|
867
|
-
const result = await callRetreiveAccountWithCredentials(actionCtx, args);
|
|
868
|
-
if (typeof result === "string") {
|
|
869
|
-
throw new Error(result);
|
|
870
|
-
}
|
|
871
|
-
return result as any;
|
|
872
|
-
}
|
|
873
|
-
|
|
874
|
-
/**
|
|
875
|
-
* Use this function to modify the account credentials
|
|
876
|
-
* from a [`ConvexCredentials`](https://labs.convex.dev/auth/api_reference/providers/ConvexCredentials)
|
|
877
|
-
* provider.
|
|
878
|
-
*/
|
|
879
|
-
export async function modifyAccountCredentials<
|
|
880
|
-
DataModel extends GenericDataModel = GenericDataModel,
|
|
881
|
-
>(
|
|
882
|
-
ctx: GenericActionCtx<DataModel>,
|
|
883
|
-
args: {
|
|
884
|
-
/**
|
|
885
|
-
* The provider ID (like "password"), used to disambiguate accounts.
|
|
886
|
-
*
|
|
887
|
-
* It is also used to configure account secret hashing via the `crypto` option.
|
|
888
|
-
*/
|
|
889
|
-
provider: string;
|
|
890
|
-
account: {
|
|
891
|
-
/**
|
|
892
|
-
* The unique external ID for the account, for example email address.
|
|
893
|
-
*/
|
|
894
|
-
id: string;
|
|
895
|
-
/**
|
|
896
|
-
* The new secret credential to store for this account.
|
|
897
|
-
*/
|
|
898
|
-
secret: string;
|
|
899
|
-
};
|
|
900
|
-
},
|
|
901
|
-
): Promise<void> {
|
|
902
|
-
const actionCtx = ctx as unknown as ActionCtx;
|
|
903
|
-
return await callModifyAccount(actionCtx, args);
|
|
904
|
-
}
|
|
905
|
-
|
|
906
|
-
/**
|
|
907
|
-
* Use this function to invalidate existing sessions.
|
|
908
|
-
*/
|
|
909
|
-
export async function invalidateSessions<
|
|
910
|
-
DataModel extends GenericDataModel = GenericDataModel,
|
|
911
|
-
>(
|
|
912
|
-
ctx: GenericActionCtx<DataModel>,
|
|
913
|
-
args: {
|
|
914
|
-
userId: GenericId<"user">;
|
|
915
|
-
except?: GenericId<"session">[];
|
|
916
|
-
},
|
|
917
|
-
): Promise<void> {
|
|
918
|
-
const actionCtx = ctx as unknown as ActionCtx;
|
|
919
|
-
return await callInvalidateSessions(actionCtx, args);
|
|
920
|
-
}
|
|
921
|
-
|
|
922
|
-
/**
|
|
923
|
-
* Use this function from a
|
|
924
|
-
* [`ConvexCredentials`](https://labs.convex.dev/auth/api_reference/providers/ConvexCredentials)
|
|
925
|
-
* provider to sign in the user via another provider (usually
|
|
926
|
-
* for email verification on sign up or password reset).
|
|
927
|
-
*
|
|
928
|
-
* Returns the user ID if the sign can proceed,
|
|
929
|
-
* or `null`.
|
|
930
|
-
*/
|
|
931
|
-
export async function signInViaProvider<
|
|
932
|
-
DataModel extends GenericDataModel = GenericDataModel,
|
|
933
|
-
>(
|
|
934
|
-
ctx: GenericActionCtxWithAuthConfig<DataModel>,
|
|
935
|
-
provider: AuthProviderConfig,
|
|
936
|
-
args: {
|
|
937
|
-
accountId?: GenericId<"account">;
|
|
938
|
-
params?: Record<string, Value | undefined>;
|
|
939
|
-
},
|
|
940
|
-
) {
|
|
941
|
-
const result = await signInImpl(
|
|
942
|
-
ctx,
|
|
943
|
-
materializeProvider(provider),
|
|
944
|
-
args as any,
|
|
945
|
-
{
|
|
946
|
-
generateTokens: false,
|
|
947
|
-
allowExtraProviders: true,
|
|
948
|
-
},
|
|
949
|
-
);
|
|
950
|
-
return result.kind === "signedIn"
|
|
951
|
-
? result.signedIn !== null
|
|
952
|
-
? { userId: result.signedIn.userId, sessionId: result.signedIn.sessionId }
|
|
953
|
-
: null
|
|
954
|
-
: null;
|
|
955
|
-
}
|
|
956
|
-
|
|
957
946
|
function convertErrorsToResponse(
|
|
958
947
|
errorStatusCode: number,
|
|
959
948
|
action: (ctx: GenericActionCtx<any>, request: Request) => Promise<Response>,
|