@robelest/convex-auth 0.0.4-preview.13 → 0.0.4-preview.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +140 -9
- package/dist/bin.cjs +5957 -5478
- package/dist/client/index.d.ts +3 -7
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +27 -26
- package/dist/client/index.js.map +1 -1
- package/dist/component/_generated/api.d.ts +14 -0
- package/dist/component/_generated/api.d.ts.map +1 -1
- package/dist/component/_generated/api.js.map +1 -1
- package/dist/component/_generated/component.d.ts +1672 -24
- package/dist/component/_generated/component.d.ts.map +1 -1
- package/dist/component/convex.config.d.ts +2 -2
- package/dist/component/convex.config.d.ts.map +1 -1
- package/dist/component/index.d.ts +1 -1
- package/dist/component/index.js +2 -2
- package/dist/component/model.d.ts +153 -0
- package/dist/component/model.d.ts.map +1 -0
- package/dist/component/model.js +343 -0
- package/dist/component/model.js.map +1 -0
- package/dist/component/providers/sso.d.ts +1 -1
- package/dist/component/public/enterprise.d.ts +54 -0
- package/dist/component/public/enterprise.d.ts.map +1 -0
- package/dist/component/public/enterprise.js +515 -0
- package/dist/component/public/enterprise.js.map +1 -0
- package/dist/component/public/factors.d.ts +52 -0
- package/dist/component/public/factors.d.ts.map +1 -0
- package/dist/component/public/factors.js +285 -0
- package/dist/component/public/factors.js.map +1 -0
- package/dist/component/public/groups.d.ts +116 -0
- package/dist/component/public/groups.d.ts.map +1 -0
- package/dist/component/public/groups.js +596 -0
- package/dist/component/public/groups.js.map +1 -0
- package/dist/component/public/identity.d.ts +93 -0
- package/dist/component/public/identity.d.ts.map +1 -0
- package/dist/component/public/identity.js +426 -0
- package/dist/component/public/identity.js.map +1 -0
- package/dist/component/public/keys.d.ts +41 -0
- package/dist/component/public/keys.d.ts.map +1 -0
- package/dist/component/public/keys.js +157 -0
- package/dist/component/public/keys.js.map +1 -0
- package/dist/component/public/shared.d.ts +26 -0
- package/dist/component/public/shared.d.ts.map +1 -0
- package/dist/component/public/shared.js +32 -0
- package/dist/component/public/shared.js.map +1 -0
- package/dist/component/public.d.ts +9 -321
- package/dist/component/public.d.ts.map +1 -1
- package/dist/component/public.js +6 -2145
- package/dist/component/schema.d.ts +406 -260
- package/dist/component/schema.js +37 -32
- package/dist/component/schema.js.map +1 -1
- package/dist/component/server/auth.d.ts +161 -15
- package/dist/component/server/auth.d.ts.map +1 -1
- package/dist/component/server/auth.js +100 -7
- package/dist/component/server/auth.js.map +1 -1
- package/dist/component/server/cookies.js +3 -0
- package/dist/component/server/cookies.js.map +1 -1
- package/dist/component/server/db.js +1 -0
- package/dist/component/server/db.js.map +1 -1
- package/dist/component/server/device.js +3 -1
- package/dist/component/server/device.js.map +1 -1
- package/dist/component/server/domains/core.js +629 -0
- package/dist/component/server/domains/core.js.map +1 -0
- package/dist/component/server/domains/sso.js +884 -0
- package/dist/component/server/domains/sso.js.map +1 -0
- package/dist/component/server/factory.d.ts +136 -0
- package/dist/component/server/factory.d.ts.map +1 -0
- package/dist/component/server/factory.js +1134 -0
- package/dist/component/server/factory.js.map +1 -0
- package/dist/component/server/fx.js +2 -1
- package/dist/component/server/fx.js.map +1 -1
- package/dist/component/server/http.js +287 -0
- package/dist/component/server/http.js.map +1 -0
- package/dist/component/server/identity.js +13 -0
- package/dist/component/server/identity.js.map +1 -0
- package/dist/component/server/keys.js +4 -0
- package/dist/component/server/keys.js.map +1 -1
- package/dist/component/server/mutations/account.js +1 -1
- package/dist/component/server/mutations/index.js +2 -2
- package/dist/component/server/mutations/index.js.map +1 -1
- package/dist/component/server/mutations/invalidate.js +1 -1
- package/dist/component/server/mutations/oauth.js +10 -7
- package/dist/component/server/mutations/oauth.js.map +1 -1
- package/dist/component/server/mutations/refresh.js +1 -1
- package/dist/component/server/mutations/register.js +1 -1
- package/dist/component/server/mutations/retrieve.js +1 -1
- package/dist/component/server/mutations/signature.js +1 -1
- package/dist/component/server/mutations/store.js +6 -3
- package/dist/component/server/mutations/store.js.map +1 -1
- package/dist/component/server/mutations/verify.js +1 -1
- package/dist/component/server/oauth.js +3 -0
- package/dist/component/server/oauth.js.map +1 -1
- package/dist/component/server/passkey.js +3 -2
- package/dist/component/server/passkey.js.map +1 -1
- package/dist/component/server/provider.js +2 -0
- package/dist/component/server/provider.js.map +1 -1
- package/dist/component/server/providers.js +10 -0
- package/dist/component/server/providers.js.map +1 -1
- package/dist/component/server/ratelimit.js +3 -0
- package/dist/component/server/ratelimit.js.map +1 -1
- package/dist/component/server/redirects.js +2 -0
- package/dist/component/server/redirects.js.map +1 -1
- package/dist/component/server/refresh.js +5 -0
- package/dist/component/server/refresh.js.map +1 -1
- package/dist/component/server/sessions.js +5 -0
- package/dist/component/server/sessions.js.map +1 -1
- package/dist/component/server/signin.js +2 -1
- package/dist/component/server/signin.js.map +1 -1
- package/dist/component/server/sso.js +166 -19
- package/dist/component/server/sso.js.map +1 -1
- package/dist/component/server/tokens.js +1 -0
- package/dist/component/server/tokens.js.map +1 -1
- package/dist/component/server/totp.js +4 -2
- package/dist/component/server/totp.js.map +1 -1
- package/dist/component/server/types.d.ts +106 -38
- package/dist/component/server/types.d.ts.map +1 -1
- package/dist/component/server/types.js.map +1 -1
- package/dist/component/server/users.js +1 -0
- package/dist/component/server/users.js.map +1 -1
- package/dist/component/server/utils.js +44 -2
- package/dist/component/server/utils.js.map +1 -1
- package/dist/providers/anonymous.d.ts +1 -1
- package/dist/providers/credentials.d.ts +1 -1
- package/dist/providers/password.d.ts +1 -1
- package/dist/providers/sso.d.ts +1 -1
- package/dist/providers/sso.js.map +1 -1
- package/dist/server/auth.d.ts +163 -17
- package/dist/server/auth.d.ts.map +1 -1
- package/dist/server/auth.js +100 -7
- package/dist/server/auth.js.map +1 -1
- package/dist/server/cookies.d.ts +1 -38
- package/dist/server/cookies.js +3 -0
- package/dist/server/cookies.js.map +1 -1
- package/dist/server/db.d.ts +1 -125
- package/dist/server/db.js +1 -0
- package/dist/server/db.js.map +1 -1
- package/dist/server/device.d.ts +1 -24
- package/dist/server/device.js +3 -1
- package/dist/server/device.js.map +1 -1
- package/dist/server/domains/core.d.ts +434 -0
- package/dist/server/domains/core.d.ts.map +1 -0
- package/dist/server/domains/core.js +629 -0
- package/dist/server/domains/core.js.map +1 -0
- package/dist/server/domains/sso.d.ts +409 -0
- package/dist/server/domains/sso.d.ts.map +1 -0
- package/dist/server/domains/sso.js +884 -0
- package/dist/server/domains/sso.js.map +1 -0
- package/dist/server/enterpriseValidators.d.ts +1 -0
- package/dist/server/enterpriseValidators.js +60 -0
- package/dist/server/enterpriseValidators.js.map +1 -0
- package/dist/server/factory.d.ts +136 -0
- package/dist/server/factory.d.ts.map +1 -0
- package/dist/server/factory.js +1134 -0
- package/dist/server/factory.js.map +1 -0
- package/dist/server/fx.d.ts +1 -16
- package/dist/server/fx.d.ts.map +1 -1
- package/dist/server/fx.js +1 -0
- package/dist/server/fx.js.map +1 -1
- package/dist/server/http.d.ts +59 -0
- package/dist/server/http.d.ts.map +1 -0
- package/dist/server/http.js +287 -0
- package/dist/server/http.js.map +1 -0
- package/dist/server/identity.d.ts +1 -0
- package/dist/server/identity.js +13 -0
- package/dist/server/identity.js.map +1 -0
- package/dist/server/index.d.ts +468 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +530 -36
- package/dist/server/index.js.map +1 -1
- package/dist/server/keys.d.ts +1 -57
- package/dist/server/keys.js +4 -0
- package/dist/server/keys.js.map +1 -1
- package/dist/server/mutations/account.d.ts +7 -7
- package/dist/server/mutations/account.d.ts.map +1 -1
- package/dist/server/mutations/code.d.ts +13 -13
- package/dist/server/mutations/code.d.ts.map +1 -1
- package/dist/server/mutations/index.d.ts +107 -107
- package/dist/server/mutations/index.d.ts.map +1 -1
- package/dist/server/mutations/index.js +1 -1
- package/dist/server/mutations/index.js.map +1 -1
- package/dist/server/mutations/invalidate.d.ts +5 -5
- package/dist/server/mutations/invalidate.d.ts.map +1 -1
- package/dist/server/mutations/oauth.d.ts +10 -10
- package/dist/server/mutations/oauth.d.ts.map +1 -1
- package/dist/server/mutations/oauth.js +9 -6
- package/dist/server/mutations/oauth.js.map +1 -1
- package/dist/server/mutations/refresh.d.ts +4 -4
- package/dist/server/mutations/register.d.ts +12 -12
- package/dist/server/mutations/register.d.ts.map +1 -1
- package/dist/server/mutations/retrieve.d.ts +7 -7
- package/dist/server/mutations/signature.d.ts +5 -5
- package/dist/server/mutations/signin.d.ts +6 -6
- package/dist/server/mutations/signin.d.ts.map +1 -1
- package/dist/server/mutations/signout.d.ts +1 -1
- package/dist/server/mutations/store.d.ts +3 -2
- package/dist/server/mutations/store.d.ts.map +1 -1
- package/dist/server/mutations/store.js +6 -3
- package/dist/server/mutations/store.js.map +1 -1
- package/dist/server/mutations/verifier.d.ts +1 -1
- package/dist/server/mutations/verify.d.ts +11 -11
- package/dist/server/mutations/verify.d.ts.map +1 -1
- package/dist/server/oauth.d.ts +1 -59
- package/dist/server/oauth.js +3 -0
- package/dist/server/oauth.js.map +1 -1
- package/dist/server/passkey.d.ts.map +1 -1
- package/dist/server/passkey.js +3 -2
- package/dist/server/passkey.js.map +1 -1
- package/dist/server/provider.d.ts +1 -14
- package/dist/server/provider.d.ts.map +1 -1
- package/dist/server/provider.js +2 -0
- package/dist/server/provider.js.map +1 -1
- package/dist/server/providers.js +10 -0
- package/dist/server/providers.js.map +1 -1
- package/dist/server/ratelimit.d.ts +1 -22
- package/dist/server/ratelimit.js +3 -0
- package/dist/server/ratelimit.js.map +1 -1
- package/dist/server/redirects.d.ts +1 -10
- package/dist/server/redirects.js +2 -0
- package/dist/server/redirects.js.map +1 -1
- package/dist/server/refresh.d.ts +1 -37
- package/dist/server/refresh.js +5 -0
- package/dist/server/refresh.js.map +1 -1
- package/dist/server/sessions.d.ts +1 -28
- package/dist/server/sessions.js +5 -0
- package/dist/server/sessions.js.map +1 -1
- package/dist/server/signin.d.ts +1 -55
- package/dist/server/signin.js +2 -1
- package/dist/server/signin.js.map +1 -1
- package/dist/server/sso.d.ts +1 -348
- package/dist/server/sso.js +165 -18
- package/dist/server/sso.js.map +1 -1
- package/dist/server/templates.d.ts +1 -21
- package/dist/server/templates.js +1 -0
- package/dist/server/templates.js.map +1 -1
- package/dist/server/tokens.d.ts +1 -11
- package/dist/server/tokens.js +1 -0
- package/dist/server/tokens.js.map +1 -1
- package/dist/server/totp.d.ts +1 -23
- package/dist/server/totp.js +4 -2
- package/dist/server/totp.js.map +1 -1
- package/dist/server/types.d.ts +114 -77
- package/dist/server/types.d.ts.map +1 -1
- package/dist/server/types.js.map +1 -1
- package/dist/server/users.d.ts +1 -31
- package/dist/server/users.js +1 -0
- package/dist/server/users.js.map +1 -1
- package/dist/server/utils.d.ts +1 -27
- package/dist/server/utils.js +44 -2
- package/dist/server/utils.js.map +1 -1
- package/dist/server/version.d.ts +1 -1
- package/dist/server/version.js +1 -1
- package/dist/server/version.js.map +1 -1
- package/package.json +4 -5
- package/src/cli/bin.ts +5 -0
- package/src/cli/index.ts +22 -9
- package/src/cli/keys.ts +3 -0
- package/src/client/index.ts +36 -37
- package/src/component/_generated/api.ts +14 -0
- package/src/component/_generated/component.ts +2106 -9
- package/src/component/index.ts +3 -1
- package/src/component/model.ts +441 -0
- package/src/component/public/enterprise.ts +753 -0
- package/src/component/public/factors.ts +332 -0
- package/src/component/public/groups.ts +932 -0
- package/src/component/public/identity.ts +566 -0
- package/src/component/public/keys.ts +209 -0
- package/src/component/public/shared.ts +119 -0
- package/src/component/public.ts +5 -2965
- package/src/component/schema.ts +68 -63
- package/src/providers/sso.ts +1 -1
- package/src/server/auth.ts +413 -18
- package/src/server/cookies.ts +3 -0
- package/src/server/db.ts +3 -0
- package/src/server/device.ts +3 -1
- package/src/server/domains/core.ts +1071 -0
- package/src/server/domains/sso.ts +1749 -0
- package/src/server/enterpriseValidators.ts +93 -0
- package/src/server/factory.ts +2181 -0
- package/src/server/fx.ts +1 -0
- package/src/server/http.ts +529 -0
- package/src/server/identity.ts +18 -0
- package/src/server/index.ts +806 -40
- package/src/server/keys.ts +4 -0
- package/src/server/mutations/index.ts +1 -1
- package/src/server/mutations/oauth.ts +36 -8
- package/src/server/mutations/store.ts +6 -3
- package/src/server/oauth.ts +6 -0
- package/src/server/passkey.ts +3 -2
- package/src/server/provider.ts +2 -0
- package/src/server/providers.ts +20 -0
- package/src/server/ratelimit.ts +3 -0
- package/src/server/redirects.ts +2 -0
- package/src/server/refresh.ts +5 -0
- package/src/server/sessions.ts +5 -0
- package/src/server/signin.ts +1 -0
- package/src/server/sso.ts +259 -17
- package/src/server/templates.ts +1 -0
- package/src/server/tokens.ts +1 -0
- package/src/server/totp.ts +4 -2
- package/src/server/types.ts +178 -83
- package/src/server/users.ts +1 -0
- package/src/server/utils.ts +71 -1
- package/src/server/version.ts +1 -1
- package/dist/component/public.js.map +0 -1
- package/dist/component/server/implementation.d.ts +0 -1264
- package/dist/component/server/implementation.d.ts.map +0 -1
- package/dist/component/server/implementation.js +0 -2365
- package/dist/component/server/implementation.js.map +0 -1
- package/dist/server/cookies.d.ts.map +0 -1
- package/dist/server/db.d.ts.map +0 -1
- package/dist/server/device.d.ts.map +0 -1
- package/dist/server/implementation.d.ts +0 -1264
- package/dist/server/implementation.d.ts.map +0 -1
- package/dist/server/implementation.js +0 -2365
- package/dist/server/implementation.js.map +0 -1
- package/dist/server/keys.d.ts.map +0 -1
- package/dist/server/oauth.d.ts.map +0 -1
- package/dist/server/ratelimit.d.ts.map +0 -1
- package/dist/server/redirects.d.ts.map +0 -1
- package/dist/server/refresh.d.ts.map +0 -1
- package/dist/server/sessions.d.ts.map +0 -1
- package/dist/server/signin.d.ts.map +0 -1
- package/dist/server/sso.d.ts.map +0 -1
- package/dist/server/templates.d.ts.map +0 -1
- package/dist/server/tokens.d.ts.map +0 -1
- package/dist/server/totp.d.ts.map +0 -1
- package/dist/server/users.d.ts.map +0 -1
- package/dist/server/utils.d.ts.map +0 -1
- package/src/server/implementation.ts +0 -5336
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { Fx } from "./fx.js";
|
|
2
|
-
import { Auth } from "./
|
|
1
|
+
import { AuthError, Fx } from "./fx.js";
|
|
2
|
+
import { Auth } from "./factory.js";
|
|
3
3
|
|
|
4
4
|
//#region src/server/auth.ts
|
|
5
5
|
/**
|
|
6
6
|
* Create an auth API object.
|
|
7
7
|
*
|
|
8
|
-
* When `new SSO()` is included in providers, `auth.sso`
|
|
9
|
-
* on the returned object. Without it,
|
|
10
|
-
* accessing
|
|
8
|
+
* When `new SSO()` is included in providers, `auth.sso` and `auth.scim`
|
|
9
|
+
* are available on the returned object. Without it, those namespaces are
|
|
10
|
+
* absent and accessing them is a TypeScript compile error.
|
|
11
11
|
*/
|
|
12
12
|
function createAuth(component, config) {
|
|
13
13
|
const authResult = Auth({
|
|
@@ -15,6 +15,86 @@ function createAuth(component, config) {
|
|
|
15
15
|
component,
|
|
16
16
|
providers: [...config.providers]
|
|
17
17
|
});
|
|
18
|
+
const { domain: domainApi, scim: scimApi, connection: connectionApi, audit: auditApi, webhook: webhookApi, oidc: oidcApi, saml: samlApi, ...restSso } = authResult.auth.sso;
|
|
19
|
+
const setEnterpriseDomains = async (ctx, enterpriseId, domains) => {
|
|
20
|
+
const enterprise = await connectionApi.get(ctx, enterpriseId);
|
|
21
|
+
if (enterprise === null) throw new AuthError("INVALID_PARAMETERS", "Enterprise not found.").toConvexError();
|
|
22
|
+
const normalized = domains.map((entry) => ({
|
|
23
|
+
...entry,
|
|
24
|
+
domain: entry.domain.trim().toLowerCase()
|
|
25
|
+
}));
|
|
26
|
+
const deduped = /* @__PURE__ */ new Map();
|
|
27
|
+
for (const entry of normalized) {
|
|
28
|
+
if (entry.domain.length === 0) throw new AuthError("INVALID_PARAMETERS", "Domain must not be empty.").toConvexError();
|
|
29
|
+
if (deduped.has(entry.domain)) throw new AuthError("INVALID_PARAMETERS", `Duplicate domain: ${entry.domain}`).toConvexError();
|
|
30
|
+
deduped.set(entry.domain, entry);
|
|
31
|
+
}
|
|
32
|
+
const nextDomains = [...deduped.values()];
|
|
33
|
+
const primaryCount = nextDomains.filter((entry) => entry.isPrimary).length;
|
|
34
|
+
if (primaryCount > 1) throw new AuthError("INVALID_PARAMETERS", "Only one primary domain may be set.").toConvexError();
|
|
35
|
+
if (nextDomains.length > 0 && primaryCount === 0) nextDomains[0] = {
|
|
36
|
+
...nextDomains[0],
|
|
37
|
+
isPrimary: true
|
|
38
|
+
};
|
|
39
|
+
const currentDomains = await domainApi.list(ctx, enterpriseId);
|
|
40
|
+
const currentByDomain = new Map(currentDomains.map((entry) => [entry.domain.toLowerCase(), entry]));
|
|
41
|
+
for (const existing of currentDomains) if (!deduped.has(existing.domain.toLowerCase())) await domainApi.remove(ctx, existing._id);
|
|
42
|
+
for (const nextDomain of nextDomains) {
|
|
43
|
+
const current = currentByDomain.get(nextDomain.domain);
|
|
44
|
+
if (current && current.isPrimary === Boolean(nextDomain.isPrimary)) continue;
|
|
45
|
+
if (current) await domainApi.remove(ctx, current._id);
|
|
46
|
+
const domainId = await domainApi.add(ctx, {
|
|
47
|
+
enterpriseId: enterprise._id,
|
|
48
|
+
groupId: enterprise.groupId,
|
|
49
|
+
domain: nextDomain.domain,
|
|
50
|
+
isPrimary: nextDomain.isPrimary
|
|
51
|
+
});
|
|
52
|
+
if (current?.verifiedAt !== void 0) await ctx.runMutation(component.public.enterpriseDomainVerify, {
|
|
53
|
+
domainId,
|
|
54
|
+
verifiedAt: current.verifiedAt
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
return {
|
|
58
|
+
ok: true,
|
|
59
|
+
enterpriseId,
|
|
60
|
+
domains: (await domainApi.list(ctx, enterpriseId)).map((domain) => ({
|
|
61
|
+
domainId: domain._id,
|
|
62
|
+
domain: domain.domain,
|
|
63
|
+
isPrimary: domain.isPrimary,
|
|
64
|
+
verified: domain.verifiedAt !== void 0,
|
|
65
|
+
verifiedAt: domain.verifiedAt ?? null
|
|
66
|
+
}))
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
const publicSso = {
|
|
70
|
+
admin: {
|
|
71
|
+
...restSso,
|
|
72
|
+
oidc: { ...oidcApi },
|
|
73
|
+
saml: { ...samlApi },
|
|
74
|
+
connection: {
|
|
75
|
+
...connectionApi,
|
|
76
|
+
domain: {
|
|
77
|
+
list: domainApi.list,
|
|
78
|
+
validate: domainApi.validate,
|
|
79
|
+
set: setEnterpriseDomains,
|
|
80
|
+
verification: {
|
|
81
|
+
request: domainApi.verification.request,
|
|
82
|
+
confirm: domainApi.verification.confirm
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
policy: restSso.policy,
|
|
87
|
+
audit: { list: auditApi.list },
|
|
88
|
+
webhook: {
|
|
89
|
+
endpoint: webhookApi.endpoint,
|
|
90
|
+
delivery: { list: webhookApi.delivery.list }
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
client: {
|
|
94
|
+
signIn: oidcApi.signIn,
|
|
95
|
+
metadata: samlApi.metadata
|
|
96
|
+
}
|
|
97
|
+
};
|
|
18
98
|
return {
|
|
19
99
|
signIn: authResult.signIn,
|
|
20
100
|
signOut: authResult.signOut,
|
|
@@ -25,12 +105,25 @@ function createAuth(component, config) {
|
|
|
25
105
|
account: authResult.auth.account,
|
|
26
106
|
group: authResult.auth.group,
|
|
27
107
|
member: authResult.auth.member,
|
|
108
|
+
access: authResult.auth.access,
|
|
28
109
|
invite: authResult.auth.invite,
|
|
29
110
|
key: authResult.auth.key,
|
|
30
|
-
sso:
|
|
111
|
+
sso: publicSso,
|
|
112
|
+
scim: { admin: {
|
|
113
|
+
configure: scimApi.configure,
|
|
114
|
+
get: scimApi.get,
|
|
115
|
+
validate: scimApi.validate
|
|
116
|
+
} },
|
|
31
117
|
http: authResult.auth.http
|
|
32
118
|
};
|
|
33
119
|
}
|
|
120
|
+
function defineRoles(roles) {
|
|
121
|
+
return Object.fromEntries(Object.entries(roles).map(([id, role]) => [id, {
|
|
122
|
+
id,
|
|
123
|
+
...role.label ? { label: role.label } : {},
|
|
124
|
+
grants: [...role.grants]
|
|
125
|
+
}]));
|
|
126
|
+
}
|
|
34
127
|
function AuthCtx(auth, config) {
|
|
35
128
|
return {
|
|
36
129
|
args: {},
|
|
@@ -77,5 +170,5 @@ function AuthCtx(auth, config) {
|
|
|
77
170
|
}
|
|
78
171
|
|
|
79
172
|
//#endregion
|
|
80
|
-
export { AuthCtx, createAuth };
|
|
173
|
+
export { AuthCtx, createAuth, defineRoles };
|
|
81
174
|
//# sourceMappingURL=auth.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.js","names":["AuthFactory"],"sources":["../../../src/server/auth.ts"],"sourcesContent":["/**\n * Auth configuration helpers for Convex Auth.\n *\n * @module\n */\n\nimport type { UserIdentity } from \"convex/server\";\nimport type { GenericId } from \"convex/values\";\n\nimport type { AuthApiRefs } from \"../client/index\";\nimport { Fx } from \"./fx\";\nimport { AuthError } from \"./fx\";\nimport { Auth as AuthFactory } from \"./implementation\";\nimport type { Doc } from \"./types\";\nimport type {\n AuthProviderConfig,\n ConvexAuthConfig,\n HasDeviceProvider,\n HasPasskeyProvider,\n HasSSO,\n HasTotpProvider,\n} from \"./types\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Config for auth setup. Extends the standard auth config\n * minus `component` (which is passed as the first constructor argument).\n */\nexport type AuthConfig = Omit<ConvexAuthConfig, \"component\">;\n\n/** The base auth API surface, without conditional namespaces. */\nexport type AuthApiBase = {\n signIn: ReturnType<typeof AuthFactory>[\"signIn\"];\n signOut: ReturnType<typeof AuthFactory>[\"signOut\"];\n store: ReturnType<typeof AuthFactory>[\"store\"];\n user: ReturnType<typeof AuthFactory>[\"auth\"][\"user\"];\n session: ReturnType<typeof AuthFactory>[\"auth\"][\"session\"];\n provider: ReturnType<typeof AuthFactory>[\"auth\"][\"provider\"];\n account: ReturnType<typeof AuthFactory>[\"auth\"][\"account\"];\n group: ReturnType<typeof AuthFactory>[\"auth\"][\"group\"];\n member: ReturnType<typeof AuthFactory>[\"auth\"][\"member\"];\n invite: ReturnType<typeof AuthFactory>[\"auth\"][\"invite\"];\n key: ReturnType<typeof AuthFactory>[\"auth\"][\"key\"];\n http: ReturnType<typeof AuthFactory>[\"auth\"][\"http\"];\n};\n\n/** Auth API with SSO namespace — present only when `new SSO()` is in providers. */\nexport type AuthApi = AuthApiBase & {\n sso: ReturnType<typeof AuthFactory>[\"auth\"][\"sso\"];\n};\n\n/**\n * The return type of `createAuth`. Conditional namespaces:\n * - `auth.sso` — only when `new SSO()` is in providers\n * - `auth.clientApi` — typed API refs for the client SDK with capabilities\n */\nexport type ConvexAuthResult<P extends AuthProviderConfig[]> =\n HasSSO<P> extends true ? AuthApi : AuthApiBase;\n\n/**\n * Infer the typed `AuthApiRefs` for the client SDK from a `createAuth` call.\n *\n * Use this as the generic parameter for `client()` on the frontend:\n *\n * ```ts\n * // convex/auth.ts\n * export const auth = createAuth(components.auth, { providers: [...] });\n *\n * // Frontend\n * import type { auth } from \"../convex/auth\";\n * import type { InferClientApi } from \"@robelest/convex-auth/component\";\n * const c = client<InferClientApi<typeof auth>>({ convex, api: { ... } });\n * ```\n */\nexport type InferClientApi<T> =\n T extends ConvexAuthResult<infer P>\n ? AuthApiRefs<\n HasPasskeyProvider<P>,\n HasTotpProvider<P>,\n HasDeviceProvider<P>\n >\n : AuthApiRefs;\n\n/** @internal */\nexport type AuthLike = Pick<AuthApiBase, \"user\">;\n\n// ============================================================================\n// Auth setup APIs\n// ============================================================================\n\n/**\n * Create an auth API object.\n *\n * When `new SSO()` is included in providers, `auth.sso` is available\n * on the returned object. Without it, `auth.sso` is absent and\n * accessing it is a TypeScript compile error.\n */\nexport function createAuth<P extends AuthProviderConfig[]>(\n component: ConvexAuthConfig[\"component\"],\n config: Omit<AuthConfig, \"providers\"> & { providers: P },\n): ConvexAuthResult<P> {\n const authResult = AuthFactory({\n ...config,\n component,\n providers: [...config.providers],\n });\n\n return {\n signIn: authResult.signIn,\n signOut: authResult.signOut,\n store: authResult.store,\n user: authResult.auth.user,\n session: authResult.auth.session,\n provider: authResult.auth.provider,\n account: authResult.auth.account,\n group: authResult.auth.group,\n member: authResult.auth.member,\n invite: authResult.auth.invite,\n key: authResult.auth.key,\n sso: authResult.auth.sso,\n http: authResult.auth.http,\n } as ConvexAuthResult<P>;\n}\n\n// ============================================================================\n// AuthCtx — ctx enrichment for customQuery / customMutation\n// ============================================================================\n\nexport type UserDoc = Doc<\"User\">;\n\nexport type AuthCtxConfig<\n TResolve extends Record<string, unknown> = Record<string, never>,\n> = {\n optional?: boolean;\n resolve?: (ctx: any, user: UserDoc) => Promise<TResolve> | TResolve;\n};\n\n/** Overload: optional auth */\nexport function AuthCtx<\n TResolve extends Record<string, unknown> = Record<string, never>,\n>(\n auth: AuthLike,\n config: AuthCtxConfig<TResolve> & { optional: true },\n): {\n args: {};\n input: (\n ctx: any,\n _args: any,\n _extra?: any,\n ) => Promise<{\n ctx: {\n auth: {\n getUserIdentity: () => Promise<UserIdentity | null>;\n userId: GenericId<\"User\"> | null;\n user: UserDoc | null;\n } & TResolve;\n };\n args: {};\n }>;\n};\n/** Overload: required auth (default) */\nexport function AuthCtx<\n TResolve extends Record<string, unknown> = Record<string, never>,\n>(\n auth: AuthLike,\n config?: AuthCtxConfig<TResolve>,\n): {\n args: {};\n input: (\n ctx: any,\n _args: any,\n _extra?: any,\n ) => Promise<{\n ctx: {\n auth: {\n getUserIdentity: () => Promise<UserIdentity | null>;\n userId: GenericId<\"User\">;\n user: UserDoc;\n } & TResolve;\n };\n args: {};\n }>;\n};\n// Implementation\nexport function AuthCtx(auth: AuthLike, config?: AuthCtxConfig<any>) {\n return {\n args: {},\n input: async (ctx: any, _args: any, _extra?: any) => {\n const nativeAuth = ctx.auth;\n const modeDispatch =\n config?.optional === true\n ? { mode: \"optional\" as const }\n : { mode: \"required\" as const };\n\n const userContext = await Fx.run(\n Fx.match(modeDispatch, modeDispatch.mode, {\n optional: async () => {\n const userId = await auth.user.current(ctx);\n if (!userId) {\n return null;\n }\n const user = await auth.user.get(ctx, userId);\n return { userId, user };\n },\n required: async () => {\n const userId = await auth.user.require(ctx);\n const user = await auth.user.get(ctx, userId);\n return { userId, user };\n },\n }),\n );\n\n if (userContext === null) {\n return {\n ctx: {\n auth: {\n getUserIdentity: nativeAuth.getUserIdentity.bind(nativeAuth),\n userId: null,\n user: null,\n },\n },\n args: {},\n };\n }\n\n const extra = config?.resolve\n ? await config.resolve(ctx, userContext.user)\n : {};\n\n return {\n ctx: {\n auth: {\n getUserIdentity: nativeAuth.getUserIdentity.bind(nativeAuth),\n userId: userContext.userId,\n user: userContext.user,\n ...extra,\n },\n },\n args: {},\n };\n },\n };\n}\n\nexport type InferAuth<\n T extends { input: (...args: any[]) => Promise<{ ctx: { auth: any } }> },\n> = Awaited<ReturnType<T[\"input\"]>>[\"ctx\"][\"auth\"];\n"],"mappings":";;;;;;;;;;;AAoGA,SAAgB,WACd,WACA,QACqB;CACrB,MAAM,aAAaA,KAAY;EAC7B,GAAG;EACH;EACA,WAAW,CAAC,GAAG,OAAO,UAAU;EACjC,CAAC;AAEF,QAAO;EACL,QAAQ,WAAW;EACnB,SAAS,WAAW;EACpB,OAAO,WAAW;EAClB,MAAM,WAAW,KAAK;EACtB,SAAS,WAAW,KAAK;EACzB,UAAU,WAAW,KAAK;EAC1B,SAAS,WAAW,KAAK;EACzB,OAAO,WAAW,KAAK;EACvB,QAAQ,WAAW,KAAK;EACxB,QAAQ,WAAW,KAAK;EACxB,KAAK,WAAW,KAAK;EACrB,KAAK,WAAW,KAAK;EACrB,MAAM,WAAW,KAAK;EACvB;;AA+DH,SAAgB,QAAQ,MAAgB,QAA6B;AACnE,QAAO;EACL,MAAM,EAAE;EACR,OAAO,OAAO,KAAU,OAAY,WAAiB;GACnD,MAAM,aAAa,IAAI;GACvB,MAAM,eACJ,QAAQ,aAAa,OACjB,EAAE,MAAM,YAAqB,GAC7B,EAAE,MAAM,YAAqB;GAEnC,MAAM,cAAc,MAAM,GAAG,IAC3B,GAAG,MAAM,cAAc,aAAa,MAAM;IACxC,UAAU,YAAY;KACpB,MAAM,SAAS,MAAM,KAAK,KAAK,QAAQ,IAAI;AAC3C,SAAI,CAAC,OACH,QAAO;AAGT,YAAO;MAAE;MAAQ,MADJ,MAAM,KAAK,KAAK,IAAI,KAAK,OAAO;MACtB;;IAEzB,UAAU,YAAY;KACpB,MAAM,SAAS,MAAM,KAAK,KAAK,QAAQ,IAAI;AAE3C,YAAO;MAAE;MAAQ,MADJ,MAAM,KAAK,KAAK,IAAI,KAAK,OAAO;MACtB;;IAE1B,CAAC,CACH;AAED,OAAI,gBAAgB,KAClB,QAAO;IACL,KAAK,EACH,MAAM;KACJ,iBAAiB,WAAW,gBAAgB,KAAK,WAAW;KAC5D,QAAQ;KACR,MAAM;KACP,EACF;IACD,MAAM,EAAE;IACT;GAGH,MAAM,QAAQ,QAAQ,UAClB,MAAM,OAAO,QAAQ,KAAK,YAAY,KAAK,GAC3C,EAAE;AAEN,UAAO;IACL,KAAK,EACH,MAAM;KACJ,iBAAiB,WAAW,gBAAgB,KAAK,WAAW;KAC5D,QAAQ,YAAY;KACpB,MAAM,YAAY;KAClB,GAAG;KACJ,EACF;IACD,MAAM,EAAE;IACT;;EAEJ"}
|
|
1
|
+
{"version":3,"file":"auth.js","names":["AuthFactory"],"sources":["../../../src/server/auth.ts"],"sourcesContent":["/**\n * Auth configuration helpers for Convex Auth.\n *\n * @module\n */\n\nimport type { UserIdentity } from \"convex/server\";\nimport type { GenericId } from \"convex/values\";\n\nimport type { AuthApiRefs } from \"../client/index\";\nimport { Auth as AuthFactory } from \"./factory\";\nimport { Fx } from \"./fx\";\nimport { AuthError } from \"./fx\";\nimport type { Doc } from \"./types\";\nimport type {\n AuthAuthorizationConfig,\n AuthGrant,\n AuthProviderConfig,\n AuthRoleDefinition,\n AuthRoleId,\n ConvexAuthConfig,\n HasDeviceProvider,\n HasPasskeyProvider,\n HasSSO,\n HasTotpProvider,\n} from \"./types\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\n/**\n * Config for auth setup. Extends the standard auth config\n * minus `component` (which is passed as the first constructor argument).\n */\nexport type AuthConfig = Omit<ConvexAuthConfig, \"component\">;\n\ntype MemberApiWithAuthorization<\n TAuthorization extends AuthAuthorizationConfig | undefined,\n> = Omit<\n ReturnType<typeof AuthFactory>[\"auth\"][\"member\"],\n \"create\" | \"list\" | \"update\" | \"inherit\" | \"require\"\n> & {\n create: (\n ctx: Parameters<\n ReturnType<typeof AuthFactory>[\"auth\"][\"member\"][\"create\"]\n >[0],\n data: {\n groupId: string;\n userId: string;\n roleIds?: AuthRoleId<TAuthorization>[];\n status?: string;\n extend?: Record<string, unknown>;\n },\n ) => Promise<{ ok: true; memberId: string }>;\n list: (\n ctx: Parameters<\n ReturnType<typeof AuthFactory>[\"auth\"][\"member\"][\"list\"]\n >[0],\n opts?: {\n where?: {\n groupId?: string;\n userId?: string;\n roleId?: AuthRoleId<TAuthorization>;\n status?: string;\n };\n limit?: number;\n cursor?: string | null;\n orderBy?: \"_creationTime\" | \"status\";\n order?: \"asc\" | \"desc\";\n },\n ) => ReturnType<ReturnType<typeof AuthFactory>[\"auth\"][\"member\"][\"list\"]>;\n update: (\n ctx: Parameters<\n ReturnType<typeof AuthFactory>[\"auth\"][\"member\"][\"update\"]\n >[0],\n memberId: string,\n data: Record<string, unknown> & { roleIds?: AuthRoleId<TAuthorization>[] },\n ) => Promise<{ ok: true; memberId: string }>;\n inherit: (\n ctx: Parameters<\n ReturnType<typeof AuthFactory>[\"auth\"][\"member\"][\"inherit\"]\n >[0],\n opts: {\n userId: string;\n groupId: string;\n roleIds?: AuthRoleId<TAuthorization>[];\n grants?: AuthGrant<TAuthorization>[];\n maxDepth?: number;\n },\n ) => ReturnType<ReturnType<typeof AuthFactory>[\"auth\"][\"member\"][\"inherit\"]>;\n require: (\n ctx: Parameters<\n ReturnType<typeof AuthFactory>[\"auth\"][\"member\"][\"require\"]\n >[0],\n opts: {\n userId: string;\n groupId: string;\n roleIds?: AuthRoleId<TAuthorization>[];\n grants?: AuthGrant<TAuthorization>[];\n maxDepth?: number;\n },\n ) => ReturnType<ReturnType<typeof AuthFactory>[\"auth\"][\"member\"][\"require\"]>;\n};\n\ntype AccessApiWithAuthorization<\n TAuthorization extends AuthAuthorizationConfig | undefined,\n> = {\n check: (\n ctx: Parameters<\n ReturnType<typeof AuthFactory>[\"auth\"][\"access\"][\"check\"]\n >[0],\n opts: {\n userId: string;\n groupId: string;\n grants: AuthGrant<TAuthorization>[];\n maxDepth?: number;\n },\n ) => ReturnType<ReturnType<typeof AuthFactory>[\"auth\"][\"access\"][\"check\"]>;\n require: (\n ctx: Parameters<\n ReturnType<typeof AuthFactory>[\"auth\"][\"access\"][\"require\"]\n >[0],\n opts: {\n userId: string;\n groupId: string;\n grants: AuthGrant<TAuthorization>[];\n maxDepth?: number;\n },\n ) => ReturnType<ReturnType<typeof AuthFactory>[\"auth\"][\"access\"][\"require\"]>;\n};\n\n/** The base auth API surface, without conditional namespaces. */\nexport type AuthApiBase<\n TAuthorization extends AuthAuthorizationConfig | undefined = undefined,\n> = {\n signIn: ReturnType<typeof AuthFactory>[\"signIn\"];\n signOut: ReturnType<typeof AuthFactory>[\"signOut\"];\n store: ReturnType<typeof AuthFactory>[\"store\"];\n user: ReturnType<typeof AuthFactory>[\"auth\"][\"user\"];\n session: ReturnType<typeof AuthFactory>[\"auth\"][\"session\"];\n provider: ReturnType<typeof AuthFactory>[\"auth\"][\"provider\"];\n account: ReturnType<typeof AuthFactory>[\"auth\"][\"account\"];\n group: ReturnType<typeof AuthFactory>[\"auth\"][\"group\"];\n member: MemberApiWithAuthorization<TAuthorization>;\n access: AccessApiWithAuthorization<TAuthorization>;\n invite: ReturnType<typeof AuthFactory>[\"auth\"][\"invite\"];\n key: ReturnType<typeof AuthFactory>[\"auth\"][\"key\"];\n http: ReturnType<typeof AuthFactory>[\"auth\"][\"http\"];\n};\n\ntype InternalSsoApi = ReturnType<typeof AuthFactory>[\"auth\"][\"sso\"];\n\ntype PublicSsoAdminApi = {\n connection: InternalSsoApi[\"connection\"] & {\n domain: {\n list: InternalSsoApi[\"domain\"][\"list\"];\n validate: InternalSsoApi[\"domain\"][\"validate\"];\n set: (\n ctx: Parameters<InternalSsoApi[\"connection\"][\"create\"]>[0],\n enterpriseId: string,\n domains: Array<{\n domain: string;\n isPrimary?: boolean;\n }>,\n ) => Promise<{\n ok: true;\n enterpriseId: string;\n domains: Array<{\n domainId: string;\n domain: string;\n isPrimary: boolean;\n verified: boolean;\n verifiedAt: number | null;\n }>;\n }>;\n verification: {\n request: (\n ctx: Parameters<InternalSsoApi[\"connection\"][\"create\"]>[0],\n args: { enterpriseId: string; domain: string },\n ) => Promise<{\n ok: true;\n enterpriseId: string;\n domain: string;\n requestedAt: number;\n expiresAt: number;\n challenge: {\n recordType: \"TXT\";\n recordName: string;\n recordValue: string;\n };\n }>;\n confirm: (\n ctx: Parameters<InternalSsoApi[\"connection\"][\"create\"]>[0],\n args: { enterpriseId: string; domain: string },\n ) => Promise<{\n ok: boolean;\n enterpriseId: string;\n domain: string;\n verifiedAt?: number;\n checks: Array<{ name: string; ok: boolean; message?: string }>;\n }>;\n };\n };\n };\n oidc: Omit<InternalSsoApi[\"oidc\"], \"signIn\">;\n saml: Omit<InternalSsoApi[\"saml\"], \"metadata\">;\n policy: InternalSsoApi[\"policy\"];\n audit: {\n list: InternalSsoApi[\"audit\"][\"list\"];\n };\n webhook: {\n endpoint: InternalSsoApi[\"webhook\"][\"endpoint\"];\n delivery: {\n list: InternalSsoApi[\"webhook\"][\"delivery\"][\"list\"];\n };\n };\n};\n\ntype PublicSsoClientApi = {\n signIn: InternalSsoApi[\"oidc\"][\"signIn\"];\n metadata: InternalSsoApi[\"saml\"][\"metadata\"];\n};\n\ntype PublicSsoApi = {\n admin: PublicSsoAdminApi;\n client: PublicSsoClientApi;\n};\n\ntype PublicScimApi = {\n admin: Omit<InternalSsoApi[\"scim\"], \"getConfigByToken\" | \"identity\">;\n};\n\n/** Auth API with enterprise namespaces — present only when `new SSO()` is in providers. */\nexport type AuthApi<\n TAuthorization extends AuthAuthorizationConfig | undefined = undefined,\n> = AuthApiBase<TAuthorization> & {\n sso: PublicSsoApi;\n scim: PublicScimApi;\n};\n\n/**\n * The return type of `createAuth`. Conditional namespaces:\n * - `auth.sso` and `auth.scim` — only when `new SSO()` is in providers\n * - `auth.clientApi` — typed API refs for the client SDK with capabilities\n */\nexport type ConvexAuthResult<\n P extends AuthProviderConfig[],\n TAuthorization extends AuthAuthorizationConfig | undefined = undefined,\n> =\n HasSSO<P> extends true\n ? AuthApi<TAuthorization>\n : AuthApiBase<TAuthorization>;\n\n/**\n * Infer the typed `AuthApiRefs` for the client SDK from a `createAuth` call.\n *\n * Use this as the generic parameter for `client()` on the frontend:\n *\n * ```ts\n * // convex/auth.ts\n * export const auth = createAuth(components.auth, { providers: [...] });\n *\n * // Frontend\n * import type { auth } from \"../convex/auth\";\n * import type { InferClientApi } from \"@robelest/convex-auth/component\";\n * const c = client<InferClientApi<typeof auth>>({ convex, api: api.auth });\n * ```\n */\nexport type InferClientApi<T> =\n T extends ConvexAuthResult<infer P>\n ? AuthApiRefs<\n HasPasskeyProvider<P>,\n HasTotpProvider<P>,\n HasDeviceProvider<P>\n >\n : AuthApiRefs;\n\n/** @internal */\nexport type AuthLike = Pick<AuthApiBase, \"user\">;\n\n// ============================================================================\n// Auth setup APIs\n// ============================================================================\n\n/**\n * Create an auth API object.\n *\n * When `new SSO()` is included in providers, `auth.sso` and `auth.scim`\n * are available on the returned object. Without it, those namespaces are\n * absent and accessing them is a TypeScript compile error.\n */\nexport function createAuth<\n P extends AuthProviderConfig[],\n TAuthorization extends AuthAuthorizationConfig | undefined = undefined,\n>(\n component: ConvexAuthConfig[\"component\"],\n config: Omit<AuthConfig, \"providers\" | \"authorization\"> & {\n providers: P;\n authorization?: TAuthorization;\n },\n): ConvexAuthResult<P, TAuthorization> {\n const authResult = AuthFactory({\n ...config,\n component,\n providers: [...config.providers],\n });\n const {\n domain: domainApi,\n scim: scimApi,\n connection: connectionApi,\n audit: auditApi,\n webhook: webhookApi,\n oidc: oidcApi,\n saml: samlApi,\n ...restSso\n } = authResult.auth.sso as InternalSsoApi;\n\n type SetEnterpriseDomains = PublicSsoAdminApi[\"connection\"][\"domain\"][\"set\"];\n type EnterpriseDomainInput = Array<{\n domain: string;\n isPrimary?: boolean;\n }>;\n const setEnterpriseDomains: PublicSsoAdminApi[\"connection\"][\"domain\"][\"set\"] =\n async (\n ctx: Parameters<SetEnterpriseDomains>[0],\n enterpriseId: Parameters<SetEnterpriseDomains>[1],\n domains: EnterpriseDomainInput,\n ) => {\n const enterprise = await connectionApi.get(ctx, enterpriseId);\n if (enterprise === null) {\n throw new AuthError(\n \"INVALID_PARAMETERS\",\n \"Enterprise not found.\",\n ).toConvexError();\n }\n\n const normalized = domains.map((entry: (typeof domains)[number]) => ({\n ...entry,\n domain: entry.domain.trim().toLowerCase(),\n }));\n const deduped = new Map<string, (typeof normalized)[number]>();\n for (const entry of normalized) {\n if (entry.domain.length === 0) {\n throw new AuthError(\n \"INVALID_PARAMETERS\",\n \"Domain must not be empty.\",\n ).toConvexError();\n }\n if (deduped.has(entry.domain)) {\n throw new AuthError(\n \"INVALID_PARAMETERS\",\n `Duplicate domain: ${entry.domain}`,\n ).toConvexError();\n }\n deduped.set(entry.domain, entry);\n }\n\n const nextDomains = [...deduped.values()];\n const primaryCount = nextDomains.filter(\n (entry) => entry.isPrimary,\n ).length;\n if (primaryCount > 1) {\n throw new AuthError(\n \"INVALID_PARAMETERS\",\n \"Only one primary domain may be set.\",\n ).toConvexError();\n }\n if (nextDomains.length > 0 && primaryCount === 0) {\n nextDomains[0] = { ...nextDomains[0], isPrimary: true };\n }\n\n const currentDomains = await domainApi.list(ctx, enterpriseId);\n const currentByDomain = new Map<string, (typeof currentDomains)[number]>(\n currentDomains.map((entry: (typeof currentDomains)[number]) => [\n entry.domain.toLowerCase(),\n entry,\n ]),\n );\n\n for (const existing of currentDomains) {\n if (!deduped.has(existing.domain.toLowerCase())) {\n await domainApi.remove(ctx, existing._id);\n }\n }\n\n for (const nextDomain of nextDomains) {\n const current = currentByDomain.get(nextDomain.domain);\n if (current && current.isPrimary === Boolean(nextDomain.isPrimary)) {\n continue;\n }\n if (current) {\n await domainApi.remove(ctx, current._id);\n }\n const domainId = await domainApi.add(ctx, {\n enterpriseId: enterprise._id,\n groupId: enterprise.groupId,\n domain: nextDomain.domain,\n isPrimary: nextDomain.isPrimary,\n });\n if (current?.verifiedAt !== undefined) {\n await (ctx as any).runMutation(\n component.public.enterpriseDomainVerify,\n {\n domainId,\n verifiedAt: current.verifiedAt,\n },\n );\n }\n }\n\n const updatedDomains = await domainApi.list(ctx, enterpriseId);\n return {\n ok: true as const,\n enterpriseId,\n domains: updatedDomains.map(\n (domain: (typeof updatedDomains)[number]) => ({\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 };\n };\n\n const publicSso: PublicSsoApi = {\n admin: {\n ...restSso,\n oidc: {\n ...oidcApi,\n },\n saml: {\n ...samlApi,\n },\n connection: {\n ...connectionApi,\n domain: {\n list: domainApi.list,\n validate: domainApi.validate,\n set: setEnterpriseDomains,\n verification: {\n request: domainApi.verification.request,\n confirm: domainApi.verification.confirm,\n },\n },\n },\n policy: restSso.policy,\n audit: {\n list: auditApi.list,\n },\n webhook: {\n endpoint: webhookApi.endpoint,\n delivery: {\n list: webhookApi.delivery.list,\n },\n },\n },\n client: {\n signIn: oidcApi.signIn,\n metadata: samlApi.metadata,\n },\n };\n\n return {\n signIn: authResult.signIn,\n signOut: authResult.signOut,\n store: authResult.store,\n user: authResult.auth.user,\n session: authResult.auth.session,\n provider: authResult.auth.provider,\n account: authResult.auth.account,\n group: authResult.auth.group,\n member: authResult.auth.member,\n access: authResult.auth.access,\n invite: authResult.auth.invite,\n key: authResult.auth.key,\n sso: publicSso,\n scim: {\n admin: {\n configure: scimApi.configure,\n get: scimApi.get,\n validate: scimApi.validate,\n },\n },\n http: authResult.auth.http,\n } as unknown as ConvexAuthResult<P, TAuthorization>;\n}\n\nexport function defineRoles<\n const TRoles extends Record<\n string,\n { label?: string; grants: readonly string[] }\n >,\n>(\n roles: TRoles,\n): {\n [K in keyof TRoles]: {\n id: K & string;\n label?: TRoles[K][\"label\"];\n grants: Array<TRoles[K][\"grants\"][number] & string>;\n };\n} {\n return Object.fromEntries(\n Object.entries(roles).map(([id, role]) => [\n id,\n {\n id,\n ...(role.label ? { label: role.label } : {}),\n grants: [...role.grants],\n },\n ]),\n ) as {\n [K in keyof TRoles]: {\n id: K & string;\n label?: TRoles[K][\"label\"];\n grants: Array<TRoles[K][\"grants\"][number] & string>;\n };\n };\n}\n\n// ============================================================================\n// AuthCtx — ctx enrichment for customQuery / customMutation\n// ============================================================================\n\nexport type UserDoc = Doc<\"User\">;\n\nexport type AuthCtxConfig<\n TResolve extends Record<string, unknown> = Record<string, never>,\n> = {\n optional?: boolean;\n resolve?: (ctx: any, user: UserDoc) => Promise<TResolve> | TResolve;\n};\n\n/** Overload: optional auth */\nexport function AuthCtx<\n TResolve extends Record<string, unknown> = Record<string, never>,\n>(\n auth: AuthLike,\n config: AuthCtxConfig<TResolve> & { optional: true },\n): {\n args: {};\n input: (\n ctx: any,\n _args: any,\n _extra?: any,\n ) => Promise<{\n ctx: {\n auth: {\n getUserIdentity: () => Promise<UserIdentity | null>;\n userId: GenericId<\"User\"> | null;\n user: UserDoc | null;\n } & TResolve;\n };\n args: {};\n }>;\n};\n/** Overload: required auth (default) */\nexport function AuthCtx<\n TResolve extends Record<string, unknown> = Record<string, never>,\n>(\n auth: AuthLike,\n config?: AuthCtxConfig<TResolve>,\n): {\n args: {};\n input: (\n ctx: any,\n _args: any,\n _extra?: any,\n ) => Promise<{\n ctx: {\n auth: {\n getUserIdentity: () => Promise<UserIdentity | null>;\n userId: GenericId<\"User\">;\n user: UserDoc;\n } & TResolve;\n };\n args: {};\n }>;\n};\n// Implementation\nexport function AuthCtx(auth: AuthLike, config?: AuthCtxConfig<any>) {\n return {\n args: {},\n input: async (ctx: any, _args: any, _extra?: any) => {\n const nativeAuth = ctx.auth;\n const modeDispatch =\n config?.optional === true\n ? { mode: \"optional\" as const }\n : { mode: \"required\" as const };\n\n const userContext = await Fx.run(\n Fx.match(modeDispatch, modeDispatch.mode, {\n optional: async () => {\n const userId = await auth.user.current(ctx);\n if (!userId) {\n return null;\n }\n const user = await auth.user.get(ctx, userId);\n return { userId, user };\n },\n required: async () => {\n const userId = await auth.user.require(ctx);\n const user = await auth.user.get(ctx, userId);\n return { userId, user };\n },\n }),\n );\n\n if (userContext === null) {\n return {\n ctx: {\n auth: {\n getUserIdentity: nativeAuth.getUserIdentity.bind(nativeAuth),\n userId: null,\n user: null,\n },\n },\n args: {},\n };\n }\n\n const extra = config?.resolve\n ? await config.resolve(ctx, userContext.user)\n : {};\n\n return {\n ctx: {\n auth: {\n getUserIdentity: nativeAuth.getUserIdentity.bind(nativeAuth),\n userId: userContext.userId,\n user: userContext.user,\n ...extra,\n },\n },\n args: {},\n };\n },\n };\n}\n\nexport type InferAuth<\n T extends { input: (...args: any[]) => Promise<{ ctx: { auth: any } }> },\n> = Awaited<ReturnType<T[\"input\"]>>[\"ctx\"][\"auth\"];\n"],"mappings":"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"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { isLocalHost } from "./utils.js";
|
|
2
2
|
|
|
3
3
|
//#region src/server/cookies.ts
|
|
4
|
+
/** @internal */
|
|
4
5
|
const SHARED_COOKIE_OPTIONS = {
|
|
5
6
|
httpOnly: true,
|
|
6
7
|
sameSite: "none",
|
|
@@ -9,6 +10,7 @@ const SHARED_COOKIE_OPTIONS = {
|
|
|
9
10
|
partitioned: true
|
|
10
11
|
};
|
|
11
12
|
const REDIRECT_MAX_AGE = 900;
|
|
13
|
+
/** @internal */
|
|
12
14
|
function redirectToParamCookie(providerId, redirectTo) {
|
|
13
15
|
return {
|
|
14
16
|
name: redirectToParamCookieName(providerId),
|
|
@@ -19,6 +21,7 @@ function redirectToParamCookie(providerId, redirectTo) {
|
|
|
19
21
|
}
|
|
20
22
|
};
|
|
21
23
|
}
|
|
24
|
+
/** @internal */
|
|
22
25
|
function useRedirectToParam(providerId, cookies) {
|
|
23
26
|
const cookieName = redirectToParamCookieName(providerId);
|
|
24
27
|
const redirectTo = cookies[cookieName];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cookies.js","names":[],"sources":["../../../src/server/cookies.ts"],"sourcesContent":["import { isLocalHost } from \"./utils\";\n\nexport const SHARED_COOKIE_OPTIONS = {\n httpOnly: true,\n sameSite: \"none\" as const,\n secure: true,\n path: \"/\",\n partitioned: true,\n};\n\nconst REDIRECT_MAX_AGE = 60 * 15; // 15 minutes in seconds\nexport function redirectToParamCookie(providerId: string, redirectTo: string) {\n return {\n name: redirectToParamCookieName(providerId),\n value: redirectTo,\n options: { ...SHARED_COOKIE_OPTIONS, maxAge: REDIRECT_MAX_AGE },\n };\n}\n\nexport function useRedirectToParam(\n providerId: string,\n cookies: Record<string, string | undefined>,\n) {\n const cookieName = redirectToParamCookieName(providerId);\n const redirectTo = cookies[cookieName];\n if (redirectTo === undefined) {\n return null;\n }\n\n // Clear the cookie\n const updatedCookie = {\n name: cookieName,\n value: \"\",\n options: { ...SHARED_COOKIE_OPTIONS, maxAge: 0 },\n };\n\n return { redirectTo, updatedCookie };\n}\n\nfunction redirectToParamCookieName(providerId: string) {\n return (\n (!isLocalHost(process.env.CONVEX_SITE_URL) ? \"__Host-\" : \"\") +\n providerId +\n \"RedirectTo\"\n );\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"cookies.js","names":[],"sources":["../../../src/server/cookies.ts"],"sourcesContent":["import { isLocalHost } from \"./utils\";\n\n/** @internal */\nexport const SHARED_COOKIE_OPTIONS = {\n httpOnly: true,\n sameSite: \"none\" as const,\n secure: true,\n path: \"/\",\n partitioned: true,\n};\n\nconst REDIRECT_MAX_AGE = 60 * 15; // 15 minutes in seconds\n/** @internal */\nexport function redirectToParamCookie(providerId: string, redirectTo: string) {\n return {\n name: redirectToParamCookieName(providerId),\n value: redirectTo,\n options: { ...SHARED_COOKIE_OPTIONS, maxAge: REDIRECT_MAX_AGE },\n };\n}\n\n/** @internal */\nexport function useRedirectToParam(\n providerId: string,\n cookies: Record<string, string | undefined>,\n) {\n const cookieName = redirectToParamCookieName(providerId);\n const redirectTo = cookies[cookieName];\n if (redirectTo === undefined) {\n return null;\n }\n\n // Clear the cookie\n const updatedCookie = {\n name: cookieName,\n value: \"\",\n options: { ...SHARED_COOKIE_OPTIONS, maxAge: 0 },\n };\n\n return { redirectTo, updatedCookie };\n}\n\nfunction redirectToParamCookieName(providerId: string) {\n return (\n (!isLocalHost(process.env.CONVEX_SITE_URL) ? \"__Host-\" : \"\") +\n providerId +\n \"RedirectTo\"\n );\n}\n"],"mappings":";;;;AAGA,MAAa,wBAAwB;CACnC,UAAU;CACV,UAAU;CACV,QAAQ;CACR,MAAM;CACN,aAAa;CACd;AAED,MAAM,mBAAmB;;AAEzB,SAAgB,sBAAsB,YAAoB,YAAoB;AAC5E,QAAO;EACL,MAAM,0BAA0B,WAAW;EAC3C,OAAO;EACP,SAAS;GAAE,GAAG;GAAuB,QAAQ;GAAkB;EAChE;;;AAIH,SAAgB,mBACd,YACA,SACA;CACA,MAAM,aAAa,0BAA0B,WAAW;CACxD,MAAM,aAAa,QAAQ;AAC3B,KAAI,eAAe,OACjB,QAAO;AAUT,QAAO;EAAE;EAAY,eANC;GACpB,MAAM;GACN,OAAO;GACP,SAAS;IAAE,GAAG;IAAuB,QAAQ;IAAG;GACjD;EAEmC;;AAGtC,SAAS,0BAA0B,YAAoB;AACrD,SACG,CAAC,YAAY,QAAQ,IAAI,gBAAgB,GAAG,YAAY,MACzD,aACA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"db.js","names":[],"sources":["../../../src/server/db.ts"],"sourcesContent":["import {\n GenericActionCtx,\n GenericDataModel,\n GenericMutationCtx,\n FunctionReference,\n} from \"convex/server\";\n\ntype MutationCtxLike = Pick<\n GenericMutationCtx<GenericDataModel>,\n \"runQuery\" | \"runMutation\"\n>;\ntype ActionCtxLike = Pick<\n GenericActionCtx<GenericDataModel>,\n \"runQuery\" | \"runMutation\" | \"runAction\"\n>;\n\ntype CtxLike = MutationCtxLike | ActionCtxLike;\n\ntype AuthComponentApiLike = {\n public: {\n userGetById: FunctionReference<\"query\", \"internal\">;\n userFindByVerifiedEmail: FunctionReference<\"query\", \"internal\">;\n userFindByVerifiedPhone: FunctionReference<\"query\", \"internal\">;\n userInsert: FunctionReference<\"mutation\", \"internal\">;\n userPatch: FunctionReference<\"mutation\", \"internal\">;\n userUpsert: FunctionReference<\"mutation\", \"internal\">;\n accountGet: FunctionReference<\"query\", \"internal\">;\n accountGetById: FunctionReference<\"query\", \"internal\">;\n accountInsert: FunctionReference<\"mutation\", \"internal\">;\n accountPatch: FunctionReference<\"mutation\", \"internal\">;\n accountDelete: FunctionReference<\"mutation\", \"internal\">;\n sessionCreate: FunctionReference<\"mutation\", \"internal\">;\n sessionGetById: FunctionReference<\"query\", \"internal\">;\n sessionDelete: FunctionReference<\"mutation\", \"internal\">;\n sessionListByUser: FunctionReference<\"query\", \"internal\">;\n verifierCreate: FunctionReference<\"mutation\", \"internal\">;\n verifierGetById: FunctionReference<\"query\", \"internal\">;\n verifierGetBySignature: FunctionReference<\"query\", \"internal\">;\n verifierPatch: FunctionReference<\"mutation\", \"internal\">;\n verifierDelete: FunctionReference<\"mutation\", \"internal\">;\n verificationCodeGetByAccountId: FunctionReference<\"query\", \"internal\">;\n verificationCodeGetByCode: FunctionReference<\"query\", \"internal\">;\n verificationCodeCreate: FunctionReference<\"mutation\", \"internal\">;\n verificationCodeDelete: FunctionReference<\"mutation\", \"internal\">;\n refreshTokenCreate: FunctionReference<\"mutation\", \"internal\">;\n refreshTokenGetById: FunctionReference<\"query\", \"internal\">;\n refreshTokenPatch: FunctionReference<\"mutation\", \"internal\">;\n refreshTokenGetChildren: FunctionReference<\"query\", \"internal\">;\n refreshTokenListBySession: FunctionReference<\"query\", \"internal\">;\n refreshTokenDeleteAll: FunctionReference<\"mutation\", \"internal\">;\n refreshTokenGetActive: FunctionReference<\"query\", \"internal\">;\n rateLimitGet: FunctionReference<\"query\", \"internal\">;\n rateLimitCreate: FunctionReference<\"mutation\", \"internal\">;\n rateLimitPatch: FunctionReference<\"mutation\", \"internal\">;\n rateLimitDelete: FunctionReference<\"mutation\", \"internal\">;\n };\n};\n\nexport type AuthDbConfig = { component: AuthComponentApiLike };\n\nexport type AuthDb = ReturnType<typeof authDb>;\n\nexport function authDb(ctx: CtxLike, config: AuthDbConfig) {\n const component = config.component;\n return {\n users: {\n getById: (userId: string) =>\n ctx.runQuery(component.public.userGetById, { userId }),\n findByVerifiedEmail: (email: string) =>\n ctx.runQuery(component.public.userFindByVerifiedEmail, { email }),\n findByVerifiedPhone: (phone: string) =>\n ctx.runQuery(component.public.userFindByVerifiedPhone, { phone }),\n insert: (data: Record<string, unknown>) =>\n ctx.runMutation(component.public.userInsert, {\n data,\n }) as Promise<string>,\n patch: (userId: string, data: Record<string, unknown>) =>\n ctx.runMutation(component.public.userPatch, { userId, data }),\n upsert: (userId: string | undefined, data: Record<string, unknown>) =>\n ctx.runMutation(component.public.userUpsert, {\n userId,\n data,\n }) as Promise<string>,\n },\n accounts: {\n get: (provider: string, providerAccountId: string) =>\n ctx.runQuery(component.public.accountGet, {\n provider,\n providerAccountId,\n }),\n getById: (accountId: string) =>\n ctx.runQuery(component.public.accountGetById, { accountId }),\n create: (args: {\n userId: string;\n provider: string;\n providerAccountId: string;\n secret?: string;\n extend?: Record<string, unknown>;\n }) =>\n ctx.runMutation(\n component.public.accountInsert,\n args,\n ) as Promise<string>,\n patch: (accountId: string, data: Record<string, unknown>) =>\n ctx.runMutation(component.public.accountPatch, { accountId, data }),\n delete: (accountId: string) =>\n ctx.runMutation(component.public.accountDelete, { accountId }),\n },\n sessions: {\n create: (userId: string, expirationTime: number) =>\n ctx.runMutation(component.public.sessionCreate, {\n userId,\n expirationTime,\n }) as Promise<string>,\n getById: (sessionId: string) =>\n ctx.runQuery(component.public.sessionGetById, { sessionId }),\n delete: (sessionId: string) =>\n ctx.runMutation(component.public.sessionDelete, { sessionId }),\n listByUser: (userId: string) =>\n ctx.runQuery(component.public.sessionListByUser, { userId }),\n },\n verifiers: {\n create: (sessionId?: string) =>\n ctx.runMutation(component.public.verifierCreate, {\n sessionId,\n }) as Promise<string>,\n getById: (verifierId: string) =>\n ctx.runQuery(component.public.verifierGetById, { verifierId }),\n getBySignature: (signature: string) =>\n ctx.runQuery(component.public.verifierGetBySignature, { signature }),\n patch: (verifierId: string, data: Record<string, unknown>) =>\n ctx.runMutation(component.public.verifierPatch, { verifierId, data }),\n delete: (verifierId: string) =>\n ctx.runMutation(component.public.verifierDelete, { verifierId }),\n },\n verificationCodes: {\n getByAccountId: (accountId: string) =>\n ctx.runQuery(component.public.verificationCodeGetByAccountId, {\n accountId,\n }),\n getByCode: (code: string) =>\n ctx.runQuery(component.public.verificationCodeGetByCode, { code }),\n create: (args: {\n accountId: string;\n provider: string;\n code: string;\n expirationTime: number;\n verifier?: string;\n emailVerified?: string;\n phoneVerified?: string;\n }) => ctx.runMutation(component.public.verificationCodeCreate, args),\n delete: (verificationCodeId: string) =>\n ctx.runMutation(component.public.verificationCodeDelete, {\n verificationCodeId,\n }),\n },\n refreshTokens: {\n create: (args: {\n sessionId: string;\n expirationTime: number;\n parentRefreshTokenId?: string;\n }) =>\n ctx.runMutation(\n component.public.refreshTokenCreate,\n args,\n ) as Promise<string>,\n getById: (refreshTokenId: string) =>\n ctx.runQuery(component.public.refreshTokenGetById, { refreshTokenId }),\n patch: (refreshTokenId: string, data: Record<string, unknown>) =>\n ctx.runMutation(component.public.refreshTokenPatch, {\n refreshTokenId,\n data,\n }),\n getChildren: (sessionId: string, parentRefreshTokenId: string) =>\n ctx.runQuery(component.public.refreshTokenGetChildren, {\n sessionId,\n parentRefreshTokenId,\n }),\n listBySession: (sessionId: string) =>\n ctx.runQuery(component.public.refreshTokenListBySession, { sessionId }),\n deleteAll: (sessionId: string) =>\n ctx.runMutation(component.public.refreshTokenDeleteAll, { sessionId }),\n getActive: (sessionId: string) =>\n ctx.runQuery(component.public.refreshTokenGetActive, { sessionId }),\n },\n rateLimits: {\n get: (identifier: string) =>\n ctx.runQuery(component.public.rateLimitGet, { identifier }),\n create: (args: {\n identifier: string;\n attemptsLeft: number;\n lastAttemptTime: number;\n }) => ctx.runMutation(component.public.rateLimitCreate, args),\n patch: (rateLimitId: string, data: Record<string, unknown>) =>\n ctx.runMutation(component.public.rateLimitPatch, { rateLimitId, data }),\n delete: (rateLimitId: string) =>\n ctx.runMutation(component.public.rateLimitDelete, { rateLimitId }),\n },\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"db.js","names":[],"sources":["../../../src/server/db.ts"],"sourcesContent":["import {\n GenericActionCtx,\n GenericDataModel,\n GenericMutationCtx,\n FunctionReference,\n} from \"convex/server\";\n\ntype MutationCtxLike = Pick<\n GenericMutationCtx<GenericDataModel>,\n \"runQuery\" | \"runMutation\"\n>;\ntype ActionCtxLike = Pick<\n GenericActionCtx<GenericDataModel>,\n \"runQuery\" | \"runMutation\" | \"runAction\"\n>;\n\ntype CtxLike = MutationCtxLike | ActionCtxLike;\n\ntype AuthComponentApiLike = {\n public: {\n userGetById: FunctionReference<\"query\", \"internal\">;\n userFindByVerifiedEmail: FunctionReference<\"query\", \"internal\">;\n userFindByVerifiedPhone: FunctionReference<\"query\", \"internal\">;\n userInsert: FunctionReference<\"mutation\", \"internal\">;\n userPatch: FunctionReference<\"mutation\", \"internal\">;\n userUpsert: FunctionReference<\"mutation\", \"internal\">;\n accountGet: FunctionReference<\"query\", \"internal\">;\n accountGetById: FunctionReference<\"query\", \"internal\">;\n accountInsert: FunctionReference<\"mutation\", \"internal\">;\n accountPatch: FunctionReference<\"mutation\", \"internal\">;\n accountDelete: FunctionReference<\"mutation\", \"internal\">;\n sessionCreate: FunctionReference<\"mutation\", \"internal\">;\n sessionGetById: FunctionReference<\"query\", \"internal\">;\n sessionDelete: FunctionReference<\"mutation\", \"internal\">;\n sessionListByUser: FunctionReference<\"query\", \"internal\">;\n verifierCreate: FunctionReference<\"mutation\", \"internal\">;\n verifierGetById: FunctionReference<\"query\", \"internal\">;\n verifierGetBySignature: FunctionReference<\"query\", \"internal\">;\n verifierPatch: FunctionReference<\"mutation\", \"internal\">;\n verifierDelete: FunctionReference<\"mutation\", \"internal\">;\n verificationCodeGetByAccountId: FunctionReference<\"query\", \"internal\">;\n verificationCodeGetByCode: FunctionReference<\"query\", \"internal\">;\n verificationCodeCreate: FunctionReference<\"mutation\", \"internal\">;\n verificationCodeDelete: FunctionReference<\"mutation\", \"internal\">;\n refreshTokenCreate: FunctionReference<\"mutation\", \"internal\">;\n refreshTokenGetById: FunctionReference<\"query\", \"internal\">;\n refreshTokenPatch: FunctionReference<\"mutation\", \"internal\">;\n refreshTokenGetChildren: FunctionReference<\"query\", \"internal\">;\n refreshTokenListBySession: FunctionReference<\"query\", \"internal\">;\n refreshTokenDeleteAll: FunctionReference<\"mutation\", \"internal\">;\n refreshTokenGetActive: FunctionReference<\"query\", \"internal\">;\n rateLimitGet: FunctionReference<\"query\", \"internal\">;\n rateLimitCreate: FunctionReference<\"mutation\", \"internal\">;\n rateLimitPatch: FunctionReference<\"mutation\", \"internal\">;\n rateLimitDelete: FunctionReference<\"mutation\", \"internal\">;\n };\n};\n\n/** @internal */\nexport type AuthDbConfig = { component: AuthComponentApiLike };\n\n/** @internal */\nexport type AuthDb = ReturnType<typeof authDb>;\n\n/** @internal */\nexport function authDb(ctx: CtxLike, config: AuthDbConfig) {\n const component = config.component;\n return {\n users: {\n getById: (userId: string) =>\n ctx.runQuery(component.public.userGetById, { userId }),\n findByVerifiedEmail: (email: string) =>\n ctx.runQuery(component.public.userFindByVerifiedEmail, { email }),\n findByVerifiedPhone: (phone: string) =>\n ctx.runQuery(component.public.userFindByVerifiedPhone, { phone }),\n insert: (data: Record<string, unknown>) =>\n ctx.runMutation(component.public.userInsert, {\n data,\n }) as Promise<string>,\n patch: (userId: string, data: Record<string, unknown>) =>\n ctx.runMutation(component.public.userPatch, { userId, data }),\n upsert: (userId: string | undefined, data: Record<string, unknown>) =>\n ctx.runMutation(component.public.userUpsert, {\n userId,\n data,\n }) as Promise<string>,\n },\n accounts: {\n get: (provider: string, providerAccountId: string) =>\n ctx.runQuery(component.public.accountGet, {\n provider,\n providerAccountId,\n }),\n getById: (accountId: string) =>\n ctx.runQuery(component.public.accountGetById, { accountId }),\n create: (args: {\n userId: string;\n provider: string;\n providerAccountId: string;\n secret?: string;\n extend?: Record<string, unknown>;\n }) =>\n ctx.runMutation(\n component.public.accountInsert,\n args,\n ) as Promise<string>,\n patch: (accountId: string, data: Record<string, unknown>) =>\n ctx.runMutation(component.public.accountPatch, { accountId, data }),\n delete: (accountId: string) =>\n ctx.runMutation(component.public.accountDelete, { accountId }),\n },\n sessions: {\n create: (userId: string, expirationTime: number) =>\n ctx.runMutation(component.public.sessionCreate, {\n userId,\n expirationTime,\n }) as Promise<string>,\n getById: (sessionId: string) =>\n ctx.runQuery(component.public.sessionGetById, { sessionId }),\n delete: (sessionId: string) =>\n ctx.runMutation(component.public.sessionDelete, { sessionId }),\n listByUser: (userId: string) =>\n ctx.runQuery(component.public.sessionListByUser, { userId }),\n },\n verifiers: {\n create: (sessionId?: string) =>\n ctx.runMutation(component.public.verifierCreate, {\n sessionId,\n }) as Promise<string>,\n getById: (verifierId: string) =>\n ctx.runQuery(component.public.verifierGetById, { verifierId }),\n getBySignature: (signature: string) =>\n ctx.runQuery(component.public.verifierGetBySignature, { signature }),\n patch: (verifierId: string, data: Record<string, unknown>) =>\n ctx.runMutation(component.public.verifierPatch, { verifierId, data }),\n delete: (verifierId: string) =>\n ctx.runMutation(component.public.verifierDelete, { verifierId }),\n },\n verificationCodes: {\n getByAccountId: (accountId: string) =>\n ctx.runQuery(component.public.verificationCodeGetByAccountId, {\n accountId,\n }),\n getByCode: (code: string) =>\n ctx.runQuery(component.public.verificationCodeGetByCode, { code }),\n create: (args: {\n accountId: string;\n provider: string;\n code: string;\n expirationTime: number;\n verifier?: string;\n emailVerified?: string;\n phoneVerified?: string;\n }) => ctx.runMutation(component.public.verificationCodeCreate, args),\n delete: (verificationCodeId: string) =>\n ctx.runMutation(component.public.verificationCodeDelete, {\n verificationCodeId,\n }),\n },\n refreshTokens: {\n create: (args: {\n sessionId: string;\n expirationTime: number;\n parentRefreshTokenId?: string;\n }) =>\n ctx.runMutation(\n component.public.refreshTokenCreate,\n args,\n ) as Promise<string>,\n getById: (refreshTokenId: string) =>\n ctx.runQuery(component.public.refreshTokenGetById, { refreshTokenId }),\n patch: (refreshTokenId: string, data: Record<string, unknown>) =>\n ctx.runMutation(component.public.refreshTokenPatch, {\n refreshTokenId,\n data,\n }),\n getChildren: (sessionId: string, parentRefreshTokenId: string) =>\n ctx.runQuery(component.public.refreshTokenGetChildren, {\n sessionId,\n parentRefreshTokenId,\n }),\n listBySession: (sessionId: string) =>\n ctx.runQuery(component.public.refreshTokenListBySession, { sessionId }),\n deleteAll: (sessionId: string) =>\n ctx.runMutation(component.public.refreshTokenDeleteAll, { sessionId }),\n getActive: (sessionId: string) =>\n ctx.runQuery(component.public.refreshTokenGetActive, { sessionId }),\n },\n rateLimits: {\n get: (identifier: string) =>\n ctx.runQuery(component.public.rateLimitGet, { identifier }),\n create: (args: {\n identifier: string;\n attemptsLeft: number;\n lastAttemptTime: number;\n }) => ctx.runMutation(component.public.rateLimitCreate, args),\n patch: (rateLimitId: string, data: Record<string, unknown>) =>\n ctx.runMutation(component.public.rateLimitPatch, { rateLimitId, data }),\n delete: (rateLimitId: string) =>\n ctx.runMutation(component.public.rateLimitDelete, { rateLimitId }),\n },\n };\n}\n"],"mappings":"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"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { AuthError } from "./fx.js";
|
|
2
2
|
import { generateRandomString, requireEnv, sha256 } from "./utils.js";
|
|
3
|
+
import { userIdFromIdentitySubject } from "./identity.js";
|
|
3
4
|
import { callSignIn } from "./mutations/signin.js";
|
|
4
5
|
import { mutateDeviceAuthorize, mutateDeviceDelete, mutateDeviceInsert, mutateDeviceUpdateLastPolled, queryDeviceByCodeHash, queryDeviceByUserCode } from "./types.js";
|
|
5
6
|
import { Fx } from "@robelest/fx";
|
|
@@ -23,6 +24,7 @@ const DEVICE_FLOWS = [
|
|
|
23
24
|
"poll",
|
|
24
25
|
"verify"
|
|
25
26
|
];
|
|
27
|
+
/** @internal */
|
|
26
28
|
const handleDevice = (ctx, provider, args) => Fx.from({
|
|
27
29
|
ok: async () => {
|
|
28
30
|
const params = args.params ?? {};
|
|
@@ -81,7 +83,7 @@ const handleDevice = (ctx, provider, args) => Fx.from({
|
|
|
81
83
|
if (typeof params.userCode !== "string") throw new AuthError("DEVICE_INVALID_USER_CODE", "Missing `userCode` parameter for verify flow.");
|
|
82
84
|
const identity = await ctx.auth.getUserIdentity();
|
|
83
85
|
if (identity === null) throw new AuthError("NOT_SIGNED_IN", "You must be signed in to authorize a device.");
|
|
84
|
-
const userId = identity.subject
|
|
86
|
+
const userId = userIdFromIdentitySubject(identity.subject);
|
|
85
87
|
const doc = await queryDeviceByUserCode(ctx, params.userCode);
|
|
86
88
|
if (doc === null) throw new AuthError("DEVICE_INVALID_USER_CODE");
|
|
87
89
|
if (Date.now() > doc.expiresAt) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"device.js","names":["doc"],"sources":["../../../src/server/device.ts"],"sourcesContent":["/**\n * Server-side device authorization flow logic (RFC 8628).\n *\n * Handles the three phases of the device flow:\n * 1. (default) — Generate a device code + user code pair\n * 2. poll — Device checks whether the user has authorized yet\n * 3. verify — Authenticated user links a user code to their session\n *\n * Uses `@oslojs/crypto/random` for code generation and\n * `@oslojs/crypto/sha2` for hashing device codes before storage.\n */\n\nimport { Fx } from \"@robelest/fx\";\n\nimport { AuthError } from \"./fx\";\nimport { callSignIn } from \"./mutations/index\";\nimport { DeviceProviderConfig, GenericActionCtxWithAuthConfig } from \"./types\";\nimport {\n AuthDataModel,\n SessionInfo,\n mutateDeviceInsert,\n queryDeviceByCodeHash,\n queryDeviceByUserCode,\n mutateDeviceAuthorize,\n mutateDeviceUpdateLastPolled,\n mutateDeviceDelete,\n} from \"./types\";\nimport { generateRandomString, sha256 } from \"./utils\";\nimport { requireEnv } from \"./utils\";\n\ntype EnrichedActionCtx = GenericActionCtxWithAuthConfig<AuthDataModel>;\n\n// ============================================================================\n// Constants\n// ============================================================================\n\nconst DEVICE_CODE_ALPHABET =\n \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\";\nconst DEVICE_CODE_LENGTH = 40;\nconst DEVICE_FLOWS = [\"create\", \"poll\", \"verify\"] as const;\n\n// ============================================================================\n// Create flow\n// ============================================================================\n\n// ============================================================================\n// Poll flow — pipeline of validations + status dispatch\n// ============================================================================\n\n// ============================================================================\n// Main dispatch\n// ============================================================================\n\ntype DeviceResult =\n | {\n kind: \"deviceCode\";\n deviceCode: string;\n userCode: string;\n verificationUri: string;\n verificationUriComplete: string;\n expiresIn: number;\n interval: number;\n }\n | { kind: \"signedIn\"; signedIn: SessionInfo | null };\n\nexport const handleDevice = (\n ctx: EnrichedActionCtx,\n provider: DeviceProviderConfig,\n args: { params?: Record<string, any> },\n): Fx<DeviceResult, AuthError> =>\n Fx.from({\n ok: async () => {\n const params = (args.params ?? {}) as Record<string, unknown>;\n const flow = (typeof params.flow === \"string\" ? params.flow : \"create\") as\n | \"create\"\n | \"poll\"\n | \"verify\";\n\n if (!DEVICE_FLOWS.some((candidate) => candidate === flow)) {\n throw new AuthError(\n \"DEVICE_MISSING_FLOW\",\n \"Missing `flow` parameter. Expected one of: create, poll, verify\",\n );\n }\n\n if (flow === \"create\") {\n const deviceCode = generateRandomString(\n DEVICE_CODE_LENGTH,\n DEVICE_CODE_ALPHABET,\n );\n const deviceCodeHash = await sha256(deviceCode);\n\n const rawUserCode = generateRandomString(\n provider.userCodeLength,\n provider.charset,\n );\n const mid = Math.floor(rawUserCode.length / 2);\n const userCode =\n rawUserCode.slice(0, mid) + \"-\" + rawUserCode.slice(mid);\n\n const expiresAt = Date.now() + provider.expiresIn * 1000;\n await mutateDeviceInsert(ctx, {\n deviceCodeHash,\n userCode,\n expiresAt,\n interval: provider.interval,\n status: \"pending\",\n });\n\n const verificationUri =\n provider.verificationUri ??\n `${process.env.SITE_URL ?? requireEnv(\"SITE_URL\")}/device`;\n\n return {\n kind: \"deviceCode\" as const,\n deviceCode,\n userCode,\n verificationUri,\n verificationUriComplete: `${verificationUri}?user_code=${encodeURIComponent(userCode)}`,\n expiresIn: provider.expiresIn,\n interval: provider.interval,\n };\n }\n\n if (flow === \"poll\") {\n if (typeof params.deviceCode !== \"string\") {\n throw new AuthError(\n \"DEVICE_MISSING_FLOW\",\n \"Missing `deviceCode` parameter for poll flow.\",\n );\n }\n\n const hash = await sha256(params.deviceCode);\n const doc = await queryDeviceByCodeHash(ctx, hash);\n if (doc === null) {\n throw new AuthError(\"DEVICE_CODE_EXPIRED\");\n }\n if (Date.now() > doc.expiresAt) {\n await mutateDeviceDelete(ctx, doc._id);\n throw new AuthError(\"DEVICE_CODE_EXPIRED\");\n }\n if (\n doc.lastPolledAt !== undefined &&\n (Date.now() - doc.lastPolledAt) / 1000 < doc.interval\n ) {\n throw new AuthError(\"DEVICE_SLOW_DOWN\");\n }\n\n await mutateDeviceUpdateLastPolled(ctx, doc._id, Date.now());\n\n if (doc.status === \"pending\") {\n throw new AuthError(\"DEVICE_AUTHORIZATION_PENDING\");\n }\n if (doc.status === \"denied\") {\n await mutateDeviceDelete(ctx, doc._id);\n throw new AuthError(\"DEVICE_CODE_DENIED\");\n }\n\n if (!doc.userId || !doc.sessionId) {\n throw new AuthError(\n \"INTERNAL_ERROR\",\n \"Authorized device code missing userId or sessionId\",\n );\n }\n\n await mutateDeviceDelete(ctx, doc._id);\n const signInResult = await callSignIn(ctx, {\n userId: doc.userId,\n sessionId: doc.sessionId,\n generateTokens: true,\n });\n return { kind: \"signedIn\" as const, signedIn: signInResult };\n }\n\n if (typeof params.userCode !== \"string\") {\n throw new AuthError(\n \"DEVICE_INVALID_USER_CODE\",\n \"Missing `userCode` parameter for verify flow.\",\n );\n }\n\n const identity = await ctx.auth.getUserIdentity();\n if (identity === null) {\n throw new AuthError(\n \"NOT_SIGNED_IN\",\n \"You must be signed in to authorize a device.\",\n );\n }\n\n const userId = identity.subject
|
|
1
|
+
{"version":3,"file":"device.js","names":["doc"],"sources":["../../../src/server/device.ts"],"sourcesContent":["/**\n * Server-side device authorization flow logic (RFC 8628).\n *\n * Handles the three phases of the device flow:\n * 1. (default) — Generate a device code + user code pair\n * 2. poll — Device checks whether the user has authorized yet\n * 3. verify — Authenticated user links a user code to their session\n *\n * Uses `@oslojs/crypto/random` for code generation and\n * `@oslojs/crypto/sha2` for hashing device codes before storage.\n */\n\nimport { Fx } from \"@robelest/fx\";\n\nimport { AuthError } from \"./fx\";\nimport { userIdFromIdentitySubject } from \"./identity\";\nimport { callSignIn } from \"./mutations/index\";\nimport { DeviceProviderConfig, GenericActionCtxWithAuthConfig } from \"./types\";\nimport {\n AuthDataModel,\n SessionInfo,\n mutateDeviceInsert,\n queryDeviceByCodeHash,\n queryDeviceByUserCode,\n mutateDeviceAuthorize,\n mutateDeviceUpdateLastPolled,\n mutateDeviceDelete,\n} from \"./types\";\nimport { generateRandomString, sha256 } from \"./utils\";\nimport { requireEnv } from \"./utils\";\n\ntype EnrichedActionCtx = GenericActionCtxWithAuthConfig<AuthDataModel>;\n\n// ============================================================================\n// Constants\n// ============================================================================\n\nconst DEVICE_CODE_ALPHABET =\n \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\";\nconst DEVICE_CODE_LENGTH = 40;\nconst DEVICE_FLOWS = [\"create\", \"poll\", \"verify\"] as const;\n\n// ============================================================================\n// Create flow\n// ============================================================================\n\n// ============================================================================\n// Poll flow — pipeline of validations + status dispatch\n// ============================================================================\n\n// ============================================================================\n// Main dispatch\n// ============================================================================\n\ntype DeviceResult =\n | {\n kind: \"deviceCode\";\n deviceCode: string;\n userCode: string;\n verificationUri: string;\n verificationUriComplete: string;\n expiresIn: number;\n interval: number;\n }\n | { kind: \"signedIn\"; signedIn: SessionInfo | null };\n\n/** @internal */\nexport const handleDevice = (\n ctx: EnrichedActionCtx,\n provider: DeviceProviderConfig,\n args: { params?: Record<string, any> },\n): Fx<DeviceResult, AuthError> =>\n Fx.from({\n ok: async () => {\n const params = (args.params ?? {}) as Record<string, unknown>;\n const flow = (typeof params.flow === \"string\" ? params.flow : \"create\") as\n | \"create\"\n | \"poll\"\n | \"verify\";\n\n if (!DEVICE_FLOWS.some((candidate) => candidate === flow)) {\n throw new AuthError(\n \"DEVICE_MISSING_FLOW\",\n \"Missing `flow` parameter. Expected one of: create, poll, verify\",\n );\n }\n\n if (flow === \"create\") {\n const deviceCode = generateRandomString(\n DEVICE_CODE_LENGTH,\n DEVICE_CODE_ALPHABET,\n );\n const deviceCodeHash = await sha256(deviceCode);\n\n const rawUserCode = generateRandomString(\n provider.userCodeLength,\n provider.charset,\n );\n const mid = Math.floor(rawUserCode.length / 2);\n const userCode =\n rawUserCode.slice(0, mid) + \"-\" + rawUserCode.slice(mid);\n\n const expiresAt = Date.now() + provider.expiresIn * 1000;\n await mutateDeviceInsert(ctx, {\n deviceCodeHash,\n userCode,\n expiresAt,\n interval: provider.interval,\n status: \"pending\",\n });\n\n const verificationUri =\n provider.verificationUri ??\n `${process.env.SITE_URL ?? requireEnv(\"SITE_URL\")}/device`;\n\n return {\n kind: \"deviceCode\" as const,\n deviceCode,\n userCode,\n verificationUri,\n verificationUriComplete: `${verificationUri}?user_code=${encodeURIComponent(userCode)}`,\n expiresIn: provider.expiresIn,\n interval: provider.interval,\n };\n }\n\n if (flow === \"poll\") {\n if (typeof params.deviceCode !== \"string\") {\n throw new AuthError(\n \"DEVICE_MISSING_FLOW\",\n \"Missing `deviceCode` parameter for poll flow.\",\n );\n }\n\n const hash = await sha256(params.deviceCode);\n const doc = await queryDeviceByCodeHash(ctx, hash);\n if (doc === null) {\n throw new AuthError(\"DEVICE_CODE_EXPIRED\");\n }\n if (Date.now() > doc.expiresAt) {\n await mutateDeviceDelete(ctx, doc._id);\n throw new AuthError(\"DEVICE_CODE_EXPIRED\");\n }\n if (\n doc.lastPolledAt !== undefined &&\n (Date.now() - doc.lastPolledAt) / 1000 < doc.interval\n ) {\n throw new AuthError(\"DEVICE_SLOW_DOWN\");\n }\n\n await mutateDeviceUpdateLastPolled(ctx, doc._id, Date.now());\n\n if (doc.status === \"pending\") {\n throw new AuthError(\"DEVICE_AUTHORIZATION_PENDING\");\n }\n if (doc.status === \"denied\") {\n await mutateDeviceDelete(ctx, doc._id);\n throw new AuthError(\"DEVICE_CODE_DENIED\");\n }\n\n if (!doc.userId || !doc.sessionId) {\n throw new AuthError(\n \"INTERNAL_ERROR\",\n \"Authorized device code missing userId or sessionId\",\n );\n }\n\n await mutateDeviceDelete(ctx, doc._id);\n const signInResult = await callSignIn(ctx, {\n userId: doc.userId,\n sessionId: doc.sessionId,\n generateTokens: true,\n });\n return { kind: \"signedIn\" as const, signedIn: signInResult };\n }\n\n if (typeof params.userCode !== \"string\") {\n throw new AuthError(\n \"DEVICE_INVALID_USER_CODE\",\n \"Missing `userCode` parameter for verify flow.\",\n );\n }\n\n const identity = await ctx.auth.getUserIdentity();\n if (identity === null) {\n throw new AuthError(\n \"NOT_SIGNED_IN\",\n \"You must be signed in to authorize a device.\",\n );\n }\n\n const userId = userIdFromIdentitySubject(identity.subject);\n const doc = await queryDeviceByUserCode(ctx, params.userCode);\n if (doc === null) {\n throw new AuthError(\"DEVICE_INVALID_USER_CODE\");\n }\n if (Date.now() > doc.expiresAt) {\n await mutateDeviceDelete(ctx, doc._id);\n throw new AuthError(\"DEVICE_CODE_EXPIRED\");\n }\n if (doc.status !== \"pending\") {\n throw new AuthError(\"DEVICE_ALREADY_AUTHORIZED\");\n }\n\n const signInResult = await callSignIn(ctx, {\n userId,\n generateTokens: false,\n });\n await mutateDeviceAuthorize(\n ctx,\n doc._id,\n signInResult.userId,\n signInResult.sessionId,\n );\n return { kind: \"signedIn\" as const, signedIn: null };\n },\n err: (e) =>\n e instanceof AuthError\n ? e\n : new AuthError(\"INTERNAL_ERROR\", `Device flow failed: ${String(e)}`),\n });\n"],"mappings":"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"}
|