@robelest/convex-auth 0.0.4-preview.13 → 0.0.4-preview.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +140 -9
- package/dist/bin.cjs +5957 -5478
- package/dist/client/index.d.ts +3 -7
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +27 -26
- package/dist/client/index.js.map +1 -1
- package/dist/component/_generated/api.d.ts +14 -0
- package/dist/component/_generated/api.d.ts.map +1 -1
- package/dist/component/_generated/api.js.map +1 -1
- package/dist/component/_generated/component.d.ts +1672 -24
- package/dist/component/_generated/component.d.ts.map +1 -1
- package/dist/component/convex.config.d.ts +2 -2
- package/dist/component/convex.config.d.ts.map +1 -1
- package/dist/component/index.d.ts +1 -1
- package/dist/component/index.js +2 -2
- package/dist/component/model.d.ts +153 -0
- package/dist/component/model.d.ts.map +1 -0
- package/dist/component/model.js +343 -0
- package/dist/component/model.js.map +1 -0
- package/dist/component/providers/sso.d.ts +1 -1
- package/dist/component/public/enterprise.d.ts +54 -0
- package/dist/component/public/enterprise.d.ts.map +1 -0
- package/dist/component/public/enterprise.js +515 -0
- package/dist/component/public/enterprise.js.map +1 -0
- package/dist/component/public/factors.d.ts +52 -0
- package/dist/component/public/factors.d.ts.map +1 -0
- package/dist/component/public/factors.js +285 -0
- package/dist/component/public/factors.js.map +1 -0
- package/dist/component/public/groups.d.ts +116 -0
- package/dist/component/public/groups.d.ts.map +1 -0
- package/dist/component/public/groups.js +596 -0
- package/dist/component/public/groups.js.map +1 -0
- package/dist/component/public/identity.d.ts +93 -0
- package/dist/component/public/identity.d.ts.map +1 -0
- package/dist/component/public/identity.js +426 -0
- package/dist/component/public/identity.js.map +1 -0
- package/dist/component/public/keys.d.ts +41 -0
- package/dist/component/public/keys.d.ts.map +1 -0
- package/dist/component/public/keys.js +157 -0
- package/dist/component/public/keys.js.map +1 -0
- package/dist/component/public/shared.d.ts +26 -0
- package/dist/component/public/shared.d.ts.map +1 -0
- package/dist/component/public/shared.js +32 -0
- package/dist/component/public/shared.js.map +1 -0
- package/dist/component/public.d.ts +9 -321
- package/dist/component/public.d.ts.map +1 -1
- package/dist/component/public.js +6 -2145
- package/dist/component/schema.d.ts +406 -260
- package/dist/component/schema.js +37 -32
- package/dist/component/schema.js.map +1 -1
- package/dist/component/server/auth.d.ts +161 -15
- package/dist/component/server/auth.d.ts.map +1 -1
- package/dist/component/server/auth.js +100 -7
- package/dist/component/server/auth.js.map +1 -1
- package/dist/component/server/cookies.js +3 -0
- package/dist/component/server/cookies.js.map +1 -1
- package/dist/component/server/db.js +1 -0
- package/dist/component/server/db.js.map +1 -1
- package/dist/component/server/device.js +3 -1
- package/dist/component/server/device.js.map +1 -1
- package/dist/component/server/domains/core.js +629 -0
- package/dist/component/server/domains/core.js.map +1 -0
- package/dist/component/server/domains/sso.js +884 -0
- package/dist/component/server/domains/sso.js.map +1 -0
- package/dist/component/server/factory.d.ts +136 -0
- package/dist/component/server/factory.d.ts.map +1 -0
- package/dist/component/server/factory.js +1134 -0
- package/dist/component/server/factory.js.map +1 -0
- package/dist/component/server/fx.js +2 -1
- package/dist/component/server/fx.js.map +1 -1
- package/dist/component/server/http.js +287 -0
- package/dist/component/server/http.js.map +1 -0
- package/dist/component/server/identity.js +13 -0
- package/dist/component/server/identity.js.map +1 -0
- package/dist/component/server/keys.js +4 -0
- package/dist/component/server/keys.js.map +1 -1
- package/dist/component/server/mutations/account.js +1 -1
- package/dist/component/server/mutations/index.js +2 -2
- package/dist/component/server/mutations/index.js.map +1 -1
- package/dist/component/server/mutations/invalidate.js +1 -1
- package/dist/component/server/mutations/oauth.js +10 -7
- package/dist/component/server/mutations/oauth.js.map +1 -1
- package/dist/component/server/mutations/refresh.js +1 -1
- package/dist/component/server/mutations/register.js +1 -1
- package/dist/component/server/mutations/retrieve.js +1 -1
- package/dist/component/server/mutations/signature.js +1 -1
- package/dist/component/server/mutations/store.js +6 -3
- package/dist/component/server/mutations/store.js.map +1 -1
- package/dist/component/server/mutations/verify.js +1 -1
- package/dist/component/server/oauth.js +3 -0
- package/dist/component/server/oauth.js.map +1 -1
- package/dist/component/server/passkey.js +3 -2
- package/dist/component/server/passkey.js.map +1 -1
- package/dist/component/server/provider.js +2 -0
- package/dist/component/server/provider.js.map +1 -1
- package/dist/component/server/providers.js +10 -0
- package/dist/component/server/providers.js.map +1 -1
- package/dist/component/server/ratelimit.js +3 -0
- package/dist/component/server/ratelimit.js.map +1 -1
- package/dist/component/server/redirects.js +2 -0
- package/dist/component/server/redirects.js.map +1 -1
- package/dist/component/server/refresh.js +5 -0
- package/dist/component/server/refresh.js.map +1 -1
- package/dist/component/server/sessions.js +5 -0
- package/dist/component/server/sessions.js.map +1 -1
- package/dist/component/server/signin.js +2 -1
- package/dist/component/server/signin.js.map +1 -1
- package/dist/component/server/sso.js +166 -19
- package/dist/component/server/sso.js.map +1 -1
- package/dist/component/server/tokens.js +1 -0
- package/dist/component/server/tokens.js.map +1 -1
- package/dist/component/server/totp.js +4 -2
- package/dist/component/server/totp.js.map +1 -1
- package/dist/component/server/types.d.ts +106 -38
- package/dist/component/server/types.d.ts.map +1 -1
- package/dist/component/server/types.js.map +1 -1
- package/dist/component/server/users.js +1 -0
- package/dist/component/server/users.js.map +1 -1
- package/dist/component/server/utils.js +44 -2
- package/dist/component/server/utils.js.map +1 -1
- package/dist/providers/anonymous.d.ts +1 -1
- package/dist/providers/credentials.d.ts +1 -1
- package/dist/providers/password.d.ts +1 -1
- package/dist/providers/sso.d.ts +1 -1
- package/dist/providers/sso.js.map +1 -1
- package/dist/server/auth.d.ts +163 -17
- package/dist/server/auth.d.ts.map +1 -1
- package/dist/server/auth.js +100 -7
- package/dist/server/auth.js.map +1 -1
- package/dist/server/cookies.d.ts +1 -38
- package/dist/server/cookies.js +3 -0
- package/dist/server/cookies.js.map +1 -1
- package/dist/server/db.d.ts +1 -125
- package/dist/server/db.js +1 -0
- package/dist/server/db.js.map +1 -1
- package/dist/server/device.d.ts +1 -24
- package/dist/server/device.js +3 -1
- package/dist/server/device.js.map +1 -1
- package/dist/server/domains/core.d.ts +434 -0
- package/dist/server/domains/core.d.ts.map +1 -0
- package/dist/server/domains/core.js +629 -0
- package/dist/server/domains/core.js.map +1 -0
- package/dist/server/domains/sso.d.ts +409 -0
- package/dist/server/domains/sso.d.ts.map +1 -0
- package/dist/server/domains/sso.js +884 -0
- package/dist/server/domains/sso.js.map +1 -0
- package/dist/server/enterpriseValidators.d.ts +1 -0
- package/dist/server/enterpriseValidators.js +60 -0
- package/dist/server/enterpriseValidators.js.map +1 -0
- package/dist/server/factory.d.ts +136 -0
- package/dist/server/factory.d.ts.map +1 -0
- package/dist/server/factory.js +1134 -0
- package/dist/server/factory.js.map +1 -0
- package/dist/server/fx.d.ts +1 -16
- package/dist/server/fx.d.ts.map +1 -1
- package/dist/server/fx.js +1 -0
- package/dist/server/fx.js.map +1 -1
- package/dist/server/http.d.ts +59 -0
- package/dist/server/http.d.ts.map +1 -0
- package/dist/server/http.js +287 -0
- package/dist/server/http.js.map +1 -0
- package/dist/server/identity.d.ts +1 -0
- package/dist/server/identity.js +13 -0
- package/dist/server/identity.js.map +1 -0
- package/dist/server/index.d.ts +468 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +530 -36
- package/dist/server/index.js.map +1 -1
- package/dist/server/keys.d.ts +1 -57
- package/dist/server/keys.js +4 -0
- package/dist/server/keys.js.map +1 -1
- package/dist/server/mutations/account.d.ts +7 -7
- package/dist/server/mutations/account.d.ts.map +1 -1
- package/dist/server/mutations/code.d.ts +13 -13
- package/dist/server/mutations/code.d.ts.map +1 -1
- package/dist/server/mutations/index.d.ts +107 -107
- package/dist/server/mutations/index.d.ts.map +1 -1
- package/dist/server/mutations/index.js +1 -1
- package/dist/server/mutations/index.js.map +1 -1
- package/dist/server/mutations/invalidate.d.ts +5 -5
- package/dist/server/mutations/invalidate.d.ts.map +1 -1
- package/dist/server/mutations/oauth.d.ts +10 -10
- package/dist/server/mutations/oauth.d.ts.map +1 -1
- package/dist/server/mutations/oauth.js +9 -6
- package/dist/server/mutations/oauth.js.map +1 -1
- package/dist/server/mutations/refresh.d.ts +4 -4
- package/dist/server/mutations/register.d.ts +12 -12
- package/dist/server/mutations/register.d.ts.map +1 -1
- package/dist/server/mutations/retrieve.d.ts +7 -7
- package/dist/server/mutations/signature.d.ts +5 -5
- package/dist/server/mutations/signin.d.ts +6 -6
- package/dist/server/mutations/signin.d.ts.map +1 -1
- package/dist/server/mutations/signout.d.ts +1 -1
- package/dist/server/mutations/store.d.ts +3 -2
- package/dist/server/mutations/store.d.ts.map +1 -1
- package/dist/server/mutations/store.js +6 -3
- package/dist/server/mutations/store.js.map +1 -1
- package/dist/server/mutations/verifier.d.ts +1 -1
- package/dist/server/mutations/verify.d.ts +11 -11
- package/dist/server/mutations/verify.d.ts.map +1 -1
- package/dist/server/oauth.d.ts +1 -59
- package/dist/server/oauth.js +3 -0
- package/dist/server/oauth.js.map +1 -1
- package/dist/server/passkey.d.ts.map +1 -1
- package/dist/server/passkey.js +3 -2
- package/dist/server/passkey.js.map +1 -1
- package/dist/server/provider.d.ts +1 -14
- package/dist/server/provider.d.ts.map +1 -1
- package/dist/server/provider.js +2 -0
- package/dist/server/provider.js.map +1 -1
- package/dist/server/providers.js +10 -0
- package/dist/server/providers.js.map +1 -1
- package/dist/server/ratelimit.d.ts +1 -22
- package/dist/server/ratelimit.js +3 -0
- package/dist/server/ratelimit.js.map +1 -1
- package/dist/server/redirects.d.ts +1 -10
- package/dist/server/redirects.js +2 -0
- package/dist/server/redirects.js.map +1 -1
- package/dist/server/refresh.d.ts +1 -37
- package/dist/server/refresh.js +5 -0
- package/dist/server/refresh.js.map +1 -1
- package/dist/server/sessions.d.ts +1 -28
- package/dist/server/sessions.js +5 -0
- package/dist/server/sessions.js.map +1 -1
- package/dist/server/signin.d.ts +1 -55
- package/dist/server/signin.js +2 -1
- package/dist/server/signin.js.map +1 -1
- package/dist/server/sso.d.ts +1 -348
- package/dist/server/sso.js +165 -18
- package/dist/server/sso.js.map +1 -1
- package/dist/server/templates.d.ts +1 -21
- package/dist/server/templates.js +1 -0
- package/dist/server/templates.js.map +1 -1
- package/dist/server/tokens.d.ts +1 -11
- package/dist/server/tokens.js +1 -0
- package/dist/server/tokens.js.map +1 -1
- package/dist/server/totp.d.ts +1 -23
- package/dist/server/totp.js +4 -2
- package/dist/server/totp.js.map +1 -1
- package/dist/server/types.d.ts +114 -77
- package/dist/server/types.d.ts.map +1 -1
- package/dist/server/types.js.map +1 -1
- package/dist/server/users.d.ts +1 -31
- package/dist/server/users.js +1 -0
- package/dist/server/users.js.map +1 -1
- package/dist/server/utils.d.ts +1 -27
- package/dist/server/utils.js +44 -2
- package/dist/server/utils.js.map +1 -1
- package/dist/server/version.d.ts +1 -1
- package/dist/server/version.js +1 -1
- package/dist/server/version.js.map +1 -1
- package/package.json +4 -5
- package/src/cli/bin.ts +5 -0
- package/src/cli/index.ts +22 -9
- package/src/cli/keys.ts +3 -0
- package/src/client/index.ts +36 -37
- package/src/component/_generated/api.ts +14 -0
- package/src/component/_generated/component.ts +2106 -9
- package/src/component/index.ts +3 -1
- package/src/component/model.ts +441 -0
- package/src/component/public/enterprise.ts +753 -0
- package/src/component/public/factors.ts +332 -0
- package/src/component/public/groups.ts +932 -0
- package/src/component/public/identity.ts +566 -0
- package/src/component/public/keys.ts +209 -0
- package/src/component/public/shared.ts +119 -0
- package/src/component/public.ts +5 -2965
- package/src/component/schema.ts +68 -63
- package/src/providers/sso.ts +1 -1
- package/src/server/auth.ts +413 -18
- package/src/server/cookies.ts +3 -0
- package/src/server/db.ts +3 -0
- package/src/server/device.ts +3 -1
- package/src/server/domains/core.ts +1071 -0
- package/src/server/domains/sso.ts +1749 -0
- package/src/server/enterpriseValidators.ts +93 -0
- package/src/server/factory.ts +2181 -0
- package/src/server/fx.ts +1 -0
- package/src/server/http.ts +529 -0
- package/src/server/identity.ts +18 -0
- package/src/server/index.ts +806 -40
- package/src/server/keys.ts +4 -0
- package/src/server/mutations/index.ts +1 -1
- package/src/server/mutations/oauth.ts +36 -8
- package/src/server/mutations/store.ts +6 -3
- package/src/server/oauth.ts +6 -0
- package/src/server/passkey.ts +3 -2
- package/src/server/provider.ts +2 -0
- package/src/server/providers.ts +20 -0
- package/src/server/ratelimit.ts +3 -0
- package/src/server/redirects.ts +2 -0
- package/src/server/refresh.ts +5 -0
- package/src/server/sessions.ts +5 -0
- package/src/server/signin.ts +1 -0
- package/src/server/sso.ts +259 -17
- package/src/server/templates.ts +1 -0
- package/src/server/tokens.ts +1 -0
- package/src/server/totp.ts +4 -2
- package/src/server/types.ts +178 -83
- package/src/server/users.ts +1 -0
- package/src/server/utils.ts +71 -1
- package/src/server/version.ts +1 -1
- package/dist/component/public.js.map +0 -1
- package/dist/component/server/implementation.d.ts +0 -1264
- package/dist/component/server/implementation.d.ts.map +0 -1
- package/dist/component/server/implementation.js +0 -2365
- package/dist/component/server/implementation.js.map +0 -1
- package/dist/server/cookies.d.ts.map +0 -1
- package/dist/server/db.d.ts.map +0 -1
- package/dist/server/device.d.ts.map +0 -1
- package/dist/server/implementation.d.ts +0 -1264
- package/dist/server/implementation.d.ts.map +0 -1
- package/dist/server/implementation.js +0 -2365
- package/dist/server/implementation.js.map +0 -1
- package/dist/server/keys.d.ts.map +0 -1
- package/dist/server/oauth.d.ts.map +0 -1
- package/dist/server/ratelimit.d.ts.map +0 -1
- package/dist/server/redirects.d.ts.map +0 -1
- package/dist/server/refresh.d.ts.map +0 -1
- package/dist/server/sessions.d.ts.map +0 -1
- package/dist/server/signin.d.ts.map +0 -1
- package/dist/server/sso.d.ts.map +0 -1
- package/dist/server/templates.d.ts.map +0 -1
- package/dist/server/tokens.d.ts.map +0 -1
- package/dist/server/totp.d.ts.map +0 -1
- package/dist/server/users.d.ts.map +0 -1
- package/dist/server/utils.d.ts.map +0 -1
- package/src/server/implementation.ts +0 -5336
package/src/server/index.ts
CHANGED
|
@@ -1,21 +1,787 @@
|
|
|
1
1
|
import { ConvexHttpClient } from "convex/browser";
|
|
2
|
-
import {
|
|
3
|
-
|
|
2
|
+
import {
|
|
3
|
+
actionGeneric,
|
|
4
|
+
makeFunctionReference,
|
|
5
|
+
mutationGeneric,
|
|
6
|
+
queryGeneric,
|
|
7
|
+
} from "convex/server";
|
|
8
|
+
import { ConvexError, v } from "convex/values";
|
|
4
9
|
import { parse, serialize } from "cookie";
|
|
5
10
|
import { jwtDecode } from "jwt-decode";
|
|
6
11
|
|
|
7
|
-
import {
|
|
12
|
+
import type { AuthApi } from "./auth";
|
|
13
|
+
import {
|
|
14
|
+
enterpriseConnectionWhereValidator,
|
|
15
|
+
enterpriseDomainInputValidator,
|
|
16
|
+
enterpriseDomainVerificationInputValidator,
|
|
17
|
+
enterprisePolicyPatchValidator,
|
|
18
|
+
enterpriseSamlAttributeMappingValidator,
|
|
19
|
+
enterpriseSamlSpValidator,
|
|
20
|
+
enterpriseStatusValidator,
|
|
21
|
+
} from "./enterpriseValidators";
|
|
8
22
|
import type {
|
|
9
23
|
SignInAction,
|
|
10
24
|
SignInActionResult,
|
|
11
25
|
SignOutAction,
|
|
12
|
-
} from "./
|
|
26
|
+
} from "./factory";
|
|
27
|
+
import { Fx } from "./fx";
|
|
28
|
+
import type { AuthAuthorizationConfig, AuthRoleId } from "./types";
|
|
13
29
|
import { isLocalHost } from "./utils";
|
|
14
30
|
|
|
15
|
-
const signInActionRef: SignInAction =
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
31
|
+
const signInActionRef: SignInAction = makeFunctionReference("auth:signIn");
|
|
32
|
+
const signOutActionRef: SignOutAction = makeFunctionReference("auth:signOut");
|
|
33
|
+
|
|
34
|
+
export type EnterpriseAdminPermission =
|
|
35
|
+
| "sso.connection.create"
|
|
36
|
+
| "sso.connection.read"
|
|
37
|
+
| "sso.connection.manage"
|
|
38
|
+
| "sso.domain.manage"
|
|
39
|
+
| "sso.protocol.manage"
|
|
40
|
+
| "sso.policy.manage"
|
|
41
|
+
| "sso.audit.read"
|
|
42
|
+
| "sso.webhook.manage"
|
|
43
|
+
| "scim.manage";
|
|
44
|
+
|
|
45
|
+
export type EnterpriseAdminAuthorizationInput = {
|
|
46
|
+
userId: string;
|
|
47
|
+
permission: EnterpriseAdminPermission;
|
|
48
|
+
enterpriseId?: string;
|
|
49
|
+
groupId?: string;
|
|
50
|
+
resolvedGroupId: string | null;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export type EnterpriseAuthorizer = (
|
|
54
|
+
ctx: { auth: import("convex/server").Auth },
|
|
55
|
+
input: EnterpriseAdminAuthorizationInput,
|
|
56
|
+
) => Promise<void>;
|
|
57
|
+
|
|
58
|
+
type RoleRef<TRoleId extends string> = { id: TRoleId };
|
|
59
|
+
|
|
60
|
+
type MountedEnterpriseOptions<TRoleId extends string = string> = {
|
|
61
|
+
admin?: {
|
|
62
|
+
authorized?: EnterpriseAuthorizer;
|
|
63
|
+
roles?: Array<TRoleId | RoleRef<TRoleId>>;
|
|
64
|
+
};
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
export type EnterpriseMountOptions<TRoleId extends string = string> = {
|
|
68
|
+
admin: {
|
|
69
|
+
authorized: EnterpriseAuthorizer;
|
|
70
|
+
roles?: Array<TRoleId | RoleRef<TRoleId>>;
|
|
71
|
+
};
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
type MountedEnterpriseTarget = {
|
|
75
|
+
enterpriseId?: string;
|
|
76
|
+
groupId?: string;
|
|
77
|
+
domain?: string;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
function requireSignedInUser(auth: Pick<AuthApi, "user">) {
|
|
81
|
+
return async (ctx: { auth: import("convex/server").Auth }) => {
|
|
82
|
+
return await auth.user.require(ctx as never);
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function normalizeCreatorRoleIds<TRoleId extends string>(
|
|
87
|
+
roles?: Array<TRoleId | RoleRef<TRoleId>>,
|
|
88
|
+
) {
|
|
89
|
+
return roles?.map((role) => (typeof role === "string" ? role : role.id));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async function resolveMountedEnterpriseTarget(
|
|
93
|
+
auth: Pick<AuthApi, "sso">,
|
|
94
|
+
ctx: { auth: import("convex/server").Auth },
|
|
95
|
+
target: MountedEnterpriseTarget,
|
|
96
|
+
) {
|
|
97
|
+
if (target.groupId !== undefined) {
|
|
98
|
+
return {
|
|
99
|
+
enterpriseId: target.enterpriseId,
|
|
100
|
+
groupId: target.groupId,
|
|
101
|
+
resolvedGroupId: target.groupId,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (target.enterpriseId !== undefined) {
|
|
106
|
+
const enterprise = await auth.sso.admin.connection.get(
|
|
107
|
+
ctx as never,
|
|
108
|
+
target.enterpriseId,
|
|
109
|
+
);
|
|
110
|
+
if (enterprise === null) {
|
|
111
|
+
throw new ConvexError({
|
|
112
|
+
code: "INVALID_PARAMETERS",
|
|
113
|
+
message: "Enterprise not found.",
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
return {
|
|
117
|
+
enterpriseId: enterprise._id,
|
|
118
|
+
groupId: enterprise.groupId,
|
|
119
|
+
resolvedGroupId: enterprise.groupId,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (target.domain !== undefined) {
|
|
124
|
+
const resolved = await auth.sso.admin.connection.getByDomain(
|
|
125
|
+
ctx as never,
|
|
126
|
+
target.domain,
|
|
127
|
+
);
|
|
128
|
+
if (resolved?.enterprise === undefined) {
|
|
129
|
+
throw new ConvexError({
|
|
130
|
+
code: "INVALID_PARAMETERS",
|
|
131
|
+
message: "Enterprise not found.",
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
return {
|
|
135
|
+
enterpriseId: resolved.enterprise._id,
|
|
136
|
+
groupId: resolved.enterprise.groupId,
|
|
137
|
+
resolvedGroupId: resolved.enterprise.groupId,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return {
|
|
142
|
+
enterpriseId: undefined,
|
|
143
|
+
groupId: undefined,
|
|
144
|
+
resolvedGroupId: null,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function createMountedAdminAuthorizer(
|
|
149
|
+
auth: Pick<AuthApi, "sso" | "user">,
|
|
150
|
+
options?: MountedEnterpriseOptions,
|
|
151
|
+
) {
|
|
152
|
+
const requireUserId = requireSignedInUser(auth);
|
|
153
|
+
|
|
154
|
+
return async (
|
|
155
|
+
ctx: { auth: import("convex/server").Auth },
|
|
156
|
+
permission: EnterpriseAdminPermission,
|
|
157
|
+
target: MountedEnterpriseTarget = {},
|
|
158
|
+
) => {
|
|
159
|
+
const userId = await requireUserId(ctx);
|
|
160
|
+
if (!options?.admin?.authorized) {
|
|
161
|
+
throw new ConvexError({
|
|
162
|
+
code: "FORBIDDEN",
|
|
163
|
+
message:
|
|
164
|
+
"Mounted enterprise admin APIs require an authorized callback.",
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
const resolved = await resolveMountedEnterpriseTarget(auth, ctx, target);
|
|
168
|
+
await options.admin.authorized(ctx, {
|
|
169
|
+
userId,
|
|
170
|
+
permission,
|
|
171
|
+
enterpriseId: resolved.enterpriseId,
|
|
172
|
+
groupId: resolved.groupId,
|
|
173
|
+
resolvedGroupId: resolved.resolvedGroupId,
|
|
174
|
+
});
|
|
175
|
+
return { userId, ...resolved };
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Build optional public SSO management actions that apps can mount under
|
|
181
|
+
* `convex/auth/sso/**` when they want client-callable enterprise APIs.
|
|
182
|
+
*
|
|
183
|
+
* `admin` is for tenant-admin control-plane operations and should be mounted
|
|
184
|
+
* with an explicit authorization policy. `client` is for end-user sign-in
|
|
185
|
+
* helpers and does not require tenant-admin authorization.
|
|
186
|
+
*/
|
|
187
|
+
export function sso<
|
|
188
|
+
TAuthorization extends AuthAuthorizationConfig | undefined = undefined,
|
|
189
|
+
>(
|
|
190
|
+
auth: Pick<AuthApi<TAuthorization>, "group" | "member" | "sso" | "user">,
|
|
191
|
+
options?: MountedEnterpriseOptions<AuthRoleId<TAuthorization>>,
|
|
192
|
+
) {
|
|
193
|
+
const authorize = createMountedAdminAuthorizer(auth, options);
|
|
194
|
+
const adminRoleIds = normalizeCreatorRoleIds(options?.admin?.roles);
|
|
195
|
+
|
|
196
|
+
return {
|
|
197
|
+
admin: {
|
|
198
|
+
connection: {
|
|
199
|
+
create: mutationGeneric({
|
|
200
|
+
args: {
|
|
201
|
+
groupId: v.optional(v.string()),
|
|
202
|
+
name: v.optional(v.string()),
|
|
203
|
+
slug: v.optional(v.string()),
|
|
204
|
+
status: v.optional(enterpriseStatusValidator),
|
|
205
|
+
domain: v.optional(v.string()),
|
|
206
|
+
},
|
|
207
|
+
handler: async (ctx, args) => {
|
|
208
|
+
const { userId } = await authorize(ctx, "sso.connection.create", {
|
|
209
|
+
groupId: args.groupId,
|
|
210
|
+
});
|
|
211
|
+
const createsGroup = args.groupId === undefined;
|
|
212
|
+
const groupId =
|
|
213
|
+
args.groupId ??
|
|
214
|
+
(
|
|
215
|
+
await auth.group.create(ctx as never, {
|
|
216
|
+
name: args.name?.trim() || args.slug?.trim() || "Enterprise",
|
|
217
|
+
slug: args.slug,
|
|
218
|
+
type: "enterprise",
|
|
219
|
+
})
|
|
220
|
+
).groupId;
|
|
221
|
+
if (createsGroup) {
|
|
222
|
+
await auth.member.create(ctx as never, {
|
|
223
|
+
groupId,
|
|
224
|
+
userId,
|
|
225
|
+
roleIds: adminRoleIds,
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
const created = await auth.sso.admin.connection.create(
|
|
229
|
+
ctx as never,
|
|
230
|
+
{
|
|
231
|
+
groupId,
|
|
232
|
+
name: args.name,
|
|
233
|
+
slug: args.slug,
|
|
234
|
+
status: args.status,
|
|
235
|
+
},
|
|
236
|
+
);
|
|
237
|
+
if (args.domain) {
|
|
238
|
+
await auth.sso.admin.connection.domain.set(
|
|
239
|
+
ctx as never,
|
|
240
|
+
created.enterpriseId,
|
|
241
|
+
[{ domain: args.domain, isPrimary: true }],
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
return {
|
|
245
|
+
...created,
|
|
246
|
+
groupId,
|
|
247
|
+
createdGroup: createsGroup,
|
|
248
|
+
};
|
|
249
|
+
},
|
|
250
|
+
}),
|
|
251
|
+
get: queryGeneric({
|
|
252
|
+
args: { enterpriseId: v.string() },
|
|
253
|
+
handler: async (ctx, args) => {
|
|
254
|
+
await authorize(ctx, "sso.connection.read", {
|
|
255
|
+
enterpriseId: args.enterpriseId,
|
|
256
|
+
});
|
|
257
|
+
return await auth.sso.admin.connection.get(
|
|
258
|
+
ctx as never,
|
|
259
|
+
args.enterpriseId,
|
|
260
|
+
);
|
|
261
|
+
},
|
|
262
|
+
}),
|
|
263
|
+
getByGroup: queryGeneric({
|
|
264
|
+
args: { groupId: v.string() },
|
|
265
|
+
handler: async (ctx, args) => {
|
|
266
|
+
await authorize(ctx, "sso.connection.read", {
|
|
267
|
+
groupId: args.groupId,
|
|
268
|
+
});
|
|
269
|
+
return await auth.sso.admin.connection.getByGroup(
|
|
270
|
+
ctx as never,
|
|
271
|
+
args.groupId,
|
|
272
|
+
);
|
|
273
|
+
},
|
|
274
|
+
}),
|
|
275
|
+
getByDomain: queryGeneric({
|
|
276
|
+
args: { domain: v.string() },
|
|
277
|
+
handler: async (ctx, args) => {
|
|
278
|
+
await authorize(ctx, "sso.connection.read", {
|
|
279
|
+
domain: args.domain,
|
|
280
|
+
});
|
|
281
|
+
return await auth.sso.admin.connection.getByDomain(
|
|
282
|
+
ctx as never,
|
|
283
|
+
args.domain,
|
|
284
|
+
);
|
|
285
|
+
},
|
|
286
|
+
}),
|
|
287
|
+
list: queryGeneric({
|
|
288
|
+
args: {
|
|
289
|
+
where: v.optional(enterpriseConnectionWhereValidator),
|
|
290
|
+
limit: v.optional(v.number()),
|
|
291
|
+
cursor: v.optional(v.union(v.string(), v.null())),
|
|
292
|
+
orderBy: v.optional(v.string()),
|
|
293
|
+
order: v.optional(v.union(v.literal("asc"), v.literal("desc"))),
|
|
294
|
+
},
|
|
295
|
+
handler: async (ctx, args) => {
|
|
296
|
+
await authorize(ctx, "sso.connection.read", {
|
|
297
|
+
groupId: args.where?.groupId,
|
|
298
|
+
});
|
|
299
|
+
return await auth.sso.admin.connection.list(
|
|
300
|
+
ctx as never,
|
|
301
|
+
args as never,
|
|
302
|
+
);
|
|
303
|
+
},
|
|
304
|
+
}),
|
|
305
|
+
update: mutationGeneric({
|
|
306
|
+
args: {
|
|
307
|
+
enterpriseId: v.string(),
|
|
308
|
+
data: v.object({
|
|
309
|
+
name: v.optional(v.string()),
|
|
310
|
+
slug: v.optional(v.string()),
|
|
311
|
+
status: v.optional(enterpriseStatusValidator),
|
|
312
|
+
}),
|
|
313
|
+
},
|
|
314
|
+
handler: async (ctx, args) => {
|
|
315
|
+
await authorize(ctx, "sso.connection.manage", {
|
|
316
|
+
enterpriseId: args.enterpriseId,
|
|
317
|
+
});
|
|
318
|
+
await auth.sso.admin.connection.update(
|
|
319
|
+
ctx as never,
|
|
320
|
+
args.enterpriseId,
|
|
321
|
+
args.data,
|
|
322
|
+
);
|
|
323
|
+
return { ok: true as const, enterpriseId: args.enterpriseId };
|
|
324
|
+
},
|
|
325
|
+
}),
|
|
326
|
+
delete: mutationGeneric({
|
|
327
|
+
args: { enterpriseId: v.string() },
|
|
328
|
+
handler: async (ctx, args) => {
|
|
329
|
+
await authorize(ctx, "sso.connection.manage", {
|
|
330
|
+
enterpriseId: args.enterpriseId,
|
|
331
|
+
});
|
|
332
|
+
return await auth.sso.admin.connection.delete(
|
|
333
|
+
ctx as never,
|
|
334
|
+
args.enterpriseId,
|
|
335
|
+
);
|
|
336
|
+
},
|
|
337
|
+
}),
|
|
338
|
+
status: queryGeneric({
|
|
339
|
+
args: { enterpriseId: v.string() },
|
|
340
|
+
handler: async (ctx, args) => {
|
|
341
|
+
await authorize(ctx, "sso.connection.read", {
|
|
342
|
+
enterpriseId: args.enterpriseId,
|
|
343
|
+
});
|
|
344
|
+
return await auth.sso.admin.connection.status(
|
|
345
|
+
ctx as never,
|
|
346
|
+
args.enterpriseId,
|
|
347
|
+
);
|
|
348
|
+
},
|
|
349
|
+
}),
|
|
350
|
+
domain: {
|
|
351
|
+
list: queryGeneric({
|
|
352
|
+
args: { enterpriseId: v.string() },
|
|
353
|
+
handler: async (ctx, args) => {
|
|
354
|
+
await authorize(ctx, "sso.connection.read", {
|
|
355
|
+
enterpriseId: args.enterpriseId,
|
|
356
|
+
});
|
|
357
|
+
return await auth.sso.admin.connection.domain.list(
|
|
358
|
+
ctx as never,
|
|
359
|
+
args.enterpriseId,
|
|
360
|
+
);
|
|
361
|
+
},
|
|
362
|
+
}),
|
|
363
|
+
validate: queryGeneric({
|
|
364
|
+
args: { enterpriseId: v.string() },
|
|
365
|
+
handler: async (ctx, args) => {
|
|
366
|
+
await authorize(ctx, "sso.domain.manage", {
|
|
367
|
+
enterpriseId: args.enterpriseId,
|
|
368
|
+
});
|
|
369
|
+
return await auth.sso.admin.connection.domain.validate(
|
|
370
|
+
ctx as never,
|
|
371
|
+
args.enterpriseId,
|
|
372
|
+
);
|
|
373
|
+
},
|
|
374
|
+
}),
|
|
375
|
+
set: mutationGeneric({
|
|
376
|
+
args: {
|
|
377
|
+
enterpriseId: v.string(),
|
|
378
|
+
domains: v.array(enterpriseDomainInputValidator),
|
|
379
|
+
},
|
|
380
|
+
handler: async (ctx, args) => {
|
|
381
|
+
await authorize(ctx, "sso.domain.manage", {
|
|
382
|
+
enterpriseId: args.enterpriseId,
|
|
383
|
+
});
|
|
384
|
+
return await auth.sso.admin.connection.domain.set(
|
|
385
|
+
ctx as never,
|
|
386
|
+
args.enterpriseId,
|
|
387
|
+
args.domains,
|
|
388
|
+
);
|
|
389
|
+
},
|
|
390
|
+
}),
|
|
391
|
+
verification: {
|
|
392
|
+
request: mutationGeneric({
|
|
393
|
+
args: enterpriseDomainVerificationInputValidator,
|
|
394
|
+
handler: async (ctx, args) => {
|
|
395
|
+
await authorize(ctx, "sso.domain.manage", {
|
|
396
|
+
enterpriseId: args.enterpriseId,
|
|
397
|
+
});
|
|
398
|
+
return await auth.sso.admin.connection.domain.verification.request(
|
|
399
|
+
ctx as never,
|
|
400
|
+
args,
|
|
401
|
+
);
|
|
402
|
+
},
|
|
403
|
+
}),
|
|
404
|
+
confirm: actionGeneric({
|
|
405
|
+
args: enterpriseDomainVerificationInputValidator,
|
|
406
|
+
handler: async (ctx, args) => {
|
|
407
|
+
await authorize(ctx, "sso.domain.manage", {
|
|
408
|
+
enterpriseId: args.enterpriseId,
|
|
409
|
+
});
|
|
410
|
+
return await auth.sso.admin.connection.domain.verification.confirm(
|
|
411
|
+
ctx as never,
|
|
412
|
+
args,
|
|
413
|
+
);
|
|
414
|
+
},
|
|
415
|
+
}),
|
|
416
|
+
},
|
|
417
|
+
},
|
|
418
|
+
},
|
|
419
|
+
oidc: {
|
|
420
|
+
configure: mutationGeneric({
|
|
421
|
+
args: {
|
|
422
|
+
enterpriseId: v.string(),
|
|
423
|
+
issuer: v.optional(v.string()),
|
|
424
|
+
discoveryUrl: v.optional(v.string()),
|
|
425
|
+
clientId: v.string(),
|
|
426
|
+
clientSecret: v.optional(v.string()),
|
|
427
|
+
scopes: v.optional(v.array(v.string())),
|
|
428
|
+
authorizationParams: v.optional(v.record(v.string(), v.string())),
|
|
429
|
+
clockToleranceSeconds: v.optional(v.number()),
|
|
430
|
+
strictIssuer: v.optional(v.boolean()),
|
|
431
|
+
extraFields: v.optional(v.record(v.string(), v.string())),
|
|
432
|
+
},
|
|
433
|
+
handler: async (ctx, args) => {
|
|
434
|
+
await authorize(ctx, "sso.protocol.manage", {
|
|
435
|
+
enterpriseId: args.enterpriseId,
|
|
436
|
+
});
|
|
437
|
+
return await auth.sso.admin.oidc.configure(ctx as never, args);
|
|
438
|
+
},
|
|
439
|
+
}),
|
|
440
|
+
get: queryGeneric({
|
|
441
|
+
args: { enterpriseId: v.string() },
|
|
442
|
+
handler: async (ctx, args) => {
|
|
443
|
+
await authorize(ctx, "sso.connection.read", {
|
|
444
|
+
enterpriseId: args.enterpriseId,
|
|
445
|
+
});
|
|
446
|
+
return await auth.sso.admin.oidc.get(
|
|
447
|
+
ctx as never,
|
|
448
|
+
args.enterpriseId,
|
|
449
|
+
);
|
|
450
|
+
},
|
|
451
|
+
}),
|
|
452
|
+
validate: actionGeneric({
|
|
453
|
+
args: { enterpriseId: v.string() },
|
|
454
|
+
handler: async (ctx, args) => {
|
|
455
|
+
await authorize(ctx, "sso.protocol.manage", {
|
|
456
|
+
enterpriseId: args.enterpriseId,
|
|
457
|
+
});
|
|
458
|
+
return await auth.sso.admin.oidc.validate(
|
|
459
|
+
ctx as never,
|
|
460
|
+
args.enterpriseId,
|
|
461
|
+
);
|
|
462
|
+
},
|
|
463
|
+
}),
|
|
464
|
+
},
|
|
465
|
+
saml: {
|
|
466
|
+
configure: actionGeneric({
|
|
467
|
+
args: {
|
|
468
|
+
enterpriseId: v.string(),
|
|
469
|
+
metadataXml: v.optional(v.string()),
|
|
470
|
+
metadataUrl: v.optional(v.string()),
|
|
471
|
+
domains: v.optional(v.array(v.string())),
|
|
472
|
+
signAuthnRequests: v.optional(v.boolean()),
|
|
473
|
+
attributeMapping: v.optional(
|
|
474
|
+
enterpriseSamlAttributeMappingValidator,
|
|
475
|
+
),
|
|
476
|
+
sp: v.optional(enterpriseSamlSpValidator),
|
|
477
|
+
},
|
|
478
|
+
handler: async (ctx, args) => {
|
|
479
|
+
await authorize(ctx, "sso.protocol.manage", {
|
|
480
|
+
enterpriseId: args.enterpriseId,
|
|
481
|
+
});
|
|
482
|
+
return await auth.sso.admin.saml.configure(ctx as never, args);
|
|
483
|
+
},
|
|
484
|
+
}),
|
|
485
|
+
validate: queryGeneric({
|
|
486
|
+
args: { enterpriseId: v.string() },
|
|
487
|
+
handler: async (ctx, args) => {
|
|
488
|
+
await authorize(ctx, "sso.protocol.manage", {
|
|
489
|
+
enterpriseId: args.enterpriseId,
|
|
490
|
+
});
|
|
491
|
+
return await auth.sso.admin.saml.validate(
|
|
492
|
+
ctx as never,
|
|
493
|
+
args.enterpriseId,
|
|
494
|
+
);
|
|
495
|
+
},
|
|
496
|
+
}),
|
|
497
|
+
},
|
|
498
|
+
policy: {
|
|
499
|
+
get: queryGeneric({
|
|
500
|
+
args: { enterpriseId: v.string() },
|
|
501
|
+
handler: async (ctx, args) => {
|
|
502
|
+
await authorize(ctx, "sso.connection.read", {
|
|
503
|
+
enterpriseId: args.enterpriseId,
|
|
504
|
+
});
|
|
505
|
+
return await auth.sso.admin.policy.get(
|
|
506
|
+
ctx as never,
|
|
507
|
+
args.enterpriseId,
|
|
508
|
+
);
|
|
509
|
+
},
|
|
510
|
+
}),
|
|
511
|
+
update: mutationGeneric({
|
|
512
|
+
args: {
|
|
513
|
+
enterpriseId: v.string(),
|
|
514
|
+
patch: enterprisePolicyPatchValidator,
|
|
515
|
+
},
|
|
516
|
+
handler: async (ctx, args) => {
|
|
517
|
+
await authorize(ctx, "sso.policy.manage", {
|
|
518
|
+
enterpriseId: args.enterpriseId,
|
|
519
|
+
});
|
|
520
|
+
return await auth.sso.admin.policy.update(
|
|
521
|
+
ctx as never,
|
|
522
|
+
args.enterpriseId,
|
|
523
|
+
args.patch,
|
|
524
|
+
);
|
|
525
|
+
},
|
|
526
|
+
}),
|
|
527
|
+
validate: queryGeneric({
|
|
528
|
+
args: { enterpriseId: v.string() },
|
|
529
|
+
handler: async (ctx, args) => {
|
|
530
|
+
await authorize(ctx, "sso.policy.manage", {
|
|
531
|
+
enterpriseId: args.enterpriseId,
|
|
532
|
+
});
|
|
533
|
+
return await auth.sso.admin.policy.validate(
|
|
534
|
+
ctx as never,
|
|
535
|
+
args.enterpriseId,
|
|
536
|
+
);
|
|
537
|
+
},
|
|
538
|
+
}),
|
|
539
|
+
},
|
|
540
|
+
audit: {
|
|
541
|
+
list: queryGeneric({
|
|
542
|
+
args: {
|
|
543
|
+
enterpriseId: v.optional(v.string()),
|
|
544
|
+
groupId: v.optional(v.string()),
|
|
545
|
+
limit: v.optional(v.number()),
|
|
546
|
+
},
|
|
547
|
+
handler: async (ctx, args) => {
|
|
548
|
+
await authorize(ctx, "sso.audit.read", {
|
|
549
|
+
enterpriseId: args.enterpriseId,
|
|
550
|
+
groupId: args.groupId,
|
|
551
|
+
});
|
|
552
|
+
return await auth.sso.admin.audit.list(ctx as never, args);
|
|
553
|
+
},
|
|
554
|
+
}),
|
|
555
|
+
},
|
|
556
|
+
webhook: {
|
|
557
|
+
delivery: {
|
|
558
|
+
list: queryGeneric({
|
|
559
|
+
args: {
|
|
560
|
+
enterpriseId: v.string(),
|
|
561
|
+
limit: v.optional(v.number()),
|
|
562
|
+
},
|
|
563
|
+
handler: async (ctx, args) => {
|
|
564
|
+
await authorize(ctx, "sso.webhook.manage", {
|
|
565
|
+
enterpriseId: args.enterpriseId,
|
|
566
|
+
});
|
|
567
|
+
return await (auth.sso.admin.webhook as any).delivery.list(
|
|
568
|
+
ctx as never,
|
|
569
|
+
args,
|
|
570
|
+
);
|
|
571
|
+
},
|
|
572
|
+
}),
|
|
573
|
+
},
|
|
574
|
+
endpoint: {
|
|
575
|
+
create: mutationGeneric({
|
|
576
|
+
args: {
|
|
577
|
+
enterpriseId: v.string(),
|
|
578
|
+
url: v.string(),
|
|
579
|
+
secret: v.string(),
|
|
580
|
+
subscriptions: v.array(v.string()),
|
|
581
|
+
createdByUserId: v.optional(v.string()),
|
|
582
|
+
},
|
|
583
|
+
handler: async (ctx, args) => {
|
|
584
|
+
const { userId } = await authorize(ctx, "sso.webhook.manage", {
|
|
585
|
+
enterpriseId: args.enterpriseId,
|
|
586
|
+
});
|
|
587
|
+
const result = await auth.sso.admin.webhook.endpoint.create(
|
|
588
|
+
ctx as never,
|
|
589
|
+
{
|
|
590
|
+
...args,
|
|
591
|
+
createdByUserId: args.createdByUserId ?? userId,
|
|
592
|
+
},
|
|
593
|
+
);
|
|
594
|
+
return {
|
|
595
|
+
_id: result.endpointId,
|
|
596
|
+
enterpriseId: args.enterpriseId,
|
|
597
|
+
url: args.url,
|
|
598
|
+
subscriptions: args.subscriptions,
|
|
599
|
+
createdByUserId: args.createdByUserId ?? userId,
|
|
600
|
+
status: "active",
|
|
601
|
+
failureCount: 0,
|
|
602
|
+
};
|
|
603
|
+
},
|
|
604
|
+
}),
|
|
605
|
+
list: queryGeneric({
|
|
606
|
+
args: { enterpriseId: v.string() },
|
|
607
|
+
handler: async (ctx, args) => {
|
|
608
|
+
await authorize(ctx, "sso.webhook.manage", {
|
|
609
|
+
enterpriseId: args.enterpriseId,
|
|
610
|
+
});
|
|
611
|
+
const endpoints = await auth.sso.admin.webhook.endpoint.list(
|
|
612
|
+
ctx as never,
|
|
613
|
+
args.enterpriseId,
|
|
614
|
+
);
|
|
615
|
+
return endpoints.map((endpoint: Record<string, unknown>) => {
|
|
616
|
+
const { secretHash: _secretHash, ...rest } = endpoint;
|
|
617
|
+
return rest;
|
|
618
|
+
});
|
|
619
|
+
},
|
|
620
|
+
}),
|
|
621
|
+
disable: mutationGeneric({
|
|
622
|
+
args: { endpointId: v.string() },
|
|
623
|
+
handler: async (ctx, args) => {
|
|
624
|
+
const endpoint = await auth.sso.admin.webhook.endpoint.get(
|
|
625
|
+
ctx as never,
|
|
626
|
+
args.endpointId,
|
|
627
|
+
);
|
|
628
|
+
if (!endpoint) {
|
|
629
|
+
throw new ConvexError({
|
|
630
|
+
code: "INVALID_PARAMETERS",
|
|
631
|
+
message: "Webhook endpoint not found.",
|
|
632
|
+
});
|
|
633
|
+
}
|
|
634
|
+
await authorize(ctx, "sso.webhook.manage", {
|
|
635
|
+
enterpriseId: endpoint.enterpriseId,
|
|
636
|
+
groupId: endpoint.groupId,
|
|
637
|
+
});
|
|
638
|
+
return await auth.sso.admin.webhook.endpoint.disable(
|
|
639
|
+
ctx as never,
|
|
640
|
+
args.endpointId,
|
|
641
|
+
);
|
|
642
|
+
},
|
|
643
|
+
}),
|
|
644
|
+
},
|
|
645
|
+
},
|
|
646
|
+
},
|
|
647
|
+
client: {
|
|
648
|
+
signIn: queryGeneric({
|
|
649
|
+
args: {
|
|
650
|
+
enterpriseId: v.optional(v.string()),
|
|
651
|
+
email: v.optional(v.string()),
|
|
652
|
+
domain: v.optional(v.string()),
|
|
653
|
+
redirectTo: v.optional(v.string()),
|
|
654
|
+
},
|
|
655
|
+
handler: async (ctx, args) => {
|
|
656
|
+
return await auth.sso.client.signIn(ctx as never, args);
|
|
657
|
+
},
|
|
658
|
+
}),
|
|
659
|
+
metadata: queryGeneric({
|
|
660
|
+
args: {
|
|
661
|
+
enterpriseId: v.string(),
|
|
662
|
+
entityId: v.optional(v.string()),
|
|
663
|
+
acsUrl: v.optional(v.string()),
|
|
664
|
+
sloUrl: v.optional(v.string()),
|
|
665
|
+
},
|
|
666
|
+
handler: async (ctx, args) => {
|
|
667
|
+
return await auth.sso.client.metadata(ctx as never, args);
|
|
668
|
+
},
|
|
669
|
+
}),
|
|
670
|
+
},
|
|
671
|
+
};
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
/**
|
|
675
|
+
* Build optional public SCIM management actions that apps can mount under
|
|
676
|
+
* `convex/auth/scim/**` when they want client-callable enterprise admin APIs.
|
|
677
|
+
*/
|
|
678
|
+
export function scim<
|
|
679
|
+
TAuthorization extends AuthAuthorizationConfig | undefined = undefined,
|
|
680
|
+
>(
|
|
681
|
+
auth: Pick<AuthApi<TAuthorization>, "scim" | "sso" | "user">,
|
|
682
|
+
options?: MountedEnterpriseOptions<AuthRoleId<TAuthorization>>,
|
|
683
|
+
) {
|
|
684
|
+
const authorize = createMountedAdminAuthorizer(auth, options);
|
|
685
|
+
|
|
686
|
+
return {
|
|
687
|
+
admin: {
|
|
688
|
+
configure: mutationGeneric({
|
|
689
|
+
args: {
|
|
690
|
+
enterpriseId: v.string(),
|
|
691
|
+
basePath: v.optional(v.string()),
|
|
692
|
+
status: v.optional(enterpriseStatusValidator),
|
|
693
|
+
},
|
|
694
|
+
handler: async (ctx, args) => {
|
|
695
|
+
await authorize(ctx, "scim.manage", {
|
|
696
|
+
enterpriseId: args.enterpriseId,
|
|
697
|
+
});
|
|
698
|
+
return await auth.scim.admin.configure(ctx as never, args);
|
|
699
|
+
},
|
|
700
|
+
}),
|
|
701
|
+
get: queryGeneric({
|
|
702
|
+
args: { enterpriseId: v.string() },
|
|
703
|
+
handler: async (ctx, args) => {
|
|
704
|
+
await authorize(ctx, "scim.manage", {
|
|
705
|
+
enterpriseId: args.enterpriseId,
|
|
706
|
+
});
|
|
707
|
+
return await auth.scim.admin.get(ctx as never, args.enterpriseId);
|
|
708
|
+
},
|
|
709
|
+
}),
|
|
710
|
+
validate: queryGeneric({
|
|
711
|
+
args: { enterpriseId: v.string() },
|
|
712
|
+
handler: async (ctx, args) => {
|
|
713
|
+
await authorize(ctx, "scim.manage", {
|
|
714
|
+
enterpriseId: args.enterpriseId,
|
|
715
|
+
});
|
|
716
|
+
return await auth.scim.admin.validate(
|
|
717
|
+
ctx as never,
|
|
718
|
+
args.enterpriseId,
|
|
719
|
+
);
|
|
720
|
+
},
|
|
721
|
+
}),
|
|
722
|
+
},
|
|
723
|
+
};
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
/**
|
|
727
|
+
* Build a flat mounted enterprise API surface for app-owned Convex exports.
|
|
728
|
+
*
|
|
729
|
+
* The returned object contains tenant-admin SSO and SCIM control-plane
|
|
730
|
+
* functions plus end-user enterprise sign-in helpers. The `authorized`
|
|
731
|
+
* callback is required for admin operations.
|
|
732
|
+
*/
|
|
733
|
+
export function enterprise<
|
|
734
|
+
TAuthorization extends AuthAuthorizationConfig | undefined = undefined,
|
|
735
|
+
>(
|
|
736
|
+
auth: Pick<
|
|
737
|
+
AuthApi<TAuthorization>,
|
|
738
|
+
"group" | "member" | "scim" | "sso" | "user"
|
|
739
|
+
>,
|
|
740
|
+
options: EnterpriseMountOptions<AuthRoleId<TAuthorization>>,
|
|
741
|
+
) {
|
|
742
|
+
const mountedSso = sso(auth, {
|
|
743
|
+
admin: options.admin,
|
|
744
|
+
});
|
|
745
|
+
const mountedScim = scim(auth, {
|
|
746
|
+
admin: { authorized: options.admin.authorized },
|
|
747
|
+
});
|
|
748
|
+
|
|
749
|
+
return {
|
|
750
|
+
createConnection: mountedSso.admin.connection.create,
|
|
751
|
+
getConnection: mountedSso.admin.connection.get,
|
|
752
|
+
getConnectionByGroup: mountedSso.admin.connection.getByGroup,
|
|
753
|
+
getConnectionByDomain: mountedSso.admin.connection.getByDomain,
|
|
754
|
+
listConnections: mountedSso.admin.connection.list,
|
|
755
|
+
updateConnection: mountedSso.admin.connection.update,
|
|
756
|
+
deleteConnection: mountedSso.admin.connection.delete,
|
|
757
|
+
getConnectionStatus: mountedSso.admin.connection.status,
|
|
758
|
+
listDomains: mountedSso.admin.connection.domain.list,
|
|
759
|
+
validateDomains: mountedSso.admin.connection.domain.validate,
|
|
760
|
+
setDomains: mountedSso.admin.connection.domain.set,
|
|
761
|
+
requestDomainVerification:
|
|
762
|
+
mountedSso.admin.connection.domain.verification.request,
|
|
763
|
+
confirmDomainVerification:
|
|
764
|
+
mountedSso.admin.connection.domain.verification.confirm,
|
|
765
|
+
configureOidc: mountedSso.admin.oidc.configure,
|
|
766
|
+
getOidc: mountedSso.admin.oidc.get,
|
|
767
|
+
validateOidc: mountedSso.admin.oidc.validate,
|
|
768
|
+
configureSaml: mountedSso.admin.saml.configure,
|
|
769
|
+
validateSaml: mountedSso.admin.saml.validate,
|
|
770
|
+
getPolicy: mountedSso.admin.policy.get,
|
|
771
|
+
updatePolicy: mountedSso.admin.policy.update,
|
|
772
|
+
validatePolicy: mountedSso.admin.policy.validate,
|
|
773
|
+
listAudit: mountedSso.admin.audit.list,
|
|
774
|
+
createWebhookEndpoint: mountedSso.admin.webhook.endpoint.create,
|
|
775
|
+
listWebhookEndpoints: mountedSso.admin.webhook.endpoint.list,
|
|
776
|
+
listWebhookDeliveries: mountedSso.admin.webhook.delivery.list,
|
|
777
|
+
disableWebhookEndpoint: mountedSso.admin.webhook.endpoint.disable,
|
|
778
|
+
configureScim: mountedScim.admin.configure,
|
|
779
|
+
getScim: mountedScim.admin.get,
|
|
780
|
+
validateScim: mountedScim.admin.validate,
|
|
781
|
+
signIn: mountedSso.client.signIn,
|
|
782
|
+
metadata: mountedSso.client.metadata,
|
|
783
|
+
};
|
|
784
|
+
}
|
|
19
785
|
|
|
20
786
|
/** Cookie lifetime configuration for auth tokens. */
|
|
21
787
|
export type AuthCookieConfig = {
|
|
@@ -588,9 +1354,9 @@ export function server(options: ServerOptions) {
|
|
|
588
1354
|
: {};
|
|
589
1355
|
|
|
590
1356
|
const actionDispatch =
|
|
591
|
-
action === "auth
|
|
1357
|
+
action === "auth:signIn"
|
|
592
1358
|
? { action: "sessionStart" as const }
|
|
593
|
-
: action === "auth
|
|
1359
|
+
: action === "auth:signOut"
|
|
594
1360
|
? { action: "sessionStop" as const }
|
|
595
1361
|
: null;
|
|
596
1362
|
|
|
@@ -943,37 +1709,37 @@ export function server(options: ServerOptions) {
|
|
|
943
1709
|
redirect: () =>
|
|
944
1710
|
Fx.fatal(
|
|
945
1711
|
new Error(
|
|
946
|
-
"Invalid `auth
|
|
1712
|
+
"Invalid `auth:signIn` result for sign-out fallback refresh",
|
|
947
1713
|
),
|
|
948
1714
|
),
|
|
949
1715
|
started: () =>
|
|
950
1716
|
Fx.fatal(
|
|
951
1717
|
new Error(
|
|
952
|
-
"Invalid `auth
|
|
1718
|
+
"Invalid `auth:signIn` result for sign-out fallback refresh",
|
|
953
1719
|
),
|
|
954
1720
|
),
|
|
955
1721
|
passkeyOptions: () =>
|
|
956
1722
|
Fx.fatal(
|
|
957
1723
|
new Error(
|
|
958
|
-
"Invalid `auth
|
|
1724
|
+
"Invalid `auth:signIn` result for sign-out fallback refresh",
|
|
959
1725
|
),
|
|
960
1726
|
),
|
|
961
1727
|
totpRequired: () =>
|
|
962
1728
|
Fx.fatal(
|
|
963
1729
|
new Error(
|
|
964
|
-
"Invalid `auth
|
|
1730
|
+
"Invalid `auth:signIn` result for sign-out fallback refresh",
|
|
965
1731
|
),
|
|
966
1732
|
),
|
|
967
1733
|
totpSetup: () =>
|
|
968
1734
|
Fx.fatal(
|
|
969
1735
|
new Error(
|
|
970
|
-
"Invalid `auth
|
|
1736
|
+
"Invalid `auth:signIn` result for sign-out fallback refresh",
|
|
971
1737
|
),
|
|
972
1738
|
),
|
|
973
1739
|
deviceCode: () =>
|
|
974
1740
|
Fx.fatal(
|
|
975
1741
|
new Error(
|
|
976
|
-
"Invalid `auth
|
|
1742
|
+
"Invalid `auth:signIn` result for sign-out fallback refresh",
|
|
977
1743
|
),
|
|
978
1744
|
),
|
|
979
1745
|
}),
|
|
@@ -1146,37 +1912,37 @@ export function server(options: ServerOptions) {
|
|
|
1146
1912
|
redirect: () =>
|
|
1147
1913
|
Fx.fatal(
|
|
1148
1914
|
new Error(
|
|
1149
|
-
"Invalid `auth
|
|
1915
|
+
"Invalid `auth:signIn` result for code exchange",
|
|
1150
1916
|
),
|
|
1151
1917
|
),
|
|
1152
1918
|
started: () =>
|
|
1153
1919
|
Fx.fatal(
|
|
1154
1920
|
new Error(
|
|
1155
|
-
"Invalid `auth
|
|
1921
|
+
"Invalid `auth:signIn` result for code exchange",
|
|
1156
1922
|
),
|
|
1157
1923
|
),
|
|
1158
1924
|
passkeyOptions: () =>
|
|
1159
1925
|
Fx.fatal(
|
|
1160
1926
|
new Error(
|
|
1161
|
-
"Invalid `auth
|
|
1927
|
+
"Invalid `auth:signIn` result for code exchange",
|
|
1162
1928
|
),
|
|
1163
1929
|
),
|
|
1164
1930
|
totpRequired: () =>
|
|
1165
1931
|
Fx.fatal(
|
|
1166
1932
|
new Error(
|
|
1167
|
-
"Invalid `auth
|
|
1933
|
+
"Invalid `auth:signIn` result for code exchange",
|
|
1168
1934
|
),
|
|
1169
1935
|
),
|
|
1170
1936
|
totpSetup: () =>
|
|
1171
1937
|
Fx.fatal(
|
|
1172
1938
|
new Error(
|
|
1173
|
-
"Invalid `auth
|
|
1939
|
+
"Invalid `auth:signIn` result for code exchange",
|
|
1174
1940
|
),
|
|
1175
1941
|
),
|
|
1176
1942
|
deviceCode: () =>
|
|
1177
1943
|
Fx.fatal(
|
|
1178
1944
|
new Error(
|
|
1179
|
-
"Invalid `auth
|
|
1945
|
+
"Invalid `auth:signIn` result for code exchange",
|
|
1180
1946
|
),
|
|
1181
1947
|
),
|
|
1182
1948
|
}),
|
|
@@ -1367,37 +2133,37 @@ export function server(options: ServerOptions) {
|
|
|
1367
2133
|
redirect: () =>
|
|
1368
2134
|
Fx.fatal(
|
|
1369
2135
|
new Error(
|
|
1370
|
-
"Invalid `auth
|
|
2136
|
+
"Invalid `auth:signIn` result for token refresh",
|
|
1371
2137
|
),
|
|
1372
2138
|
),
|
|
1373
2139
|
started: () =>
|
|
1374
2140
|
Fx.fatal(
|
|
1375
2141
|
new Error(
|
|
1376
|
-
"Invalid `auth
|
|
2142
|
+
"Invalid `auth:signIn` result for token refresh",
|
|
1377
2143
|
),
|
|
1378
2144
|
),
|
|
1379
2145
|
passkeyOptions: () =>
|
|
1380
2146
|
Fx.fatal(
|
|
1381
2147
|
new Error(
|
|
1382
|
-
"Invalid `auth
|
|
2148
|
+
"Invalid `auth:signIn` result for token refresh",
|
|
1383
2149
|
),
|
|
1384
2150
|
),
|
|
1385
2151
|
totpRequired: () =>
|
|
1386
2152
|
Fx.fatal(
|
|
1387
2153
|
new Error(
|
|
1388
|
-
"Invalid `auth
|
|
2154
|
+
"Invalid `auth:signIn` result for token refresh",
|
|
1389
2155
|
),
|
|
1390
2156
|
),
|
|
1391
2157
|
totpSetup: () =>
|
|
1392
2158
|
Fx.fatal(
|
|
1393
2159
|
new Error(
|
|
1394
|
-
"Invalid `auth
|
|
2160
|
+
"Invalid `auth:signIn` result for token refresh",
|
|
1395
2161
|
),
|
|
1396
2162
|
),
|
|
1397
2163
|
deviceCode: () =>
|
|
1398
2164
|
Fx.fatal(
|
|
1399
2165
|
new Error(
|
|
1400
|
-
"Invalid `auth
|
|
2166
|
+
"Invalid `auth:signIn` result for token refresh",
|
|
1401
2167
|
),
|
|
1402
2168
|
),
|
|
1403
2169
|
}),
|
|
@@ -1518,37 +2284,37 @@ export function server(options: ServerOptions) {
|
|
|
1518
2284
|
redirect: () =>
|
|
1519
2285
|
Fx.fatal(
|
|
1520
2286
|
new Error(
|
|
1521
|
-
"Invalid `auth
|
|
2287
|
+
"Invalid `auth:signIn` result for token refresh",
|
|
1522
2288
|
),
|
|
1523
2289
|
),
|
|
1524
2290
|
started: () =>
|
|
1525
2291
|
Fx.fatal(
|
|
1526
2292
|
new Error(
|
|
1527
|
-
"Invalid `auth
|
|
2293
|
+
"Invalid `auth:signIn` result for token refresh",
|
|
1528
2294
|
),
|
|
1529
2295
|
),
|
|
1530
2296
|
passkeyOptions: () =>
|
|
1531
2297
|
Fx.fatal(
|
|
1532
2298
|
new Error(
|
|
1533
|
-
"Invalid `auth
|
|
2299
|
+
"Invalid `auth:signIn` result for token refresh",
|
|
1534
2300
|
),
|
|
1535
2301
|
),
|
|
1536
2302
|
totpRequired: () =>
|
|
1537
2303
|
Fx.fatal(
|
|
1538
2304
|
new Error(
|
|
1539
|
-
"Invalid `auth
|
|
2305
|
+
"Invalid `auth:signIn` result for token refresh",
|
|
1540
2306
|
),
|
|
1541
2307
|
),
|
|
1542
2308
|
totpSetup: () =>
|
|
1543
2309
|
Fx.fatal(
|
|
1544
2310
|
new Error(
|
|
1545
|
-
"Invalid `auth
|
|
2311
|
+
"Invalid `auth:signIn` result for token refresh",
|
|
1546
2312
|
),
|
|
1547
2313
|
),
|
|
1548
2314
|
deviceCode: () =>
|
|
1549
2315
|
Fx.fatal(
|
|
1550
2316
|
new Error(
|
|
1551
|
-
"Invalid `auth
|
|
2317
|
+
"Invalid `auth:signIn` result for token refresh",
|
|
1552
2318
|
),
|
|
1553
2319
|
),
|
|
1554
2320
|
}),
|
|
@@ -1643,37 +2409,37 @@ export function server(options: ServerOptions) {
|
|
|
1643
2409
|
redirect: () =>
|
|
1644
2410
|
Fx.fatal(
|
|
1645
2411
|
new Error(
|
|
1646
|
-
"Invalid `auth
|
|
2412
|
+
"Invalid `auth:signIn` result for token refresh",
|
|
1647
2413
|
),
|
|
1648
2414
|
),
|
|
1649
2415
|
started: () =>
|
|
1650
2416
|
Fx.fatal(
|
|
1651
2417
|
new Error(
|
|
1652
|
-
"Invalid `auth
|
|
2418
|
+
"Invalid `auth:signIn` result for token refresh",
|
|
1653
2419
|
),
|
|
1654
2420
|
),
|
|
1655
2421
|
passkeyOptions: () =>
|
|
1656
2422
|
Fx.fatal(
|
|
1657
2423
|
new Error(
|
|
1658
|
-
"Invalid `auth
|
|
2424
|
+
"Invalid `auth:signIn` result for token refresh",
|
|
1659
2425
|
),
|
|
1660
2426
|
),
|
|
1661
2427
|
totpRequired: () =>
|
|
1662
2428
|
Fx.fatal(
|
|
1663
2429
|
new Error(
|
|
1664
|
-
"Invalid `auth
|
|
2430
|
+
"Invalid `auth:signIn` result for token refresh",
|
|
1665
2431
|
),
|
|
1666
2432
|
),
|
|
1667
2433
|
totpSetup: () =>
|
|
1668
2434
|
Fx.fatal(
|
|
1669
2435
|
new Error(
|
|
1670
|
-
"Invalid `auth
|
|
2436
|
+
"Invalid `auth:signIn` result for token refresh",
|
|
1671
2437
|
),
|
|
1672
2438
|
),
|
|
1673
2439
|
deviceCode: () =>
|
|
1674
2440
|
Fx.fatal(
|
|
1675
2441
|
new Error(
|
|
1676
|
-
"Invalid `auth
|
|
2442
|
+
"Invalid `auth:signIn` result for token refresh",
|
|
1677
2443
|
),
|
|
1678
2444
|
),
|
|
1679
2445
|
}),
|