@robelest/convex-auth 0.0.4-preview.13 → 0.0.4-preview.16
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/README.md +140 -9
- package/dist/bin.cjs +5957 -5478
- package/dist/client/index.d.ts +3 -7
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +27 -26
- package/dist/client/index.js.map +1 -1
- package/dist/component/_generated/api.d.ts +14 -0
- 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 +1672 -24
- package/dist/component/_generated/component.d.ts.map +1 -1
- package/dist/component/convex.config.d.ts +2 -2
- package/dist/component/convex.config.d.ts.map +1 -1
- package/dist/component/index.d.ts +1 -1
- package/dist/component/index.js +2 -2
- package/dist/component/model.d.ts +153 -0
- package/dist/component/model.d.ts.map +1 -0
- package/dist/component/model.js +343 -0
- package/dist/component/model.js.map +1 -0
- package/dist/component/providers/sso.d.ts +1 -1
- package/dist/component/public/enterprise.d.ts +54 -0
- package/dist/component/public/enterprise.d.ts.map +1 -0
- package/dist/component/public/enterprise.js +515 -0
- package/dist/component/public/enterprise.js.map +1 -0
- package/dist/component/public/factors.d.ts +52 -0
- package/dist/component/public/factors.d.ts.map +1 -0
- package/dist/component/public/factors.js +285 -0
- package/dist/component/public/factors.js.map +1 -0
- package/dist/component/public/groups.d.ts +116 -0
- package/dist/component/public/groups.d.ts.map +1 -0
- package/dist/component/public/groups.js +596 -0
- package/dist/component/public/groups.js.map +1 -0
- package/dist/component/public/identity.d.ts +93 -0
- package/dist/component/public/identity.d.ts.map +1 -0
- package/dist/component/public/identity.js +426 -0
- package/dist/component/public/identity.js.map +1 -0
- package/dist/component/public/keys.d.ts +41 -0
- package/dist/component/public/keys.d.ts.map +1 -0
- package/dist/component/public/keys.js +157 -0
- package/dist/component/public/keys.js.map +1 -0
- package/dist/component/public/shared.d.ts +26 -0
- package/dist/component/public/shared.d.ts.map +1 -0
- package/dist/component/public/shared.js +32 -0
- package/dist/component/public/shared.js.map +1 -0
- package/dist/component/public.d.ts +9 -321
- package/dist/component/public.d.ts.map +1 -1
- package/dist/component/public.js +6 -2145
- package/dist/component/schema.d.ts +406 -260
- package/dist/component/schema.js +37 -32
- package/dist/component/schema.js.map +1 -1
- package/dist/component/server/auth.d.ts +161 -15
- package/dist/component/server/auth.d.ts.map +1 -1
- package/dist/component/server/auth.js +100 -7
- package/dist/component/server/auth.js.map +1 -1
- package/dist/component/server/cookies.js +3 -0
- package/dist/component/server/cookies.js.map +1 -1
- package/dist/component/server/db.js +1 -0
- package/dist/component/server/db.js.map +1 -1
- package/dist/component/server/device.js +3 -1
- package/dist/component/server/device.js.map +1 -1
- package/dist/component/server/domains/core.js +629 -0
- package/dist/component/server/domains/core.js.map +1 -0
- package/dist/component/server/domains/sso.js +884 -0
- package/dist/component/server/domains/sso.js.map +1 -0
- package/dist/component/server/factory.d.ts +136 -0
- package/dist/component/server/factory.d.ts.map +1 -0
- package/dist/component/server/factory.js +1134 -0
- package/dist/component/server/factory.js.map +1 -0
- package/dist/component/server/fx.js +2 -1
- package/dist/component/server/fx.js.map +1 -1
- package/dist/component/server/http.js +287 -0
- package/dist/component/server/http.js.map +1 -0
- package/dist/component/server/identity.js +13 -0
- package/dist/component/server/identity.js.map +1 -0
- package/dist/component/server/keys.js +4 -0
- package/dist/component/server/keys.js.map +1 -1
- package/dist/component/server/mutations/account.js +1 -1
- package/dist/component/server/mutations/index.js +2 -2
- package/dist/component/server/mutations/index.js.map +1 -1
- package/dist/component/server/mutations/invalidate.js +1 -1
- package/dist/component/server/mutations/oauth.js +10 -7
- package/dist/component/server/mutations/oauth.js.map +1 -1
- package/dist/component/server/mutations/refresh.js +1 -1
- package/dist/component/server/mutations/register.js +1 -1
- package/dist/component/server/mutations/retrieve.js +1 -1
- package/dist/component/server/mutations/signature.js +1 -1
- package/dist/component/server/mutations/store.js +6 -3
- package/dist/component/server/mutations/store.js.map +1 -1
- package/dist/component/server/mutations/verify.js +1 -1
- package/dist/component/server/oauth.js +3 -0
- package/dist/component/server/oauth.js.map +1 -1
- package/dist/component/server/passkey.js +3 -2
- package/dist/component/server/passkey.js.map +1 -1
- package/dist/component/server/provider.js +2 -0
- package/dist/component/server/provider.js.map +1 -1
- package/dist/component/server/providers.js +10 -0
- package/dist/component/server/providers.js.map +1 -1
- package/dist/component/server/ratelimit.js +3 -0
- package/dist/component/server/ratelimit.js.map +1 -1
- package/dist/component/server/redirects.js +2 -0
- package/dist/component/server/redirects.js.map +1 -1
- package/dist/component/server/refresh.js +5 -0
- package/dist/component/server/refresh.js.map +1 -1
- package/dist/component/server/sessions.js +5 -0
- package/dist/component/server/sessions.js.map +1 -1
- package/dist/component/server/signin.js +2 -1
- package/dist/component/server/signin.js.map +1 -1
- package/dist/component/server/sso.js +166 -19
- package/dist/component/server/sso.js.map +1 -1
- package/dist/component/server/tokens.js +1 -0
- package/dist/component/server/tokens.js.map +1 -1
- package/dist/component/server/totp.js +4 -2
- package/dist/component/server/totp.js.map +1 -1
- package/dist/component/server/types.d.ts +106 -38
- package/dist/component/server/types.d.ts.map +1 -1
- package/dist/component/server/types.js.map +1 -1
- package/dist/component/server/users.js +1 -0
- package/dist/component/server/users.js.map +1 -1
- package/dist/component/server/utils.js +44 -2
- package/dist/component/server/utils.js.map +1 -1
- package/dist/providers/anonymous.d.ts +1 -1
- package/dist/providers/credentials.d.ts +1 -1
- package/dist/providers/password.d.ts +1 -1
- package/dist/providers/sso.d.ts +1 -1
- package/dist/providers/sso.js.map +1 -1
- package/dist/server/auth.d.ts +163 -17
- package/dist/server/auth.d.ts.map +1 -1
- package/dist/server/auth.js +100 -7
- package/dist/server/auth.js.map +1 -1
- package/dist/server/cookies.d.ts +1 -38
- package/dist/server/cookies.js +3 -0
- package/dist/server/cookies.js.map +1 -1
- package/dist/server/db.d.ts +1 -125
- package/dist/server/db.js +1 -0
- package/dist/server/db.js.map +1 -1
- package/dist/server/device.d.ts +1 -24
- package/dist/server/device.js +3 -1
- package/dist/server/device.js.map +1 -1
- package/dist/server/domains/core.d.ts +434 -0
- package/dist/server/domains/core.d.ts.map +1 -0
- package/dist/server/domains/core.js +629 -0
- package/dist/server/domains/core.js.map +1 -0
- package/dist/server/domains/sso.d.ts +409 -0
- package/dist/server/domains/sso.d.ts.map +1 -0
- package/dist/server/domains/sso.js +884 -0
- package/dist/server/domains/sso.js.map +1 -0
- package/dist/server/enterpriseValidators.d.ts +1 -0
- package/dist/server/enterpriseValidators.js +60 -0
- package/dist/server/enterpriseValidators.js.map +1 -0
- package/dist/server/factory.d.ts +136 -0
- package/dist/server/factory.d.ts.map +1 -0
- package/dist/server/factory.js +1134 -0
- package/dist/server/factory.js.map +1 -0
- package/dist/server/fx.d.ts +1 -16
- package/dist/server/fx.d.ts.map +1 -1
- package/dist/server/fx.js +1 -0
- package/dist/server/fx.js.map +1 -1
- package/dist/server/http.d.ts +59 -0
- package/dist/server/http.d.ts.map +1 -0
- package/dist/server/http.js +287 -0
- package/dist/server/http.js.map +1 -0
- package/dist/server/identity.d.ts +1 -0
- package/dist/server/identity.js +13 -0
- package/dist/server/identity.js.map +1 -0
- package/dist/server/index.d.ts +468 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +530 -36
- package/dist/server/index.js.map +1 -1
- package/dist/server/keys.d.ts +1 -57
- package/dist/server/keys.js +4 -0
- package/dist/server/keys.js.map +1 -1
- package/dist/server/mutations/account.d.ts +7 -7
- package/dist/server/mutations/account.d.ts.map +1 -1
- package/dist/server/mutations/code.d.ts +13 -13
- package/dist/server/mutations/code.d.ts.map +1 -1
- package/dist/server/mutations/index.d.ts +107 -107
- package/dist/server/mutations/index.d.ts.map +1 -1
- package/dist/server/mutations/index.js +1 -1
- package/dist/server/mutations/index.js.map +1 -1
- package/dist/server/mutations/invalidate.d.ts +5 -5
- package/dist/server/mutations/invalidate.d.ts.map +1 -1
- package/dist/server/mutations/oauth.d.ts +10 -10
- package/dist/server/mutations/oauth.d.ts.map +1 -1
- package/dist/server/mutations/oauth.js +9 -6
- package/dist/server/mutations/oauth.js.map +1 -1
- package/dist/server/mutations/refresh.d.ts +4 -4
- package/dist/server/mutations/register.d.ts +12 -12
- package/dist/server/mutations/register.d.ts.map +1 -1
- package/dist/server/mutations/retrieve.d.ts +7 -7
- package/dist/server/mutations/signature.d.ts +5 -5
- package/dist/server/mutations/signin.d.ts +6 -6
- package/dist/server/mutations/signin.d.ts.map +1 -1
- package/dist/server/mutations/signout.d.ts +1 -1
- package/dist/server/mutations/store.d.ts +3 -2
- package/dist/server/mutations/store.d.ts.map +1 -1
- package/dist/server/mutations/store.js +6 -3
- package/dist/server/mutations/store.js.map +1 -1
- package/dist/server/mutations/verifier.d.ts +1 -1
- package/dist/server/mutations/verify.d.ts +11 -11
- package/dist/server/mutations/verify.d.ts.map +1 -1
- package/dist/server/oauth.d.ts +1 -59
- package/dist/server/oauth.js +3 -0
- package/dist/server/oauth.js.map +1 -1
- package/dist/server/passkey.d.ts.map +1 -1
- package/dist/server/passkey.js +3 -2
- package/dist/server/passkey.js.map +1 -1
- package/dist/server/provider.d.ts +1 -14
- package/dist/server/provider.d.ts.map +1 -1
- package/dist/server/provider.js +2 -0
- package/dist/server/provider.js.map +1 -1
- package/dist/server/providers.js +10 -0
- package/dist/server/providers.js.map +1 -1
- package/dist/server/ratelimit.d.ts +1 -22
- package/dist/server/ratelimit.js +3 -0
- package/dist/server/ratelimit.js.map +1 -1
- package/dist/server/redirects.d.ts +1 -10
- package/dist/server/redirects.js +2 -0
- package/dist/server/redirects.js.map +1 -1
- package/dist/server/refresh.d.ts +1 -37
- package/dist/server/refresh.js +5 -0
- package/dist/server/refresh.js.map +1 -1
- package/dist/server/sessions.d.ts +1 -28
- package/dist/server/sessions.js +5 -0
- package/dist/server/sessions.js.map +1 -1
- package/dist/server/signin.d.ts +1 -55
- package/dist/server/signin.js +2 -1
- package/dist/server/signin.js.map +1 -1
- package/dist/server/sso.d.ts +1 -348
- package/dist/server/sso.js +165 -18
- package/dist/server/sso.js.map +1 -1
- package/dist/server/templates.d.ts +1 -21
- package/dist/server/templates.js +1 -0
- package/dist/server/templates.js.map +1 -1
- package/dist/server/tokens.d.ts +1 -11
- package/dist/server/tokens.js +1 -0
- package/dist/server/tokens.js.map +1 -1
- package/dist/server/totp.d.ts +1 -23
- package/dist/server/totp.js +4 -2
- package/dist/server/totp.js.map +1 -1
- package/dist/server/types.d.ts +114 -77
- package/dist/server/types.d.ts.map +1 -1
- package/dist/server/types.js.map +1 -1
- package/dist/server/users.d.ts +1 -31
- package/dist/server/users.js +1 -0
- package/dist/server/users.js.map +1 -1
- package/dist/server/utils.d.ts +1 -27
- package/dist/server/utils.js +44 -2
- package/dist/server/utils.js.map +1 -1
- package/dist/server/version.d.ts +1 -1
- package/dist/server/version.js +1 -1
- package/dist/server/version.js.map +1 -1
- package/package.json +4 -5
- package/src/cli/bin.ts +5 -0
- package/src/cli/index.ts +22 -9
- package/src/cli/keys.ts +3 -0
- package/src/client/index.ts +36 -37
- package/src/component/_generated/api.ts +14 -0
- package/src/component/_generated/component.ts +2106 -9
- package/src/component/index.ts +3 -1
- package/src/component/model.ts +441 -0
- package/src/component/public/enterprise.ts +753 -0
- package/src/component/public/factors.ts +332 -0
- package/src/component/public/groups.ts +932 -0
- package/src/component/public/identity.ts +566 -0
- package/src/component/public/keys.ts +209 -0
- package/src/component/public/shared.ts +119 -0
- package/src/component/public.ts +5 -2965
- package/src/component/schema.ts +68 -63
- package/src/providers/sso.ts +1 -1
- package/src/server/auth.ts +413 -18
- package/src/server/cookies.ts +3 -0
- package/src/server/db.ts +3 -0
- package/src/server/device.ts +3 -1
- package/src/server/domains/core.ts +1071 -0
- package/src/server/domains/sso.ts +1749 -0
- package/src/server/enterpriseValidators.ts +93 -0
- package/src/server/factory.ts +2181 -0
- package/src/server/fx.ts +1 -0
- package/src/server/http.ts +529 -0
- package/src/server/identity.ts +18 -0
- package/src/server/index.ts +806 -40
- package/src/server/keys.ts +4 -0
- package/src/server/mutations/index.ts +1 -1
- package/src/server/mutations/oauth.ts +36 -8
- package/src/server/mutations/store.ts +6 -3
- package/src/server/oauth.ts +6 -0
- package/src/server/passkey.ts +3 -2
- package/src/server/provider.ts +2 -0
- package/src/server/providers.ts +20 -0
- package/src/server/ratelimit.ts +3 -0
- package/src/server/redirects.ts +2 -0
- package/src/server/refresh.ts +5 -0
- package/src/server/sessions.ts +5 -0
- package/src/server/signin.ts +1 -0
- package/src/server/sso.ts +259 -17
- package/src/server/templates.ts +1 -0
- package/src/server/tokens.ts +1 -0
- package/src/server/totp.ts +4 -2
- package/src/server/types.ts +178 -83
- package/src/server/users.ts +1 -0
- package/src/server/utils.ts +71 -1
- package/src/server/version.ts +1 -1
- package/dist/component/public.js.map +0 -1
- package/dist/component/server/implementation.d.ts +0 -1264
- package/dist/component/server/implementation.d.ts.map +0 -1
- package/dist/component/server/implementation.js +0 -2365
- package/dist/component/server/implementation.js.map +0 -1
- package/dist/server/cookies.d.ts.map +0 -1
- package/dist/server/db.d.ts.map +0 -1
- package/dist/server/device.d.ts.map +0 -1
- package/dist/server/implementation.d.ts +0 -1264
- package/dist/server/implementation.d.ts.map +0 -1
- package/dist/server/implementation.js +0 -2365
- package/dist/server/implementation.js.map +0 -1
- package/dist/server/keys.d.ts.map +0 -1
- package/dist/server/oauth.d.ts.map +0 -1
- package/dist/server/ratelimit.d.ts.map +0 -1
- package/dist/server/redirects.d.ts.map +0 -1
- package/dist/server/refresh.d.ts.map +0 -1
- package/dist/server/sessions.d.ts.map +0 -1
- package/dist/server/signin.d.ts.map +0 -1
- package/dist/server/sso.d.ts.map +0 -1
- package/dist/server/templates.d.ts.map +0 -1
- package/dist/server/tokens.d.ts.map +0 -1
- package/dist/server/totp.d.ts.map +0 -1
- package/dist/server/users.d.ts.map +0 -1
- package/dist/server/utils.d.ts.map +0 -1
- package/src/server/implementation.ts +0 -5336
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"users.js","names":[],"sources":["../../../src/server/users.ts"],"sourcesContent":["import { Fx } from \"@robelest/fx\";\nimport { GenericId } from \"convex/values\";\n\nimport { authDb } from \"./db\";\nimport { AuthError } from \"./fx\";\nimport { Doc, MutationCtx } from \"./types\";\nimport { AuthProviderMaterializedConfig, ConvexAuthConfig } from \"./types\";\nimport { LOG_LEVELS, logWithLevel } from \"./utils\";\n\ntype CreateOrUpdateUserArgs = {\n type: \"oauth\" | \"credentials\" | \"email\" | \"phone\" | \"verification\";\n provider: AuthProviderMaterializedConfig;\n profile: Record<string, unknown> & {\n email?: string;\n phone?: string;\n emailVerified?: boolean;\n phoneVerified?: boolean;\n };\n accountExtend?: Record<string, unknown>;\n shouldLinkViaEmail?: boolean;\n shouldLinkViaPhone?: boolean;\n};\n\nfunction mergeExtend(\n existing: unknown,\n incoming: Record<string, unknown> | undefined,\n) {\n if (!incoming) {\n return undefined;\n }\n const existingRecord =\n typeof existing === \"object\" &&\n existing !== null &&\n !Array.isArray(existing)\n ? (existing as Record<string, unknown>)\n : undefined;\n return existingRecord ? { ...existingRecord, ...incoming } : incoming;\n}\n\nexport async function upsertUserAndAccount(\n ctx: MutationCtx,\n sessionId: GenericId<\"Session\"> | null,\n account:\n | { existingAccount: Doc<\"Account\"> }\n | {\n providerAccountId: string;\n secret?: string;\n },\n args: CreateOrUpdateUserArgs,\n config: ConvexAuthConfig,\n opts?: { existingUserId?: GenericId<\"User\"> },\n): Promise<{\n userId: GenericId<\"User\">;\n accountId: GenericId<\"Account\">;\n}> {\n const userId = await defaultCreateOrUpdateUser(\n ctx,\n sessionId,\n \"existingAccount\" in account ? account.existingAccount : null,\n args,\n config,\n opts?.existingUserId ?? null,\n );\n const accountId = await createOrUpdateAccount(\n ctx,\n userId,\n account,\n args,\n config,\n );\n return { userId, accountId };\n}\n\nasync function defaultCreateOrUpdateUser(\n ctx: MutationCtx,\n existingSessionId: GenericId<\"Session\"> | null,\n existingAccount: Doc<\"Account\"> | null,\n args: CreateOrUpdateUserArgs,\n config: ConvexAuthConfig,\n existingUserIdOverride: GenericId<\"User\"> | null,\n) {\n logWithLevel(LOG_LEVELS.DEBUG, \"defaultCreateOrUpdateUser args:\", {\n existingAccountId: existingAccount?._id,\n existingSessionId,\n args,\n });\n const existingUserId = existingAccount?.userId ?? null;\n const db = authDb(ctx, config);\n if (config.callbacks?.createOrUpdateUser !== undefined) {\n logWithLevel(LOG_LEVELS.DEBUG, \"Using custom createOrUpdateUser callback\");\n return await config.callbacks.createOrUpdateUser(ctx, {\n existingUserId,\n ...args,\n });\n }\n\n const {\n provider,\n profile: {\n id: _profileId,\n emailVerified: profileEmailVerified,\n phoneVerified: profilePhoneVerified,\n ...profile\n },\n } = args;\n const emailVerified =\n profileEmailVerified ??\n (provider.type === \"oauth\" && provider.accountLinking !== \"none\");\n const phoneVerified = profilePhoneVerified ?? false;\n const shouldLinkViaEmail =\n args.shouldLinkViaEmail || emailVerified || provider.type === \"email\";\n const shouldLinkViaPhone =\n args.shouldLinkViaPhone || phoneVerified || provider.type === \"phone\";\n\n let userId = existingUserId ?? existingUserIdOverride;\n if (existingUserId === null) {\n const existingUserWithVerifiedEmailId =\n typeof profile.email === \"string\" && shouldLinkViaEmail\n ? ((await uniqueUserWithVerifiedEmail(ctx, profile.email, config))\n ?._id ?? null)\n : null;\n\n const existingUserWithVerifiedPhoneId =\n typeof profile.phone === \"string\" && shouldLinkViaPhone\n ? ((await uniqueUserWithVerifiedPhone(ctx, profile.phone, config))\n ?._id ?? null)\n : null;\n const linkDispatch = {\n tag:\n existingUserWithVerifiedEmailId !== null &&\n existingUserWithVerifiedPhoneId !== null\n ? \"both\"\n : existingUserWithVerifiedEmailId !== null\n ? \"email\"\n : existingUserWithVerifiedPhoneId !== null\n ? \"phone\"\n : \"none\",\n existingUserWithVerifiedEmailId,\n existingUserWithVerifiedPhoneId,\n } as const;\n\n const linkHandlers = {\n both: () =>\n Fx.sync(() => {\n logWithLevel(\n LOG_LEVELS.DEBUG,\n `Found existing email and phone verified users, so not linking: email: ${linkDispatch.existingUserWithVerifiedEmailId}, phone: ${linkDispatch.existingUserWithVerifiedPhoneId}`,\n );\n return null;\n }),\n email: () =>\n Fx.sync(() => {\n logWithLevel(\n LOG_LEVELS.DEBUG,\n `Found existing email verified user, linking: ${linkDispatch.existingUserWithVerifiedEmailId}`,\n );\n return linkDispatch.existingUserWithVerifiedEmailId;\n }),\n phone: () =>\n Fx.sync(() => {\n logWithLevel(\n LOG_LEVELS.DEBUG,\n `Found existing phone verified user, linking: ${linkDispatch.existingUserWithVerifiedPhoneId}`,\n );\n return linkDispatch.existingUserWithVerifiedPhoneId;\n }),\n none: () =>\n Fx.sync(() => {\n logWithLevel(\n LOG_LEVELS.DEBUG,\n \"No existing verified users found, creating new user\",\n );\n return null;\n }),\n } as const;\n\n userId = await Fx.run(linkHandlers[linkDispatch.tag]());\n }\n const userData = {\n ...(emailVerified ? { emailVerificationTime: Date.now() } : null),\n ...(phoneVerified ? { phoneVerificationTime: Date.now() } : null),\n ...profile,\n };\n const existingOrLinkedUserId = userId;\n if (userId !== null) {\n await Fx.run(\n Fx.from({\n ok: () => db.users.patch(userId!, userData),\n err: (error) =>\n new AuthError(\n \"USER_UPDATE_FAILED\",\n `Could not update user document with ID \\`${userId}\\`, ` +\n `either the user has been deleted but their account has not, ` +\n `or the profile data doesn't match the \\`users\\` table schema: ` +\n `${(error as Error).message}`,\n ),\n }).pipe(Fx.recover((e) => Fx.fatal(e.toConvexError()))),\n );\n } else {\n userId = (await db.users.insert(userData)) as GenericId<\"User\">;\n }\n const afterUserCreatedOrUpdated = config.callbacks?.afterUserCreatedOrUpdated;\n if (afterUserCreatedOrUpdated !== undefined) {\n logWithLevel(\n LOG_LEVELS.DEBUG,\n \"Calling custom afterUserCreatedOrUpdated callback\",\n );\n await afterUserCreatedOrUpdated(ctx, {\n userId,\n existingUserId: existingOrLinkedUserId,\n ...args,\n });\n } else {\n logWithLevel(\n LOG_LEVELS.DEBUG,\n \"No custom afterUserCreatedOrUpdated callback, skipping\",\n );\n }\n return userId;\n}\n\nasync function uniqueUserWithVerifiedEmail(\n ctx: MutationCtx,\n email: string,\n config: ConvexAuthConfig,\n) {\n const db = authDb(ctx, config);\n return (await db.users.findByVerifiedEmail(email)) as Doc<\"User\"> | null;\n}\n\nasync function uniqueUserWithVerifiedPhone(\n ctx: MutationCtx,\n phone: string,\n config: ConvexAuthConfig,\n) {\n const db = authDb(ctx, config);\n return (await db.users.findByVerifiedPhone(phone)) as Doc<\"User\"> | null;\n}\n\nasync function createOrUpdateAccount(\n ctx: MutationCtx,\n userId: GenericId<\"User\">,\n account:\n | { existingAccount: Doc<\"Account\"> }\n | {\n providerAccountId: string;\n secret?: string;\n },\n args: CreateOrUpdateUserArgs,\n config: ConvexAuthConfig,\n) {\n const db = authDb(ctx, config);\n const mergedExtend =\n \"existingAccount\" in account\n ? mergeExtend(account.existingAccount.extend, args.accountExtend)\n : args.accountExtend;\n const accountId =\n \"existingAccount\" in account\n ? account.existingAccount._id\n : ((await db.accounts.create({\n userId,\n provider: args.provider.id,\n providerAccountId: account.providerAccountId,\n secret: account.secret,\n extend: mergedExtend,\n })) as GenericId<\"Account\">);\n // This is never used with the default `createOrUpdateUser` implementation,\n // but it is used for manual linking via custom `createOrUpdateUser`:\n if (\n \"existingAccount\" in account &&\n account.existingAccount.userId !== userId\n ) {\n await db.accounts.patch(accountId, { userId });\n }\n const accountPatchData: Record<string, unknown> = {};\n if (mergedExtend) {\n accountPatchData.extend = mergedExtend;\n }\n if (args.profile.emailVerified) {\n accountPatchData.emailVerified = args.profile.email;\n }\n if (args.profile.phoneVerified) {\n accountPatchData.phoneVerified = args.profile.phone;\n }\n if (Object.keys(accountPatchData).length > 0) {\n await db.accounts.patch(accountId, accountPatchData);\n }\n return accountId;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"users.js","names":[],"sources":["../../../src/server/users.ts"],"sourcesContent":["import { Fx } from \"@robelest/fx\";\nimport { GenericId } from \"convex/values\";\n\nimport { authDb } from \"./db\";\nimport { AuthError } from \"./fx\";\nimport { Doc, MutationCtx } from \"./types\";\nimport { AuthProviderMaterializedConfig, ConvexAuthConfig } from \"./types\";\nimport { LOG_LEVELS, logWithLevel } from \"./utils\";\n\ntype CreateOrUpdateUserArgs = {\n type: \"oauth\" | \"credentials\" | \"email\" | \"phone\" | \"verification\";\n provider: AuthProviderMaterializedConfig;\n profile: Record<string, unknown> & {\n email?: string;\n phone?: string;\n emailVerified?: boolean;\n phoneVerified?: boolean;\n };\n accountExtend?: Record<string, unknown>;\n shouldLinkViaEmail?: boolean;\n shouldLinkViaPhone?: boolean;\n};\n\nfunction mergeExtend(\n existing: unknown,\n incoming: Record<string, unknown> | undefined,\n) {\n if (!incoming) {\n return undefined;\n }\n const existingRecord =\n typeof existing === \"object\" &&\n existing !== null &&\n !Array.isArray(existing)\n ? (existing as Record<string, unknown>)\n : undefined;\n return existingRecord ? { ...existingRecord, ...incoming } : incoming;\n}\n\n/** @internal */\nexport async function upsertUserAndAccount(\n ctx: MutationCtx,\n sessionId: GenericId<\"Session\"> | null,\n account:\n | { existingAccount: Doc<\"Account\"> }\n | {\n providerAccountId: string;\n secret?: string;\n },\n args: CreateOrUpdateUserArgs,\n config: ConvexAuthConfig,\n opts?: { existingUserId?: GenericId<\"User\"> },\n): Promise<{\n userId: GenericId<\"User\">;\n accountId: GenericId<\"Account\">;\n}> {\n const userId = await defaultCreateOrUpdateUser(\n ctx,\n sessionId,\n \"existingAccount\" in account ? account.existingAccount : null,\n args,\n config,\n opts?.existingUserId ?? null,\n );\n const accountId = await createOrUpdateAccount(\n ctx,\n userId,\n account,\n args,\n config,\n );\n return { userId, accountId };\n}\n\nasync function defaultCreateOrUpdateUser(\n ctx: MutationCtx,\n existingSessionId: GenericId<\"Session\"> | null,\n existingAccount: Doc<\"Account\"> | null,\n args: CreateOrUpdateUserArgs,\n config: ConvexAuthConfig,\n existingUserIdOverride: GenericId<\"User\"> | null,\n) {\n logWithLevel(LOG_LEVELS.DEBUG, \"defaultCreateOrUpdateUser args:\", {\n existingAccountId: existingAccount?._id,\n existingSessionId,\n args,\n });\n const existingUserId = existingAccount?.userId ?? null;\n const db = authDb(ctx, config);\n if (config.callbacks?.createOrUpdateUser !== undefined) {\n logWithLevel(LOG_LEVELS.DEBUG, \"Using custom createOrUpdateUser callback\");\n return await config.callbacks.createOrUpdateUser(ctx, {\n existingUserId,\n ...args,\n });\n }\n\n const {\n provider,\n profile: {\n id: _profileId,\n emailVerified: profileEmailVerified,\n phoneVerified: profilePhoneVerified,\n ...profile\n },\n } = args;\n const emailVerified =\n profileEmailVerified ??\n (provider.type === \"oauth\" && provider.accountLinking !== \"none\");\n const phoneVerified = profilePhoneVerified ?? false;\n const shouldLinkViaEmail =\n args.shouldLinkViaEmail || emailVerified || provider.type === \"email\";\n const shouldLinkViaPhone =\n args.shouldLinkViaPhone || phoneVerified || provider.type === \"phone\";\n\n let userId = existingUserId ?? existingUserIdOverride;\n if (existingUserId === null) {\n const existingUserWithVerifiedEmailId =\n typeof profile.email === \"string\" && shouldLinkViaEmail\n ? ((await uniqueUserWithVerifiedEmail(ctx, profile.email, config))\n ?._id ?? null)\n : null;\n\n const existingUserWithVerifiedPhoneId =\n typeof profile.phone === \"string\" && shouldLinkViaPhone\n ? ((await uniqueUserWithVerifiedPhone(ctx, profile.phone, config))\n ?._id ?? null)\n : null;\n const linkDispatch = {\n tag:\n existingUserWithVerifiedEmailId !== null &&\n existingUserWithVerifiedPhoneId !== null\n ? \"both\"\n : existingUserWithVerifiedEmailId !== null\n ? \"email\"\n : existingUserWithVerifiedPhoneId !== null\n ? \"phone\"\n : \"none\",\n existingUserWithVerifiedEmailId,\n existingUserWithVerifiedPhoneId,\n } as const;\n\n const linkHandlers = {\n both: () =>\n Fx.sync(() => {\n logWithLevel(\n LOG_LEVELS.DEBUG,\n `Found existing email and phone verified users, so not linking: email: ${linkDispatch.existingUserWithVerifiedEmailId}, phone: ${linkDispatch.existingUserWithVerifiedPhoneId}`,\n );\n return null;\n }),\n email: () =>\n Fx.sync(() => {\n logWithLevel(\n LOG_LEVELS.DEBUG,\n `Found existing email verified user, linking: ${linkDispatch.existingUserWithVerifiedEmailId}`,\n );\n return linkDispatch.existingUserWithVerifiedEmailId;\n }),\n phone: () =>\n Fx.sync(() => {\n logWithLevel(\n LOG_LEVELS.DEBUG,\n `Found existing phone verified user, linking: ${linkDispatch.existingUserWithVerifiedPhoneId}`,\n );\n return linkDispatch.existingUserWithVerifiedPhoneId;\n }),\n none: () =>\n Fx.sync(() => {\n logWithLevel(\n LOG_LEVELS.DEBUG,\n \"No existing verified users found, creating new user\",\n );\n return null;\n }),\n } as const;\n\n userId = await Fx.run(linkHandlers[linkDispatch.tag]());\n }\n const userData = {\n ...(emailVerified ? { emailVerificationTime: Date.now() } : null),\n ...(phoneVerified ? { phoneVerificationTime: Date.now() } : null),\n ...profile,\n };\n const existingOrLinkedUserId = userId;\n if (userId !== null) {\n await Fx.run(\n Fx.from({\n ok: () => db.users.patch(userId!, userData),\n err: (error) =>\n new AuthError(\n \"USER_UPDATE_FAILED\",\n `Could not update user document with ID \\`${userId}\\`, ` +\n `either the user has been deleted but their account has not, ` +\n `or the profile data doesn't match the \\`users\\` table schema: ` +\n `${(error as Error).message}`,\n ),\n }).pipe(Fx.recover((e) => Fx.fatal(e.toConvexError()))),\n );\n } else {\n userId = (await db.users.insert(userData)) as GenericId<\"User\">;\n }\n const afterUserCreatedOrUpdated = config.callbacks?.afterUserCreatedOrUpdated;\n if (afterUserCreatedOrUpdated !== undefined) {\n logWithLevel(\n LOG_LEVELS.DEBUG,\n \"Calling custom afterUserCreatedOrUpdated callback\",\n );\n await afterUserCreatedOrUpdated(ctx, {\n userId,\n existingUserId: existingOrLinkedUserId,\n ...args,\n });\n } else {\n logWithLevel(\n LOG_LEVELS.DEBUG,\n \"No custom afterUserCreatedOrUpdated callback, skipping\",\n );\n }\n return userId;\n}\n\nasync function uniqueUserWithVerifiedEmail(\n ctx: MutationCtx,\n email: string,\n config: ConvexAuthConfig,\n) {\n const db = authDb(ctx, config);\n return (await db.users.findByVerifiedEmail(email)) as Doc<\"User\"> | null;\n}\n\nasync function uniqueUserWithVerifiedPhone(\n ctx: MutationCtx,\n phone: string,\n config: ConvexAuthConfig,\n) {\n const db = authDb(ctx, config);\n return (await db.users.findByVerifiedPhone(phone)) as Doc<\"User\"> | null;\n}\n\nasync function createOrUpdateAccount(\n ctx: MutationCtx,\n userId: GenericId<\"User\">,\n account:\n | { existingAccount: Doc<\"Account\"> }\n | {\n providerAccountId: string;\n secret?: string;\n },\n args: CreateOrUpdateUserArgs,\n config: ConvexAuthConfig,\n) {\n const db = authDb(ctx, config);\n const mergedExtend =\n \"existingAccount\" in account\n ? mergeExtend(account.existingAccount.extend, args.accountExtend)\n : args.accountExtend;\n const accountId =\n \"existingAccount\" in account\n ? account.existingAccount._id\n : ((await db.accounts.create({\n userId,\n provider: args.provider.id,\n providerAccountId: account.providerAccountId,\n secret: account.secret,\n extend: mergedExtend,\n })) as GenericId<\"Account\">);\n // This is never used with the default `createOrUpdateUser` implementation,\n // but it is used for manual linking via custom `createOrUpdateUser`:\n if (\n \"existingAccount\" in account &&\n account.existingAccount.userId !== userId\n ) {\n await db.accounts.patch(accountId, { userId });\n }\n const accountPatchData: Record<string, unknown> = {};\n if (mergedExtend) {\n accountPatchData.extend = mergedExtend;\n }\n if (args.profile.emailVerified) {\n accountPatchData.emailVerified = args.profile.email;\n }\n if (args.profile.phoneVerified) {\n accountPatchData.phoneVerified = args.profile.phone;\n }\n if (Object.keys(accountPatchData).length > 0) {\n await db.accounts.patch(accountId, accountPatchData);\n }\n return accountId;\n}\n"],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { AuthError } from "./fx.js";
|
|
2
2
|
import { generateRandomString } from "@oslojs/crypto/random";
|
|
3
3
|
import { sha256 } from "@oslojs/crypto/sha2";
|
|
4
|
-
import { encodeHexLowerCase } from "@oslojs/encoding";
|
|
4
|
+
import { decodeBase64urlIgnorePadding, encodeBase64urlNoPadding, encodeHexLowerCase } from "@oslojs/encoding";
|
|
5
5
|
|
|
6
6
|
//#region src/server/utils.ts
|
|
7
7
|
/**
|
|
@@ -10,11 +10,13 @@ import { encodeHexLowerCase } from "@oslojs/encoding";
|
|
|
10
10
|
* Uses `AuthError.toConvexError()` directly since this is a synchronous guard
|
|
11
11
|
* called inline in many expressions — not suitable for Fx pipeline wrapping.
|
|
12
12
|
*/
|
|
13
|
+
/** @internal */
|
|
13
14
|
function requireEnv(name) {
|
|
14
15
|
const value = process.env[name];
|
|
15
16
|
if (value === void 0) throw new AuthError("MISSING_ENV_VAR", `Missing environment variable \`${name}\``, { variable: name }).toConvexError();
|
|
16
17
|
return value;
|
|
17
18
|
}
|
|
19
|
+
/** @internal */
|
|
18
20
|
function isLocalHost(host) {
|
|
19
21
|
if (host === void 0) return false;
|
|
20
22
|
const raw = host.includes("://") ? host : `http://${host}`;
|
|
@@ -26,28 +28,36 @@ function isLocalHost(host) {
|
|
|
26
28
|
}
|
|
27
29
|
return url.hostname === "localhost" || url.hostname === "127.0.0.1" || url.hostname === "::1";
|
|
28
30
|
}
|
|
31
|
+
/** @internal */
|
|
29
32
|
const TOKEN_SUB_CLAIM_DIVIDER = "|";
|
|
33
|
+
/** @internal */
|
|
30
34
|
const REFRESH_TOKEN_DIVIDER = "|";
|
|
35
|
+
/** @internal */
|
|
31
36
|
async function sha256$1(input) {
|
|
32
37
|
return encodeHexLowerCase(sha256(new TextEncoder().encode(input)));
|
|
33
38
|
}
|
|
39
|
+
/** @internal */
|
|
34
40
|
function generateRandomString$1(length, alphabet) {
|
|
35
41
|
return generateRandomString({ read(bytes) {
|
|
36
42
|
crypto.getRandomValues(bytes);
|
|
37
43
|
} }, alphabet, length);
|
|
38
44
|
}
|
|
45
|
+
/** @internal */
|
|
39
46
|
function errorMessage(error) {
|
|
40
47
|
return error instanceof Error ? error.message : String(error);
|
|
41
48
|
}
|
|
49
|
+
/** @internal */
|
|
42
50
|
function logError(error) {
|
|
43
51
|
logWithLevel(LOG_LEVELS.ERROR, error instanceof Error ? error.message + "\n" + error.stack?.replace("\\n", "\n") : error);
|
|
44
52
|
}
|
|
53
|
+
/** @internal */
|
|
45
54
|
const LOG_LEVELS = {
|
|
46
55
|
ERROR: "ERROR",
|
|
47
56
|
WARN: "WARN",
|
|
48
57
|
INFO: "INFO",
|
|
49
58
|
DEBUG: "DEBUG"
|
|
50
59
|
};
|
|
60
|
+
/** @internal */
|
|
51
61
|
function logWithLevel(level, ...args) {
|
|
52
62
|
const configuredLogLevel = LOG_LEVELS[process.env.AUTH_LOG_LEVEL ?? "INFO"] ?? "INFO";
|
|
53
63
|
switch (level) {
|
|
@@ -66,6 +76,7 @@ function logWithLevel(level, ...args) {
|
|
|
66
76
|
}
|
|
67
77
|
}
|
|
68
78
|
const UNREDACTED_LENGTH = 5;
|
|
79
|
+
/** @internal */
|
|
69
80
|
function maybeRedact(value) {
|
|
70
81
|
if (value === "") return "";
|
|
71
82
|
if (process.env.AUTH_LOG_SECRETS !== "true") {
|
|
@@ -73,7 +84,38 @@ function maybeRedact(value) {
|
|
|
73
84
|
return value.substring(0, UNREDACTED_LENGTH) + "<redacted>" + value.substring(value.length - UNREDACTED_LENGTH);
|
|
74
85
|
} else return value;
|
|
75
86
|
}
|
|
87
|
+
const SECRET_KEY_ENV = "AUTH_SECRET_ENCRYPTION_KEY";
|
|
88
|
+
const SECRET_IV_LENGTH = 12;
|
|
89
|
+
function toArrayBuffer(bytes) {
|
|
90
|
+
return bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
|
|
91
|
+
}
|
|
92
|
+
async function getSecretCryptoKey() {
|
|
93
|
+
const material = requireEnv(SECRET_KEY_ENV);
|
|
94
|
+
const rawKey = sha256(new TextEncoder().encode(material));
|
|
95
|
+
return await crypto.subtle.importKey("raw", toArrayBuffer(rawKey), { name: "AES-GCM" }, false, ["encrypt", "decrypt"]);
|
|
96
|
+
}
|
|
97
|
+
/** @internal */
|
|
98
|
+
async function encryptSecret(value) {
|
|
99
|
+
const key = await getSecretCryptoKey();
|
|
100
|
+
const iv = crypto.getRandomValues(new Uint8Array(SECRET_IV_LENGTH));
|
|
101
|
+
const encrypted = await crypto.subtle.encrypt({
|
|
102
|
+
name: "AES-GCM",
|
|
103
|
+
iv: toArrayBuffer(iv)
|
|
104
|
+
}, key, toArrayBuffer(new TextEncoder().encode(value)));
|
|
105
|
+
return `${encodeBase64urlNoPadding(iv)}.${encodeBase64urlNoPadding(new Uint8Array(encrypted))}`;
|
|
106
|
+
}
|
|
107
|
+
/** @internal */
|
|
108
|
+
async function decryptSecret(ciphertext) {
|
|
109
|
+
const [ivEncoded, payloadEncoded] = ciphertext.split(".");
|
|
110
|
+
if (!ivEncoded || !payloadEncoded) throw new AuthError("INVALID_PARAMETERS", "Stored enterprise secret is malformed.").toConvexError();
|
|
111
|
+
const key = await getSecretCryptoKey();
|
|
112
|
+
const decrypted = await crypto.subtle.decrypt({
|
|
113
|
+
name: "AES-GCM",
|
|
114
|
+
iv: toArrayBuffer(decodeBase64urlIgnorePadding(ivEncoded))
|
|
115
|
+
}, key, toArrayBuffer(decodeBase64urlIgnorePadding(payloadEncoded)));
|
|
116
|
+
return new TextDecoder().decode(decrypted);
|
|
117
|
+
}
|
|
76
118
|
|
|
77
119
|
//#endregion
|
|
78
|
-
export { LOG_LEVELS, REFRESH_TOKEN_DIVIDER, TOKEN_SUB_CLAIM_DIVIDER, errorMessage, generateRandomString$1 as generateRandomString, isLocalHost, logError, logWithLevel, maybeRedact, requireEnv, sha256$1 as sha256 };
|
|
120
|
+
export { LOG_LEVELS, REFRESH_TOKEN_DIVIDER, TOKEN_SUB_CLAIM_DIVIDER, decryptSecret, encryptSecret, errorMessage, generateRandomString$1 as generateRandomString, isLocalHost, logError, logWithLevel, maybeRedact, requireEnv, sha256$1 as sha256 };
|
|
79
121
|
//# sourceMappingURL=utils.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","names":["sha256","rawSha256","generateRandomString","osloGenerateRandomString"],"sources":["../../../src/server/utils.ts"],"sourcesContent":["import {\n RandomReader,\n generateRandomString as osloGenerateRandomString,\n} from \"@oslojs/crypto/random\";\nimport { sha256 as rawSha256 } from \"@oslojs/crypto/sha2\";\nimport {
|
|
1
|
+
{"version":3,"file":"utils.js","names":["sha256","rawSha256","generateRandomString","osloGenerateRandomString"],"sources":["../../../src/server/utils.ts"],"sourcesContent":["import {\n RandomReader,\n generateRandomString as osloGenerateRandomString,\n} from \"@oslojs/crypto/random\";\nimport { sha256 as rawSha256 } from \"@oslojs/crypto/sha2\";\nimport {\n decodeBase64urlIgnorePadding,\n encodeBase64urlNoPadding,\n encodeHexLowerCase,\n} from \"@oslojs/encoding\";\n\nimport { AuthError } from \"./fx\";\n\n/**\n * Require an environment variable to be set, throwing at config time if missing.\n *\n * Uses `AuthError.toConvexError()` directly since this is a synchronous guard\n * called inline in many expressions — not suitable for Fx pipeline wrapping.\n */\n/** @internal */\nexport function requireEnv(name: string) {\n const value = process.env[name];\n if (value === undefined) {\n throw new AuthError(\n \"MISSING_ENV_VAR\",\n `Missing environment variable \\`${name}\\``,\n { variable: name },\n ).toConvexError();\n }\n return value;\n}\n\n/** @internal */\nexport function isLocalHost(host?: string) {\n if (host === undefined) {\n return false;\n }\n const raw = host.includes(\"://\") ? host : `http://${host}`;\n let url: URL;\n try {\n url = new URL(raw);\n } catch {\n return false;\n }\n return (\n url.hostname === \"localhost\" ||\n url.hostname === \"127.0.0.1\" ||\n url.hostname === \"::1\"\n );\n}\n\n// Internal server utilities (merged from former internalUtils.ts)\n\n/** @internal */\nexport const TOKEN_SUB_CLAIM_DIVIDER = \"|\";\n/** @internal */\nexport const REFRESH_TOKEN_DIVIDER = \"|\";\n\n/** @internal */\nexport async function sha256(input: string) {\n return encodeHexLowerCase(rawSha256(new TextEncoder().encode(input)));\n}\n\n/** @internal */\nexport function generateRandomString(length: number, alphabet: string) {\n const random: RandomReader = {\n read(bytes) {\n crypto.getRandomValues(bytes as Uint8Array<ArrayBuffer>);\n },\n };\n\n return osloGenerateRandomString(random, alphabet, length);\n}\n\n/** @internal */\nexport function errorMessage(error: unknown) {\n return error instanceof Error ? error.message : String(error);\n}\n\n/** @internal */\nexport function logError(error: unknown) {\n logWithLevel(\n LOG_LEVELS.ERROR,\n error instanceof Error\n ? error.message + \"\\n\" + error.stack?.replace(\"\\\\n\", \"\\n\")\n : error,\n );\n}\n\n/** @internal */\nexport const LOG_LEVELS = {\n ERROR: \"ERROR\",\n WARN: \"WARN\",\n INFO: \"INFO\",\n DEBUG: \"DEBUG\",\n} as const;\ntype LogLevel = keyof typeof LOG_LEVELS;\n\n/** @internal */\nexport function logWithLevel(level: LogLevel, ...args: unknown[]) {\n const configuredLogLevel =\n LOG_LEVELS[\n (process.env.AUTH_LOG_LEVEL as LogLevel | undefined) ?? \"INFO\"\n ] ?? \"INFO\";\n switch (level) {\n case \"ERROR\":\n console.error(...args);\n break;\n case \"WARN\":\n if (configuredLogLevel !== \"ERROR\") {\n console.warn(...args);\n }\n break;\n case \"INFO\":\n if (configuredLogLevel === \"INFO\" || configuredLogLevel === \"DEBUG\") {\n console.info(...args);\n }\n break;\n case \"DEBUG\":\n if (configuredLogLevel === \"DEBUG\") {\n console.debug(...args);\n }\n break;\n }\n}\n\nconst UNREDACTED_LENGTH = 5;\n/** @internal */\nexport function maybeRedact(value: string) {\n if (value === \"\") {\n return \"\";\n }\n const shouldRedact = process.env.AUTH_LOG_SECRETS !== \"true\";\n if (shouldRedact) {\n if (value.length < UNREDACTED_LENGTH * 2) {\n return \"<redacted>\";\n }\n return (\n value.substring(0, UNREDACTED_LENGTH) +\n \"<redacted>\" +\n value.substring(value.length - UNREDACTED_LENGTH)\n );\n } else {\n return value;\n }\n}\n\nconst SECRET_KEY_ENV = \"AUTH_SECRET_ENCRYPTION_KEY\";\nconst SECRET_IV_LENGTH = 12;\n\nfunction toArrayBuffer(bytes: Uint8Array) {\n return bytes.buffer.slice(\n bytes.byteOffset,\n bytes.byteOffset + bytes.byteLength,\n ) as ArrayBuffer;\n}\n\nasync function getSecretCryptoKey() {\n const material = requireEnv(SECRET_KEY_ENV);\n const rawKey = rawSha256(new TextEncoder().encode(material));\n return await crypto.subtle.importKey(\n \"raw\",\n toArrayBuffer(rawKey),\n { name: \"AES-GCM\" },\n false,\n [\"encrypt\", \"decrypt\"],\n );\n}\n\n/** @internal */\nexport async function encryptSecret(value: string) {\n const key = await getSecretCryptoKey();\n const iv = crypto.getRandomValues(new Uint8Array(SECRET_IV_LENGTH));\n const encrypted = await crypto.subtle.encrypt(\n { name: \"AES-GCM\", iv: toArrayBuffer(iv) },\n key,\n toArrayBuffer(new TextEncoder().encode(value)),\n );\n return `${encodeBase64urlNoPadding(iv)}.${encodeBase64urlNoPadding(new Uint8Array(encrypted))}`;\n}\n\n/** @internal */\nexport async function decryptSecret(ciphertext: string) {\n const [ivEncoded, payloadEncoded] = ciphertext.split(\".\");\n if (!ivEncoded || !payloadEncoded) {\n throw new AuthError(\n \"INVALID_PARAMETERS\",\n \"Stored enterprise secret is malformed.\",\n ).toConvexError();\n }\n const key = await getSecretCryptoKey();\n const decrypted = await crypto.subtle.decrypt(\n {\n name: \"AES-GCM\",\n iv: toArrayBuffer(decodeBase64urlIgnorePadding(ivEncoded)),\n },\n key,\n toArrayBuffer(decodeBase64urlIgnorePadding(payloadEncoded)),\n );\n return new TextDecoder().decode(decrypted);\n}\n"],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { GenericActionCtxWithAuthConfig } from "../server/types.js";
|
|
2
|
-
import { Value } from "convex/values";
|
|
3
2
|
import { DocumentByName, GenericDataModel, WithoutSystemFields } from "convex/server";
|
|
3
|
+
import { Value } from "convex/values";
|
|
4
4
|
|
|
5
5
|
//#region src/providers/anonymous.d.ts
|
|
6
6
|
/**
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { AuthProviderConfig, GenericActionCtxWithAuthConfig } from "../server/types.js";
|
|
2
|
-
import { GenericId, Value } from "convex/values";
|
|
3
2
|
import { GenericDataModel } from "convex/server";
|
|
3
|
+
import { GenericId, Value } from "convex/values";
|
|
4
4
|
|
|
5
5
|
//#region src/providers/credentials.d.ts
|
|
6
6
|
/**
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { CredentialsConfig } from "./credentials.js";
|
|
2
2
|
import { EmailConfig, GenericActionCtxWithAuthConfig } from "../server/types.js";
|
|
3
|
-
import { Value } from "convex/values";
|
|
4
3
|
import { DocumentByName, GenericDataModel, WithoutSystemFields } from "convex/server";
|
|
4
|
+
import { Value } from "convex/values";
|
|
5
5
|
|
|
6
6
|
//#region src/providers/password.d.ts
|
|
7
7
|
/**
|
package/dist/providers/sso.d.ts
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* });
|
|
16
16
|
*
|
|
17
17
|
* // auth.sso is now available
|
|
18
|
-
* await auth.sso.oidc.configure(ctx, { enterpriseId, clientId, ... });
|
|
18
|
+
* await auth.sso.admin.oidc.configure(ctx, { enterpriseId, clientId, ... });
|
|
19
19
|
* ```
|
|
20
20
|
*
|
|
21
21
|
* Without `new SSO()` in the providers list, `auth.sso` is not
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sso.js","names":[],"sources":["../../src/providers/sso.ts"],"sourcesContent":["/**\n * Enterprise SSO provider (OIDC + SAML + SCIM).\n *\n * Adding `new SSO()` to your providers list enables enterprise SSO\n * sign-in flows and registers the OIDC, SAML, and SCIM runtime HTTP\n * routes. It also makes `auth.sso.*` available on the auth\n * object returned by `createAuth`.\n *\n * ```ts\n * import { SSO } from \"@robelest/convex-auth/providers\";\n *\n * const auth = createAuth(components.auth, {\n * providers: [new SSO(), new Password()],\n * });\n *\n * // auth.sso is now available\n * await auth.sso.oidc.configure(ctx, { enterpriseId, clientId, ... });\n * ```\n *\n * Without `new SSO()` in the providers list, `auth.sso` is not\n * present on the returned object and accessing it is a TypeScript error.\n *\n * @module\n */\n\nimport type { SSOProviderConfig } from \"../server/types\";\n\n/**\n * Enterprise SSO provider.\n *\n * Zero-configuration — sensible defaults are applied for all enterprise\n * protocols (OIDC, SAML, SCIM). Per-tenant configuration is done at\n * runtime via `auth.sso.*` helpers.\n *\n * @example\n * ```ts\n * import { createAuth } from \"@robelest/convex-auth/component\";\n * import { SSO, Password } from \"@robelest/convex-auth/providers\";\n * import { components } from \"./_generated/api\";\n *\n * export const auth = createAuth(components.auth, {\n * providers: [new SSO(), new Password()],\n * });\n * ```\n */\nexport class SSO {\n readonly id = \"enterprise-sso\";\n readonly type = \"sso\" as const;\n\n /** @internal Convert to the internal materialized config shape. */\n _toMaterialized(): SSOProviderConfig {\n return { id: this.id, type: \"sso\" };\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AA6CA,IAAa,MAAb,MAAiB;CACf,AAAS,KAAK;CACd,AAAS,OAAO;;CAGhB,kBAAqC;AACnC,SAAO;GAAE,IAAI,KAAK;GAAI,MAAM;GAAO"}
|
|
1
|
+
{"version":3,"file":"sso.js","names":[],"sources":["../../src/providers/sso.ts"],"sourcesContent":["/**\n * Enterprise SSO provider (OIDC + SAML + SCIM).\n *\n * Adding `new SSO()` to your providers list enables enterprise SSO\n * sign-in flows and registers the OIDC, SAML, and SCIM runtime HTTP\n * routes. It also makes `auth.sso.*` available on the auth\n * object returned by `createAuth`.\n *\n * ```ts\n * import { SSO } from \"@robelest/convex-auth/providers\";\n *\n * const auth = createAuth(components.auth, {\n * providers: [new SSO(), new Password()],\n * });\n *\n * // auth.sso is now available\n * await auth.sso.admin.oidc.configure(ctx, { enterpriseId, clientId, ... });\n * ```\n *\n * Without `new SSO()` in the providers list, `auth.sso` is not\n * present on the returned object and accessing it is a TypeScript error.\n *\n * @module\n */\n\nimport type { SSOProviderConfig } from \"../server/types\";\n\n/**\n * Enterprise SSO provider.\n *\n * Zero-configuration — sensible defaults are applied for all enterprise\n * protocols (OIDC, SAML, SCIM). Per-tenant configuration is done at\n * runtime via `auth.sso.*` helpers.\n *\n * @example\n * ```ts\n * import { createAuth } from \"@robelest/convex-auth/component\";\n * import { SSO, Password } from \"@robelest/convex-auth/providers\";\n * import { components } from \"./_generated/api\";\n *\n * export const auth = createAuth(components.auth, {\n * providers: [new SSO(), new Password()],\n * });\n * ```\n */\nexport class SSO {\n readonly id = \"enterprise-sso\";\n readonly type = \"sso\" as const;\n\n /** @internal Convert to the internal materialized config shape. */\n _toMaterialized(): SSOProviderConfig {\n return { id: this.id, type: \"sso\" };\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AA6CA,IAAa,MAAb,MAAiB;CACf,AAAS,KAAK;CACd,AAAS,OAAO;;CAGhB,kBAAqC;AACnC,SAAO;GAAE,IAAI,KAAK;GAAI,MAAM;GAAO"}
|
package/dist/server/auth.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { AuthProviderConfig, ConvexAuthConfig, Doc, HasDeviceProvider, HasPasskeyProvider, HasSSO, HasTotpProvider } from "./types.js";
|
|
1
|
+
import { AuthAuthorizationConfig, AuthGrant, AuthProviderConfig, AuthRoleId, ConvexAuthConfig, Doc, HasDeviceProvider, HasPasskeyProvider, HasSSO, HasTotpProvider } from "./types.js";
|
|
2
2
|
import { AuthApiRefs } from "../client/index.js";
|
|
3
|
-
import { Auth as Auth$1 } from "./
|
|
4
|
-
import { GenericId } from "convex/values";
|
|
3
|
+
import { Auth as Auth$1 } from "./factory.js";
|
|
5
4
|
import { UserIdentity } from "convex/server";
|
|
5
|
+
import { GenericId } from "convex/values";
|
|
6
6
|
|
|
7
7
|
//#region src/server/auth.d.ts
|
|
8
8
|
/**
|
|
@@ -10,8 +10,66 @@ import { UserIdentity } from "convex/server";
|
|
|
10
10
|
* minus `component` (which is passed as the first constructor argument).
|
|
11
11
|
*/
|
|
12
12
|
type AuthConfig = Omit<ConvexAuthConfig, "component">;
|
|
13
|
+
type MemberApiWithAuthorization<TAuthorization extends AuthAuthorizationConfig | undefined> = Omit<ReturnType<typeof Auth$1>["auth"]["member"], "create" | "list" | "update" | "inherit" | "require"> & {
|
|
14
|
+
create: (ctx: Parameters<ReturnType<typeof Auth$1>["auth"]["member"]["create"]>[0], data: {
|
|
15
|
+
groupId: string;
|
|
16
|
+
userId: string;
|
|
17
|
+
roleIds?: AuthRoleId<TAuthorization>[];
|
|
18
|
+
status?: string;
|
|
19
|
+
extend?: Record<string, unknown>;
|
|
20
|
+
}) => Promise<{
|
|
21
|
+
ok: true;
|
|
22
|
+
memberId: string;
|
|
23
|
+
}>;
|
|
24
|
+
list: (ctx: Parameters<ReturnType<typeof Auth$1>["auth"]["member"]["list"]>[0], opts?: {
|
|
25
|
+
where?: {
|
|
26
|
+
groupId?: string;
|
|
27
|
+
userId?: string;
|
|
28
|
+
roleId?: AuthRoleId<TAuthorization>;
|
|
29
|
+
status?: string;
|
|
30
|
+
};
|
|
31
|
+
limit?: number;
|
|
32
|
+
cursor?: string | null;
|
|
33
|
+
orderBy?: "_creationTime" | "status";
|
|
34
|
+
order?: "asc" | "desc";
|
|
35
|
+
}) => ReturnType<ReturnType<typeof Auth$1>["auth"]["member"]["list"]>;
|
|
36
|
+
update: (ctx: Parameters<ReturnType<typeof Auth$1>["auth"]["member"]["update"]>[0], memberId: string, data: Record<string, unknown> & {
|
|
37
|
+
roleIds?: AuthRoleId<TAuthorization>[];
|
|
38
|
+
}) => Promise<{
|
|
39
|
+
ok: true;
|
|
40
|
+
memberId: string;
|
|
41
|
+
}>;
|
|
42
|
+
inherit: (ctx: Parameters<ReturnType<typeof Auth$1>["auth"]["member"]["inherit"]>[0], opts: {
|
|
43
|
+
userId: string;
|
|
44
|
+
groupId: string;
|
|
45
|
+
roleIds?: AuthRoleId<TAuthorization>[];
|
|
46
|
+
grants?: AuthGrant<TAuthorization>[];
|
|
47
|
+
maxDepth?: number;
|
|
48
|
+
}) => ReturnType<ReturnType<typeof Auth$1>["auth"]["member"]["inherit"]>;
|
|
49
|
+
require: (ctx: Parameters<ReturnType<typeof Auth$1>["auth"]["member"]["require"]>[0], opts: {
|
|
50
|
+
userId: string;
|
|
51
|
+
groupId: string;
|
|
52
|
+
roleIds?: AuthRoleId<TAuthorization>[];
|
|
53
|
+
grants?: AuthGrant<TAuthorization>[];
|
|
54
|
+
maxDepth?: number;
|
|
55
|
+
}) => ReturnType<ReturnType<typeof Auth$1>["auth"]["member"]["require"]>;
|
|
56
|
+
};
|
|
57
|
+
type AccessApiWithAuthorization<TAuthorization extends AuthAuthorizationConfig | undefined> = {
|
|
58
|
+
check: (ctx: Parameters<ReturnType<typeof Auth$1>["auth"]["access"]["check"]>[0], opts: {
|
|
59
|
+
userId: string;
|
|
60
|
+
groupId: string;
|
|
61
|
+
grants: AuthGrant<TAuthorization>[];
|
|
62
|
+
maxDepth?: number;
|
|
63
|
+
}) => ReturnType<ReturnType<typeof Auth$1>["auth"]["access"]["check"]>;
|
|
64
|
+
require: (ctx: Parameters<ReturnType<typeof Auth$1>["auth"]["access"]["require"]>[0], opts: {
|
|
65
|
+
userId: string;
|
|
66
|
+
groupId: string;
|
|
67
|
+
grants: AuthGrant<TAuthorization>[];
|
|
68
|
+
maxDepth?: number;
|
|
69
|
+
}) => ReturnType<ReturnType<typeof Auth$1>["auth"]["access"]["require"]>;
|
|
70
|
+
};
|
|
13
71
|
/** The base auth API surface, without conditional namespaces. */
|
|
14
|
-
type AuthApiBase = {
|
|
72
|
+
type AuthApiBase<TAuthorization extends AuthAuthorizationConfig | undefined = undefined> = {
|
|
15
73
|
signIn: ReturnType<typeof Auth$1>["signIn"];
|
|
16
74
|
signOut: ReturnType<typeof Auth$1>["signOut"];
|
|
17
75
|
store: ReturnType<typeof Auth$1>["store"];
|
|
@@ -20,21 +78,100 @@ type AuthApiBase = {
|
|
|
20
78
|
provider: ReturnType<typeof Auth$1>["auth"]["provider"];
|
|
21
79
|
account: ReturnType<typeof Auth$1>["auth"]["account"];
|
|
22
80
|
group: ReturnType<typeof Auth$1>["auth"]["group"];
|
|
23
|
-
member:
|
|
81
|
+
member: MemberApiWithAuthorization<TAuthorization>;
|
|
82
|
+
access: AccessApiWithAuthorization<TAuthorization>;
|
|
24
83
|
invite: ReturnType<typeof Auth$1>["auth"]["invite"];
|
|
25
84
|
key: ReturnType<typeof Auth$1>["auth"]["key"];
|
|
26
85
|
http: ReturnType<typeof Auth$1>["auth"]["http"];
|
|
27
86
|
};
|
|
28
|
-
|
|
29
|
-
type
|
|
30
|
-
|
|
87
|
+
type InternalSsoApi = ReturnType<typeof Auth$1>["auth"]["sso"];
|
|
88
|
+
type PublicSsoAdminApi = {
|
|
89
|
+
connection: InternalSsoApi["connection"] & {
|
|
90
|
+
domain: {
|
|
91
|
+
list: InternalSsoApi["domain"]["list"];
|
|
92
|
+
validate: InternalSsoApi["domain"]["validate"];
|
|
93
|
+
set: (ctx: Parameters<InternalSsoApi["connection"]["create"]>[0], enterpriseId: string, domains: Array<{
|
|
94
|
+
domain: string;
|
|
95
|
+
isPrimary?: boolean;
|
|
96
|
+
}>) => Promise<{
|
|
97
|
+
ok: true;
|
|
98
|
+
enterpriseId: string;
|
|
99
|
+
domains: Array<{
|
|
100
|
+
domainId: string;
|
|
101
|
+
domain: string;
|
|
102
|
+
isPrimary: boolean;
|
|
103
|
+
verified: boolean;
|
|
104
|
+
verifiedAt: number | null;
|
|
105
|
+
}>;
|
|
106
|
+
}>;
|
|
107
|
+
verification: {
|
|
108
|
+
request: (ctx: Parameters<InternalSsoApi["connection"]["create"]>[0], args: {
|
|
109
|
+
enterpriseId: string;
|
|
110
|
+
domain: string;
|
|
111
|
+
}) => Promise<{
|
|
112
|
+
ok: true;
|
|
113
|
+
enterpriseId: string;
|
|
114
|
+
domain: string;
|
|
115
|
+
requestedAt: number;
|
|
116
|
+
expiresAt: number;
|
|
117
|
+
challenge: {
|
|
118
|
+
recordType: "TXT";
|
|
119
|
+
recordName: string;
|
|
120
|
+
recordValue: string;
|
|
121
|
+
};
|
|
122
|
+
}>;
|
|
123
|
+
confirm: (ctx: Parameters<InternalSsoApi["connection"]["create"]>[0], args: {
|
|
124
|
+
enterpriseId: string;
|
|
125
|
+
domain: string;
|
|
126
|
+
}) => Promise<{
|
|
127
|
+
ok: boolean;
|
|
128
|
+
enterpriseId: string;
|
|
129
|
+
domain: string;
|
|
130
|
+
verifiedAt?: number;
|
|
131
|
+
checks: Array<{
|
|
132
|
+
name: string;
|
|
133
|
+
ok: boolean;
|
|
134
|
+
message?: string;
|
|
135
|
+
}>;
|
|
136
|
+
}>;
|
|
137
|
+
};
|
|
138
|
+
};
|
|
139
|
+
};
|
|
140
|
+
oidc: Omit<InternalSsoApi["oidc"], "signIn">;
|
|
141
|
+
saml: Omit<InternalSsoApi["saml"], "metadata">;
|
|
142
|
+
policy: InternalSsoApi["policy"];
|
|
143
|
+
audit: {
|
|
144
|
+
list: InternalSsoApi["audit"]["list"];
|
|
145
|
+
};
|
|
146
|
+
webhook: {
|
|
147
|
+
endpoint: InternalSsoApi["webhook"]["endpoint"];
|
|
148
|
+
delivery: {
|
|
149
|
+
list: InternalSsoApi["webhook"]["delivery"]["list"];
|
|
150
|
+
};
|
|
151
|
+
};
|
|
152
|
+
};
|
|
153
|
+
type PublicSsoClientApi = {
|
|
154
|
+
signIn: InternalSsoApi["oidc"]["signIn"];
|
|
155
|
+
metadata: InternalSsoApi["saml"]["metadata"];
|
|
156
|
+
};
|
|
157
|
+
type PublicSsoApi = {
|
|
158
|
+
admin: PublicSsoAdminApi;
|
|
159
|
+
client: PublicSsoClientApi;
|
|
160
|
+
};
|
|
161
|
+
type PublicScimApi = {
|
|
162
|
+
admin: Omit<InternalSsoApi["scim"], "getConfigByToken" | "identity">;
|
|
163
|
+
};
|
|
164
|
+
/** Auth API with enterprise namespaces — present only when `new SSO()` is in providers. */
|
|
165
|
+
type AuthApi<TAuthorization extends AuthAuthorizationConfig | undefined = undefined> = AuthApiBase<TAuthorization> & {
|
|
166
|
+
sso: PublicSsoApi;
|
|
167
|
+
scim: PublicScimApi;
|
|
31
168
|
};
|
|
32
169
|
/**
|
|
33
170
|
* The return type of `createAuth`. Conditional namespaces:
|
|
34
|
-
* - `auth.sso` — only when `new SSO()` is in providers
|
|
171
|
+
* - `auth.sso` and `auth.scim` — only when `new SSO()` is in providers
|
|
35
172
|
* - `auth.clientApi` — typed API refs for the client SDK with capabilities
|
|
36
173
|
*/
|
|
37
|
-
type ConvexAuthResult<P extends AuthProviderConfig[]> = HasSSO<P> extends true ? AuthApi : AuthApiBase
|
|
174
|
+
type ConvexAuthResult<P extends AuthProviderConfig[], TAuthorization extends AuthAuthorizationConfig | undefined = undefined> = HasSSO<P> extends true ? AuthApi<TAuthorization> : AuthApiBase<TAuthorization>;
|
|
38
175
|
/**
|
|
39
176
|
* Infer the typed `AuthApiRefs` for the client SDK from a `createAuth` call.
|
|
40
177
|
*
|
|
@@ -47,20 +184,29 @@ type ConvexAuthResult<P extends AuthProviderConfig[]> = HasSSO<P> extends true ?
|
|
|
47
184
|
* // Frontend
|
|
48
185
|
* import type { auth } from "../convex/auth";
|
|
49
186
|
* import type { InferClientApi } from "@robelest/convex-auth/component";
|
|
50
|
-
* const c = client<InferClientApi<typeof auth>>({ convex, api:
|
|
187
|
+
* const c = client<InferClientApi<typeof auth>>({ convex, api: api.auth });
|
|
51
188
|
* ```
|
|
52
189
|
*/
|
|
53
190
|
type InferClientApi<T> = T extends ConvexAuthResult<infer P> ? AuthApiRefs<HasPasskeyProvider<P>, HasTotpProvider<P>, HasDeviceProvider<P>> : AuthApiRefs;
|
|
54
191
|
/**
|
|
55
192
|
* Create an auth API object.
|
|
56
193
|
*
|
|
57
|
-
* When `new SSO()` is included in providers, `auth.sso`
|
|
58
|
-
* on the returned object. Without it,
|
|
59
|
-
* accessing
|
|
194
|
+
* When `new SSO()` is included in providers, `auth.sso` and `auth.scim`
|
|
195
|
+
* are available on the returned object. Without it, those namespaces are
|
|
196
|
+
* absent and accessing them is a TypeScript compile error.
|
|
60
197
|
*/
|
|
61
|
-
declare function createAuth<P extends AuthProviderConfig[]>(component: ConvexAuthConfig["component"], config: Omit<AuthConfig, "providers"> & {
|
|
198
|
+
declare function createAuth<P extends AuthProviderConfig[], TAuthorization extends AuthAuthorizationConfig | undefined = undefined>(component: ConvexAuthConfig["component"], config: Omit<AuthConfig, "providers" | "authorization"> & {
|
|
62
199
|
providers: P;
|
|
63
|
-
|
|
200
|
+
authorization?: TAuthorization;
|
|
201
|
+
}): ConvexAuthResult<P, TAuthorization>;
|
|
202
|
+
declare function defineRoles<const TRoles extends Record<string, {
|
|
203
|
+
label?: string;
|
|
204
|
+
grants: readonly string[];
|
|
205
|
+
}>>(roles: TRoles): { [K in keyof TRoles]: {
|
|
206
|
+
id: K & string;
|
|
207
|
+
label?: TRoles[K]["label"];
|
|
208
|
+
grants: Array<TRoles[K]["grants"][number] & string>;
|
|
209
|
+
} };
|
|
64
210
|
type UserDoc = Doc<"User">;
|
|
65
211
|
type AuthCtxConfig<TResolve extends Record<string, unknown> = Record<string, never>> = {
|
|
66
212
|
optional?: boolean;
|
|
@@ -104,5 +250,5 @@ type InferAuth<T extends {
|
|
|
104
250
|
}>;
|
|
105
251
|
}> = Awaited<ReturnType<T["input"]>>["ctx"]["auth"];
|
|
106
252
|
//#endregion
|
|
107
|
-
export { AuthApi, AuthApiBase, AuthConfig, AuthCtx, AuthCtxConfig, ConvexAuthResult, InferAuth, InferClientApi, UserDoc, createAuth };
|
|
253
|
+
export { AuthApi, AuthApiBase, AuthConfig, AuthCtx, AuthCtxConfig, ConvexAuthResult, InferAuth, InferClientApi, UserDoc, createAuth, defineRoles };
|
|
108
254
|
//# sourceMappingURL=auth.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.d.ts","names":[],"sources":["../../src/server/auth.ts"],"mappings":";;;;;;;;
|
|
1
|
+
{"version":3,"file":"auth.d.ts","names":[],"sources":["../../src/server/auth.ts"],"mappings":"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"}
|
package/dist/server/auth.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { Fx } from "./fx.js";
|
|
2
|
-
import { Auth } from "./
|
|
1
|
+
import { AuthError, Fx } from "./fx.js";
|
|
2
|
+
import { Auth } from "./factory.js";
|
|
3
3
|
|
|
4
4
|
//#region src/server/auth.ts
|
|
5
5
|
/**
|
|
6
6
|
* Create an auth API object.
|
|
7
7
|
*
|
|
8
|
-
* When `new SSO()` is included in providers, `auth.sso`
|
|
9
|
-
* on the returned object. Without it,
|
|
10
|
-
* accessing
|
|
8
|
+
* When `new SSO()` is included in providers, `auth.sso` and `auth.scim`
|
|
9
|
+
* are available on the returned object. Without it, those namespaces are
|
|
10
|
+
* absent and accessing them is a TypeScript compile error.
|
|
11
11
|
*/
|
|
12
12
|
function createAuth(component, config) {
|
|
13
13
|
const authResult = Auth({
|
|
@@ -15,6 +15,86 @@ function createAuth(component, config) {
|
|
|
15
15
|
component,
|
|
16
16
|
providers: [...config.providers]
|
|
17
17
|
});
|
|
18
|
+
const { domain: domainApi, scim: scimApi, connection: connectionApi, audit: auditApi, webhook: webhookApi, oidc: oidcApi, saml: samlApi, ...restSso } = authResult.auth.sso;
|
|
19
|
+
const setEnterpriseDomains = async (ctx, enterpriseId, domains) => {
|
|
20
|
+
const enterprise = await connectionApi.get(ctx, enterpriseId);
|
|
21
|
+
if (enterprise === null) throw new AuthError("INVALID_PARAMETERS", "Enterprise not found.").toConvexError();
|
|
22
|
+
const normalized = domains.map((entry) => ({
|
|
23
|
+
...entry,
|
|
24
|
+
domain: entry.domain.trim().toLowerCase()
|
|
25
|
+
}));
|
|
26
|
+
const deduped = /* @__PURE__ */ new Map();
|
|
27
|
+
for (const entry of normalized) {
|
|
28
|
+
if (entry.domain.length === 0) throw new AuthError("INVALID_PARAMETERS", "Domain must not be empty.").toConvexError();
|
|
29
|
+
if (deduped.has(entry.domain)) throw new AuthError("INVALID_PARAMETERS", `Duplicate domain: ${entry.domain}`).toConvexError();
|
|
30
|
+
deduped.set(entry.domain, entry);
|
|
31
|
+
}
|
|
32
|
+
const nextDomains = [...deduped.values()];
|
|
33
|
+
const primaryCount = nextDomains.filter((entry) => entry.isPrimary).length;
|
|
34
|
+
if (primaryCount > 1) throw new AuthError("INVALID_PARAMETERS", "Only one primary domain may be set.").toConvexError();
|
|
35
|
+
if (nextDomains.length > 0 && primaryCount === 0) nextDomains[0] = {
|
|
36
|
+
...nextDomains[0],
|
|
37
|
+
isPrimary: true
|
|
38
|
+
};
|
|
39
|
+
const currentDomains = await domainApi.list(ctx, enterpriseId);
|
|
40
|
+
const currentByDomain = new Map(currentDomains.map((entry) => [entry.domain.toLowerCase(), entry]));
|
|
41
|
+
for (const existing of currentDomains) if (!deduped.has(existing.domain.toLowerCase())) await domainApi.remove(ctx, existing._id);
|
|
42
|
+
for (const nextDomain of nextDomains) {
|
|
43
|
+
const current = currentByDomain.get(nextDomain.domain);
|
|
44
|
+
if (current && current.isPrimary === Boolean(nextDomain.isPrimary)) continue;
|
|
45
|
+
if (current) await domainApi.remove(ctx, current._id);
|
|
46
|
+
const domainId = await domainApi.add(ctx, {
|
|
47
|
+
enterpriseId: enterprise._id,
|
|
48
|
+
groupId: enterprise.groupId,
|
|
49
|
+
domain: nextDomain.domain,
|
|
50
|
+
isPrimary: nextDomain.isPrimary
|
|
51
|
+
});
|
|
52
|
+
if (current?.verifiedAt !== void 0) await ctx.runMutation(component.public.enterpriseDomainVerify, {
|
|
53
|
+
domainId,
|
|
54
|
+
verifiedAt: current.verifiedAt
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
return {
|
|
58
|
+
ok: true,
|
|
59
|
+
enterpriseId,
|
|
60
|
+
domains: (await domainApi.list(ctx, enterpriseId)).map((domain) => ({
|
|
61
|
+
domainId: domain._id,
|
|
62
|
+
domain: domain.domain,
|
|
63
|
+
isPrimary: domain.isPrimary,
|
|
64
|
+
verified: domain.verifiedAt !== void 0,
|
|
65
|
+
verifiedAt: domain.verifiedAt ?? null
|
|
66
|
+
}))
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
const publicSso = {
|
|
70
|
+
admin: {
|
|
71
|
+
...restSso,
|
|
72
|
+
oidc: { ...oidcApi },
|
|
73
|
+
saml: { ...samlApi },
|
|
74
|
+
connection: {
|
|
75
|
+
...connectionApi,
|
|
76
|
+
domain: {
|
|
77
|
+
list: domainApi.list,
|
|
78
|
+
validate: domainApi.validate,
|
|
79
|
+
set: setEnterpriseDomains,
|
|
80
|
+
verification: {
|
|
81
|
+
request: domainApi.verification.request,
|
|
82
|
+
confirm: domainApi.verification.confirm
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
policy: restSso.policy,
|
|
87
|
+
audit: { list: auditApi.list },
|
|
88
|
+
webhook: {
|
|
89
|
+
endpoint: webhookApi.endpoint,
|
|
90
|
+
delivery: { list: webhookApi.delivery.list }
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
client: {
|
|
94
|
+
signIn: oidcApi.signIn,
|
|
95
|
+
metadata: samlApi.metadata
|
|
96
|
+
}
|
|
97
|
+
};
|
|
18
98
|
return {
|
|
19
99
|
signIn: authResult.signIn,
|
|
20
100
|
signOut: authResult.signOut,
|
|
@@ -25,12 +105,25 @@ function createAuth(component, config) {
|
|
|
25
105
|
account: authResult.auth.account,
|
|
26
106
|
group: authResult.auth.group,
|
|
27
107
|
member: authResult.auth.member,
|
|
108
|
+
access: authResult.auth.access,
|
|
28
109
|
invite: authResult.auth.invite,
|
|
29
110
|
key: authResult.auth.key,
|
|
30
|
-
sso:
|
|
111
|
+
sso: publicSso,
|
|
112
|
+
scim: { admin: {
|
|
113
|
+
configure: scimApi.configure,
|
|
114
|
+
get: scimApi.get,
|
|
115
|
+
validate: scimApi.validate
|
|
116
|
+
} },
|
|
31
117
|
http: authResult.auth.http
|
|
32
118
|
};
|
|
33
119
|
}
|
|
120
|
+
function defineRoles(roles) {
|
|
121
|
+
return Object.fromEntries(Object.entries(roles).map(([id, role]) => [id, {
|
|
122
|
+
id,
|
|
123
|
+
...role.label ? { label: role.label } : {},
|
|
124
|
+
grants: [...role.grants]
|
|
125
|
+
}]));
|
|
126
|
+
}
|
|
34
127
|
function AuthCtx(auth, config) {
|
|
35
128
|
return {
|
|
36
129
|
args: {},
|
|
@@ -77,5 +170,5 @@ function AuthCtx(auth, config) {
|
|
|
77
170
|
}
|
|
78
171
|
|
|
79
172
|
//#endregion
|
|
80
|
-
export { AuthCtx, createAuth };
|
|
173
|
+
export { AuthCtx, createAuth, defineRoles };
|
|
81
174
|
//# sourceMappingURL=auth.js.map
|