@robelest/convex-auth 0.0.4-preview.21 → 0.0.4-preview.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/authorization/index.d.ts +1 -1
- package/dist/authorization/index.js +1 -1
- package/dist/authorization/index.js.map +1 -1
- package/dist/client/index.d.ts +1 -2
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +36 -39
- package/dist/client/index.js.map +1 -1
- package/dist/component/client/index.d.ts +1 -2
- package/dist/component/convex.config.d.ts +2 -2
- package/dist/component/convex.config.d.ts.map +1 -1
- package/dist/component/model.d.ts +5 -5
- package/dist/component/model.d.ts.map +1 -1
- package/dist/component/public/enterprise/audit.d.ts.map +1 -1
- package/dist/component/public/enterprise/audit.js.map +1 -1
- package/dist/component/public/enterprise/core.d.ts.map +1 -1
- package/dist/component/public/enterprise/core.js.map +1 -1
- package/dist/component/public/enterprise/domains.d.ts.map +1 -1
- package/dist/component/public/enterprise/domains.js.map +1 -1
- package/dist/component/public/enterprise/scim.d.ts.map +1 -1
- package/dist/component/public/enterprise/scim.js.map +1 -1
- package/dist/component/public/enterprise/secrets.d.ts.map +1 -1
- package/dist/component/public/enterprise/secrets.js.map +1 -1
- package/dist/component/public/enterprise/webhooks.d.ts.map +1 -1
- package/dist/component/public/enterprise/webhooks.js.map +1 -1
- package/dist/component/public/factors/devices.d.ts.map +1 -1
- package/dist/component/public/factors/devices.js.map +1 -1
- package/dist/component/public/factors/passkeys.d.ts.map +1 -1
- package/dist/component/public/factors/passkeys.js.map +1 -1
- package/dist/component/public/factors/totp.d.ts.map +1 -1
- package/dist/component/public/factors/totp.js.map +1 -1
- package/dist/component/public/groups/core.js.map +1 -1
- package/dist/component/public/groups/invites.d.ts.map +1 -1
- package/dist/component/public/groups/invites.js.map +1 -1
- package/dist/component/public/groups/members.d.ts.map +1 -1
- package/dist/component/public/groups/members.js.map +1 -1
- package/dist/component/public/identity/accounts.d.ts.map +1 -1
- package/dist/component/public/identity/accounts.js.map +1 -1
- package/dist/component/public/identity/codes.d.ts.map +1 -1
- package/dist/component/public/identity/codes.js.map +1 -1
- package/dist/component/public/identity/sessions.d.ts.map +1 -1
- package/dist/component/public/identity/sessions.js.map +1 -1
- package/dist/component/public/identity/tokens.d.ts.map +1 -1
- package/dist/component/public/identity/tokens.js.map +1 -1
- package/dist/component/public/identity/users.d.ts.map +1 -1
- package/dist/component/public/identity/users.js.map +1 -1
- package/dist/component/public/identity/verifiers.d.ts.map +1 -1
- package/dist/component/public/identity/verifiers.js.map +1 -1
- package/dist/component/public/security/keys.d.ts.map +1 -1
- package/dist/component/public/security/keys.js.map +1 -1
- package/dist/component/public/security/limits.d.ts.map +1 -1
- package/dist/component/public/security/limits.js.map +1 -1
- package/dist/component/schema.d.ts +39 -39
- package/dist/component/server/auth.d.ts +95 -52
- package/dist/component/server/auth.d.ts.map +1 -1
- package/dist/component/server/auth.js +63 -43
- package/dist/component/server/auth.js.map +1 -1
- package/dist/component/server/core.js +116 -235
- package/dist/component/server/core.js.map +1 -1
- package/dist/component/server/crypto.js +25 -7
- package/dist/component/server/crypto.js.map +1 -1
- package/dist/component/server/device.js +58 -15
- package/dist/component/server/device.js.map +1 -1
- package/dist/component/server/enterprise/domain.js +148 -59
- package/dist/component/server/enterprise/domain.js.map +1 -1
- package/dist/component/server/enterprise/http.js +36 -15
- package/dist/component/server/enterprise/http.js.map +1 -1
- package/dist/component/server/enterprise/oidc.js +1 -1
- package/dist/component/server/http.js +26 -21
- package/dist/component/server/http.js.map +1 -1
- package/dist/component/server/identity.js +5 -2
- package/dist/component/server/identity.js.map +1 -1
- package/dist/component/server/limits.js +21 -30
- package/dist/component/server/limits.js.map +1 -1
- package/dist/component/server/mutations/account.js +12 -10
- package/dist/component/server/mutations/account.js.map +1 -1
- package/dist/component/server/mutations/code.js +5 -2
- package/dist/component/server/mutations/code.js.map +1 -1
- package/dist/component/server/mutations/invalidate.js +1 -1
- package/dist/component/server/mutations/invalidate.js.map +1 -1
- package/dist/component/server/mutations/oauth.js +10 -4
- package/dist/component/server/mutations/oauth.js.map +1 -1
- package/dist/component/server/mutations/refresh.js +2 -2
- package/dist/component/server/mutations/refresh.js.map +1 -1
- package/dist/component/server/mutations/register.js +46 -42
- package/dist/component/server/mutations/register.js.map +1 -1
- package/dist/component/server/mutations/retrieve.js +21 -25
- package/dist/component/server/mutations/retrieve.js.map +1 -1
- package/dist/component/server/mutations/signature.js +10 -4
- package/dist/component/server/mutations/signature.js.map +1 -1
- package/dist/component/server/mutations/signout.js.map +1 -1
- package/dist/component/server/mutations/store.js +9 -24
- package/dist/component/server/mutations/store.js.map +1 -1
- package/dist/component/server/mutations/verifier.js.map +1 -1
- package/dist/component/server/mutations/verify.js +1 -1
- package/dist/component/server/mutations/verify.js.map +1 -1
- package/dist/component/server/oauth.js +53 -16
- package/dist/component/server/oauth.js.map +1 -1
- package/dist/component/server/passkey.js +115 -31
- package/dist/component/server/passkey.js.map +1 -1
- package/dist/component/server/redirects.js +9 -3
- package/dist/component/server/redirects.js.map +1 -1
- package/dist/component/server/refresh.js +10 -7
- package/dist/component/server/refresh.js.map +1 -1
- package/dist/component/server/runtime.d.ts +3 -3
- package/dist/component/server/runtime.d.ts.map +1 -1
- package/dist/component/server/runtime.js +62 -20
- package/dist/component/server/runtime.js.map +1 -1
- package/dist/component/server/signin.js +34 -10
- package/dist/component/server/signin.js.map +1 -1
- package/dist/component/server/totp.js +79 -19
- package/dist/component/server/totp.js.map +1 -1
- package/dist/component/server/types.d.ts +12 -20
- package/dist/component/server/types.d.ts.map +1 -1
- package/dist/component/server/types.js.map +1 -1
- package/dist/component/server/users.js +6 -3
- package/dist/component/server/users.js.map +1 -1
- package/dist/component/server/utils.js +10 -4
- package/dist/component/server/utils.js.map +1 -1
- package/dist/core/types.d.ts +14 -22
- package/dist/core/types.d.ts.map +1 -1
- package/dist/factors/device.js +8 -9
- package/dist/factors/device.js.map +1 -1
- package/dist/factors/passkey.js +18 -21
- package/dist/factors/passkey.js.map +1 -1
- package/dist/providers/password.js +66 -81
- package/dist/providers/password.js.map +1 -1
- package/dist/runtime/invite.js +2 -8
- package/dist/runtime/invite.js.map +1 -1
- package/dist/server/auth.d.ts +95 -52
- package/dist/server/auth.d.ts.map +1 -1
- package/dist/server/auth.js +63 -43
- package/dist/server/auth.js.map +1 -1
- package/dist/server/core.d.ts +71 -159
- package/dist/server/core.d.ts.map +1 -1
- package/dist/server/core.js +116 -235
- package/dist/server/core.js.map +1 -1
- package/dist/server/crypto.d.ts.map +1 -1
- package/dist/server/crypto.js +25 -7
- package/dist/server/crypto.js.map +1 -1
- package/dist/server/device.js +58 -15
- package/dist/server/device.js.map +1 -1
- package/dist/server/enterprise/domain.d.ts +0 -8
- package/dist/server/enterprise/domain.d.ts.map +1 -1
- package/dist/server/enterprise/domain.js +148 -59
- package/dist/server/enterprise/domain.js.map +1 -1
- package/dist/server/enterprise/http.d.ts.map +1 -1
- package/dist/server/enterprise/http.js +35 -14
- package/dist/server/enterprise/http.js.map +1 -1
- package/dist/server/http.d.ts +2 -2
- package/dist/server/http.d.ts.map +1 -1
- package/dist/server/http.js +25 -20
- package/dist/server/http.js.map +1 -1
- package/dist/server/identity.js +5 -2
- package/dist/server/identity.js.map +1 -1
- package/dist/server/index.d.ts +2 -2
- package/dist/server/limits.js +21 -30
- package/dist/server/limits.js.map +1 -1
- package/dist/server/mounts.d.ts +26 -64
- package/dist/server/mounts.d.ts.map +1 -1
- package/dist/server/mounts.js +45 -106
- package/dist/server/mounts.js.map +1 -1
- package/dist/server/mutations/account.d.ts +8 -9
- package/dist/server/mutations/account.d.ts.map +1 -1
- package/dist/server/mutations/account.js +11 -9
- package/dist/server/mutations/account.js.map +1 -1
- package/dist/server/mutations/code.d.ts +13 -13
- package/dist/server/mutations/code.d.ts.map +1 -1
- package/dist/server/mutations/code.js +5 -2
- package/dist/server/mutations/code.js.map +1 -1
- package/dist/server/mutations/invalidate.d.ts +4 -4
- package/dist/server/mutations/invalidate.d.ts.map +1 -1
- package/dist/server/mutations/invalidate.js.map +1 -1
- package/dist/server/mutations/oauth.d.ts +12 -10
- package/dist/server/mutations/oauth.d.ts.map +1 -1
- package/dist/server/mutations/oauth.js +9 -3
- package/dist/server/mutations/oauth.js.map +1 -1
- package/dist/server/mutations/refresh.d.ts +3 -3
- package/dist/server/mutations/refresh.d.ts.map +1 -1
- package/dist/server/mutations/refresh.js +1 -1
- package/dist/server/mutations/refresh.js.map +1 -1
- package/dist/server/mutations/register.d.ts +11 -11
- package/dist/server/mutations/register.d.ts.map +1 -1
- package/dist/server/mutations/register.js +45 -41
- package/dist/server/mutations/register.js.map +1 -1
- package/dist/server/mutations/retrieve.d.ts +6 -6
- package/dist/server/mutations/retrieve.d.ts.map +1 -1
- package/dist/server/mutations/retrieve.js +20 -24
- package/dist/server/mutations/retrieve.js.map +1 -1
- package/dist/server/mutations/signature.d.ts +6 -7
- package/dist/server/mutations/signature.d.ts.map +1 -1
- package/dist/server/mutations/signature.js +9 -3
- package/dist/server/mutations/signature.js.map +1 -1
- package/dist/server/mutations/signin.d.ts +5 -5
- package/dist/server/mutations/signin.d.ts.map +1 -1
- package/dist/server/mutations/signout.js.map +1 -1
- package/dist/server/mutations/store.d.ts +97 -97
- package/dist/server/mutations/store.d.ts.map +1 -1
- package/dist/server/mutations/store.js +8 -23
- package/dist/server/mutations/store.js.map +1 -1
- package/dist/server/mutations/verifier.js.map +1 -1
- package/dist/server/mutations/verify.d.ts +10 -10
- package/dist/server/mutations/verify.d.ts.map +1 -1
- package/dist/server/mutations/verify.js.map +1 -1
- package/dist/server/oauth.js +53 -16
- package/dist/server/oauth.js.map +1 -1
- package/dist/server/passkey.d.ts +2 -2
- package/dist/server/passkey.d.ts.map +1 -1
- package/dist/server/passkey.js +114 -30
- package/dist/server/passkey.js.map +1 -1
- package/dist/server/redirects.js +9 -3
- package/dist/server/redirects.js.map +1 -1
- package/dist/server/refresh.js +10 -7
- package/dist/server/refresh.js.map +1 -1
- package/dist/server/runtime.d.ts +14 -14
- package/dist/server/runtime.d.ts.map +1 -1
- package/dist/server/runtime.js +61 -19
- package/dist/server/runtime.js.map +1 -1
- package/dist/server/signin.js +34 -10
- package/dist/server/signin.js.map +1 -1
- package/dist/server/ssr.d.ts.map +1 -1
- package/dist/server/ssr.js +175 -184
- package/dist/server/ssr.js.map +1 -1
- package/dist/server/totp.js +78 -18
- package/dist/server/totp.js.map +1 -1
- package/dist/server/types.d.ts +13 -21
- package/dist/server/types.d.ts.map +1 -1
- package/dist/server/types.js.map +1 -1
- package/dist/server/users.js +6 -3
- package/dist/server/users.js.map +1 -1
- package/dist/server/utils.js +10 -4
- package/dist/server/utils.js.map +1 -1
- package/package.json +2 -6
- package/src/authorization/index.ts +1 -1
- package/src/cli/index.ts +1 -1
- package/src/client/core/types.ts +14 -14
- package/src/client/factors/device.ts +10 -12
- package/src/client/factors/passkey.ts +23 -26
- package/src/client/index.ts +54 -64
- package/src/client/runtime/invite.ts +5 -7
- package/src/component/index.ts +1 -0
- package/src/component/public/enterprise/audit.ts +6 -1
- package/src/component/public/enterprise/core.ts +1 -0
- package/src/component/public/enterprise/domains.ts +5 -1
- package/src/component/public/enterprise/scim.ts +1 -0
- package/src/component/public/enterprise/secrets.ts +1 -0
- package/src/component/public/enterprise/webhooks.ts +1 -0
- package/src/component/public/factors/devices.ts +1 -0
- package/src/component/public/factors/passkeys.ts +1 -0
- package/src/component/public/factors/totp.ts +1 -0
- package/src/component/public/groups/core.ts +1 -1
- package/src/component/public/groups/invites.ts +7 -1
- package/src/component/public/groups/members.ts +1 -0
- package/src/component/public/identity/accounts.ts +1 -0
- package/src/component/public/identity/codes.ts +1 -0
- package/src/component/public/identity/sessions.ts +1 -0
- package/src/component/public/identity/tokens.ts +1 -0
- package/src/component/public/identity/users.ts +1 -0
- package/src/component/public/identity/verifiers.ts +1 -0
- package/src/component/public/security/keys.ts +1 -0
- package/src/component/public/security/limits.ts +1 -0
- package/src/providers/password.ts +89 -110
- package/src/server/auth.ts +177 -111
- package/src/server/core.ts +197 -233
- package/src/server/crypto.ts +31 -29
- package/src/server/device.ts +65 -32
- package/src/server/enterprise/domain.ts +158 -170
- package/src/server/enterprise/http.ts +46 -39
- package/src/server/http.ts +36 -30
- package/src/server/identity.ts +5 -5
- package/src/server/index.ts +2 -0
- package/src/server/limits.ts +53 -80
- package/src/server/mounts.ts +47 -74
- package/src/server/mutations/account.ts +22 -36
- package/src/server/mutations/code.ts +6 -6
- package/src/server/mutations/invalidate.ts +1 -1
- package/src/server/mutations/oauth.ts +14 -8
- package/src/server/mutations/refresh.ts +5 -4
- package/src/server/mutations/register.ts +87 -132
- package/src/server/mutations/retrieve.ts +44 -44
- package/src/server/mutations/signature.ts +13 -6
- package/src/server/mutations/signout.ts +1 -1
- package/src/server/mutations/store.ts +16 -31
- package/src/server/mutations/verifier.ts +1 -1
- package/src/server/mutations/verify.ts +3 -5
- package/src/server/oauth.ts +60 -69
- package/src/server/passkey.ts +567 -517
- package/src/server/redirects.ts +10 -6
- package/src/server/refresh.ts +14 -18
- package/src/server/runtime.ts +70 -55
- package/src/server/signin.ts +44 -37
- package/src/server/ssr.ts +390 -407
- package/src/server/totp.ts +85 -35
- package/src/server/types.ts +19 -22
- package/src/server/users.ts +7 -6
- package/src/server/utils.ts +10 -12
- package/dist/component/server/authError.js +0 -34
- package/dist/component/server/authError.js.map +0 -1
- package/dist/component/server/errors.d.ts +0 -1
- package/dist/component/server/errors.js +0 -137
- package/dist/component/server/errors.js.map +0 -1
- package/dist/server/authError.d.ts +0 -46
- package/dist/server/authError.d.ts.map +0 -1
- package/dist/server/authError.js +0 -34
- package/dist/server/authError.js.map +0 -1
- package/dist/server/errors.d.ts +0 -177
- package/dist/server/errors.d.ts.map +0 -1
- package/dist/server/errors.js +0 -212
- package/dist/server/errors.js.map +0 -1
- package/src/server/authError.ts +0 -44
- package/src/server/errors.ts +0 -290
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { AuthError } from "./authError.js";
|
|
2
1
|
import { generateRandomString, requireEnv, sha256 } from "./utils.js";
|
|
3
2
|
import { userIdFromIdentitySubject } from "./identity.js";
|
|
4
3
|
import { callSignIn } from "./mutations/signin.js";
|
|
5
4
|
import { mutateDeviceAuthorize, mutateDeviceDelete, mutateDeviceInsert, mutateDeviceUpdateLastPolled, queryDeviceByCodeHash, queryDeviceByUserCode } from "./types.js";
|
|
5
|
+
import { Cv } from "@robelest/fx/convex";
|
|
6
6
|
import { Fx } from "@robelest/fx";
|
|
7
|
+
import { ConvexError } from "convex/values";
|
|
7
8
|
|
|
8
9
|
//#region src/server/device.ts
|
|
9
10
|
/**
|
|
@@ -29,7 +30,10 @@ const handleDevice = (ctx, provider, args) => Fx.from({
|
|
|
29
30
|
ok: async () => {
|
|
30
31
|
const params = args.params ?? {};
|
|
31
32
|
const flow = typeof params.flow === "string" ? params.flow : "create";
|
|
32
|
-
if (!DEVICE_FLOWS.some((candidate) => candidate === flow)) throw
|
|
33
|
+
if (!DEVICE_FLOWS.some((candidate) => candidate === flow)) throw Cv.error({
|
|
34
|
+
code: "DEVICE_MISSING_FLOW",
|
|
35
|
+
message: "Missing `flow` parameter. Expected one of: create, poll, verify"
|
|
36
|
+
});
|
|
33
37
|
if (flow === "create") {
|
|
34
38
|
const deviceCode = generateRandomString(DEVICE_CODE_LENGTH, DEVICE_CODE_ALPHABET);
|
|
35
39
|
const deviceCodeHash = await sha256(deviceCode);
|
|
@@ -55,21 +59,42 @@ const handleDevice = (ctx, provider, args) => Fx.from({
|
|
|
55
59
|
};
|
|
56
60
|
}
|
|
57
61
|
if (flow === "poll") {
|
|
58
|
-
if (typeof params.deviceCode !== "string") throw
|
|
62
|
+
if (typeof params.deviceCode !== "string") throw Cv.error({
|
|
63
|
+
code: "DEVICE_MISSING_FLOW",
|
|
64
|
+
message: "Missing `deviceCode` parameter for poll flow."
|
|
65
|
+
});
|
|
59
66
|
const doc$1 = await queryDeviceByCodeHash(ctx, await sha256(params.deviceCode));
|
|
60
|
-
if (doc$1 === null) throw
|
|
67
|
+
if (doc$1 === null) throw Cv.error({
|
|
68
|
+
code: "DEVICE_CODE_EXPIRED",
|
|
69
|
+
message: "The device code has expired. Please start a new authorization request."
|
|
70
|
+
});
|
|
61
71
|
if (Date.now() > doc$1.expiresAt) {
|
|
62
72
|
await mutateDeviceDelete(ctx, doc$1._id);
|
|
63
|
-
throw
|
|
73
|
+
throw Cv.error({
|
|
74
|
+
code: "DEVICE_CODE_EXPIRED",
|
|
75
|
+
message: "The device code has expired. Please start a new authorization request."
|
|
76
|
+
});
|
|
64
77
|
}
|
|
65
|
-
if (doc$1.lastPolledAt !== void 0 && (Date.now() - doc$1.lastPolledAt) / 1e3 < doc$1.interval) throw
|
|
78
|
+
if (doc$1.lastPolledAt !== void 0 && (Date.now() - doc$1.lastPolledAt) / 1e3 < doc$1.interval) throw Cv.error({
|
|
79
|
+
code: "DEVICE_SLOW_DOWN",
|
|
80
|
+
message: "Polling too frequently. Increase the interval between requests."
|
|
81
|
+
});
|
|
66
82
|
await mutateDeviceUpdateLastPolled(ctx, doc$1._id, Date.now());
|
|
67
|
-
if (doc$1.status === "pending") throw
|
|
83
|
+
if (doc$1.status === "pending") throw Cv.error({
|
|
84
|
+
code: "DEVICE_AUTHORIZATION_PENDING",
|
|
85
|
+
message: "The user has not yet authorized this device."
|
|
86
|
+
});
|
|
68
87
|
if (doc$1.status === "denied") {
|
|
69
88
|
await mutateDeviceDelete(ctx, doc$1._id);
|
|
70
|
-
throw
|
|
89
|
+
throw Cv.error({
|
|
90
|
+
code: "DEVICE_CODE_DENIED",
|
|
91
|
+
message: "The authorization request was denied."
|
|
92
|
+
});
|
|
71
93
|
}
|
|
72
|
-
if (!doc$1.userId || !doc$1.sessionId) throw
|
|
94
|
+
if (!doc$1.userId || !doc$1.sessionId) throw Cv.error({
|
|
95
|
+
code: "INTERNAL_ERROR",
|
|
96
|
+
message: "Authorized device code missing userId or sessionId"
|
|
97
|
+
});
|
|
73
98
|
await mutateDeviceDelete(ctx, doc$1._id);
|
|
74
99
|
return {
|
|
75
100
|
kind: "signedIn",
|
|
@@ -80,17 +105,32 @@ const handleDevice = (ctx, provider, args) => Fx.from({
|
|
|
80
105
|
})
|
|
81
106
|
};
|
|
82
107
|
}
|
|
83
|
-
if (typeof params.userCode !== "string") throw
|
|
108
|
+
if (typeof params.userCode !== "string") throw Cv.error({
|
|
109
|
+
code: "DEVICE_INVALID_USER_CODE",
|
|
110
|
+
message: "Missing `userCode` parameter for verify flow."
|
|
111
|
+
});
|
|
84
112
|
const identity = await ctx.auth.getUserIdentity();
|
|
85
|
-
if (identity === null) throw
|
|
113
|
+
if (identity === null) throw Cv.error({
|
|
114
|
+
code: "NOT_SIGNED_IN",
|
|
115
|
+
message: "You must be signed in to authorize a device."
|
|
116
|
+
});
|
|
86
117
|
const userId = userIdFromIdentitySubject(identity.subject);
|
|
87
118
|
const doc = await queryDeviceByUserCode(ctx, params.userCode);
|
|
88
|
-
if (doc === null) throw
|
|
119
|
+
if (doc === null) throw Cv.error({
|
|
120
|
+
code: "DEVICE_INVALID_USER_CODE",
|
|
121
|
+
message: "Invalid or expired user code."
|
|
122
|
+
});
|
|
89
123
|
if (Date.now() > doc.expiresAt) {
|
|
90
124
|
await mutateDeviceDelete(ctx, doc._id);
|
|
91
|
-
throw
|
|
125
|
+
throw Cv.error({
|
|
126
|
+
code: "DEVICE_CODE_EXPIRED",
|
|
127
|
+
message: "The device code has expired. Please start a new authorization request."
|
|
128
|
+
});
|
|
92
129
|
}
|
|
93
|
-
if (doc.status !== "pending") throw
|
|
130
|
+
if (doc.status !== "pending") throw Cv.error({
|
|
131
|
+
code: "DEVICE_ALREADY_AUTHORIZED",
|
|
132
|
+
message: "This device code has already been authorized."
|
|
133
|
+
});
|
|
94
134
|
const signInResult = await callSignIn(ctx, {
|
|
95
135
|
userId,
|
|
96
136
|
generateTokens: false
|
|
@@ -101,7 +141,10 @@ const handleDevice = (ctx, provider, args) => Fx.from({
|
|
|
101
141
|
signedIn: null
|
|
102
142
|
};
|
|
103
143
|
},
|
|
104
|
-
err: (e) => e instanceof
|
|
144
|
+
err: (e) => e instanceof ConvexError ? e : Cv.error({
|
|
145
|
+
code: "INTERNAL_ERROR",
|
|
146
|
+
message: `Device flow failed: ${String(e)}`
|
|
147
|
+
})
|
|
105
148
|
});
|
|
106
149
|
|
|
107
150
|
//#endregion
|
|
@@ -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 \"./authError\";\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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"}
|
|
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\";\nimport { Cv } from \"@robelest/fx/convex\";\nimport { ConvexError } from \"convex/values\";\n\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, ConvexError<any>> =>\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 Cv.error({\n code: \"DEVICE_MISSING_FLOW\",\n message:\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 Cv.error({\n code: \"DEVICE_MISSING_FLOW\",\n message: \"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 Cv.error({\n code: \"DEVICE_CODE_EXPIRED\",\n message:\n \"The device code has expired. Please start a new authorization request.\",\n });\n }\n if (Date.now() > doc.expiresAt) {\n await mutateDeviceDelete(ctx, doc._id);\n throw Cv.error({\n code: \"DEVICE_CODE_EXPIRED\",\n message:\n \"The device code has expired. Please start a new authorization request.\",\n });\n }\n if (\n doc.lastPolledAt !== undefined &&\n (Date.now() - doc.lastPolledAt) / 1000 < doc.interval\n ) {\n throw Cv.error({\n code: \"DEVICE_SLOW_DOWN\",\n message:\n \"Polling too frequently. Increase the interval between requests.\",\n });\n }\n\n await mutateDeviceUpdateLastPolled(ctx, doc._id, Date.now());\n\n if (doc.status === \"pending\") {\n throw Cv.error({\n code: \"DEVICE_AUTHORIZATION_PENDING\",\n message: \"The user has not yet authorized this device.\",\n });\n }\n if (doc.status === \"denied\") {\n await mutateDeviceDelete(ctx, doc._id);\n throw Cv.error({\n code: \"DEVICE_CODE_DENIED\",\n message: \"The authorization request was denied.\",\n });\n }\n\n if (!doc.userId || !doc.sessionId) {\n throw Cv.error({\n code: \"INTERNAL_ERROR\",\n message: \"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 Cv.error({\n code: \"DEVICE_INVALID_USER_CODE\",\n message: \"Missing `userCode` parameter for verify flow.\",\n });\n }\n\n const identity = await ctx.auth.getUserIdentity();\n if (identity === null) {\n throw Cv.error({\n code: \"NOT_SIGNED_IN\",\n message: \"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 Cv.error({\n code: \"DEVICE_INVALID_USER_CODE\",\n message: \"Invalid or expired user code.\",\n });\n }\n if (Date.now() > doc.expiresAt) {\n await mutateDeviceDelete(ctx, doc._id);\n throw Cv.error({\n code: \"DEVICE_CODE_EXPIRED\",\n message:\n \"The device code has expired. Please start a new authorization request.\",\n });\n }\n if (doc.status !== \"pending\") {\n throw Cv.error({\n code: \"DEVICE_ALREADY_AUTHORIZED\",\n message: \"This device code has already been authorized.\",\n });\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 ConvexError\n ? e\n : Cv.error({\n code: \"INTERNAL_ERROR\",\n message: `Device flow failed: ${String(e)}`,\n }),\n });\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Cv } from "@robelest/fx/convex";
|
|
2
2
|
import { Fx } from "@robelest/fx";
|
|
3
3
|
|
|
4
4
|
//#region src/server/enterprise/domain.ts
|
|
@@ -34,7 +34,6 @@ function createEnterpriseDomain(deps) {
|
|
|
34
34
|
connection: {
|
|
35
35
|
create: async (ctx, data) => {
|
|
36
36
|
return {
|
|
37
|
-
ok: true,
|
|
38
37
|
enterpriseId: await ctx.runMutation(config.component.public.enterpriseCreate, {
|
|
39
38
|
...data,
|
|
40
39
|
policy: normalizeEnterprisePolicy(data.policy)
|
|
@@ -65,21 +64,18 @@ function createEnterpriseDomain(deps) {
|
|
|
65
64
|
enterpriseId,
|
|
66
65
|
data
|
|
67
66
|
});
|
|
68
|
-
return {
|
|
69
|
-
ok: true,
|
|
70
|
-
enterpriseId
|
|
71
|
-
};
|
|
67
|
+
return { enterpriseId };
|
|
72
68
|
},
|
|
73
69
|
delete: async (ctx, enterpriseId) => {
|
|
74
70
|
await ctx.runMutation(config.component.public.enterpriseDelete, { enterpriseId });
|
|
75
|
-
return {
|
|
76
|
-
ok: true,
|
|
77
|
-
enterpriseId
|
|
78
|
-
};
|
|
71
|
+
return { enterpriseId };
|
|
79
72
|
},
|
|
80
73
|
status: async (ctx, enterpriseId) => {
|
|
81
74
|
const enterprise = await ctx.runQuery(config.component.public.enterpriseGet, { enterpriseId });
|
|
82
|
-
if (!enterprise) throw
|
|
75
|
+
if (!enterprise) throw Cv.error({
|
|
76
|
+
code: "INVALID_PARAMETERS",
|
|
77
|
+
message: enterpriseNotFoundError
|
|
78
|
+
});
|
|
83
79
|
const policy = getPolicyFromEnterprise(enterprise);
|
|
84
80
|
const protocols = enterprise.config?.protocols ?? {};
|
|
85
81
|
const oidcConfig = protocols.oidc;
|
|
@@ -130,7 +126,10 @@ function createEnterpriseDomain(deps) {
|
|
|
130
126
|
},
|
|
131
127
|
validate: async (ctx, enterpriseId) => {
|
|
132
128
|
const enterprise = await ctx.runQuery(config.component.public.enterpriseGet, { enterpriseId });
|
|
133
|
-
if (enterprise === null) throw
|
|
129
|
+
if (enterprise === null) throw Cv.error({
|
|
130
|
+
code: "INVALID_PARAMETERS",
|
|
131
|
+
message: enterpriseNotFoundError
|
|
132
|
+
});
|
|
134
133
|
const domains = await ctx.runQuery(config.component.public.enterpriseDomainList, { enterpriseId });
|
|
135
134
|
const primaryDomains = domains.filter((domain) => domain.isPrimary);
|
|
136
135
|
const verifiedDomains = domains.filter((domain) => domain.verifiedAt !== void 0);
|
|
@@ -159,7 +158,10 @@ function createEnterpriseDomain(deps) {
|
|
|
159
158
|
const enterprise = await loadEnterpriseOrThrow(ctx, args.enterpriseId);
|
|
160
159
|
const normalizedDomain = normalizeDomain(args.domain);
|
|
161
160
|
const domain = (await ctx.runQuery(config.component.public.enterpriseDomainList, { enterpriseId: enterprise._id })).find((entry) => entry.domain === normalizedDomain);
|
|
162
|
-
if (!domain) throw
|
|
161
|
+
if (!domain) throw Cv.error({
|
|
162
|
+
code: "INVALID_PARAMETERS",
|
|
163
|
+
message: "Domain is not attached to this enterprise."
|
|
164
|
+
});
|
|
163
165
|
const requestedAt = Date.now();
|
|
164
166
|
const expiresAt = requestedAt + ENTERPRISE_DOMAIN_VERIFICATION_TTL_MS;
|
|
165
167
|
const token = generateRandomString(32, INVITE_TOKEN_ALPHABET);
|
|
@@ -191,7 +193,6 @@ function createEnterpriseDomain(deps) {
|
|
|
191
193
|
}
|
|
192
194
|
});
|
|
193
195
|
return {
|
|
194
|
-
ok: true,
|
|
195
196
|
enterpriseId: enterprise._id,
|
|
196
197
|
domain: normalizedDomain,
|
|
197
198
|
requestedAt,
|
|
@@ -207,7 +208,10 @@ function createEnterpriseDomain(deps) {
|
|
|
207
208
|
const enterprise = await loadEnterpriseOrThrow(ctx, args.enterpriseId);
|
|
208
209
|
const normalizedDomain = normalizeDomain(args.domain);
|
|
209
210
|
const domain = (await ctx.runQuery(config.component.public.enterpriseDomainList, { enterpriseId: enterprise._id })).find((entry) => entry.domain === normalizedDomain);
|
|
210
|
-
if (!domain) throw
|
|
211
|
+
if (!domain) throw Cv.error({
|
|
212
|
+
code: "INVALID_PARAMETERS",
|
|
213
|
+
message: "Domain is not attached to this enterprise."
|
|
214
|
+
});
|
|
211
215
|
if (domain.verifiedAt !== void 0) return {
|
|
212
216
|
ok: true,
|
|
213
217
|
enterpriseId: enterprise._id,
|
|
@@ -260,7 +264,10 @@ function createEnterpriseDomain(deps) {
|
|
|
260
264
|
try {
|
|
261
265
|
txtValues = await resolveTxtValues(verification.recordName);
|
|
262
266
|
} catch (error) {
|
|
263
|
-
throw
|
|
267
|
+
throw Cv.error({
|
|
268
|
+
code: "INTERNAL_ERROR",
|
|
269
|
+
message: error instanceof Error ? error.message : "Failed to resolve DNS TXT records."
|
|
270
|
+
});
|
|
264
271
|
}
|
|
265
272
|
checks.push({
|
|
266
273
|
name: "dns_record_present",
|
|
@@ -312,19 +319,37 @@ function createEnterpriseDomain(deps) {
|
|
|
312
319
|
return await Fx.run(Fx.gen(function* () {
|
|
313
320
|
const enterprise = yield* Fx.from({
|
|
314
321
|
ok: () => ctx.runQuery(config.component.public.enterpriseGet, { enterpriseId: data.enterpriseId }),
|
|
315
|
-
err: () =>
|
|
316
|
-
|
|
322
|
+
err: () => Cv.error({
|
|
323
|
+
code: "INTERNAL_ERROR",
|
|
324
|
+
message: "Failed to load enterprise."
|
|
325
|
+
})
|
|
326
|
+
}).pipe(Fx.chain((ent) => ent === null ? Cv.fail({
|
|
327
|
+
code: "INVALID_PARAMETERS",
|
|
328
|
+
message: enterpriseNotFoundError
|
|
329
|
+
}) : Fx.succeed(ent)));
|
|
317
330
|
const metadataXml = yield* data.metadataXml ? Fx.succeed(data.metadataXml) : data.metadataUrl ? Fx.defer(() => Fx.from({
|
|
318
331
|
ok: async () => {
|
|
319
332
|
const response = await fetch(data.metadataUrl);
|
|
320
333
|
if (!response.ok) throw new Error(`Failed to fetch SAML metadata: ${response.status}`);
|
|
321
334
|
return await response.text();
|
|
322
335
|
},
|
|
323
|
-
err: (error) =>
|
|
324
|
-
|
|
336
|
+
err: (error) => Cv.error({
|
|
337
|
+
code: "INVALID_PARAMETERS",
|
|
338
|
+
message: error instanceof Error ? error.message : "Failed to fetch SAML metadata"
|
|
339
|
+
})
|
|
340
|
+
})).pipe(Fx.timeout(1e4), Fx.retry(Fx.retry.compose(Fx.retry.jittered(Fx.retry.exponential(200)), Fx.retry.recurs(2))), Fx.recover((error) => Cv.fail({
|
|
341
|
+
code: "INVALID_PARAMETERS",
|
|
342
|
+
message: error instanceof Error ? error.message : "Failed to fetch SAML metadata"
|
|
343
|
+
}))) : Cv.fail({
|
|
344
|
+
code: "INVALID_PARAMETERS",
|
|
345
|
+
message: "SAML registration requires metadataXml or metadataUrl."
|
|
346
|
+
});
|
|
325
347
|
const parsed = yield* Fx.from({
|
|
326
348
|
ok: () => parseSamlIdpMetadata(metadataXml),
|
|
327
|
-
err: () =>
|
|
349
|
+
err: () => Cv.error({
|
|
350
|
+
code: "INVALID_PARAMETERS",
|
|
351
|
+
message: "Failed to parse SAML metadata."
|
|
352
|
+
})
|
|
328
353
|
});
|
|
329
354
|
const baseConfig = upsertProtocolConfig(enterprise.config, "saml", {
|
|
330
355
|
enabled: true,
|
|
@@ -349,7 +374,10 @@ function createEnterpriseDomain(deps) {
|
|
|
349
374
|
config: nextConfig
|
|
350
375
|
}
|
|
351
376
|
}),
|
|
352
|
-
err: () =>
|
|
377
|
+
err: () => Cv.error({
|
|
378
|
+
code: "INTERNAL_ERROR",
|
|
379
|
+
message: "Failed to persist SAML registration."
|
|
380
|
+
})
|
|
353
381
|
});
|
|
354
382
|
if (normalizedDomains) for (const [index, domain] of normalizedDomains.entries()) yield* Fx.from({
|
|
355
383
|
ok: () => ctx.runMutation(config.component.public.enterpriseDomainAdd, {
|
|
@@ -358,7 +386,10 @@ function createEnterpriseDomain(deps) {
|
|
|
358
386
|
domain,
|
|
359
387
|
isPrimary: index === 0
|
|
360
388
|
}),
|
|
361
|
-
err: () =>
|
|
389
|
+
err: () => Cv.error({
|
|
390
|
+
code: "INTERNAL_ERROR",
|
|
391
|
+
message: "Failed to persist enterprise domain."
|
|
392
|
+
})
|
|
362
393
|
});
|
|
363
394
|
yield* Fx.from({
|
|
364
395
|
ok: () => recordEnterpriseAuditEvent(ctx, {
|
|
@@ -374,18 +405,23 @@ function createEnterpriseDomain(deps) {
|
|
|
374
405
|
domains: normalizedDomains
|
|
375
406
|
}
|
|
376
407
|
}),
|
|
377
|
-
err: () =>
|
|
408
|
+
err: () => Cv.error({
|
|
409
|
+
code: "INTERNAL_ERROR",
|
|
410
|
+
message: "Failed to record SAML registration audit event."
|
|
411
|
+
})
|
|
378
412
|
});
|
|
379
413
|
return {
|
|
380
|
-
ok: true,
|
|
381
414
|
enterpriseId: enterprise._id,
|
|
382
415
|
groupId: enterprise.groupId
|
|
383
416
|
};
|
|
384
|
-
}).pipe(Fx.recover((e) => Fx.fatal(e
|
|
417
|
+
}).pipe(Fx.recover((e) => Fx.fatal(e))));
|
|
385
418
|
},
|
|
386
419
|
metadata: async (ctx, opts) => {
|
|
387
420
|
const enterprise = await ctx.runQuery(config.component.public.enterpriseGet, { enterpriseId: opts.enterpriseId });
|
|
388
|
-
if (!enterprise) throw
|
|
421
|
+
if (!enterprise) throw Cv.error({
|
|
422
|
+
code: "INVALID_PARAMETERS",
|
|
423
|
+
message: "Enterprise not found."
|
|
424
|
+
});
|
|
389
425
|
return createServiceProviderMetadata(getSamlServiceProviderOptions({
|
|
390
426
|
rootUrl: requireEnv("CONVEX_SITE_URL"),
|
|
391
427
|
source: {
|
|
@@ -507,11 +543,20 @@ function createEnterpriseDomain(deps) {
|
|
|
507
543
|
oidc: {
|
|
508
544
|
configure: async (ctx, data) => {
|
|
509
545
|
return await Fx.run(Fx.gen(function* () {
|
|
510
|
-
yield* Fx.guard(data.issuer === void 0 && data.discoveryUrl === void 0,
|
|
546
|
+
yield* Fx.guard(data.issuer === void 0 && data.discoveryUrl === void 0, Cv.fail({
|
|
547
|
+
code: "INVALID_PARAMETERS",
|
|
548
|
+
message: "OIDC registration requires issuer or discoveryUrl."
|
|
549
|
+
}));
|
|
511
550
|
const enterprise = yield* Fx.from({
|
|
512
551
|
ok: () => ctx.runQuery(config.component.public.enterpriseGet, { enterpriseId: data.enterpriseId }),
|
|
513
|
-
err: () =>
|
|
514
|
-
|
|
552
|
+
err: () => Cv.error({
|
|
553
|
+
code: "INTERNAL_ERROR",
|
|
554
|
+
message: "Failed to load enterprise."
|
|
555
|
+
})
|
|
556
|
+
}).pipe(Fx.chain((ent) => ent === null ? Cv.fail({
|
|
557
|
+
code: "INVALID_PARAMETERS",
|
|
558
|
+
message: enterpriseNotFoundError
|
|
559
|
+
}) : Fx.succeed(ent)));
|
|
515
560
|
const nextConfig = upsertProtocolConfig(enterprise.config, "oidc", {
|
|
516
561
|
enabled: true,
|
|
517
562
|
issuer: data.issuer,
|
|
@@ -532,12 +577,18 @@ function createEnterpriseDomain(deps) {
|
|
|
532
577
|
enterpriseId: data.enterpriseId,
|
|
533
578
|
data: { config: nextConfig }
|
|
534
579
|
}),
|
|
535
|
-
err: () =>
|
|
580
|
+
err: () => Cv.error({
|
|
581
|
+
code: "INTERNAL_ERROR",
|
|
582
|
+
message: "Failed to persist OIDC registration."
|
|
583
|
+
})
|
|
536
584
|
});
|
|
537
585
|
if (data.clientSecret !== void 0) {
|
|
538
586
|
const ciphertext = yield* Fx.from({
|
|
539
587
|
ok: () => encryptSecret(data.clientSecret),
|
|
540
|
-
err: () =>
|
|
588
|
+
err: () => Cv.error({
|
|
589
|
+
code: "INTERNAL_ERROR",
|
|
590
|
+
message: "Failed to encrypt OIDC client secret."
|
|
591
|
+
})
|
|
541
592
|
});
|
|
542
593
|
yield* Fx.from({
|
|
543
594
|
ok: () => ctx.runMutation(config.component.public.enterpriseSecretUpsert, {
|
|
@@ -547,7 +598,10 @@ function createEnterpriseDomain(deps) {
|
|
|
547
598
|
ciphertext,
|
|
548
599
|
updatedAt: Date.now()
|
|
549
600
|
}),
|
|
550
|
-
err: () =>
|
|
601
|
+
err: () => Cv.error({
|
|
602
|
+
code: "INTERNAL_ERROR",
|
|
603
|
+
message: "Failed to persist OIDC client secret."
|
|
604
|
+
})
|
|
551
605
|
});
|
|
552
606
|
}
|
|
553
607
|
yield* Fx.from({
|
|
@@ -564,39 +618,75 @@ function createEnterpriseDomain(deps) {
|
|
|
564
618
|
discoveryUrl: data.discoveryUrl
|
|
565
619
|
}
|
|
566
620
|
}),
|
|
567
|
-
err: () =>
|
|
621
|
+
err: () => Cv.error({
|
|
622
|
+
code: "INTERNAL_ERROR",
|
|
623
|
+
message: "Failed to record OIDC registration audit event."
|
|
624
|
+
})
|
|
568
625
|
});
|
|
569
626
|
const secret = yield* Fx.from({
|
|
570
627
|
ok: () => getEnterpriseSecret(ctx, data.enterpriseId, ENTERPRISE_OIDC_CLIENT_SECRET_KIND),
|
|
571
|
-
err: () =>
|
|
628
|
+
err: () => Cv.error({
|
|
629
|
+
code: "INTERNAL_ERROR",
|
|
630
|
+
message: "Failed to load OIDC secret metadata."
|
|
631
|
+
})
|
|
572
632
|
});
|
|
573
633
|
return withOidcSecretState(getPublicOidcConfig(nextConfig), secret !== null);
|
|
574
|
-
}).pipe(Fx.recover((e) => Fx.fatal(e
|
|
634
|
+
}).pipe(Fx.recover((e) => Fx.fatal(e))));
|
|
575
635
|
},
|
|
576
636
|
get: async (ctx, enterpriseId) => {
|
|
577
637
|
return await Fx.run(Fx.from({
|
|
578
638
|
ok: () => ctx.runQuery(config.component.public.enterpriseGet, { enterpriseId }),
|
|
579
|
-
err: () =>
|
|
580
|
-
|
|
639
|
+
err: () => Cv.error({
|
|
640
|
+
code: "INTERNAL_ERROR",
|
|
641
|
+
message: "Failed to load enterprise."
|
|
642
|
+
})
|
|
643
|
+
}).pipe(Fx.chain((ent) => ent === null ? Cv.fail({
|
|
644
|
+
code: "INVALID_PARAMETERS",
|
|
645
|
+
message: enterpriseNotFoundError
|
|
646
|
+
}) : Fx.succeed(ent)), Fx.chain((enterprise) => Fx.from({
|
|
581
647
|
ok: async () => {
|
|
582
648
|
const secret = await getEnterpriseSecret(ctx, enterprise._id, ENTERPRISE_OIDC_CLIENT_SECRET_KIND);
|
|
583
649
|
return withOidcSecretState(getPublicOidcConfig(enterprise.config), secret !== null);
|
|
584
650
|
},
|
|
585
|
-
err: () =>
|
|
586
|
-
|
|
651
|
+
err: () => Cv.error({
|
|
652
|
+
code: "INTERNAL_ERROR",
|
|
653
|
+
message: "Failed to load OIDC secret metadata."
|
|
654
|
+
})
|
|
655
|
+
})), Fx.recover((e) => Fx.fatal(e))));
|
|
587
656
|
},
|
|
588
657
|
signIn: async (ctx, data) => {
|
|
589
658
|
return await Fx.run(Fx.gen(function* () {
|
|
590
659
|
const enterprise = data.enterpriseId !== void 0 ? yield* Fx.from({
|
|
591
660
|
ok: () => ctx.runQuery(config.component.public.enterpriseGet, { enterpriseId: data.enterpriseId }),
|
|
592
|
-
err: () =>
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
}).pipe(Fx.chain((
|
|
597
|
-
|
|
661
|
+
err: () => Cv.error({
|
|
662
|
+
code: "INTERNAL_ERROR",
|
|
663
|
+
message: "Failed to load enterprise."
|
|
664
|
+
})
|
|
665
|
+
}).pipe(Fx.chain((ent) => ent === null ? Cv.fail({
|
|
666
|
+
code: "INVALID_PARAMETERS",
|
|
667
|
+
message: enterpriseNotFoundError
|
|
668
|
+
}) : Fx.succeed(ent))) : data.domain !== void 0 || data.email !== void 0 ? yield* Fx.from({
|
|
669
|
+
ok: () => ctx.runQuery(config.component.public.enterpriseGetByDomain, { domain: normalizeDomain(data.domain ?? String(data.email).split("@").pop() ?? "") }),
|
|
670
|
+
err: () => Cv.error({
|
|
671
|
+
code: "INTERNAL_ERROR",
|
|
672
|
+
message: "Failed to resolve enterprise by domain."
|
|
673
|
+
})
|
|
674
|
+
}).pipe(Fx.chain((result) => result?.enterprise && result.domain?.verifiedAt !== void 0 ? Fx.succeed(result.enterprise) : Cv.fail({
|
|
675
|
+
code: "INVALID_PARAMETERS",
|
|
676
|
+
message: "No enterprise OIDC connection matched the provided input."
|
|
677
|
+
}))) : yield* Cv.fail({
|
|
678
|
+
code: "INVALID_PARAMETERS",
|
|
679
|
+
message: "No enterprise OIDC connection matched the provided input."
|
|
680
|
+
});
|
|
681
|
+
yield* Fx.guard(enterprise.status !== "active", Cv.fail({
|
|
682
|
+
code: "INVALID_PARAMETERS",
|
|
683
|
+
message: "Enterprise connection is not active."
|
|
684
|
+
}));
|
|
598
685
|
const oidc = getOidcConfig(enterprise.config);
|
|
599
|
-
yield* Fx.guard(oidc.enabled !== true,
|
|
686
|
+
yield* Fx.guard(oidc.enabled !== true, Cv.fail({
|
|
687
|
+
code: "PROVIDER_NOT_CONFIGURED",
|
|
688
|
+
message: "OIDC is not configured for this enterprise."
|
|
689
|
+
}));
|
|
600
690
|
const urls = getEnterpriseOidcUrls({
|
|
601
691
|
rootUrl: requireEnv("CONVEX_SITE_URL"),
|
|
602
692
|
enterpriseId: enterprise._id
|
|
@@ -608,7 +698,7 @@ function createEnterpriseDomain(deps) {
|
|
|
608
698
|
callbackPath: urls.callbackUrl,
|
|
609
699
|
redirectTo: data.redirectTo
|
|
610
700
|
};
|
|
611
|
-
}).pipe(Fx.recover((e) => Fx.fatal(e
|
|
701
|
+
}).pipe(Fx.recover((e) => Fx.fatal(e))));
|
|
612
702
|
},
|
|
613
703
|
validate: async (ctx, enterpriseId) => {
|
|
614
704
|
const checks = [];
|
|
@@ -683,7 +773,10 @@ function createEnterpriseDomain(deps) {
|
|
|
683
773
|
scim: {
|
|
684
774
|
configure: async (ctx, data) => {
|
|
685
775
|
const enterprise = await ctx.runQuery(config.component.public.enterpriseGet, { enterpriseId: data.enterpriseId });
|
|
686
|
-
if (enterprise === null) throw
|
|
776
|
+
if (enterprise === null) throw Cv.error({
|
|
777
|
+
code: "INVALID_PARAMETERS",
|
|
778
|
+
message: "Enterprise not found."
|
|
779
|
+
});
|
|
687
780
|
const rawToken = generateRandomString(48, INVITE_TOKEN_ALPHABET);
|
|
688
781
|
const tokenHash = await sha256(rawToken);
|
|
689
782
|
const configId = await ctx.runMutation(config.component.public.enterpriseScimConfigUpsert, {
|
|
@@ -713,7 +806,6 @@ function createEnterpriseDomain(deps) {
|
|
|
713
806
|
}
|
|
714
807
|
});
|
|
715
808
|
return {
|
|
716
|
-
ok: true,
|
|
717
809
|
enterpriseId: enterprise._id,
|
|
718
810
|
configId,
|
|
719
811
|
basePath: data.basePath ?? `${requireEnv("CONVEX_SITE_URL")}/api/auth/sso/${enterprise._id}/scim/v2`,
|
|
@@ -799,7 +891,10 @@ function createEnterpriseDomain(deps) {
|
|
|
799
891
|
},
|
|
800
892
|
create: async (ctx, data) => {
|
|
801
893
|
const enterprise = await ctx.runQuery(config.component.public.enterpriseGet, { enterpriseId: data.enterpriseId });
|
|
802
|
-
if (enterprise === null) throw
|
|
894
|
+
if (enterprise === null) throw Cv.error({
|
|
895
|
+
code: "INVALID_PARAMETERS",
|
|
896
|
+
message: "Enterprise not found."
|
|
897
|
+
});
|
|
803
898
|
const secretHash = await sha256(data.secret);
|
|
804
899
|
const endpointId = await ctx.runMutation(config.component.public.enterpriseWebhookEndpointCreate, {
|
|
805
900
|
enterpriseId: enterprise._id,
|
|
@@ -819,10 +914,7 @@ function createEnterpriseDomain(deps) {
|
|
|
819
914
|
subjectId: endpointId,
|
|
820
915
|
ok: true
|
|
821
916
|
});
|
|
822
|
-
return {
|
|
823
|
-
ok: true,
|
|
824
|
-
endpointId
|
|
825
|
-
};
|
|
917
|
+
return { endpointId };
|
|
826
918
|
},
|
|
827
919
|
list: async (ctx, enterpriseId) => {
|
|
828
920
|
return await ctx.runQuery(config.component.public.enterpriseWebhookEndpointList, { enterpriseId });
|
|
@@ -832,10 +924,7 @@ function createEnterpriseDomain(deps) {
|
|
|
832
924
|
endpointId,
|
|
833
925
|
data: { status: "disabled" }
|
|
834
926
|
});
|
|
835
|
-
return {
|
|
836
|
-
ok: true,
|
|
837
|
-
endpointId
|
|
838
|
-
};
|
|
927
|
+
return { endpointId };
|
|
839
928
|
}
|
|
840
929
|
},
|
|
841
930
|
emit: async (ctx, data) => {
|