@robelest/convex-auth 0.0.4-preview.21 → 0.0.4-preview.23
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/authorization/index.d.ts +1 -1
- package/dist/authorization/index.js +1 -1
- package/dist/authorization/index.js.map +1 -1
- package/dist/client/index.d.ts +1 -2
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +36 -39
- package/dist/client/index.js.map +1 -1
- package/dist/component/client/index.d.ts +1 -2
- package/dist/component/convex.config.d.ts +2 -2
- package/dist/component/convex.config.d.ts.map +1 -1
- package/dist/component/model.d.ts +5 -5
- package/dist/component/model.d.ts.map +1 -1
- package/dist/component/public/enterprise/audit.d.ts.map +1 -1
- package/dist/component/public/enterprise/audit.js.map +1 -1
- package/dist/component/public/enterprise/core.d.ts.map +1 -1
- package/dist/component/public/enterprise/core.js.map +1 -1
- package/dist/component/public/enterprise/domains.d.ts.map +1 -1
- package/dist/component/public/enterprise/domains.js.map +1 -1
- package/dist/component/public/enterprise/scim.d.ts.map +1 -1
- package/dist/component/public/enterprise/scim.js.map +1 -1
- package/dist/component/public/enterprise/secrets.d.ts.map +1 -1
- package/dist/component/public/enterprise/secrets.js.map +1 -1
- package/dist/component/public/enterprise/webhooks.d.ts.map +1 -1
- package/dist/component/public/enterprise/webhooks.js.map +1 -1
- package/dist/component/public/factors/devices.d.ts.map +1 -1
- package/dist/component/public/factors/devices.js.map +1 -1
- package/dist/component/public/factors/passkeys.d.ts.map +1 -1
- package/dist/component/public/factors/passkeys.js.map +1 -1
- package/dist/component/public/factors/totp.d.ts.map +1 -1
- package/dist/component/public/factors/totp.js.map +1 -1
- package/dist/component/public/groups/core.js.map +1 -1
- package/dist/component/public/groups/invites.d.ts.map +1 -1
- package/dist/component/public/groups/invites.js.map +1 -1
- package/dist/component/public/groups/members.d.ts.map +1 -1
- package/dist/component/public/groups/members.js.map +1 -1
- package/dist/component/public/identity/accounts.d.ts.map +1 -1
- package/dist/component/public/identity/accounts.js.map +1 -1
- package/dist/component/public/identity/codes.d.ts.map +1 -1
- package/dist/component/public/identity/codes.js.map +1 -1
- package/dist/component/public/identity/sessions.d.ts.map +1 -1
- package/dist/component/public/identity/sessions.js.map +1 -1
- package/dist/component/public/identity/tokens.d.ts.map +1 -1
- package/dist/component/public/identity/tokens.js.map +1 -1
- package/dist/component/public/identity/users.d.ts.map +1 -1
- package/dist/component/public/identity/users.js.map +1 -1
- package/dist/component/public/identity/verifiers.d.ts.map +1 -1
- package/dist/component/public/identity/verifiers.js.map +1 -1
- package/dist/component/public/security/keys.d.ts.map +1 -1
- package/dist/component/public/security/keys.js.map +1 -1
- package/dist/component/public/security/limits.d.ts.map +1 -1
- package/dist/component/public/security/limits.js.map +1 -1
- package/dist/component/schema.d.ts +39 -39
- package/dist/component/server/auth.d.ts +95 -52
- package/dist/component/server/auth.d.ts.map +1 -1
- package/dist/component/server/auth.js +63 -43
- package/dist/component/server/auth.js.map +1 -1
- package/dist/component/server/core.js +116 -235
- package/dist/component/server/core.js.map +1 -1
- package/dist/component/server/crypto.js +25 -7
- package/dist/component/server/crypto.js.map +1 -1
- package/dist/component/server/device.js +58 -15
- package/dist/component/server/device.js.map +1 -1
- package/dist/component/server/enterprise/domain.js +148 -59
- package/dist/component/server/enterprise/domain.js.map +1 -1
- package/dist/component/server/enterprise/http.js +36 -15
- package/dist/component/server/enterprise/http.js.map +1 -1
- package/dist/component/server/enterprise/oidc.js +1 -1
- package/dist/component/server/http.js +26 -21
- package/dist/component/server/http.js.map +1 -1
- package/dist/component/server/identity.js +5 -2
- package/dist/component/server/identity.js.map +1 -1
- package/dist/component/server/limits.js +21 -30
- package/dist/component/server/limits.js.map +1 -1
- package/dist/component/server/mutations/account.js +12 -10
- package/dist/component/server/mutations/account.js.map +1 -1
- package/dist/component/server/mutations/code.js +5 -2
- package/dist/component/server/mutations/code.js.map +1 -1
- package/dist/component/server/mutations/invalidate.js +1 -1
- package/dist/component/server/mutations/invalidate.js.map +1 -1
- package/dist/component/server/mutations/oauth.js +10 -4
- package/dist/component/server/mutations/oauth.js.map +1 -1
- package/dist/component/server/mutations/refresh.js +2 -2
- package/dist/component/server/mutations/refresh.js.map +1 -1
- package/dist/component/server/mutations/register.js +46 -42
- package/dist/component/server/mutations/register.js.map +1 -1
- package/dist/component/server/mutations/retrieve.js +21 -25
- package/dist/component/server/mutations/retrieve.js.map +1 -1
- package/dist/component/server/mutations/signature.js +10 -4
- package/dist/component/server/mutations/signature.js.map +1 -1
- package/dist/component/server/mutations/signout.js.map +1 -1
- package/dist/component/server/mutations/store.js +9 -24
- package/dist/component/server/mutations/store.js.map +1 -1
- package/dist/component/server/mutations/verifier.js.map +1 -1
- package/dist/component/server/mutations/verify.js +1 -1
- package/dist/component/server/mutations/verify.js.map +1 -1
- package/dist/component/server/oauth.js +53 -16
- package/dist/component/server/oauth.js.map +1 -1
- package/dist/component/server/passkey.js +115 -31
- package/dist/component/server/passkey.js.map +1 -1
- package/dist/component/server/redirects.js +9 -3
- package/dist/component/server/redirects.js.map +1 -1
- package/dist/component/server/refresh.js +10 -7
- package/dist/component/server/refresh.js.map +1 -1
- package/dist/component/server/runtime.d.ts +3 -3
- package/dist/component/server/runtime.d.ts.map +1 -1
- package/dist/component/server/runtime.js +62 -20
- package/dist/component/server/runtime.js.map +1 -1
- package/dist/component/server/signin.js +34 -10
- package/dist/component/server/signin.js.map +1 -1
- package/dist/component/server/totp.js +79 -19
- package/dist/component/server/totp.js.map +1 -1
- package/dist/component/server/types.d.ts +12 -20
- 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 +6 -3
- package/dist/component/server/users.js.map +1 -1
- package/dist/component/server/utils.js +10 -4
- package/dist/component/server/utils.js.map +1 -1
- package/dist/core/types.d.ts +14 -22
- package/dist/core/types.d.ts.map +1 -1
- package/dist/factors/device.js +8 -9
- package/dist/factors/device.js.map +1 -1
- package/dist/factors/passkey.js +18 -21
- package/dist/factors/passkey.js.map +1 -1
- package/dist/providers/password.js +66 -81
- package/dist/providers/password.js.map +1 -1
- package/dist/runtime/invite.js +2 -8
- package/dist/runtime/invite.js.map +1 -1
- package/dist/server/auth.d.ts +95 -52
- package/dist/server/auth.d.ts.map +1 -1
- package/dist/server/auth.js +63 -43
- package/dist/server/auth.js.map +1 -1
- package/dist/server/core.d.ts +71 -159
- package/dist/server/core.d.ts.map +1 -1
- package/dist/server/core.js +116 -235
- package/dist/server/core.js.map +1 -1
- package/dist/server/crypto.d.ts.map +1 -1
- package/dist/server/crypto.js +25 -7
- package/dist/server/crypto.js.map +1 -1
- package/dist/server/device.js +58 -15
- package/dist/server/device.js.map +1 -1
- package/dist/server/enterprise/domain.d.ts +0 -8
- package/dist/server/enterprise/domain.d.ts.map +1 -1
- package/dist/server/enterprise/domain.js +148 -59
- package/dist/server/enterprise/domain.js.map +1 -1
- package/dist/server/enterprise/http.d.ts.map +1 -1
- package/dist/server/enterprise/http.js +35 -14
- package/dist/server/enterprise/http.js.map +1 -1
- package/dist/server/http.d.ts +2 -2
- package/dist/server/http.d.ts.map +1 -1
- package/dist/server/http.js +25 -20
- package/dist/server/http.js.map +1 -1
- package/dist/server/identity.js +5 -2
- package/dist/server/identity.js.map +1 -1
- package/dist/server/index.d.ts +2 -2
- package/dist/server/limits.js +21 -30
- package/dist/server/limits.js.map +1 -1
- package/dist/server/mounts.d.ts +26 -64
- package/dist/server/mounts.d.ts.map +1 -1
- package/dist/server/mounts.js +45 -106
- package/dist/server/mounts.js.map +1 -1
- package/dist/server/mutations/account.d.ts +8 -9
- package/dist/server/mutations/account.d.ts.map +1 -1
- package/dist/server/mutations/account.js +11 -9
- package/dist/server/mutations/account.js.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/code.js +5 -2
- package/dist/server/mutations/code.js.map +1 -1
- package/dist/server/mutations/invalidate.d.ts +4 -4
- package/dist/server/mutations/invalidate.d.ts.map +1 -1
- package/dist/server/mutations/invalidate.js.map +1 -1
- package/dist/server/mutations/oauth.d.ts +12 -10
- package/dist/server/mutations/oauth.d.ts.map +1 -1
- package/dist/server/mutations/oauth.js +9 -3
- package/dist/server/mutations/oauth.js.map +1 -1
- package/dist/server/mutations/refresh.d.ts +3 -3
- package/dist/server/mutations/refresh.d.ts.map +1 -1
- package/dist/server/mutations/refresh.js +1 -1
- package/dist/server/mutations/refresh.js.map +1 -1
- package/dist/server/mutations/register.d.ts +11 -11
- package/dist/server/mutations/register.d.ts.map +1 -1
- package/dist/server/mutations/register.js +45 -41
- package/dist/server/mutations/register.js.map +1 -1
- package/dist/server/mutations/retrieve.d.ts +6 -6
- package/dist/server/mutations/retrieve.d.ts.map +1 -1
- package/dist/server/mutations/retrieve.js +20 -24
- package/dist/server/mutations/retrieve.js.map +1 -1
- package/dist/server/mutations/signature.d.ts +6 -7
- package/dist/server/mutations/signature.d.ts.map +1 -1
- package/dist/server/mutations/signature.js +9 -3
- package/dist/server/mutations/signature.js.map +1 -1
- package/dist/server/mutations/signin.d.ts +5 -5
- package/dist/server/mutations/signin.d.ts.map +1 -1
- package/dist/server/mutations/signout.js.map +1 -1
- package/dist/server/mutations/store.d.ts +97 -97
- package/dist/server/mutations/store.d.ts.map +1 -1
- package/dist/server/mutations/store.js +8 -23
- package/dist/server/mutations/store.js.map +1 -1
- package/dist/server/mutations/verifier.js.map +1 -1
- package/dist/server/mutations/verify.d.ts +10 -10
- package/dist/server/mutations/verify.d.ts.map +1 -1
- package/dist/server/mutations/verify.js.map +1 -1
- package/dist/server/oauth.js +53 -16
- package/dist/server/oauth.js.map +1 -1
- package/dist/server/passkey.d.ts +2 -2
- package/dist/server/passkey.d.ts.map +1 -1
- package/dist/server/passkey.js +114 -30
- package/dist/server/passkey.js.map +1 -1
- package/dist/server/redirects.js +9 -3
- package/dist/server/redirects.js.map +1 -1
- package/dist/server/refresh.js +10 -7
- package/dist/server/refresh.js.map +1 -1
- package/dist/server/runtime.d.ts +14 -14
- package/dist/server/runtime.d.ts.map +1 -1
- package/dist/server/runtime.js +61 -19
- package/dist/server/runtime.js.map +1 -1
- package/dist/server/signin.js +34 -10
- package/dist/server/signin.js.map +1 -1
- package/dist/server/ssr.d.ts.map +1 -1
- package/dist/server/ssr.js +175 -184
- package/dist/server/ssr.js.map +1 -1
- package/dist/server/totp.js +78 -18
- package/dist/server/totp.js.map +1 -1
- package/dist/server/types.d.ts +13 -21
- package/dist/server/types.d.ts.map +1 -1
- package/dist/server/types.js.map +1 -1
- package/dist/server/users.js +6 -3
- package/dist/server/users.js.map +1 -1
- package/dist/server/utils.js +10 -4
- package/dist/server/utils.js.map +1 -1
- package/package.json +2 -6
- package/src/authorization/index.ts +1 -1
- package/src/cli/index.ts +1 -1
- package/src/client/core/types.ts +14 -14
- package/src/client/factors/device.ts +10 -12
- package/src/client/factors/passkey.ts +23 -26
- package/src/client/index.ts +54 -64
- package/src/client/runtime/invite.ts +5 -7
- package/src/component/index.ts +1 -0
- package/src/component/public/enterprise/audit.ts +6 -1
- package/src/component/public/enterprise/core.ts +1 -0
- package/src/component/public/enterprise/domains.ts +5 -1
- package/src/component/public/enterprise/scim.ts +1 -0
- package/src/component/public/enterprise/secrets.ts +1 -0
- package/src/component/public/enterprise/webhooks.ts +1 -0
- package/src/component/public/factors/devices.ts +1 -0
- package/src/component/public/factors/passkeys.ts +1 -0
- package/src/component/public/factors/totp.ts +1 -0
- package/src/component/public/groups/core.ts +1 -1
- package/src/component/public/groups/invites.ts +7 -1
- package/src/component/public/groups/members.ts +1 -0
- package/src/component/public/identity/accounts.ts +1 -0
- package/src/component/public/identity/codes.ts +1 -0
- package/src/component/public/identity/sessions.ts +1 -0
- package/src/component/public/identity/tokens.ts +1 -0
- package/src/component/public/identity/users.ts +1 -0
- package/src/component/public/identity/verifiers.ts +1 -0
- package/src/component/public/security/keys.ts +1 -0
- package/src/component/public/security/limits.ts +1 -0
- package/src/providers/password.ts +89 -110
- package/src/server/auth.ts +177 -111
- package/src/server/core.ts +197 -233
- package/src/server/crypto.ts +31 -29
- package/src/server/device.ts +65 -32
- package/src/server/enterprise/domain.ts +158 -170
- package/src/server/enterprise/http.ts +46 -39
- package/src/server/http.ts +36 -30
- package/src/server/identity.ts +5 -5
- package/src/server/index.ts +2 -0
- package/src/server/limits.ts +53 -80
- package/src/server/mounts.ts +47 -74
- package/src/server/mutations/account.ts +22 -36
- package/src/server/mutations/code.ts +6 -6
- package/src/server/mutations/invalidate.ts +1 -1
- package/src/server/mutations/oauth.ts +14 -8
- package/src/server/mutations/refresh.ts +5 -4
- package/src/server/mutations/register.ts +87 -132
- package/src/server/mutations/retrieve.ts +44 -44
- package/src/server/mutations/signature.ts +13 -6
- package/src/server/mutations/signout.ts +1 -1
- package/src/server/mutations/store.ts +16 -31
- package/src/server/mutations/verifier.ts +1 -1
- package/src/server/mutations/verify.ts +3 -5
- package/src/server/oauth.ts +60 -69
- package/src/server/passkey.ts +567 -517
- package/src/server/redirects.ts +10 -6
- package/src/server/refresh.ts +14 -18
- package/src/server/runtime.ts +70 -55
- package/src/server/signin.ts +44 -37
- package/src/server/ssr.ts +390 -407
- package/src/server/totp.ts +85 -35
- package/src/server/types.ts +19 -22
- package/src/server/users.ts +7 -6
- package/src/server/utils.ts +10 -12
- package/dist/component/server/authError.js +0 -34
- package/dist/component/server/authError.js.map +0 -1
- package/dist/component/server/errors.d.ts +0 -1
- package/dist/component/server/errors.js +0 -137
- package/dist/component/server/errors.js.map +0 -1
- package/dist/server/authError.d.ts +0 -46
- package/dist/server/authError.d.ts.map +0 -1
- package/dist/server/authError.js +0 -34
- package/dist/server/authError.js.map +0 -1
- package/dist/server/errors.d.ts +0 -177
- package/dist/server/errors.d.ts.map +0 -1
- package/dist/server/errors.js +0 -212
- package/dist/server/errors.js.map +0 -1
- package/src/server/authError.ts +0 -44
- package/src/server/errors.ts +0 -290
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"domain.js","names":[],"sources":["../../../src/server/enterprise/domain.ts"],"sourcesContent":["import { GenericActionCtx, GenericDataModel } from \"convex/server\";\n\nimport { Fx } from \"@robelest/fx\";\n\nimport { AuthError } from \"../authError\";\nimport type { EnterprisePolicyPatch } from \"../types\";\n\ntype ComponentCtx = Pick<\n GenericActionCtx<GenericDataModel>,\n \"runQuery\" | \"runMutation\"\n>;\ntype ComponentReadCtx = Pick<GenericActionCtx<GenericDataModel>, \"runQuery\">;\n\n/**\n * Build the enterprise and SSO management domain.\n */\nexport function createEnterpriseDomain(deps: any) {\n const {\n config,\n normalizeEnterprisePolicy,\n normalizeDomain,\n getEnterpriseSecret,\n loadEnterpriseOrThrow,\n validateEnterprisePolicy,\n recordEnterpriseAuditEvent,\n emitEnterpriseWebhookDeliveries,\n enterpriseNotFoundError,\n ENTERPRISE_OIDC_CLIENT_SECRET_KIND,\n requireEnv,\n generateRandomString,\n INVITE_TOKEN_ALPHABET,\n sha256,\n encryptSecret,\n upsertProtocolConfig,\n parseSamlIdpMetadata,\n createServiceProviderMetadata,\n getSamlServiceProviderOptions,\n getPublicOidcConfig,\n withOidcSecretState,\n getOidcConfig,\n getEnterpriseOidcUrls,\n enterpriseOidcProviderId,\n getPolicyFromEnterprise,\n patchEnterprisePolicy,\n } = deps;\n\n const ENTERPRISE_DOMAIN_VERIFICATION_PREFIX = \"_convex-auth-verification\";\n const ENTERPRISE_DOMAIN_VERIFICATION_TTL_MS = 1000 * 60 * 60 * 24 * 7;\n\n const toDomainSummary = (domain: {\n _id: string;\n domain: string;\n isPrimary: boolean;\n verifiedAt?: number;\n }) => ({\n domainId: domain._id,\n domain: domain.domain,\n isPrimary: domain.isPrimary,\n verified: domain.verifiedAt !== undefined,\n verifiedAt: domain.verifiedAt ?? null,\n });\n\n const getDomainVerificationRecordName = (domain: string) =>\n `${ENTERPRISE_DOMAIN_VERIFICATION_PREFIX}.${normalizeDomain(domain)}`;\n\n const parseTxtAnswer = (value: string) => {\n const quoted = [...value.matchAll(/\"([^\"]*)\"/g)].map((match) => match[1]);\n if (quoted.length > 0) {\n return quoted.join(\"\");\n }\n return value.replace(/^\"|\"$/g, \"\").trim();\n };\n\n const resolveTxtValues = async (recordName: string) => {\n const url = new URL(\"https://dns.google/resolve\");\n url.searchParams.set(\"name\", recordName);\n url.searchParams.set(\"type\", \"TXT\");\n\n const response = await fetch(url, {\n headers: { accept: \"application/json\" },\n });\n if (!response.ok) {\n throw new Error(`DNS TXT lookup failed with status ${response.status}.`);\n }\n const data = (await response.json()) as {\n Answer?: Array<{ data?: string }>;\n };\n return (data.Answer ?? [])\n .map((answer) =>\n typeof answer.data === \"string\" ? parseTxtAnswer(answer.data) : null,\n )\n .filter((value): value is string => value !== null && value.length > 0);\n };\n\n return {\n connection: {\n create: async (\n ctx: ComponentCtx,\n data: {\n groupId: string;\n slug?: string;\n name?: string;\n status?: \"draft\" | \"active\" | \"disabled\";\n policy?: EnterprisePolicyPatch;\n config?: Record<string, unknown>;\n extend?: Record<string, unknown>;\n },\n ): Promise<{ ok: true; enterpriseId: string; groupId: string }> => {\n const enterpriseId = (await ctx.runMutation(\n config.component.public.enterpriseCreate,\n {\n ...data,\n policy: normalizeEnterprisePolicy(data.policy),\n },\n )) as string;\n return {\n ok: true,\n enterpriseId,\n groupId: data.groupId,\n };\n },\n get: async (ctx: ComponentReadCtx, enterpriseId: string) => {\n return await ctx.runQuery(config.component.public.enterpriseGet, {\n enterpriseId,\n });\n },\n getByGroup: async (ctx: ComponentReadCtx, groupId: string) => {\n return await ctx.runQuery(\n config.component.public.enterpriseGetByGroup,\n {\n groupId,\n },\n );\n },\n getByDomain: async (ctx: ComponentReadCtx, domain: string) => {\n return await ctx.runQuery(\n config.component.public.enterpriseGetByDomain,\n {\n domain: normalizeDomain(domain),\n },\n );\n },\n list: async (\n ctx: ComponentReadCtx,\n opts?: {\n where?: {\n groupId?: string;\n slug?: string;\n status?: \"draft\" | \"active\" | \"disabled\";\n };\n limit?: number;\n cursor?: string | null;\n orderBy?: \"_creationTime\" | \"name\" | \"slug\" | \"status\";\n order?: \"asc\" | \"desc\";\n },\n ) => {\n return await ctx.runQuery(config.component.public.enterpriseList, {\n where: opts?.where,\n limit: opts?.limit,\n cursor: opts?.cursor,\n orderBy: opts?.orderBy,\n order: opts?.order,\n });\n },\n update: async (\n ctx: ComponentCtx,\n enterpriseId: string,\n data: Record<string, unknown>,\n ) => {\n await ctx.runMutation(config.component.public.enterpriseUpdate, {\n enterpriseId,\n data,\n });\n return { ok: true as const, enterpriseId };\n },\n delete: async (ctx: ComponentCtx, enterpriseId: string) => {\n await ctx.runMutation(config.component.public.enterpriseDelete, {\n enterpriseId,\n });\n return { ok: true as const, enterpriseId };\n },\n /**\n * Aggregate readiness status across all configured protocols for an\n * enterprise connection.\n *\n * Returns a structured result indicating whether the connection is\n * ready, with per-protocol checks so callers can surface actionable\n * diagnostics without running full network validation.\n */\n status: async (ctx: ComponentReadCtx, enterpriseId: string) => {\n const enterprise = await ctx.runQuery(\n config.component.public.enterpriseGet,\n { enterpriseId },\n );\n if (!enterprise) {\n throw new AuthError(\n \"INVALID_PARAMETERS\",\n enterpriseNotFoundError,\n ).toConvexError();\n }\n const policy = getPolicyFromEnterprise(enterprise);\n const protocols = enterprise.config?.protocols ?? {};\n const oidcConfig = protocols.oidc;\n const oidcSecret = await getEnterpriseSecret(\n ctx,\n enterprise._id,\n ENTERPRISE_OIDC_CLIENT_SECRET_KIND,\n );\n const samlConfig = protocols.saml;\n const scimConfig = await ctx.runQuery(\n config.component.public.enterpriseScimConfigGetByEnterprise,\n { enterpriseId },\n );\n const domains = await ctx.runQuery(\n config.component.public.enterpriseDomainList,\n { enterpriseId },\n );\n\n const oidcReady =\n oidcConfig?.enabled === true &&\n typeof oidcConfig?.clientId === \"string\" &&\n oidcConfig.clientId.length > 0 &&\n oidcSecret !== null &&\n (typeof oidcConfig?.issuer === \"string\" ||\n typeof oidcConfig?.discoveryUrl === \"string\");\n const samlReady =\n samlConfig?.enabled === true &&\n typeof samlConfig?.idp?.entityId === \"string\";\n const scimReady =\n scimConfig !== null &&\n scimConfig !== undefined &&\n (scimConfig as any).status === \"active\";\n\n const ready =\n enterprise.status === \"active\" && (oidcReady || samlReady);\n\n return {\n enterpriseId: enterprise._id,\n status: enterprise.status,\n ready,\n domainCount: (domains as unknown[]).length,\n protocols: {\n oidc: {\n configured: oidcReady,\n ready: oidcReady,\n clientId: oidcConfig?.clientId ?? null,\n issuer: oidcConfig?.issuer ?? oidcConfig?.discoveryUrl ?? null,\n },\n saml: {\n configured: samlReady,\n ready: samlReady,\n entityId: samlConfig?.idp?.entityId ?? null,\n },\n scim: {\n configured: scimReady,\n ready: scimReady,\n basePath: scimConfig?.basePath ?? null,\n deprovisionMode: policy.provisioning.deprovision.mode,\n },\n },\n };\n },\n },\n domain: {\n add: async (\n ctx: ComponentCtx,\n data: {\n enterpriseId: string;\n groupId: string;\n domain: string;\n isPrimary?: boolean;\n },\n ): Promise<string> => {\n return (await ctx.runMutation(\n config.component.public.enterpriseDomainAdd,\n {\n ...data,\n domain: normalizeDomain(data.domain),\n },\n )) as string;\n },\n list: async (ctx: ComponentReadCtx, enterpriseId: string) => {\n return await ctx.runQuery(\n config.component.public.enterpriseDomainList,\n {\n enterpriseId,\n },\n );\n },\n validate: async (ctx: ComponentReadCtx, enterpriseId: string) => {\n const enterprise = await ctx.runQuery(\n config.component.public.enterpriseGet,\n { enterpriseId },\n );\n if (enterprise === null) {\n throw new AuthError(\n \"INVALID_PARAMETERS\",\n enterpriseNotFoundError,\n ).toConvexError();\n }\n\n const domains = await ctx.runQuery(\n config.component.public.enterpriseDomainList,\n { enterpriseId },\n );\n const primaryDomains = domains.filter(\n (domain: (typeof domains)[number]) => domain.isPrimary,\n );\n const verifiedDomains = domains.filter(\n (domain: (typeof domains)[number]) => domain.verifiedAt !== undefined,\n );\n\n const warnings: string[] = [];\n if (domains.length === 0) {\n warnings.push(\"No domains configured.\");\n }\n if (primaryDomains.length === 0 && domains.length > 0) {\n warnings.push(\"No primary domain configured.\");\n }\n if (primaryDomains.length > 1) {\n warnings.push(\"Multiple primary domains configured.\");\n }\n if (verifiedDomains.length === 0 && domains.length > 0) {\n warnings.push(\"No verified domains yet.\");\n }\n\n return {\n enterpriseId,\n ready:\n enterprise.status === \"active\" &&\n domains.length > 0 &&\n primaryDomains.length === 1 &&\n verifiedDomains.length > 0,\n summary: {\n domainCount: domains.length,\n primaryCount: primaryDomains.length,\n verifiedCount: verifiedDomains.length,\n },\n domains: domains.map((domain: (typeof domains)[number]) =>\n toDomainSummary(domain),\n ),\n warnings,\n };\n },\n remove: async (ctx: ComponentCtx, domainId: string) => {\n await ctx.runMutation(config.component.public.enterpriseDomainDelete, {\n domainId,\n });\n },\n verification: {\n request: async (\n ctx: ComponentCtx,\n args: { enterpriseId: string; domain: string },\n ) => {\n const enterprise = await loadEnterpriseOrThrow(\n ctx,\n args.enterpriseId,\n );\n const normalizedDomain = normalizeDomain(args.domain);\n const domains = await ctx.runQuery(\n config.component.public.enterpriseDomainList,\n { enterpriseId: enterprise._id },\n );\n const domain = domains.find(\n (entry: (typeof domains)[number]) =>\n entry.domain === normalizedDomain,\n );\n if (!domain) {\n throw new AuthError(\n \"INVALID_PARAMETERS\",\n \"Domain is not attached to this enterprise.\",\n ).toConvexError();\n }\n\n const requestedAt = Date.now();\n const expiresAt = requestedAt + ENTERPRISE_DOMAIN_VERIFICATION_TTL_MS;\n const token = generateRandomString(32, INVITE_TOKEN_ALPHABET);\n const tokenHash = await sha256(token);\n const recordName = getDomainVerificationRecordName(normalizedDomain);\n\n await ctx.runMutation(\n config.component.public.enterpriseDomainVerificationUpsert,\n {\n enterpriseId: enterprise._id,\n groupId: enterprise.groupId,\n domainId: domain._id,\n domain: normalizedDomain,\n recordName,\n token,\n tokenHash,\n requestedAt,\n expiresAt,\n },\n );\n\n await recordEnterpriseAuditEvent(ctx, {\n enterpriseId: enterprise._id,\n groupId: enterprise.groupId,\n eventType: \"enterprise.domain.verification_requested\",\n actorType: \"system\",\n subjectType: \"enterprise_domain\",\n subjectId: domain._id,\n ok: true,\n metadata: { domain: normalizedDomain, recordName, expiresAt },\n });\n\n return {\n ok: true as const,\n enterpriseId: enterprise._id,\n domain: normalizedDomain,\n requestedAt,\n expiresAt,\n challenge: {\n recordType: \"TXT\" as const,\n recordName,\n recordValue: token,\n },\n };\n },\n confirm: async (\n ctx: ComponentCtx,\n args: { enterpriseId: string; domain: string },\n ) => {\n const enterprise = await loadEnterpriseOrThrow(\n ctx,\n args.enterpriseId,\n );\n const normalizedDomain = normalizeDomain(args.domain);\n const domains = await ctx.runQuery(\n config.component.public.enterpriseDomainList,\n { enterpriseId: enterprise._id },\n );\n const domain = domains.find(\n (entry: (typeof domains)[number]) =>\n entry.domain === normalizedDomain,\n );\n if (!domain) {\n throw new AuthError(\n \"INVALID_PARAMETERS\",\n \"Domain is not attached to this enterprise.\",\n ).toConvexError();\n }\n\n if (domain.verifiedAt !== undefined) {\n return {\n ok: true,\n enterpriseId: enterprise._id,\n domain: normalizedDomain,\n verifiedAt: domain.verifiedAt,\n checks: [\n {\n name: \"domain_verified\",\n ok: true,\n message: \"Domain is already verified.\",\n },\n ],\n };\n }\n\n const verification = await ctx.runQuery(\n config.component.public.enterpriseDomainVerificationGet,\n { domainId: domain._id },\n );\n const checks: Array<{ name: string; ok: boolean; message?: string }> =\n [];\n if (!verification) {\n checks.push({\n name: \"verification_requested\",\n ok: false,\n message: \"No active domain verification challenge exists.\",\n });\n return {\n ok: false,\n enterpriseId: enterprise._id,\n domain: normalizedDomain,\n checks,\n };\n }\n\n checks.push({ name: \"verification_requested\", ok: true });\n\n if (verification.expiresAt < Date.now()) {\n await ctx.runMutation(\n config.component.public.enterpriseDomainVerificationDelete,\n { domainId: domain._id },\n );\n checks.push({\n name: \"challenge_active\",\n ok: false,\n message: \"The verification challenge expired. Request a new one.\",\n });\n return {\n ok: false,\n enterpriseId: enterprise._id,\n domain: normalizedDomain,\n checks,\n };\n }\n\n checks.push({ name: \"challenge_active\", ok: true });\n\n let txtValues: string[];\n try {\n txtValues = await resolveTxtValues(verification.recordName);\n } catch (error) {\n throw new AuthError(\n \"INTERNAL_ERROR\",\n error instanceof Error\n ? error.message\n : \"Failed to resolve DNS TXT records.\",\n ).toConvexError();\n }\n\n checks.push({\n name: \"dns_record_present\",\n ok: txtValues.length > 0,\n message:\n txtValues.length > 0\n ? undefined\n : `No TXT records found at ${verification.recordName}.`,\n });\n\n const matches = txtValues.includes(verification.token);\n checks.push({\n name: \"dns_record_matches\",\n ok: matches,\n message: matches\n ? undefined\n : `TXT record at ${verification.recordName} does not match the expected value.`,\n });\n\n if (!checks.every((check) => check.ok)) {\n return {\n ok: false,\n enterpriseId: enterprise._id,\n domain: normalizedDomain,\n checks,\n };\n }\n\n const verifiedAt = Date.now();\n await ctx.runMutation(\n config.component.public.enterpriseDomainVerify,\n {\n domainId: domain._id,\n verifiedAt,\n },\n );\n\n await recordEnterpriseAuditEvent(ctx, {\n enterpriseId: enterprise._id,\n groupId: enterprise.groupId,\n eventType: \"enterprise.domain.verified\",\n actorType: \"system\",\n subjectType: \"enterprise_domain\",\n subjectId: domain._id,\n ok: true,\n metadata: { domain: normalizedDomain, verifiedAt },\n });\n\n return {\n ok: true,\n enterpriseId: enterprise._id,\n domain: normalizedDomain,\n verifiedAt,\n checks,\n };\n },\n },\n },\n saml: {\n configure: async <DataModel extends GenericDataModel>(\n ctx: GenericActionCtx<DataModel>,\n data: {\n enterpriseId: string;\n metadataXml?: string;\n metadataUrl?: string;\n domains?: string[];\n signAuthnRequests?: boolean;\n attributeMapping?: {\n subject?: string;\n email?: string;\n name?: string;\n firstName?: string;\n lastName?: string;\n };\n sp?: {\n entityId?: string;\n acsUrl?: string;\n sloUrl?: string;\n signingCert?: string | string[];\n encryptCert?: string | string[];\n privateKey?: string;\n privateKeyPass?: string;\n encPrivateKey?: string;\n encPrivateKeyPass?: string;\n };\n },\n ) => {\n return await Fx.run(\n Fx.gen(function* () {\n const enterprise = yield* Fx.from({\n ok: () =>\n ctx.runQuery(config.component.public.enterpriseGet, {\n enterpriseId: data.enterpriseId,\n }),\n err: () =>\n new AuthError(\"INTERNAL_ERROR\", \"Failed to load enterprise.\"),\n }).pipe(\n Fx.chain((ent) =>\n ent === null\n ? Fx.fail(\n new AuthError(\n \"INVALID_PARAMETERS\",\n enterpriseNotFoundError,\n ),\n )\n : Fx.succeed(ent),\n ),\n );\n const metadataXml = yield* data.metadataXml\n ? Fx.succeed(data.metadataXml)\n : data.metadataUrl\n ? Fx.defer(() =>\n Fx.from({\n ok: async () => {\n const response = await fetch(data.metadataUrl!);\n if (!response.ok) {\n throw new Error(\n `Failed to fetch SAML metadata: ${response.status}`,\n );\n }\n return await response.text();\n },\n err: (error) =>\n new AuthError(\n \"INVALID_PARAMETERS\",\n error instanceof Error\n ? error.message\n : \"Failed to fetch SAML metadata\",\n ),\n }),\n ).pipe(\n Fx.timeout(10_000),\n Fx.retry(\n Fx.retry.compose(\n Fx.retry.jittered(Fx.retry.exponential(200)),\n Fx.retry.recurs(2),\n ),\n ),\n Fx.recover((error) =>\n Fx.fail(\n new AuthError(\n \"INVALID_PARAMETERS\",\n error instanceof Error\n ? error.message\n : \"Failed to fetch SAML metadata\",\n ),\n ),\n ),\n )\n : Fx.fail(\n new AuthError(\n \"INVALID_PARAMETERS\",\n \"SAML registration requires metadataXml or metadataUrl.\",\n ),\n );\n\n const parsed = yield* Fx.from({\n ok: () => parseSamlIdpMetadata(metadataXml),\n err: () =>\n new AuthError(\n \"INVALID_PARAMETERS\",\n \"Failed to parse SAML metadata.\",\n ),\n });\n\n const baseConfig = upsertProtocolConfig(enterprise.config, \"saml\", {\n enabled: true,\n idp: {\n metadataXml,\n ...parsed,\n },\n sp: data.sp,\n signAuthnRequests:\n data.signAuthnRequests ?? parsed.wantsSignedAuthnRequests,\n attributeMapping: data.attributeMapping,\n });\n const normalizedDomains = data.domains?.map(normalizeDomain);\n const nextConfig = normalizedDomains\n ? { ...baseConfig, domains: normalizedDomains }\n : baseConfig;\n\n yield* Fx.from({\n ok: () =>\n ctx.runMutation(config.component.public.enterpriseUpdate, {\n enterpriseId: enterprise._id,\n data: {\n status: \"active\",\n config: nextConfig,\n },\n }),\n err: () =>\n new AuthError(\n \"INTERNAL_ERROR\",\n \"Failed to persist SAML registration.\",\n ),\n });\n\n if (normalizedDomains) {\n for (const [index, domain] of normalizedDomains.entries()) {\n yield* Fx.from({\n ok: () =>\n ctx.runMutation(\n config.component.public.enterpriseDomainAdd,\n {\n enterpriseId: enterprise._id,\n groupId: enterprise.groupId,\n domain,\n isPrimary: index === 0,\n },\n ),\n err: () =>\n new AuthError(\n \"INTERNAL_ERROR\",\n \"Failed to persist enterprise domain.\",\n ),\n });\n }\n }\n\n yield* Fx.from({\n ok: () =>\n recordEnterpriseAuditEvent(ctx, {\n enterpriseId: enterprise._id,\n groupId: enterprise.groupId,\n eventType: \"enterprise.saml.registered\",\n actorType: \"system\",\n subjectType: \"enterprise_saml\",\n subjectId: enterprise._id,\n ok: true,\n metadata: {\n metadataUrl: data.metadataUrl,\n domains: normalizedDomains,\n },\n }),\n err: () =>\n new AuthError(\n \"INTERNAL_ERROR\",\n \"Failed to record SAML registration audit event.\",\n ),\n });\n\n return {\n ok: true as const,\n enterpriseId: enterprise._id,\n groupId: enterprise.groupId,\n };\n }).pipe(Fx.recover((e) => Fx.fatal(e.toConvexError()))),\n );\n },\n metadata: async <DataModel extends GenericDataModel>(\n ctx: GenericActionCtx<DataModel>,\n opts: {\n enterpriseId: string;\n entityId?: string;\n acsUrl?: string;\n sloUrl?: string;\n },\n ) => {\n const enterprise = await ctx.runQuery(\n config.component.public.enterpriseGet,\n {\n enterpriseId: opts.enterpriseId,\n },\n );\n if (!enterprise) {\n throw new AuthError(\n \"INVALID_PARAMETERS\",\n \"Enterprise not found.\",\n ).toConvexError();\n }\n\n return createServiceProviderMetadata(\n getSamlServiceProviderOptions({\n rootUrl: requireEnv(\"CONVEX_SITE_URL\"),\n source: { kind: \"enterprise\", id: enterprise._id },\n config: enterprise.config,\n overrides: {\n entityId: opts.entityId,\n acsUrl: opts.acsUrl,\n sloUrl: opts.sloUrl,\n },\n }),\n );\n },\n /**\n * Validate the stored SAML config for an enterprise connection.\n *\n * Re-parses IdP metadata, checks signing cert presence, and verifies\n * SP metadata can be generated. Returns a structured result with\n * per-check details rather than throwing on first failure.\n */\n validate: async <DataModel extends GenericDataModel>(\n ctx: GenericActionCtx<DataModel>,\n enterpriseId: string,\n ) => {\n const checks: Array<{\n name: string;\n ok: boolean;\n message?: string;\n }> = [];\n\n const enterprise = await ctx.runQuery(\n config.component.public.enterpriseGet,\n { enterpriseId },\n );\n\n if (!enterprise) {\n return {\n ok: false,\n enterpriseId,\n checks: [\n {\n name: \"enterprise_exists\",\n ok: false,\n message: \"Enterprise not found.\",\n },\n ],\n };\n }\n\n const samlConfig = enterprise.config?.protocols?.saml;\n const samlConfigured =\n samlConfig?.enabled === true &&\n typeof samlConfig?.idp?.metadataXml === \"string\";\n\n checks.push({\n name: \"saml_configured\",\n ok: samlConfigured,\n message: samlConfigured ? undefined : \"SAML is not configured.\",\n });\n\n const hasIdpMetadata =\n typeof samlConfig?.idp?.metadataXml === \"string\" &&\n samlConfig.idp.metadataXml.length > 0;\n checks.push({\n name: \"idp_metadata_present\",\n ok: hasIdpMetadata,\n message: hasIdpMetadata ? undefined : \"IdP metadata XML is missing.\",\n });\n\n const hasEntityId =\n typeof samlConfig?.idp?.entityId === \"string\" &&\n samlConfig.idp.entityId.length > 0;\n checks.push({\n name: \"idp_entity_id\",\n ok: hasEntityId,\n message: hasEntityId\n ? undefined\n : \"IdP entityId could not be parsed from metadata.\",\n });\n\n let spMetadataOk = false;\n let spMetadataMessage: string | undefined;\n if (samlConfigured) {\n try {\n createServiceProviderMetadata(\n getSamlServiceProviderOptions({\n rootUrl: requireEnv(\"CONVEX_SITE_URL\"),\n source: { kind: \"enterprise\", id: enterprise._id },\n config: enterprise.config,\n overrides: {},\n }),\n );\n spMetadataOk = true;\n } catch (e) {\n spMetadataMessage =\n e instanceof Error ? e.message : \"SP metadata generation failed.\";\n }\n } else {\n spMetadataMessage = \"Skipped — SAML not configured.\";\n }\n checks.push({\n name: \"sp_metadata_generates\",\n ok: spMetadataOk,\n message: spMetadataMessage,\n });\n\n return {\n ok: checks.every((c) => c.ok),\n enterpriseId: enterprise._id,\n checks,\n };\n },\n },\n policy: {\n get: async (ctx: ComponentReadCtx, enterpriseId: string) => {\n const enterprise = await loadEnterpriseOrThrow(ctx, enterpriseId);\n return getPolicyFromEnterprise(enterprise);\n },\n update: async (\n ctx: ComponentCtx,\n enterpriseId: string,\n patch: EnterprisePolicyPatch,\n ) => {\n const enterprise = await loadEnterpriseOrThrow(ctx, enterpriseId);\n const policy = patchEnterprisePolicy(enterprise.policy, patch);\n await ctx.runMutation(config.component.public.enterpriseUpdate, {\n enterpriseId,\n data: { policy },\n });\n await recordEnterpriseAuditEvent(ctx, {\n enterpriseId: enterprise._id,\n groupId: enterprise.groupId,\n eventType: \"enterprise.policy.updated\",\n actorType: \"system\",\n subjectType: \"enterprise_policy\",\n subjectId: enterprise._id,\n ok: true,\n metadata: { version: policy.version },\n });\n return policy;\n },\n validate: async (ctx: ComponentReadCtx, enterpriseId: string) => {\n const enterprise = await ctx.runQuery(\n config.component.public.enterpriseGet,\n { enterpriseId },\n );\n if (!enterprise) {\n return {\n ok: false,\n enterpriseId,\n checks: [\n {\n name: \"enterprise_exists\",\n ok: false,\n message: enterpriseNotFoundError,\n },\n ],\n };\n }\n const policy = getPolicyFromEnterprise(enterprise);\n const checks = validateEnterprisePolicy(policy);\n return {\n ok: checks.every((check: { ok: boolean }) => check.ok),\n enterpriseId,\n policy,\n checks,\n };\n },\n },\n oidc: {\n /**\n * Register or update enterprise OIDC connection settings.\n *\n * Persists protocol config under `enterprise.config.protocols.oidc` and\n * records an `enterprise.oidc.registered` audit event.\n */\n configure: async (\n ctx: ComponentCtx,\n data: {\n enterpriseId: string;\n issuer?: string;\n discoveryUrl?: string;\n clientId: string;\n clientSecret?: string;\n scopes?: string[];\n authorizationParams?: Record<string, string>;\n clockToleranceSeconds?: number;\n strictIssuer?: boolean;\n /**\n * Map OIDC claim names to `user.extend` field names.\n * Example: `{ department: \"department\", role: \"job_title\" }` means\n * the OIDC `department` claim is stored as `user.extend.department`.\n */\n extraFields?: Record<string, string>;\n },\n ) => {\n return await Fx.run(\n Fx.gen(function* () {\n yield* Fx.guard(\n data.issuer === undefined && data.discoveryUrl === undefined,\n Fx.fail(\n new AuthError(\n \"INVALID_PARAMETERS\",\n \"OIDC registration requires issuer or discoveryUrl.\",\n ),\n ),\n );\n\n const enterprise = yield* Fx.from({\n ok: () =>\n ctx.runQuery(config.component.public.enterpriseGet, {\n enterpriseId: data.enterpriseId,\n }),\n err: () =>\n new AuthError(\"INTERNAL_ERROR\", \"Failed to load enterprise.\"),\n }).pipe(\n Fx.chain((ent) =>\n ent === null\n ? Fx.fail(\n new AuthError(\n \"INVALID_PARAMETERS\",\n enterpriseNotFoundError,\n ),\n )\n : Fx.succeed(ent),\n ),\n );\n const nextConfig = upsertProtocolConfig(enterprise.config, \"oidc\", {\n enabled: true,\n issuer: data.issuer,\n discoveryUrl: data.discoveryUrl,\n clientId: data.clientId,\n scopes: data.scopes ?? [\"openid\", \"profile\", \"email\"],\n authorizationParams: data.authorizationParams,\n clockToleranceSeconds: data.clockToleranceSeconds,\n strictIssuer: data.strictIssuer,\n extraFields: data.extraFields,\n });\n\n yield* Fx.from({\n ok: () =>\n ctx.runMutation(config.component.public.enterpriseUpdate, {\n enterpriseId: data.enterpriseId,\n data: { config: nextConfig },\n }),\n err: () =>\n new AuthError(\n \"INTERNAL_ERROR\",\n \"Failed to persist OIDC registration.\",\n ),\n });\n\n if (data.clientSecret !== undefined) {\n const ciphertext = yield* Fx.from({\n ok: () => encryptSecret(data.clientSecret!),\n err: () =>\n new AuthError(\n \"INTERNAL_ERROR\",\n \"Failed to encrypt OIDC client secret.\",\n ),\n });\n yield* Fx.from({\n ok: () =>\n ctx.runMutation(\n config.component.public.enterpriseSecretUpsert,\n {\n enterpriseId: data.enterpriseId,\n groupId: enterprise.groupId,\n kind: ENTERPRISE_OIDC_CLIENT_SECRET_KIND,\n ciphertext,\n updatedAt: Date.now(),\n },\n ),\n err: () =>\n new AuthError(\n \"INTERNAL_ERROR\",\n \"Failed to persist OIDC client secret.\",\n ),\n });\n }\n\n yield* Fx.from({\n ok: () =>\n recordEnterpriseAuditEvent(ctx, {\n enterpriseId: data.enterpriseId,\n groupId: enterprise.groupId,\n eventType: \"enterprise.oidc.registered\",\n actorType: \"system\",\n subjectType: \"enterprise_oidc\",\n subjectId: data.enterpriseId,\n ok: true,\n metadata: {\n issuer: data.issuer,\n discoveryUrl: data.discoveryUrl,\n },\n }),\n err: () =>\n new AuthError(\n \"INTERNAL_ERROR\",\n \"Failed to record OIDC registration audit event.\",\n ),\n });\n\n const secret = yield* Fx.from({\n ok: () =>\n getEnterpriseSecret(\n ctx,\n data.enterpriseId,\n ENTERPRISE_OIDC_CLIENT_SECRET_KIND,\n ),\n err: () =>\n new AuthError(\n \"INTERNAL_ERROR\",\n \"Failed to load OIDC secret metadata.\",\n ),\n });\n\n return withOidcSecretState(\n getPublicOidcConfig(nextConfig),\n secret !== null,\n );\n }).pipe(Fx.recover((e) => Fx.fatal(e.toConvexError()))),\n );\n },\n /**\n * Fetch the stored OIDC config for an enterprise.\n */\n get: async (ctx: ComponentReadCtx, enterpriseId: string) => {\n return await Fx.run(\n Fx.from({\n ok: () =>\n ctx.runQuery(config.component.public.enterpriseGet, {\n enterpriseId,\n }),\n err: () =>\n new AuthError(\"INTERNAL_ERROR\", \"Failed to load enterprise.\"),\n }).pipe(\n Fx.chain((ent) =>\n ent === null\n ? Fx.fail(\n new AuthError(\n \"INVALID_PARAMETERS\",\n enterpriseNotFoundError,\n ),\n )\n : Fx.succeed(ent),\n ),\n Fx.chain((enterprise) =>\n Fx.from({\n ok: async () => {\n const secret = await getEnterpriseSecret(\n ctx,\n enterprise._id,\n ENTERPRISE_OIDC_CLIENT_SECRET_KIND,\n );\n return withOidcSecretState(\n getPublicOidcConfig(enterprise.config),\n secret !== null,\n );\n },\n err: () =>\n new AuthError(\n \"INTERNAL_ERROR\",\n \"Failed to load OIDC secret metadata.\",\n ),\n }),\n ),\n Fx.recover((e) => Fx.fatal(e.toConvexError())),\n ),\n );\n },\n /**\n * Resolve enterprise OIDC sign-in route from enterprise id, domain, or\n * user email domain.\n */\n signIn: async (\n ctx: ComponentReadCtx,\n data: {\n enterpriseId?: string;\n email?: string;\n domain?: string;\n redirectTo?: string;\n },\n ) => {\n return await Fx.run(\n Fx.gen(function* () {\n const enterprise =\n data.enterpriseId !== undefined\n ? yield* Fx.from({\n ok: () =>\n ctx.runQuery(config.component.public.enterpriseGet, {\n enterpriseId: data.enterpriseId,\n }),\n err: () =>\n new AuthError(\n \"INTERNAL_ERROR\",\n \"Failed to load enterprise.\",\n ),\n }).pipe(\n Fx.chain((ent) =>\n ent === null\n ? Fx.fail(\n new AuthError(\n \"INVALID_PARAMETERS\",\n enterpriseNotFoundError,\n ),\n )\n : Fx.succeed(ent),\n ),\n )\n : data.domain !== undefined || data.email !== undefined\n ? yield* Fx.from({\n ok: () =>\n ctx.runQuery(\n config.component.public.enterpriseGetByDomain,\n {\n domain: normalizeDomain(\n data.domain ??\n String(data.email).split(\"@\").at(-1) ??\n \"\",\n ),\n },\n ),\n err: () =>\n new AuthError(\n \"INTERNAL_ERROR\",\n \"Failed to resolve enterprise by domain.\",\n ),\n }).pipe(\n Fx.chain((result) =>\n result?.enterprise &&\n result.domain?.verifiedAt !== undefined\n ? Fx.succeed(result.enterprise)\n : Fx.fail(\n new AuthError(\n \"INVALID_PARAMETERS\",\n \"No enterprise OIDC connection matched the provided input.\",\n ),\n ),\n ),\n )\n : yield* Fx.fail(\n new AuthError(\n \"INVALID_PARAMETERS\",\n \"No enterprise OIDC connection matched the provided input.\",\n ),\n );\n\n yield* Fx.guard(\n enterprise.status !== \"active\",\n Fx.fail(\n new AuthError(\n \"INVALID_PARAMETERS\",\n \"Enterprise connection is not active.\",\n ),\n ),\n );\n\n const oidc = getOidcConfig(enterprise.config);\n yield* Fx.guard(\n oidc.enabled !== true,\n Fx.fail(\n new AuthError(\n \"PROVIDER_NOT_CONFIGURED\",\n \"OIDC is not configured for this enterprise.\",\n ),\n ),\n );\n\n const urls = getEnterpriseOidcUrls({\n rootUrl: requireEnv(\"CONVEX_SITE_URL\"),\n enterpriseId: enterprise._id,\n });\n return {\n enterpriseId: enterprise._id,\n providerId: enterpriseOidcProviderId(enterprise._id),\n signInPath: urls.signInUrl,\n callbackPath: urls.callbackUrl,\n redirectTo: data.redirectTo,\n };\n }).pipe(Fx.recover((e) => Fx.fatal(e.toConvexError()))),\n );\n },\n /**\n * Validate the stored OIDC config for an enterprise connection.\n *\n * Fetches the OIDC discovery document from the configured issuer or\n * discoveryUrl, verifies required fields are present, and checks that\n * clientId is set. Returns a structured result with per-check details.\n */\n validate: async (ctx: ComponentReadCtx, enterpriseId: string) => {\n const checks: Array<{\n name: string;\n ok: boolean;\n message?: string;\n }> = [];\n\n const enterprise = await ctx.runQuery(\n config.component.public.enterpriseGet,\n { enterpriseId },\n );\n\n if (!enterprise) {\n return {\n ok: false,\n enterpriseId,\n checks: [\n {\n name: \"enterprise_exists\",\n ok: false,\n message: \"Enterprise not found.\",\n },\n ],\n };\n }\n\n const oidc = getOidcConfig(enterprise.config);\n const secret = await getEnterpriseSecret(\n ctx,\n enterprise._id,\n ENTERPRISE_OIDC_CLIENT_SECRET_KIND,\n );\n const oidcConfigured =\n oidc.enabled === true &&\n typeof oidc.clientId === \"string\" &&\n oidc.clientId.length > 0;\n\n checks.push({\n name: \"oidc_configured\",\n ok: oidcConfigured,\n message: oidcConfigured ? undefined : \"OIDC is not configured.\",\n });\n\n const hasClientId =\n typeof oidc.clientId === \"string\" && oidc.clientId.length > 0;\n checks.push({\n name: \"client_id_present\",\n ok: hasClientId,\n message: hasClientId ? undefined : \"clientId is missing.\",\n });\n\n checks.push({\n name: \"client_secret_stored\",\n ok: secret !== null,\n message:\n secret !== null ? undefined : \"OIDC client secret is missing.\",\n });\n\n const discoveryTarget = oidc.discoveryUrl ?? oidc.issuer;\n const hasDiscovery =\n typeof discoveryTarget === \"string\" && discoveryTarget.length > 0;\n checks.push({\n name: \"issuer_or_discovery_url_present\",\n ok: hasDiscovery,\n message: hasDiscovery\n ? undefined\n : \"issuer or discoveryUrl is missing.\",\n });\n\n let discoveryOk = false;\n let discoveryMessage: string | undefined;\n if (hasDiscovery) {\n const discoveryUrl = oidc.discoveryUrl?.length\n ? oidc.discoveryUrl\n : `${oidc.issuer}/.well-known/openid-configuration`;\n try {\n const res = await fetch(discoveryUrl, {\n headers: { Accept: \"application/json\" },\n signal: AbortSignal.timeout(8_000),\n });\n if (!res.ok) {\n discoveryMessage = `Discovery endpoint returned ${res.status}.`;\n } else {\n const json = (await res.json()) as Record<string, unknown>;\n if (typeof json.issuer !== \"string\") {\n discoveryMessage =\n \"Discovery document is missing issuer field.\";\n } else if (typeof json.authorization_endpoint !== \"string\") {\n discoveryMessage =\n \"Discovery document is missing authorization_endpoint.\";\n } else {\n discoveryOk = true;\n }\n }\n } catch (e) {\n discoveryMessage =\n e instanceof Error\n ? `Discovery fetch failed: ${e.message}`\n : \"Discovery fetch failed.\";\n }\n } else {\n discoveryMessage = \"Skipped — issuer or discoveryUrl not set.\";\n }\n checks.push({\n name: \"discovery_reachable\",\n ok: discoveryOk,\n message: discoveryMessage,\n });\n\n return {\n ok: checks.every((c) => c.ok),\n enterpriseId: enterprise._id,\n checks,\n };\n },\n },\n scim: {\n configure: async (\n ctx: ComponentCtx,\n data: {\n enterpriseId: string;\n basePath?: string;\n status?: \"draft\" | \"active\" | \"disabled\";\n },\n ) => {\n const enterprise = await ctx.runQuery(\n config.component.public.enterpriseGet,\n {\n enterpriseId: data.enterpriseId,\n },\n );\n if (enterprise === null) {\n throw new AuthError(\n \"INVALID_PARAMETERS\",\n \"Enterprise not found.\",\n ).toConvexError();\n }\n const rawToken = generateRandomString(48, INVITE_TOKEN_ALPHABET);\n const tokenHash = await sha256(rawToken);\n const configId = (await ctx.runMutation(\n config.component.public.enterpriseScimConfigUpsert,\n {\n enterpriseId: enterprise._id,\n groupId: enterprise.groupId,\n status: data.status ?? \"active\",\n basePath:\n data.basePath ??\n `${requireEnv(\"CONVEX_SITE_URL\")}/api/auth/sso/${enterprise._id}/scim/v2`,\n tokenHash,\n lastRotatedAt: Date.now(),\n },\n )) as string;\n const auditEventId = await recordEnterpriseAuditEvent(ctx, {\n enterpriseId: enterprise._id,\n groupId: enterprise.groupId,\n eventType: \"enterprise.scim.configured\",\n actorType: \"system\",\n subjectType: \"enterprise_scim\",\n subjectId: configId,\n ok: true,\n });\n await emitEnterpriseWebhookDeliveries(ctx, {\n enterpriseId: enterprise._id,\n eventType: \"enterprise.scim.configured\",\n auditEventId,\n payload: { enterpriseId: enterprise._id, scimConfigId: configId },\n });\n return {\n ok: true as const,\n enterpriseId: enterprise._id,\n configId,\n basePath:\n data.basePath ??\n `${requireEnv(\"CONVEX_SITE_URL\")}/api/auth/sso/${enterprise._id}/scim/v2`,\n token: rawToken,\n };\n },\n get: async (ctx: ComponentReadCtx, enterpriseId: string) => {\n return await ctx.runQuery(\n config.component.public.enterpriseScimConfigGetByEnterprise,\n { enterpriseId },\n );\n },\n getConfigByToken: async (ctx: ComponentReadCtx, token: string) => {\n return await ctx.runQuery(\n config.component.public.enterpriseScimConfigGetByTokenHash,\n { tokenHash: await sha256(token) },\n );\n },\n /**\n * Validate the stored SCIM config for an enterprise connection.\n *\n * Checks that a SCIM config record exists, is active, has a token\n * hash set, and has a non-empty basePath. Returns a structured result\n * with per-check details.\n */\n validate: async (ctx: ComponentReadCtx, enterpriseId: string) => {\n const checks: Array<{\n name: string;\n ok: boolean;\n message?: string;\n }> = [];\n\n const enterprise = await ctx.runQuery(\n config.component.public.enterpriseGet,\n { enterpriseId },\n );\n\n if (!enterprise) {\n return {\n ok: false,\n enterpriseId,\n checks: [\n {\n name: \"enterprise_exists\",\n ok: false,\n message: \"Enterprise not found.\",\n },\n ],\n };\n }\n\n const policy = getPolicyFromEnterprise(enterprise);\n\n const scimConfig = await ctx.runQuery(\n config.component.public.enterpriseScimConfigGetByEnterprise,\n { enterpriseId },\n );\n\n const hasConfig = scimConfig !== null && scimConfig !== undefined;\n checks.push({\n name: \"scim_config_exists\",\n ok: hasConfig,\n message: hasConfig ? undefined : \"SCIM has not been configured.\",\n });\n\n const isActive = hasConfig && (scimConfig as any).status === \"active\";\n checks.push({\n name: \"scim_config_active\",\n ok: isActive,\n message: isActive\n ? undefined\n : `SCIM config status is ${hasConfig ? (scimConfig as any).status : \"unknown\"}.`,\n });\n\n const hasToken =\n hasConfig &&\n typeof (scimConfig as any).tokenHash === \"string\" &&\n (scimConfig as any).tokenHash.length > 0;\n checks.push({\n name: \"token_hash_set\",\n ok: hasToken,\n message: hasToken ? undefined : \"SCIM bearer token has not been set.\",\n });\n\n const hasBasePath =\n hasConfig &&\n typeof (scimConfig as any).basePath === \"string\" &&\n (scimConfig as any).basePath.length > 0;\n checks.push({\n name: \"base_path_set\",\n ok: hasBasePath,\n message: hasBasePath ? undefined : \"SCIM basePath is missing.\",\n });\n\n return {\n ok: checks.every((c) => c.ok),\n enterpriseId: enterprise._id,\n basePath: hasBasePath ? (scimConfig as any).basePath : null,\n deprovisionMode: policy.provisioning.deprovision.mode,\n checks,\n };\n },\n identity: {\n get: async (\n ctx: ComponentReadCtx,\n data: {\n enterpriseId: string;\n resourceType: \"user\" | \"group\";\n externalId: string;\n },\n ) => {\n return await ctx.runQuery(\n config.component.public.enterpriseScimIdentityGet,\n data,\n );\n },\n upsert: async (\n ctx: ComponentCtx,\n data: {\n enterpriseId: string;\n groupId: string;\n resourceType: \"user\" | \"group\";\n externalId: string;\n userId?: string;\n mappedGroupId?: string;\n active?: boolean;\n raw?: Record<string, unknown>;\n },\n ) => {\n return (await ctx.runMutation(\n config.component.public.enterpriseScimIdentityUpsert,\n { ...data, lastProvisionedAt: Date.now() },\n )) as string;\n },\n },\n },\n audit: {\n record: async (\n ctx: ComponentCtx,\n data: {\n enterpriseId: string;\n groupId: string;\n eventType: string;\n actorType: \"user\" | \"system\" | \"scim\" | \"api_key\" | \"webhook\";\n actorId?: string;\n subjectType: string;\n subjectId?: string;\n ok: boolean;\n requestId?: string;\n ip?: string;\n metadata?: Record<string, unknown>;\n },\n ) => {\n return await recordEnterpriseAuditEvent(ctx, data);\n },\n list: async (\n ctx: ComponentReadCtx,\n data: { enterpriseId?: string; groupId?: string; limit?: number },\n ) => {\n return await ctx.runQuery(\n config.component.public.enterpriseAuditEventList,\n data,\n );\n },\n },\n webhook: {\n endpoint: {\n get: async (ctx: ComponentReadCtx, endpointId: string) => {\n return await ctx.runQuery(\n config.component.public.enterpriseWebhookEndpointGet,\n { endpointId },\n );\n },\n create: async (\n ctx: ComponentCtx,\n data: {\n enterpriseId: string;\n url: string;\n secret: string;\n subscriptions: string[];\n createdByUserId?: string;\n },\n ) => {\n const enterprise = await ctx.runQuery(\n config.component.public.enterpriseGet,\n {\n enterpriseId: data.enterpriseId,\n },\n );\n if (enterprise === null) {\n throw new AuthError(\n \"INVALID_PARAMETERS\",\n \"Enterprise not found.\",\n ).toConvexError();\n }\n const secretHash = await sha256(data.secret);\n const endpointId = (await ctx.runMutation(\n config.component.public.enterpriseWebhookEndpointCreate,\n {\n enterpriseId: enterprise._id,\n groupId: enterprise.groupId,\n url: data.url,\n secretHash,\n subscriptions: data.subscriptions,\n createdByUserId: data.createdByUserId,\n },\n )) as string;\n await recordEnterpriseAuditEvent(ctx, {\n enterpriseId: enterprise._id,\n groupId: enterprise.groupId,\n eventType: \"enterprise.webhook.endpoint.created\",\n actorType: data.createdByUserId ? \"user\" : \"system\",\n actorId: data.createdByUserId,\n subjectType: \"enterprise_webhook_endpoint\",\n subjectId: endpointId,\n ok: true,\n });\n return { ok: true as const, endpointId };\n },\n list: async (ctx: ComponentReadCtx, enterpriseId: string) => {\n return await ctx.runQuery(\n config.component.public.enterpriseWebhookEndpointList,\n { enterpriseId },\n );\n },\n disable: async (ctx: ComponentCtx, endpointId: string) => {\n await ctx.runMutation(\n config.component.public.enterpriseWebhookEndpointUpdate,\n { endpointId, data: { status: \"disabled\" } },\n );\n return { ok: true as const, endpointId };\n },\n },\n emit: async (\n ctx: ComponentCtx,\n data: {\n enterpriseId: string;\n eventType: string;\n payload: Record<string, unknown>;\n auditEventId?: string;\n },\n ) => {\n await emitEnterpriseWebhookDeliveries(ctx, data);\n },\n delivery: {\n list: async (\n ctx: ComponentReadCtx,\n data: { enterpriseId: string; limit?: number },\n ) => {\n return await ctx.runQuery(\n (config.component.public as any).enterpriseWebhookDeliveryList,\n data,\n );\n },\n listReady: async (ctx: ComponentReadCtx, limit?: number) => {\n return await ctx.runQuery(\n config.component.public.enterpriseWebhookDeliveryListReady,\n { now: Date.now(), limit },\n );\n },\n markDelivered: async (\n ctx: ComponentCtx,\n deliveryId: string,\n responseStatus?: number,\n ) => {\n await ctx.runMutation(\n config.component.public.enterpriseWebhookDeliveryPatch,\n {\n deliveryId,\n data: {\n status: \"delivered\",\n attemptCount: 1,\n lastAttemptAt: Date.now(),\n lastResponseStatus: responseStatus,\n },\n },\n );\n },\n markFailed: async (\n ctx: ComponentCtx,\n deliveryId: string,\n data: {\n attemptCount: number;\n responseStatus?: number;\n error?: string;\n retryAt?: number;\n },\n ) => {\n await ctx.runMutation(\n config.component.public.enterpriseWebhookDeliveryPatch,\n {\n deliveryId,\n data: {\n status: data.retryAt ? \"pending\" : \"failed\",\n attemptCount: data.attemptCount,\n lastAttemptAt: Date.now(),\n lastResponseStatus: data.responseStatus,\n lastError: data.error,\n nextAttemptAt: data.retryAt ?? Date.now(),\n },\n },\n );\n },\n },\n },\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"domain.js","names":[],"sources":["../../../src/server/enterprise/domain.ts"],"sourcesContent":["import { Fx } from \"@robelest/fx\";\nimport { Cv } from \"@robelest/fx/convex\";\nimport { GenericActionCtx, GenericDataModel } from \"convex/server\";\n\nimport type { EnterprisePolicyPatch } from \"../types\";\n\ntype ComponentCtx = Pick<\n GenericActionCtx<GenericDataModel>,\n \"runQuery\" | \"runMutation\"\n>;\ntype ComponentReadCtx = Pick<GenericActionCtx<GenericDataModel>, \"runQuery\">;\n\n/**\n * Build the enterprise and SSO management domain.\n */\nexport function createEnterpriseDomain(deps: any) {\n const {\n config,\n normalizeEnterprisePolicy,\n normalizeDomain,\n getEnterpriseSecret,\n loadEnterpriseOrThrow,\n validateEnterprisePolicy,\n recordEnterpriseAuditEvent,\n emitEnterpriseWebhookDeliveries,\n enterpriseNotFoundError,\n ENTERPRISE_OIDC_CLIENT_SECRET_KIND,\n requireEnv,\n generateRandomString,\n INVITE_TOKEN_ALPHABET,\n sha256,\n encryptSecret,\n upsertProtocolConfig,\n parseSamlIdpMetadata,\n createServiceProviderMetadata,\n getSamlServiceProviderOptions,\n getPublicOidcConfig,\n withOidcSecretState,\n getOidcConfig,\n getEnterpriseOidcUrls,\n enterpriseOidcProviderId,\n getPolicyFromEnterprise,\n patchEnterprisePolicy,\n } = deps;\n\n const ENTERPRISE_DOMAIN_VERIFICATION_PREFIX = \"_convex-auth-verification\";\n const ENTERPRISE_DOMAIN_VERIFICATION_TTL_MS = 1000 * 60 * 60 * 24 * 7;\n\n const toDomainSummary = (domain: {\n _id: string;\n domain: string;\n isPrimary: boolean;\n verifiedAt?: number;\n }) => ({\n domainId: domain._id,\n domain: domain.domain,\n isPrimary: domain.isPrimary,\n verified: domain.verifiedAt !== undefined,\n verifiedAt: domain.verifiedAt ?? null,\n });\n\n const getDomainVerificationRecordName = (domain: string) =>\n `${ENTERPRISE_DOMAIN_VERIFICATION_PREFIX}.${normalizeDomain(domain)}`;\n\n const parseTxtAnswer = (value: string) => {\n const quoted = [...value.matchAll(/\"([^\"]*)\"/g)].map((match) => match[1]);\n if (quoted.length > 0) {\n return quoted.join(\"\");\n }\n return value.replace(/^\"|\"$/g, \"\").trim();\n };\n\n const resolveTxtValues = async (recordName: string) => {\n const url = new URL(\"https://dns.google/resolve\");\n url.searchParams.set(\"name\", recordName);\n url.searchParams.set(\"type\", \"TXT\");\n\n const response = await fetch(url, {\n headers: { accept: \"application/json\" },\n });\n if (!response.ok) {\n throw new Error(`DNS TXT lookup failed with status ${response.status}.`);\n }\n const data = (await response.json()) as {\n Answer?: Array<{ data?: string }>;\n };\n return (data.Answer ?? [])\n .map((answer) =>\n typeof answer.data === \"string\" ? parseTxtAnswer(answer.data) : null,\n )\n .filter((value): value is string => value !== null && value.length > 0);\n };\n\n return {\n connection: {\n create: async (\n ctx: ComponentCtx,\n data: {\n groupId: string;\n slug?: string;\n name?: string;\n status?: \"draft\" | \"active\" | \"disabled\";\n policy?: EnterprisePolicyPatch;\n config?: Record<string, unknown>;\n extend?: Record<string, unknown>;\n },\n ): Promise<{ enterpriseId: string; groupId: string }> => {\n const enterpriseId = (await ctx.runMutation(\n config.component.public.enterpriseCreate,\n {\n ...data,\n policy: normalizeEnterprisePolicy(data.policy),\n },\n )) as string;\n return {\n enterpriseId,\n groupId: data.groupId,\n };\n },\n get: async (ctx: ComponentReadCtx, enterpriseId: string) => {\n return await ctx.runQuery(config.component.public.enterpriseGet, {\n enterpriseId,\n });\n },\n getByGroup: async (ctx: ComponentReadCtx, groupId: string) => {\n return await ctx.runQuery(\n config.component.public.enterpriseGetByGroup,\n {\n groupId,\n },\n );\n },\n getByDomain: async (ctx: ComponentReadCtx, domain: string) => {\n return await ctx.runQuery(\n config.component.public.enterpriseGetByDomain,\n {\n domain: normalizeDomain(domain),\n },\n );\n },\n list: async (\n ctx: ComponentReadCtx,\n opts?: {\n where?: {\n groupId?: string;\n slug?: string;\n status?: \"draft\" | \"active\" | \"disabled\";\n };\n limit?: number;\n cursor?: string | null;\n orderBy?: \"_creationTime\" | \"name\" | \"slug\" | \"status\";\n order?: \"asc\" | \"desc\";\n },\n ) => {\n return await ctx.runQuery(config.component.public.enterpriseList, {\n where: opts?.where,\n limit: opts?.limit,\n cursor: opts?.cursor,\n orderBy: opts?.orderBy,\n order: opts?.order,\n });\n },\n update: async (\n ctx: ComponentCtx,\n enterpriseId: string,\n data: Record<string, unknown>,\n ) => {\n await ctx.runMutation(config.component.public.enterpriseUpdate, {\n enterpriseId,\n data,\n });\n return { enterpriseId };\n },\n delete: async (ctx: ComponentCtx, enterpriseId: string) => {\n await ctx.runMutation(config.component.public.enterpriseDelete, {\n enterpriseId,\n });\n return { enterpriseId };\n },\n /**\n * Aggregate readiness status across all configured protocols for an\n * enterprise connection.\n *\n * Returns a structured result indicating whether the connection is\n * ready, with per-protocol checks so callers can surface actionable\n * diagnostics without running full network validation.\n */\n status: async (ctx: ComponentReadCtx, enterpriseId: string) => {\n const enterprise = await ctx.runQuery(\n config.component.public.enterpriseGet,\n { enterpriseId },\n );\n if (!enterprise) {\n throw Cv.error({\n code: \"INVALID_PARAMETERS\",\n message: enterpriseNotFoundError,\n });\n }\n const policy = getPolicyFromEnterprise(enterprise);\n const protocols = enterprise.config?.protocols ?? {};\n const oidcConfig = protocols.oidc;\n const oidcSecret = await getEnterpriseSecret(\n ctx,\n enterprise._id,\n ENTERPRISE_OIDC_CLIENT_SECRET_KIND,\n );\n const samlConfig = protocols.saml;\n const scimConfig = await ctx.runQuery(\n config.component.public.enterpriseScimConfigGetByEnterprise,\n { enterpriseId },\n );\n const domains = await ctx.runQuery(\n config.component.public.enterpriseDomainList,\n { enterpriseId },\n );\n\n const oidcReady =\n oidcConfig?.enabled === true &&\n typeof oidcConfig?.clientId === \"string\" &&\n oidcConfig.clientId.length > 0 &&\n oidcSecret !== null &&\n (typeof oidcConfig?.issuer === \"string\" ||\n typeof oidcConfig?.discoveryUrl === \"string\");\n const samlReady =\n samlConfig?.enabled === true &&\n typeof samlConfig?.idp?.entityId === \"string\";\n const scimReady =\n scimConfig !== null &&\n scimConfig !== undefined &&\n (scimConfig as any).status === \"active\";\n\n const ready =\n enterprise.status === \"active\" && (oidcReady || samlReady);\n\n return {\n enterpriseId: enterprise._id,\n status: enterprise.status,\n ready,\n domainCount: (domains as unknown[]).length,\n protocols: {\n oidc: {\n configured: oidcReady,\n ready: oidcReady,\n clientId: oidcConfig?.clientId ?? null,\n issuer: oidcConfig?.issuer ?? oidcConfig?.discoveryUrl ?? null,\n },\n saml: {\n configured: samlReady,\n ready: samlReady,\n entityId: samlConfig?.idp?.entityId ?? null,\n },\n scim: {\n configured: scimReady,\n ready: scimReady,\n basePath: scimConfig?.basePath ?? null,\n deprovisionMode: policy.provisioning.deprovision.mode,\n },\n },\n };\n },\n },\n domain: {\n add: async (\n ctx: ComponentCtx,\n data: {\n enterpriseId: string;\n groupId: string;\n domain: string;\n isPrimary?: boolean;\n },\n ): Promise<string> => {\n return (await ctx.runMutation(\n config.component.public.enterpriseDomainAdd,\n {\n ...data,\n domain: normalizeDomain(data.domain),\n },\n )) as string;\n },\n list: async (ctx: ComponentReadCtx, enterpriseId: string) => {\n return await ctx.runQuery(\n config.component.public.enterpriseDomainList,\n {\n enterpriseId,\n },\n );\n },\n validate: async (ctx: ComponentReadCtx, enterpriseId: string) => {\n const enterprise = await ctx.runQuery(\n config.component.public.enterpriseGet,\n { enterpriseId },\n );\n if (enterprise === null) {\n throw Cv.error({\n code: \"INVALID_PARAMETERS\",\n message: enterpriseNotFoundError,\n });\n }\n\n const domains = await ctx.runQuery(\n config.component.public.enterpriseDomainList,\n { enterpriseId },\n );\n const primaryDomains = domains.filter(\n (domain: (typeof domains)[number]) => domain.isPrimary,\n );\n const verifiedDomains = domains.filter(\n (domain: (typeof domains)[number]) => domain.verifiedAt !== undefined,\n );\n\n const warnings: string[] = [];\n if (domains.length === 0) {\n warnings.push(\"No domains configured.\");\n }\n if (primaryDomains.length === 0 && domains.length > 0) {\n warnings.push(\"No primary domain configured.\");\n }\n if (primaryDomains.length > 1) {\n warnings.push(\"Multiple primary domains configured.\");\n }\n if (verifiedDomains.length === 0 && domains.length > 0) {\n warnings.push(\"No verified domains yet.\");\n }\n\n return {\n enterpriseId,\n ready:\n enterprise.status === \"active\" &&\n domains.length > 0 &&\n primaryDomains.length === 1 &&\n verifiedDomains.length > 0,\n summary: {\n domainCount: domains.length,\n primaryCount: primaryDomains.length,\n verifiedCount: verifiedDomains.length,\n },\n domains: domains.map((domain: (typeof domains)[number]) =>\n toDomainSummary(domain),\n ),\n warnings,\n };\n },\n remove: async (ctx: ComponentCtx, domainId: string) => {\n await ctx.runMutation(config.component.public.enterpriseDomainDelete, {\n domainId,\n });\n },\n verification: {\n request: async (\n ctx: ComponentCtx,\n args: { enterpriseId: string; domain: string },\n ) => {\n const enterprise = await loadEnterpriseOrThrow(\n ctx,\n args.enterpriseId,\n );\n const normalizedDomain = normalizeDomain(args.domain);\n const domains = await ctx.runQuery(\n config.component.public.enterpriseDomainList,\n { enterpriseId: enterprise._id },\n );\n const domain = domains.find(\n (entry: (typeof domains)[number]) =>\n entry.domain === normalizedDomain,\n );\n if (!domain) {\n throw Cv.error({\n code: \"INVALID_PARAMETERS\",\n message: \"Domain is not attached to this enterprise.\",\n });\n }\n\n const requestedAt = Date.now();\n const expiresAt = requestedAt + ENTERPRISE_DOMAIN_VERIFICATION_TTL_MS;\n const token = generateRandomString(32, INVITE_TOKEN_ALPHABET);\n const tokenHash = await sha256(token);\n const recordName = getDomainVerificationRecordName(normalizedDomain);\n\n await ctx.runMutation(\n config.component.public.enterpriseDomainVerificationUpsert,\n {\n enterpriseId: enterprise._id,\n groupId: enterprise.groupId,\n domainId: domain._id,\n domain: normalizedDomain,\n recordName,\n token,\n tokenHash,\n requestedAt,\n expiresAt,\n },\n );\n\n await recordEnterpriseAuditEvent(ctx, {\n enterpriseId: enterprise._id,\n groupId: enterprise.groupId,\n eventType: \"enterprise.domain.verification_requested\",\n actorType: \"system\",\n subjectType: \"enterprise_domain\",\n subjectId: domain._id,\n ok: true,\n metadata: { domain: normalizedDomain, recordName, expiresAt },\n });\n\n return {\n enterpriseId: enterprise._id,\n domain: normalizedDomain,\n requestedAt,\n expiresAt,\n challenge: {\n recordType: \"TXT\" as const,\n recordName,\n recordValue: token,\n },\n };\n },\n confirm: async (\n ctx: ComponentCtx,\n args: { enterpriseId: string; domain: string },\n ) => {\n const enterprise = await loadEnterpriseOrThrow(\n ctx,\n args.enterpriseId,\n );\n const normalizedDomain = normalizeDomain(args.domain);\n const domains = await ctx.runQuery(\n config.component.public.enterpriseDomainList,\n { enterpriseId: enterprise._id },\n );\n const domain = domains.find(\n (entry: (typeof domains)[number]) =>\n entry.domain === normalizedDomain,\n );\n if (!domain) {\n throw Cv.error({\n code: \"INVALID_PARAMETERS\",\n message: \"Domain is not attached to this enterprise.\",\n });\n }\n\n if (domain.verifiedAt !== undefined) {\n return {\n ok: true,\n enterpriseId: enterprise._id,\n domain: normalizedDomain,\n verifiedAt: domain.verifiedAt,\n checks: [\n {\n name: \"domain_verified\",\n ok: true,\n message: \"Domain is already verified.\",\n },\n ],\n };\n }\n\n const verification = await ctx.runQuery(\n config.component.public.enterpriseDomainVerificationGet,\n { domainId: domain._id },\n );\n const checks: Array<{ name: string; ok: boolean; message?: string }> =\n [];\n if (!verification) {\n checks.push({\n name: \"verification_requested\",\n ok: false,\n message: \"No active domain verification challenge exists.\",\n });\n return {\n ok: false,\n enterpriseId: enterprise._id,\n domain: normalizedDomain,\n checks,\n };\n }\n\n checks.push({ name: \"verification_requested\", ok: true });\n\n if (verification.expiresAt < Date.now()) {\n await ctx.runMutation(\n config.component.public.enterpriseDomainVerificationDelete,\n { domainId: domain._id },\n );\n checks.push({\n name: \"challenge_active\",\n ok: false,\n message: \"The verification challenge expired. Request a new one.\",\n });\n return {\n ok: false,\n enterpriseId: enterprise._id,\n domain: normalizedDomain,\n checks,\n };\n }\n\n checks.push({ name: \"challenge_active\", ok: true });\n\n let txtValues: string[];\n try {\n txtValues = await resolveTxtValues(verification.recordName);\n } catch (error) {\n throw Cv.error({\n code: \"INTERNAL_ERROR\",\n message:\n error instanceof Error\n ? error.message\n : \"Failed to resolve DNS TXT records.\",\n });\n }\n\n checks.push({\n name: \"dns_record_present\",\n ok: txtValues.length > 0,\n message:\n txtValues.length > 0\n ? undefined\n : `No TXT records found at ${verification.recordName}.`,\n });\n\n const matches = txtValues.includes(verification.token);\n checks.push({\n name: \"dns_record_matches\",\n ok: matches,\n message: matches\n ? undefined\n : `TXT record at ${verification.recordName} does not match the expected value.`,\n });\n\n if (!checks.every((check) => check.ok)) {\n return {\n ok: false,\n enterpriseId: enterprise._id,\n domain: normalizedDomain,\n checks,\n };\n }\n\n const verifiedAt = Date.now();\n await ctx.runMutation(\n config.component.public.enterpriseDomainVerify,\n {\n domainId: domain._id,\n verifiedAt,\n },\n );\n\n await recordEnterpriseAuditEvent(ctx, {\n enterpriseId: enterprise._id,\n groupId: enterprise.groupId,\n eventType: \"enterprise.domain.verified\",\n actorType: \"system\",\n subjectType: \"enterprise_domain\",\n subjectId: domain._id,\n ok: true,\n metadata: { domain: normalizedDomain, verifiedAt },\n });\n\n return {\n ok: true,\n enterpriseId: enterprise._id,\n domain: normalizedDomain,\n verifiedAt,\n checks,\n };\n },\n },\n },\n saml: {\n configure: async <DataModel extends GenericDataModel>(\n ctx: GenericActionCtx<DataModel>,\n data: {\n enterpriseId: string;\n metadataXml?: string;\n metadataUrl?: string;\n domains?: string[];\n signAuthnRequests?: boolean;\n attributeMapping?: {\n subject?: string;\n email?: string;\n name?: string;\n firstName?: string;\n lastName?: string;\n };\n sp?: {\n entityId?: string;\n acsUrl?: string;\n sloUrl?: string;\n signingCert?: string | string[];\n encryptCert?: string | string[];\n privateKey?: string;\n privateKeyPass?: string;\n encPrivateKey?: string;\n encPrivateKeyPass?: string;\n };\n },\n ) => {\n return await Fx.run(\n Fx.gen(function* () {\n const enterprise = yield* Fx.from({\n ok: () =>\n ctx.runQuery(config.component.public.enterpriseGet, {\n enterpriseId: data.enterpriseId,\n }),\n err: () =>\n Cv.error({\n code: \"INTERNAL_ERROR\",\n message: \"Failed to load enterprise.\",\n }),\n }).pipe(\n Fx.chain((ent) =>\n ent === null\n ? Cv.fail({\n code: \"INVALID_PARAMETERS\",\n message: enterpriseNotFoundError,\n })\n : Fx.succeed(ent),\n ),\n );\n const metadataXml = yield* data.metadataXml\n ? Fx.succeed(data.metadataXml)\n : data.metadataUrl\n ? Fx.defer(() =>\n Fx.from({\n ok: async () => {\n const response = await fetch(data.metadataUrl!);\n if (!response.ok) {\n throw new Error(\n `Failed to fetch SAML metadata: ${response.status}`,\n );\n }\n return await response.text();\n },\n err: (error) =>\n Cv.error({\n code: \"INVALID_PARAMETERS\",\n message:\n error instanceof Error\n ? error.message\n : \"Failed to fetch SAML metadata\",\n }),\n }),\n ).pipe(\n Fx.timeout(10_000),\n Fx.retry(\n Fx.retry.compose(\n Fx.retry.jittered(Fx.retry.exponential(200)),\n Fx.retry.recurs(2),\n ),\n ),\n Fx.recover((error) =>\n Cv.fail({\n code: \"INVALID_PARAMETERS\",\n message:\n error instanceof Error\n ? error.message\n : \"Failed to fetch SAML metadata\",\n }),\n ),\n )\n : Cv.fail({\n code: \"INVALID_PARAMETERS\",\n message:\n \"SAML registration requires metadataXml or metadataUrl.\",\n });\n\n const parsed = yield* Fx.from({\n ok: () => parseSamlIdpMetadata(metadataXml),\n err: () =>\n Cv.error({\n code: \"INVALID_PARAMETERS\",\n message: \"Failed to parse SAML metadata.\",\n }),\n });\n\n const baseConfig = upsertProtocolConfig(enterprise.config, \"saml\", {\n enabled: true,\n idp: {\n metadataXml,\n ...parsed,\n },\n sp: data.sp,\n signAuthnRequests:\n data.signAuthnRequests ?? parsed.wantsSignedAuthnRequests,\n attributeMapping: data.attributeMapping,\n });\n const normalizedDomains = data.domains?.map(normalizeDomain);\n const nextConfig = normalizedDomains\n ? { ...baseConfig, domains: normalizedDomains }\n : baseConfig;\n\n yield* Fx.from({\n ok: () =>\n ctx.runMutation(config.component.public.enterpriseUpdate, {\n enterpriseId: enterprise._id,\n data: {\n status: \"active\",\n config: nextConfig,\n },\n }),\n err: () =>\n Cv.error({\n code: \"INTERNAL_ERROR\",\n message: \"Failed to persist SAML registration.\",\n }),\n });\n\n if (normalizedDomains) {\n for (const [index, domain] of normalizedDomains.entries()) {\n yield* Fx.from({\n ok: () =>\n ctx.runMutation(\n config.component.public.enterpriseDomainAdd,\n {\n enterpriseId: enterprise._id,\n groupId: enterprise.groupId,\n domain,\n isPrimary: index === 0,\n },\n ),\n err: () =>\n Cv.error({\n code: \"INTERNAL_ERROR\",\n message: \"Failed to persist enterprise domain.\",\n }),\n });\n }\n }\n\n yield* Fx.from({\n ok: () =>\n recordEnterpriseAuditEvent(ctx, {\n enterpriseId: enterprise._id,\n groupId: enterprise.groupId,\n eventType: \"enterprise.saml.registered\",\n actorType: \"system\",\n subjectType: \"enterprise_saml\",\n subjectId: enterprise._id,\n ok: true,\n metadata: {\n metadataUrl: data.metadataUrl,\n domains: normalizedDomains,\n },\n }),\n err: () =>\n Cv.error({\n code: \"INTERNAL_ERROR\",\n message: \"Failed to record SAML registration audit event.\",\n }),\n });\n\n return {\n enterpriseId: enterprise._id,\n groupId: enterprise.groupId,\n };\n }).pipe(Fx.recover((e) => Fx.fatal(e))),\n );\n },\n metadata: async <DataModel extends GenericDataModel>(\n ctx: GenericActionCtx<DataModel>,\n opts: {\n enterpriseId: string;\n entityId?: string;\n acsUrl?: string;\n sloUrl?: string;\n },\n ) => {\n const enterprise = await ctx.runQuery(\n config.component.public.enterpriseGet,\n {\n enterpriseId: opts.enterpriseId,\n },\n );\n if (!enterprise) {\n throw Cv.error({\n code: \"INVALID_PARAMETERS\",\n message: \"Enterprise not found.\",\n });\n }\n\n return createServiceProviderMetadata(\n getSamlServiceProviderOptions({\n rootUrl: requireEnv(\"CONVEX_SITE_URL\"),\n source: { kind: \"enterprise\", id: enterprise._id },\n config: enterprise.config,\n overrides: {\n entityId: opts.entityId,\n acsUrl: opts.acsUrl,\n sloUrl: opts.sloUrl,\n },\n }),\n );\n },\n /**\n * Validate the stored SAML config for an enterprise connection.\n *\n * Re-parses IdP metadata, checks signing cert presence, and verifies\n * SP metadata can be generated. Returns a structured result with\n * per-check details rather than throwing on first failure.\n */\n validate: async <DataModel extends GenericDataModel>(\n ctx: GenericActionCtx<DataModel>,\n enterpriseId: string,\n ) => {\n const checks: Array<{\n name: string;\n ok: boolean;\n message?: string;\n }> = [];\n\n const enterprise = await ctx.runQuery(\n config.component.public.enterpriseGet,\n { enterpriseId },\n );\n\n if (!enterprise) {\n return {\n ok: false,\n enterpriseId,\n checks: [\n {\n name: \"enterprise_exists\",\n ok: false,\n message: \"Enterprise not found.\",\n },\n ],\n };\n }\n\n const samlConfig = enterprise.config?.protocols?.saml;\n const samlConfigured =\n samlConfig?.enabled === true &&\n typeof samlConfig?.idp?.metadataXml === \"string\";\n\n checks.push({\n name: \"saml_configured\",\n ok: samlConfigured,\n message: samlConfigured ? undefined : \"SAML is not configured.\",\n });\n\n const hasIdpMetadata =\n typeof samlConfig?.idp?.metadataXml === \"string\" &&\n samlConfig.idp.metadataXml.length > 0;\n checks.push({\n name: \"idp_metadata_present\",\n ok: hasIdpMetadata,\n message: hasIdpMetadata ? undefined : \"IdP metadata XML is missing.\",\n });\n\n const hasEntityId =\n typeof samlConfig?.idp?.entityId === \"string\" &&\n samlConfig.idp.entityId.length > 0;\n checks.push({\n name: \"idp_entity_id\",\n ok: hasEntityId,\n message: hasEntityId\n ? undefined\n : \"IdP entityId could not be parsed from metadata.\",\n });\n\n let spMetadataOk = false;\n let spMetadataMessage: string | undefined;\n if (samlConfigured) {\n try {\n createServiceProviderMetadata(\n getSamlServiceProviderOptions({\n rootUrl: requireEnv(\"CONVEX_SITE_URL\"),\n source: { kind: \"enterprise\", id: enterprise._id },\n config: enterprise.config,\n overrides: {},\n }),\n );\n spMetadataOk = true;\n } catch (e) {\n spMetadataMessage =\n e instanceof Error ? e.message : \"SP metadata generation failed.\";\n }\n } else {\n spMetadataMessage = \"Skipped — SAML not configured.\";\n }\n checks.push({\n name: \"sp_metadata_generates\",\n ok: spMetadataOk,\n message: spMetadataMessage,\n });\n\n return {\n ok: checks.every((c) => c.ok),\n enterpriseId: enterprise._id,\n checks,\n };\n },\n },\n policy: {\n get: async (ctx: ComponentReadCtx, enterpriseId: string) => {\n const enterprise = await loadEnterpriseOrThrow(ctx, enterpriseId);\n return getPolicyFromEnterprise(enterprise);\n },\n update: async (\n ctx: ComponentCtx,\n enterpriseId: string,\n patch: EnterprisePolicyPatch,\n ) => {\n const enterprise = await loadEnterpriseOrThrow(ctx, enterpriseId);\n const policy = patchEnterprisePolicy(enterprise.policy, patch);\n await ctx.runMutation(config.component.public.enterpriseUpdate, {\n enterpriseId,\n data: { policy },\n });\n await recordEnterpriseAuditEvent(ctx, {\n enterpriseId: enterprise._id,\n groupId: enterprise.groupId,\n eventType: \"enterprise.policy.updated\",\n actorType: \"system\",\n subjectType: \"enterprise_policy\",\n subjectId: enterprise._id,\n ok: true,\n metadata: { version: policy.version },\n });\n return policy;\n },\n validate: async (ctx: ComponentReadCtx, enterpriseId: string) => {\n const enterprise = await ctx.runQuery(\n config.component.public.enterpriseGet,\n { enterpriseId },\n );\n if (!enterprise) {\n return {\n ok: false,\n enterpriseId,\n checks: [\n {\n name: \"enterprise_exists\",\n ok: false,\n message: enterpriseNotFoundError,\n },\n ],\n };\n }\n const policy = getPolicyFromEnterprise(enterprise);\n const checks = validateEnterprisePolicy(policy);\n return {\n ok: checks.every((check: { ok: boolean }) => check.ok),\n enterpriseId,\n policy,\n checks,\n };\n },\n },\n oidc: {\n /**\n * Register or update enterprise OIDC connection settings.\n *\n * Persists protocol config under `enterprise.config.protocols.oidc` and\n * records an `enterprise.oidc.registered` audit event.\n */\n configure: async (\n ctx: ComponentCtx,\n data: {\n enterpriseId: string;\n issuer?: string;\n discoveryUrl?: string;\n clientId: string;\n clientSecret?: string;\n scopes?: string[];\n authorizationParams?: Record<string, string>;\n clockToleranceSeconds?: number;\n strictIssuer?: boolean;\n /**\n * Map OIDC claim names to `user.extend` field names.\n * Example: `{ department: \"department\", role: \"job_title\" }` means\n * the OIDC `department` claim is stored as `user.extend.department`.\n */\n extraFields?: Record<string, string>;\n },\n ) => {\n return await Fx.run(\n Fx.gen(function* () {\n yield* Fx.guard(\n data.issuer === undefined && data.discoveryUrl === undefined,\n Cv.fail({\n code: \"INVALID_PARAMETERS\",\n message: \"OIDC registration requires issuer or discoveryUrl.\",\n }),\n );\n\n const enterprise = yield* Fx.from({\n ok: () =>\n ctx.runQuery(config.component.public.enterpriseGet, {\n enterpriseId: data.enterpriseId,\n }),\n err: () =>\n Cv.error({\n code: \"INTERNAL_ERROR\",\n message: \"Failed to load enterprise.\",\n }),\n }).pipe(\n Fx.chain((ent) =>\n ent === null\n ? Cv.fail({\n code: \"INVALID_PARAMETERS\",\n message: enterpriseNotFoundError,\n })\n : Fx.succeed(ent),\n ),\n );\n const nextConfig = upsertProtocolConfig(enterprise.config, \"oidc\", {\n enabled: true,\n issuer: data.issuer,\n discoveryUrl: data.discoveryUrl,\n clientId: data.clientId,\n scopes: data.scopes ?? [\"openid\", \"profile\", \"email\"],\n authorizationParams: data.authorizationParams,\n clockToleranceSeconds: data.clockToleranceSeconds,\n strictIssuer: data.strictIssuer,\n extraFields: data.extraFields,\n });\n\n yield* Fx.from({\n ok: () =>\n ctx.runMutation(config.component.public.enterpriseUpdate, {\n enterpriseId: data.enterpriseId,\n data: { config: nextConfig },\n }),\n err: () =>\n Cv.error({\n code: \"INTERNAL_ERROR\",\n message: \"Failed to persist OIDC registration.\",\n }),\n });\n\n if (data.clientSecret !== undefined) {\n const ciphertext = yield* Fx.from({\n ok: () => encryptSecret(data.clientSecret!),\n err: () =>\n Cv.error({\n code: \"INTERNAL_ERROR\",\n message: \"Failed to encrypt OIDC client secret.\",\n }),\n });\n yield* Fx.from({\n ok: () =>\n ctx.runMutation(\n config.component.public.enterpriseSecretUpsert,\n {\n enterpriseId: data.enterpriseId,\n groupId: enterprise.groupId,\n kind: ENTERPRISE_OIDC_CLIENT_SECRET_KIND,\n ciphertext,\n updatedAt: Date.now(),\n },\n ),\n err: () =>\n Cv.error({\n code: \"INTERNAL_ERROR\",\n message: \"Failed to persist OIDC client secret.\",\n }),\n });\n }\n\n yield* Fx.from({\n ok: () =>\n recordEnterpriseAuditEvent(ctx, {\n enterpriseId: data.enterpriseId,\n groupId: enterprise.groupId,\n eventType: \"enterprise.oidc.registered\",\n actorType: \"system\",\n subjectType: \"enterprise_oidc\",\n subjectId: data.enterpriseId,\n ok: true,\n metadata: {\n issuer: data.issuer,\n discoveryUrl: data.discoveryUrl,\n },\n }),\n err: () =>\n Cv.error({\n code: \"INTERNAL_ERROR\",\n message: \"Failed to record OIDC registration audit event.\",\n }),\n });\n\n const secret = yield* Fx.from({\n ok: () =>\n getEnterpriseSecret(\n ctx,\n data.enterpriseId,\n ENTERPRISE_OIDC_CLIENT_SECRET_KIND,\n ),\n err: () =>\n Cv.error({\n code: \"INTERNAL_ERROR\",\n message: \"Failed to load OIDC secret metadata.\",\n }),\n });\n\n return withOidcSecretState(\n getPublicOidcConfig(nextConfig),\n secret !== null,\n );\n }).pipe(Fx.recover((e) => Fx.fatal(e))),\n );\n },\n /**\n * Fetch the stored OIDC config for an enterprise.\n */\n get: async (ctx: ComponentReadCtx, enterpriseId: string) => {\n return await Fx.run(\n Fx.from({\n ok: () =>\n ctx.runQuery(config.component.public.enterpriseGet, {\n enterpriseId,\n }),\n err: () =>\n Cv.error({\n code: \"INTERNAL_ERROR\",\n message: \"Failed to load enterprise.\",\n }),\n }).pipe(\n Fx.chain((ent) =>\n ent === null\n ? Cv.fail({\n code: \"INVALID_PARAMETERS\",\n message: enterpriseNotFoundError,\n })\n : Fx.succeed(ent),\n ),\n Fx.chain((enterprise) =>\n Fx.from({\n ok: async () => {\n const secret = await getEnterpriseSecret(\n ctx,\n enterprise._id,\n ENTERPRISE_OIDC_CLIENT_SECRET_KIND,\n );\n return withOidcSecretState(\n getPublicOidcConfig(enterprise.config),\n secret !== null,\n );\n },\n err: () =>\n Cv.error({\n code: \"INTERNAL_ERROR\",\n message: \"Failed to load OIDC secret metadata.\",\n }),\n }),\n ),\n Fx.recover((e) => Fx.fatal(e)),\n ),\n );\n },\n /**\n * Resolve enterprise OIDC sign-in route from enterprise id, domain, or\n * user email domain.\n */\n signIn: async (\n ctx: ComponentReadCtx,\n data: {\n enterpriseId?: string;\n email?: string;\n domain?: string;\n redirectTo?: string;\n },\n ) => {\n return await Fx.run(\n Fx.gen(function* () {\n const enterprise =\n data.enterpriseId !== undefined\n ? yield* Fx.from({\n ok: () =>\n ctx.runQuery(config.component.public.enterpriseGet, {\n enterpriseId: data.enterpriseId,\n }),\n err: () =>\n Cv.error({\n code: \"INTERNAL_ERROR\",\n message: \"Failed to load enterprise.\",\n }),\n }).pipe(\n Fx.chain((ent) =>\n ent === null\n ? Cv.fail({\n code: \"INVALID_PARAMETERS\",\n message: enterpriseNotFoundError,\n })\n : Fx.succeed(ent),\n ),\n )\n : data.domain !== undefined || data.email !== undefined\n ? yield* Fx.from({\n ok: () =>\n ctx.runQuery(\n config.component.public.enterpriseGetByDomain,\n {\n domain: normalizeDomain(\n data.domain ??\n String(data.email).split(\"@\").pop() ??\n \"\",\n ),\n },\n ),\n err: () =>\n Cv.error({\n code: \"INTERNAL_ERROR\",\n message: \"Failed to resolve enterprise by domain.\",\n }),\n }).pipe(\n Fx.chain((result) =>\n result?.enterprise &&\n result.domain?.verifiedAt !== undefined\n ? Fx.succeed(result.enterprise)\n : Cv.fail({\n code: \"INVALID_PARAMETERS\",\n message:\n \"No enterprise OIDC connection matched the provided input.\",\n }),\n ),\n )\n : yield* Cv.fail({\n code: \"INVALID_PARAMETERS\",\n message:\n \"No enterprise OIDC connection matched the provided input.\",\n });\n\n yield* Fx.guard(\n enterprise.status !== \"active\",\n Cv.fail({\n code: \"INVALID_PARAMETERS\",\n message: \"Enterprise connection is not active.\",\n }),\n );\n\n const oidc = getOidcConfig(enterprise.config);\n yield* Fx.guard(\n oidc.enabled !== true,\n Cv.fail({\n code: \"PROVIDER_NOT_CONFIGURED\",\n message: \"OIDC is not configured for this enterprise.\",\n }),\n );\n\n const urls = getEnterpriseOidcUrls({\n rootUrl: requireEnv(\"CONVEX_SITE_URL\"),\n enterpriseId: enterprise._id,\n });\n return {\n enterpriseId: enterprise._id,\n providerId: enterpriseOidcProviderId(enterprise._id),\n signInPath: urls.signInUrl,\n callbackPath: urls.callbackUrl,\n redirectTo: data.redirectTo,\n };\n }).pipe(Fx.recover((e) => Fx.fatal(e))),\n );\n },\n /**\n * Validate the stored OIDC config for an enterprise connection.\n *\n * Fetches the OIDC discovery document from the configured issuer or\n * discoveryUrl, verifies required fields are present, and checks that\n * clientId is set. Returns a structured result with per-check details.\n */\n validate: async (ctx: ComponentReadCtx, enterpriseId: string) => {\n const checks: Array<{\n name: string;\n ok: boolean;\n message?: string;\n }> = [];\n\n const enterprise = await ctx.runQuery(\n config.component.public.enterpriseGet,\n { enterpriseId },\n );\n\n if (!enterprise) {\n return {\n ok: false,\n enterpriseId,\n checks: [\n {\n name: \"enterprise_exists\",\n ok: false,\n message: \"Enterprise not found.\",\n },\n ],\n };\n }\n\n const oidc = getOidcConfig(enterprise.config);\n const secret = await getEnterpriseSecret(\n ctx,\n enterprise._id,\n ENTERPRISE_OIDC_CLIENT_SECRET_KIND,\n );\n const oidcConfigured =\n oidc.enabled === true &&\n typeof oidc.clientId === \"string\" &&\n oidc.clientId.length > 0;\n\n checks.push({\n name: \"oidc_configured\",\n ok: oidcConfigured,\n message: oidcConfigured ? undefined : \"OIDC is not configured.\",\n });\n\n const hasClientId =\n typeof oidc.clientId === \"string\" && oidc.clientId.length > 0;\n checks.push({\n name: \"client_id_present\",\n ok: hasClientId,\n message: hasClientId ? undefined : \"clientId is missing.\",\n });\n\n checks.push({\n name: \"client_secret_stored\",\n ok: secret !== null,\n message:\n secret !== null ? undefined : \"OIDC client secret is missing.\",\n });\n\n const discoveryTarget = oidc.discoveryUrl ?? oidc.issuer;\n const hasDiscovery =\n typeof discoveryTarget === \"string\" && discoveryTarget.length > 0;\n checks.push({\n name: \"issuer_or_discovery_url_present\",\n ok: hasDiscovery,\n message: hasDiscovery\n ? undefined\n : \"issuer or discoveryUrl is missing.\",\n });\n\n let discoveryOk = false;\n let discoveryMessage: string | undefined;\n if (hasDiscovery) {\n const discoveryUrl = oidc.discoveryUrl?.length\n ? oidc.discoveryUrl\n : `${oidc.issuer}/.well-known/openid-configuration`;\n try {\n const res = await fetch(discoveryUrl, {\n headers: { Accept: \"application/json\" },\n signal: AbortSignal.timeout(8_000),\n });\n if (!res.ok) {\n discoveryMessage = `Discovery endpoint returned ${res.status}.`;\n } else {\n const json = (await res.json()) as Record<string, unknown>;\n if (typeof json.issuer !== \"string\") {\n discoveryMessage =\n \"Discovery document is missing issuer field.\";\n } else if (typeof json.authorization_endpoint !== \"string\") {\n discoveryMessage =\n \"Discovery document is missing authorization_endpoint.\";\n } else {\n discoveryOk = true;\n }\n }\n } catch (e) {\n discoveryMessage =\n e instanceof Error\n ? `Discovery fetch failed: ${e.message}`\n : \"Discovery fetch failed.\";\n }\n } else {\n discoveryMessage = \"Skipped — issuer or discoveryUrl not set.\";\n }\n checks.push({\n name: \"discovery_reachable\",\n ok: discoveryOk,\n message: discoveryMessage,\n });\n\n return {\n ok: checks.every((c) => c.ok),\n enterpriseId: enterprise._id,\n checks,\n };\n },\n },\n scim: {\n configure: async (\n ctx: ComponentCtx,\n data: {\n enterpriseId: string;\n basePath?: string;\n status?: \"draft\" | \"active\" | \"disabled\";\n },\n ) => {\n const enterprise = await ctx.runQuery(\n config.component.public.enterpriseGet,\n {\n enterpriseId: data.enterpriseId,\n },\n );\n if (enterprise === null) {\n throw Cv.error({\n code: \"INVALID_PARAMETERS\",\n message: \"Enterprise not found.\",\n });\n }\n const rawToken = generateRandomString(48, INVITE_TOKEN_ALPHABET);\n const tokenHash = await sha256(rawToken);\n const configId = (await ctx.runMutation(\n config.component.public.enterpriseScimConfigUpsert,\n {\n enterpriseId: enterprise._id,\n groupId: enterprise.groupId,\n status: data.status ?? \"active\",\n basePath:\n data.basePath ??\n `${requireEnv(\"CONVEX_SITE_URL\")}/api/auth/sso/${enterprise._id}/scim/v2`,\n tokenHash,\n lastRotatedAt: Date.now(),\n },\n )) as string;\n const auditEventId = await recordEnterpriseAuditEvent(ctx, {\n enterpriseId: enterprise._id,\n groupId: enterprise.groupId,\n eventType: \"enterprise.scim.configured\",\n actorType: \"system\",\n subjectType: \"enterprise_scim\",\n subjectId: configId,\n ok: true,\n });\n await emitEnterpriseWebhookDeliveries(ctx, {\n enterpriseId: enterprise._id,\n eventType: \"enterprise.scim.configured\",\n auditEventId,\n payload: { enterpriseId: enterprise._id, scimConfigId: configId },\n });\n return {\n enterpriseId: enterprise._id,\n configId,\n basePath:\n data.basePath ??\n `${requireEnv(\"CONVEX_SITE_URL\")}/api/auth/sso/${enterprise._id}/scim/v2`,\n token: rawToken,\n };\n },\n get: async (ctx: ComponentReadCtx, enterpriseId: string) => {\n return await ctx.runQuery(\n config.component.public.enterpriseScimConfigGetByEnterprise,\n { enterpriseId },\n );\n },\n getConfigByToken: async (ctx: ComponentReadCtx, token: string) => {\n return await ctx.runQuery(\n config.component.public.enterpriseScimConfigGetByTokenHash,\n { tokenHash: await sha256(token) },\n );\n },\n /**\n * Validate the stored SCIM config for an enterprise connection.\n *\n * Checks that a SCIM config record exists, is active, has a token\n * hash set, and has a non-empty basePath. Returns a structured result\n * with per-check details.\n */\n validate: async (ctx: ComponentReadCtx, enterpriseId: string) => {\n const checks: Array<{\n name: string;\n ok: boolean;\n message?: string;\n }> = [];\n\n const enterprise = await ctx.runQuery(\n config.component.public.enterpriseGet,\n { enterpriseId },\n );\n\n if (!enterprise) {\n return {\n ok: false,\n enterpriseId,\n checks: [\n {\n name: \"enterprise_exists\",\n ok: false,\n message: \"Enterprise not found.\",\n },\n ],\n };\n }\n\n const policy = getPolicyFromEnterprise(enterprise);\n\n const scimConfig = await ctx.runQuery(\n config.component.public.enterpriseScimConfigGetByEnterprise,\n { enterpriseId },\n );\n\n const hasConfig = scimConfig !== null && scimConfig !== undefined;\n checks.push({\n name: \"scim_config_exists\",\n ok: hasConfig,\n message: hasConfig ? undefined : \"SCIM has not been configured.\",\n });\n\n const isActive = hasConfig && (scimConfig as any).status === \"active\";\n checks.push({\n name: \"scim_config_active\",\n ok: isActive,\n message: isActive\n ? undefined\n : `SCIM config status is ${hasConfig ? (scimConfig as any).status : \"unknown\"}.`,\n });\n\n const hasToken =\n hasConfig &&\n typeof (scimConfig as any).tokenHash === \"string\" &&\n (scimConfig as any).tokenHash.length > 0;\n checks.push({\n name: \"token_hash_set\",\n ok: hasToken,\n message: hasToken ? undefined : \"SCIM bearer token has not been set.\",\n });\n\n const hasBasePath =\n hasConfig &&\n typeof (scimConfig as any).basePath === \"string\" &&\n (scimConfig as any).basePath.length > 0;\n checks.push({\n name: \"base_path_set\",\n ok: hasBasePath,\n message: hasBasePath ? undefined : \"SCIM basePath is missing.\",\n });\n\n return {\n ok: checks.every((c) => c.ok),\n enterpriseId: enterprise._id,\n basePath: hasBasePath ? (scimConfig as any).basePath : null,\n deprovisionMode: policy.provisioning.deprovision.mode,\n checks,\n };\n },\n identity: {\n get: async (\n ctx: ComponentReadCtx,\n data: {\n enterpriseId: string;\n resourceType: \"user\" | \"group\";\n externalId: string;\n },\n ) => {\n return await ctx.runQuery(\n config.component.public.enterpriseScimIdentityGet,\n data,\n );\n },\n upsert: async (\n ctx: ComponentCtx,\n data: {\n enterpriseId: string;\n groupId: string;\n resourceType: \"user\" | \"group\";\n externalId: string;\n userId?: string;\n mappedGroupId?: string;\n active?: boolean;\n raw?: Record<string, unknown>;\n },\n ) => {\n return (await ctx.runMutation(\n config.component.public.enterpriseScimIdentityUpsert,\n { ...data, lastProvisionedAt: Date.now() },\n )) as string;\n },\n },\n },\n audit: {\n record: async (\n ctx: ComponentCtx,\n data: {\n enterpriseId: string;\n groupId: string;\n eventType: string;\n actorType: \"user\" | \"system\" | \"scim\" | \"api_key\" | \"webhook\";\n actorId?: string;\n subjectType: string;\n subjectId?: string;\n ok: boolean;\n requestId?: string;\n ip?: string;\n metadata?: Record<string, unknown>;\n },\n ) => {\n return await recordEnterpriseAuditEvent(ctx, data);\n },\n list: async (\n ctx: ComponentReadCtx,\n data: { enterpriseId?: string; groupId?: string; limit?: number },\n ) => {\n return await ctx.runQuery(\n config.component.public.enterpriseAuditEventList,\n data,\n );\n },\n },\n webhook: {\n endpoint: {\n get: async (ctx: ComponentReadCtx, endpointId: string) => {\n return await ctx.runQuery(\n config.component.public.enterpriseWebhookEndpointGet,\n { endpointId },\n );\n },\n create: async (\n ctx: ComponentCtx,\n data: {\n enterpriseId: string;\n url: string;\n secret: string;\n subscriptions: string[];\n createdByUserId?: string;\n },\n ) => {\n const enterprise = await ctx.runQuery(\n config.component.public.enterpriseGet,\n {\n enterpriseId: data.enterpriseId,\n },\n );\n if (enterprise === null) {\n throw Cv.error({\n code: \"INVALID_PARAMETERS\",\n message: \"Enterprise not found.\",\n });\n }\n const secretHash = await sha256(data.secret);\n const endpointId = (await ctx.runMutation(\n config.component.public.enterpriseWebhookEndpointCreate,\n {\n enterpriseId: enterprise._id,\n groupId: enterprise.groupId,\n url: data.url,\n secretHash,\n subscriptions: data.subscriptions,\n createdByUserId: data.createdByUserId,\n },\n )) as string;\n await recordEnterpriseAuditEvent(ctx, {\n enterpriseId: enterprise._id,\n groupId: enterprise.groupId,\n eventType: \"enterprise.webhook.endpoint.created\",\n actorType: data.createdByUserId ? \"user\" : \"system\",\n actorId: data.createdByUserId,\n subjectType: \"enterprise_webhook_endpoint\",\n subjectId: endpointId,\n ok: true,\n });\n return { endpointId };\n },\n list: async (ctx: ComponentReadCtx, enterpriseId: string) => {\n return await ctx.runQuery(\n config.component.public.enterpriseWebhookEndpointList,\n { enterpriseId },\n );\n },\n disable: async (ctx: ComponentCtx, endpointId: string) => {\n await ctx.runMutation(\n config.component.public.enterpriseWebhookEndpointUpdate,\n { endpointId, data: { status: \"disabled\" } },\n );\n return { endpointId };\n },\n },\n emit: async (\n ctx: ComponentCtx,\n data: {\n enterpriseId: string;\n eventType: string;\n payload: Record<string, unknown>;\n auditEventId?: string;\n },\n ) => {\n await emitEnterpriseWebhookDeliveries(ctx, data);\n },\n delivery: {\n list: async (\n ctx: ComponentReadCtx,\n data: { enterpriseId: string; limit?: number },\n ) => {\n return await ctx.runQuery(\n (config.component.public as any).enterpriseWebhookDeliveryList,\n data,\n );\n },\n listReady: async (ctx: ComponentReadCtx, limit?: number) => {\n return await ctx.runQuery(\n config.component.public.enterpriseWebhookDeliveryListReady,\n { now: Date.now(), limit },\n );\n },\n markDelivered: async (\n ctx: ComponentCtx,\n deliveryId: string,\n responseStatus?: number,\n ) => {\n await ctx.runMutation(\n config.component.public.enterpriseWebhookDeliveryPatch,\n {\n deliveryId,\n data: {\n status: \"delivered\",\n attemptCount: 1,\n lastAttemptAt: Date.now(),\n lastResponseStatus: responseStatus,\n },\n },\n );\n },\n markFailed: async (\n ctx: ComponentCtx,\n deliveryId: string,\n data: {\n attemptCount: number;\n responseStatus?: number;\n error?: string;\n retryAt?: number;\n },\n ) => {\n await ctx.runMutation(\n config.component.public.enterpriseWebhookDeliveryPatch,\n {\n deliveryId,\n data: {\n status: data.retryAt ? \"pending\" : \"failed\",\n attemptCount: data.attemptCount,\n lastAttemptAt: Date.now(),\n lastResponseStatus: data.responseStatus,\n lastError: data.error,\n nextAttemptAt: data.retryAt ?? Date.now(),\n },\n },\n );\n },\n },\n },\n };\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http.d.ts","names":[],"sources":["../../../src/server/enterprise/http.ts"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"http.d.ts","names":[],"sources":["../../../src/server/enterprise/http.ts"],"mappings":";;;KAmCY,yBAAA;EACV,IAAA,EAAM,UAAA;EACN,MAAA;EACA,IAAA;EACA,MAAA;EACA,SAAA;EACA,UAAA,GAAa,IAAA;EACb,+BAAA;EACA,yBAAA;EACA,wBAAA;EACA,uBAAA;EACA,yBAAA;EACA,0BAAA;EACA,+BAAA;EACA,oBAAA,GAAuB,MAAA,UAAgB,QAAA;EACvC,mBAAA;EACA,aAAA;EACA,qBAAA;AAAA;AAAA,iBAGc,wBAAA,CAAyB,IAAA,EAAM,yBAAA"}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { AuthError } from "../authError.js";
|
|
1
|
+
import { redirectToParamCookie, useRedirectToParam } from "../cookies.js";
|
|
3
2
|
import { SCIM_GROUP_SCHEMA_ID, SCIM_USER_SCHEMA_ID, enterpriseSamlProviderId } from "./shared.js";
|
|
4
3
|
import { createEnterpriseOidcRuntime } from "./oidc.js";
|
|
5
4
|
import { redirectAbsoluteUrl, setURLSearchParam } from "../redirects.js";
|
|
6
|
-
import { redirectToParamCookie, useRedirectToParam } from "../cookies.js";
|
|
7
5
|
import { addSSORoutes, convertErrorsToResponse, getCookies } from "../http.js";
|
|
8
6
|
import { createOAuthAuthorizationURL, handleOAuthCallback } from "../oauth.js";
|
|
9
7
|
import { createEnterpriseSamlMetadataXml, createEnterpriseSamlSignInRequest, createSamlPostBindingResponse, encodeEnterpriseSamlRelayState, parseEnterpriseSamlLoginResponse, parseEnterpriseSamlLogoutMessage, profileFromSamlExtract, validateEnterpriseSamlLoginRelayState } from "./saml.js";
|
|
10
8
|
import { parseScimListRequest, scimError, scimJson, serializeScimGroup, serializeScimUser } from "./scim.js";
|
|
11
9
|
import { Fx } from "@robelest/fx";
|
|
10
|
+
import { Cv } from "@robelest/fx/convex";
|
|
11
|
+
import { ConvexError } from "convex/values";
|
|
12
12
|
import { serialize } from "cookie";
|
|
13
13
|
|
|
14
14
|
//#region src/server/enterprise/http.ts
|
|
@@ -94,7 +94,10 @@ function addEnterpriseHttpRuntime(deps) {
|
|
|
94
94
|
};
|
|
95
95
|
const readScimJson = async (request) => await request.json();
|
|
96
96
|
const handleSamlAcs = async (ctx, request, runtimeRoute) => Fx.run(Fx.gen(function* () {
|
|
97
|
-
yield* Fx.guard(runtimeRoute.protocol !== "saml" || runtimeRoute.rest.length !== 1 || runtimeRoute.rest[0] !== "acs",
|
|
97
|
+
yield* Fx.guard(runtimeRoute.protocol !== "saml" || runtimeRoute.rest.length !== 1 || runtimeRoute.rest[0] !== "acs", Cv.fail({
|
|
98
|
+
code: "INVALID_PARAMETERS",
|
|
99
|
+
message: "Invalid enterprise runtime path."
|
|
100
|
+
}));
|
|
98
101
|
const enterpriseId = runtimeRoute.enterpriseId;
|
|
99
102
|
const { loaded, enterprise, saml } = yield* Fx.from({
|
|
100
103
|
ok: () => loadActiveEnterpriseSamlOrThrow(ctx, enterpriseId),
|
|
@@ -110,7 +113,10 @@ function addEnterpriseHttpRuntime(deps) {
|
|
|
110
113
|
},
|
|
111
114
|
config: loaded.config
|
|
112
115
|
}),
|
|
113
|
-
err: (e) =>
|
|
116
|
+
err: (e) => Cv.error({
|
|
117
|
+
code: "OAUTH_PROVIDER_ERROR",
|
|
118
|
+
message: `SAML response parse failed: ${e instanceof Error ? e.message : String(e)}`
|
|
119
|
+
})
|
|
114
120
|
});
|
|
115
121
|
yield* Fx.from({
|
|
116
122
|
ok: () => {
|
|
@@ -124,7 +130,10 @@ function addEnterpriseHttpRuntime(deps) {
|
|
|
124
130
|
});
|
|
125
131
|
return Promise.resolve();
|
|
126
132
|
},
|
|
127
|
-
err: () =>
|
|
133
|
+
err: () => Cv.error({
|
|
134
|
+
code: "OAUTH_INVALID_STATE",
|
|
135
|
+
message: "SAML RelayState did not match the pending login request."
|
|
136
|
+
})
|
|
128
137
|
});
|
|
129
138
|
const { samlAttributes, samlSessionIndex, ...userProfile } = profileFromSamlExtract(parsedResponse.parsed.extract, saml.attributeMapping);
|
|
130
139
|
const profile = userProfile;
|
|
@@ -163,7 +172,10 @@ function addEnterpriseHttpRuntime(deps) {
|
|
|
163
172
|
});
|
|
164
173
|
}).pipe(Fx.recover((e) => Fx.fatal(e))));
|
|
165
174
|
const handleSamlSlo = async (ctx, request, runtimeRoute) => {
|
|
166
|
-
if (runtimeRoute.protocol !== "saml" || runtimeRoute.rest.length !== 1 || runtimeRoute.rest[0] !== "slo") throw
|
|
175
|
+
if (runtimeRoute.protocol !== "saml" || runtimeRoute.rest.length !== 1 || runtimeRoute.rest[0] !== "slo") throw Cv.error({
|
|
176
|
+
code: "INVALID_PARAMETERS",
|
|
177
|
+
message: "Invalid enterprise runtime path."
|
|
178
|
+
});
|
|
167
179
|
const { loaded, enterprise } = await loadActiveEnterpriseSamlOrThrow(ctx, runtimeRoute.enterpriseId);
|
|
168
180
|
const parsedMessage = await parseEnterpriseSamlLogoutMessage({
|
|
169
181
|
request,
|
|
@@ -188,7 +200,10 @@ function addEnterpriseHttpRuntime(deps) {
|
|
|
188
200
|
});
|
|
189
201
|
}
|
|
190
202
|
if (parsedMessage.hasSamlResponse) return new Response(null, { status: 204 });
|
|
191
|
-
throw
|
|
203
|
+
throw Cv.error({
|
|
204
|
+
code: "INVALID_PARAMETERS",
|
|
205
|
+
message: "Missing SAML logout payload."
|
|
206
|
+
});
|
|
192
207
|
};
|
|
193
208
|
const handleScimRequest = async (ctx, request) => {
|
|
194
209
|
try {
|
|
@@ -344,7 +359,7 @@ function addEnterpriseHttpRuntime(deps) {
|
|
|
344
359
|
userId,
|
|
345
360
|
data: patchData
|
|
346
361
|
});
|
|
347
|
-
const resolution = await auth.member.
|
|
362
|
+
const resolution = await auth.member.inspect(state$1.ctx, {
|
|
348
363
|
groupId: state$1.enterprise.groupId,
|
|
349
364
|
userId
|
|
350
365
|
});
|
|
@@ -377,7 +392,7 @@ function addEnterpriseHttpRuntime(deps) {
|
|
|
377
392
|
const missing = requireScimResourceId(state$1.parsedPath.resourceId, "User");
|
|
378
393
|
if (missing) return missing;
|
|
379
394
|
const userId = state$1.parsedPath.resourceId;
|
|
380
|
-
const resolution = await auth.member.
|
|
395
|
+
const resolution = await auth.member.inspect(state$1.ctx, {
|
|
381
396
|
groupId: state$1.enterprise.groupId,
|
|
382
397
|
userId
|
|
383
398
|
});
|
|
@@ -531,7 +546,7 @@ function addEnterpriseHttpRuntime(deps) {
|
|
|
531
546
|
if (typeof operation.path === "string" && operation.op === "remove" && operation.path.startsWith("members[")) {
|
|
532
547
|
const userId = operation.path.match(/^members\[value eq "([^"]+)"\]$/)?.[1];
|
|
533
548
|
if (userId) {
|
|
534
|
-
const resolution = await auth.member.
|
|
549
|
+
const resolution = await auth.member.inspect(state$1.ctx, {
|
|
535
550
|
groupId,
|
|
536
551
|
userId
|
|
537
552
|
});
|
|
@@ -613,7 +628,7 @@ function addEnterpriseHttpRuntime(deps) {
|
|
|
613
628
|
return handler ? await handler(state) : scimError(404, "notFound", "SCIM resource not found.");
|
|
614
629
|
} catch (error) {
|
|
615
630
|
if (error instanceof Error && error.message === "Unsupported SCIM filter.") return scimError(400, "invalidFilter", error.message);
|
|
616
|
-
if (
|
|
631
|
+
if (error instanceof ConvexError && typeof error.data === "object" && error.data !== null && "code" in error.data && "message" in error.data) {
|
|
617
632
|
const code = error.data.code;
|
|
618
633
|
return scimError(code === "MISSING_BEARER_TOKEN" || code === "INVALID_API_KEY" ? 401 : 400, code, error.data.message);
|
|
619
634
|
}
|
|
@@ -637,7 +652,10 @@ function addEnterpriseHttpRuntime(deps) {
|
|
|
637
652
|
handleSamlSignIn: async (ctx, request, runtimeRoute) => {
|
|
638
653
|
const url = new URL(request.url);
|
|
639
654
|
const verifier = url.searchParams.get("code");
|
|
640
|
-
if (!verifier) throw
|
|
655
|
+
if (!verifier) throw Cv.error({
|
|
656
|
+
code: "OAUTH_MISSING_VERIFIER",
|
|
657
|
+
message: "Missing sign-in verifier."
|
|
658
|
+
});
|
|
641
659
|
const { loaded, enterprise } = await loadActiveEnterpriseSamlOrThrow(ctx, runtimeRoute.enterpriseId);
|
|
642
660
|
const state = generateRandomString(24, INVITE_TOKEN_ALPHABET);
|
|
643
661
|
const signInRequest = createEnterpriseSamlSignInRequest({
|
|
@@ -690,7 +708,10 @@ function addEnterpriseHttpRuntime(deps) {
|
|
|
690
708
|
handleOidcSignIn: async (ctx, request, runtimeRoute) => {
|
|
691
709
|
const url = new URL(request.url);
|
|
692
710
|
const verifier = url.searchParams.get("code");
|
|
693
|
-
if (!verifier) throw
|
|
711
|
+
if (!verifier) throw Cv.error({
|
|
712
|
+
code: "OAUTH_MISSING_VERIFIER",
|
|
713
|
+
message: "Missing sign-in verifier."
|
|
714
|
+
});
|
|
694
715
|
const { enterprise, oidc } = await loadEnterpriseOidcOrThrow(ctx, runtimeRoute.enterpriseId);
|
|
695
716
|
const { providerId, provider, oauthConfig } = await createEnterpriseOidcRuntime({
|
|
696
717
|
rootUrl: requireEnv("CONVEX_SITE_URL"),
|