@camstack/server 1.2.594 → 1.2.596
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/addon-root-inventory.js +50 -11
- package/dist/agent/addon-scan.js +44 -6
- package/dist/agent/agent-addon-runtime.js +8 -3
- package/dist/agent/agent-addon-updates.js +52 -12
- package/dist/agent/agent-cap-dispatch-service.js +5 -3
- package/dist/agent/agent-config.js +42 -7
- package/dist/agent/agent-deploy-swap.js +41 -5
- package/dist/agent/agent-group-runner.js +47 -8
- package/dist/agent/agent-http-auth.js +22 -10
- package/dist/agent/agent-http.js +113 -64
- package/dist/agent/agent-hub-addon-update.js +16 -10
- package/dist/agent/agent-hub-registration.js +7 -4
- package/dist/agent/agent-infra-capabilities.js +9 -5
- package/dist/agent/agent-json-file.js +42 -5
- package/dist/agent/agent-mesh-gate.js +8 -5
- package/dist/agent/agent-node-certificates.js +28 -25
- package/dist/agent/agent-pairing-local.js +47 -10
- package/dist/agent/agent-pairing-routes.js +20 -16
- package/dist/agent/agent-pairing-store.js +58 -18
- package/dist/agent/agent-reconnect.js +4 -1
- package/dist/agent/agent-service.js +71 -35
- package/dist/agent/agent-sniffing-listener.js +48 -11
- package/dist/agent/agent-undeploy.js +41 -5
- package/dist/agent/agent-update-routes.js +19 -14
- package/dist/agent/agent-update-service.js +80 -38
- package/dist/agent/apply-model-distribution.js +4 -1
- package/dist/agent/apply-stage-dev-uploads.js +9 -6
- package/dist/agent/derive-hub-url.js +8 -3
- package/dist/agent/fetch-bundle-from-hub.js +9 -6
- package/dist/agent/hubs-found.js +43 -6
- package/dist/agent/infra-boot-guard.js +4 -1
- package/dist/agent/infra-boot-plan.js +6 -2
- package/dist/agent/main.js +183 -148
- package/dist/agent/register-agent-cap-dispatch.js +6 -3
- package/dist/agent/serial-queue.js +7 -2
- package/dist/agent-status-page.js +4 -1
- package/dist/api/addon-upload.js +61 -22
- package/dist/api/agent-addon-delivery.js +11 -6
- package/dist/api/auth-whoami.js +4 -1
- package/dist/api/backup-download-pump.js +9 -5
- package/dist/api/backup-download.js +10 -7
- package/dist/api/core/addon-settings.router.js +19 -16
- package/dist/api/core/agent-pairing.router.js +79 -76
- package/dist/api/core/agents.router.js +24 -21
- package/dist/api/core/auth-admin-tokens.procedures.js +52 -48
- package/dist/api/core/auth-device-cert.procedures.js +67 -62
- package/dist/api/core/auth-limits.js +25 -16
- package/dist/api/core/auth-reauth.procedures.js +37 -32
- package/dist/api/core/auth-revoke-credentials.procedures.js +26 -22
- package/dist/api/core/auth-sessions.procedures.js +56 -52
- package/dist/api/core/auth.router.js +304 -300
- package/dist/api/core/cap-providers.js +121 -74
- package/dist/api/core/capabilities.router.js +37 -34
- package/dist/api/core/capability-preference-writes.js +17 -10
- package/dist/api/core/certificates.router.js +109 -106
- package/dist/api/core/cluster-nodes.router.js +18 -15
- package/dist/api/core/collection-preference.js +6 -2
- package/dist/api/core/event-bus-proxy.router.js +37 -34
- package/dist/api/core/failure-contributions.js +8 -4
- package/dist/api/core/hwaccel.router.js +20 -17
- package/dist/api/core/integration-secrets.js +12 -8
- package/dist/api/core/lifecycle-job-runner.js +49 -13
- package/dist/api/core/live-events.router.js +32 -29
- package/dist/api/core/load-contributions.js +8 -4
- package/dist/api/core/logging-settings.js +43 -30
- package/dist/api/core/logs.router.js +43 -40
- package/dist/api/core/notifications.router.js +18 -15
- package/dist/api/core/public-event-filter.js +8 -4
- package/dist/api/core/repl.router.js +21 -18
- package/dist/api/core/request-census-settings.js +16 -11
- package/dist/api/core/server-management.provider.js +4 -1
- package/dist/api/core/server-name.js +17 -9
- package/dist/api/core/site-location.js +24 -16
- package/dist/api/core/stream-probe.router.js +24 -21
- package/dist/api/core/system-events.router.js +26 -23
- package/dist/api/cors/cors-policy.js +29 -20
- package/dist/api/cors/register-cors-policy.js +14 -9
- package/dist/api/deploy-stage-registry.js +10 -6
- package/dist/api/dev-uploads-bundle.js +50 -12
- package/dist/api/health/adaptive-probe.routes.js +13 -9
- package/dist/api/health/health.routes.js +13 -8
- package/dist/api/hub-cluster-url.js +9 -3
- package/dist/api/model-distributor.js +50 -12
- package/dist/api/oauth2/consent-page.js +13 -10
- package/dist/api/oauth2/oauth2-routes.js +25 -17
- package/dist/api/oauth2/private-host.js +4 -1
- package/dist/api/server-upload-propagate.js +51 -15
- package/dist/api/server-upload.js +50 -13
- package/dist/api/sse-no-compression.js +4 -1
- package/dist/api/static/frame-policy.js +9 -4
- package/dist/api/static/precompressed-asset.js +57 -15
- package/dist/api/static/spa-mount.js +47 -8
- package/dist/api/static/spa-static.js +12 -5
- package/dist/api/tarball-manifest.js +44 -7
- package/dist/api/trpc/cap-mount-helpers.js +12 -7
- package/dist/api/trpc/cap-route-error-formatter.js +13 -10
- package/dist/api/trpc/cap-router-runtime.js +51 -40
- package/dist/api/trpc/client-ip.js +20 -9
- package/dist/api/trpc/core-cap-bridge.js +42 -32
- package/dist/api/trpc/device-collection-dispatch.js +27 -23
- package/dist/api/trpc/device-config-secret-redaction.js +40 -24
- package/dist/api/trpc/generated-cap-mounts.js +225 -219
- package/dist/api/trpc/generated-cap-routers.js +5167 -4852
- package/dist/api/trpc/http-request-census-hook.js +11 -8
- package/dist/api/trpc/http-request-census.js +41 -34
- package/dist/api/trpc/http-request-census.singleton.js +8 -4
- package/dist/api/trpc/localized-schema-paths.js +4 -1
- package/dist/api/trpc/principal-visibility.js +22 -14
- package/dist/api/trpc/remote-addon-update-grant.js +4 -1
- package/dist/api/trpc/scope-access.js +15 -10
- package/dist/api/trpc/server-provided-caps.js +7 -3
- package/dist/api/trpc/share-view-access.js +14 -6
- package/dist/api/trpc/trpc-error-device-tags.js +6 -3
- package/dist/api/trpc/trpc-error-principal.js +6 -2
- package/dist/api/trpc/trpc.context.js +25 -20
- package/dist/api/trpc/trpc.middleware.js +46 -38
- package/dist/api/trpc/trpc.router.js +99 -58
- package/dist/api/trpc/user-error.js +8 -5
- package/dist/api/trpc/ws-request-census.js +24 -17
- package/dist/api/trpc/ws-slow-consumer-guard.js +16 -11
- package/dist/api/upload-auth.js +9 -5
- package/dist/auth/addon-route-jwt-gate.js +7 -4
- package/dist/auth/addon-route-share-gate.js +12 -7
- package/dist/auth/app-confirm-page.js +22 -17
- package/dist/auth/app-confirm-routes.js +31 -26
- package/dist/auth/app-confirmations.js +44 -7
- package/dist/auth/app-page-shell.js +30 -17
- package/dist/auth/app-page-strings.js +17 -11
- package/dist/auth/app-passkey-page.js +21 -16
- package/dist/auth/app-passkey-routes.js +41 -37
- package/dist/auth/app-refused-route.js +18 -13
- package/dist/auth/auth-network-policy.js +15 -11
- package/dist/auth/auth-provider-gate.js +9 -4
- package/dist/auth/auth-rate-limit.js +7 -3
- package/dist/auth/client-address.js +9 -6
- package/dist/auth/device-enroll-proofs.js +43 -6
- package/dist/auth/external-link-intents.js +7 -3
- package/dist/auth/hub-sessions.js +12 -6
- package/dist/auth/native-link-completions.js +11 -7
- package/dist/auth/native-return.js +13 -9
- package/dist/auth/passkey-provider.js +32 -26
- package/dist/auth/second-factors.js +10 -6
- package/dist/auth/session-connections.js +12 -6
- package/dist/auth/session-cookie.js +23 -12
- package/dist/auth/session-lifetimes.js +12 -8
- package/dist/auth/session-mint.js +11 -4
- package/dist/auth/sso-finish-app.js +16 -13
- package/dist/auth/sso-finish-deps.js +27 -21
- package/dist/auth/sso-finish.js +38 -28
- package/dist/auth/sso-routes.js +25 -22
- package/dist/auth/used-bridge-ids.js +5 -1
- package/dist/boot/boot-config.js +55 -16
- package/dist/boot/init-sequence.js +4 -1
- package/dist/boot/integration-id-backfill.js +8 -3
- package/dist/boot/platform-keychain-key.js +42 -5
- package/dist/boot/post-boot.service.js +18 -14
- package/dist/boot/reconcile-lifecycle-jobs.js +6 -3
- package/dist/boot/retired-public-url-env.js +8 -4
- package/dist/bootstrap-packages.js +4 -1
- package/dist/closure-node-modules.js +38 -2
- package/dist/core/addon/addon-call-gateway.js +5 -1
- package/dist/core/addon/addon-package.service.js +123 -83
- package/dist/core/addon/addon-process-view.js +6 -2
- package/dist/core/addon/addon-realign-plan.js +4 -1
- package/dist/core/addon/addon-registry.service.js +187 -146
- package/dist/core/addon/addon-row-manifest.js +4 -1
- package/dist/core/addon/addon-search.service.js +7 -3
- package/dist/core/addon/addon-settings-provider.js +3 -1
- package/dist/core/addon/addon-uninstall.js +11 -4
- package/dist/core/addon/addon.tokens.js +5 -2
- package/dist/core/addon/device-meta-mirror.js +13 -8
- package/dist/core/addon/forked-custom-actions.js +11 -6
- package/dist/core/addon/integration-visibility.js +6 -3
- package/dist/core/addon/materialize-route-path.js +4 -1
- package/dist/core/addon/package-dir-utils.js +45 -8
- package/dist/core/addon/prune-misplaced-addons.js +43 -5
- package/dist/core/addon/runner-convergence.js +4 -1
- package/dist/core/addon/runner-serialization.js +4 -1
- package/dist/core/addon/runner-spawn-fanout.js +8 -3
- package/dist/core/addon/settings-door-wiring.js +8 -5
- package/dist/core/addon/single-flight-refresh.js +4 -1
- package/dist/core/addon-bridge/addon-bridge.service.js +44 -7
- package/dist/core/addon-pages/addon-pages.service.js +40 -3
- package/dist/core/addon-widgets/addon-widgets.service.js +42 -4
- package/dist/core/agent/agent-addon-backfill.js +17 -9
- package/dist/core/agent/agent-delivery-ledger.js +7 -3
- package/dist/core/agent/agent-liveness-event.js +6 -2
- package/dist/core/agent/agent-pairing-http.js +45 -41
- package/dist/core/agent/agent-registry.service.js +89 -50
- package/dist/core/agent/cluster-node-history-mirror.js +7 -3
- package/dist/core/agent/cluster-node-history-store.js +35 -31
- package/dist/core/agent/hub-agent-pairing.js +5 -1
- package/dist/core/agent/node-certificates-client.js +6 -3
- package/dist/core/agent/node-directory.js +4 -1
- package/dist/core/app-links/well-known-app-links.js +6 -2
- package/dist/core/auth/auth.service.js +8 -3
- package/dist/core/auth/handoff-code.service.js +49 -11
- package/dist/core/auth/record-kinds.js +19 -16
- package/dist/core/auth/share-token.service.js +79 -42
- package/dist/core/capability/capability.service.js +5 -1
- package/dist/core/config/config.schema.js +6 -1
- package/dist/core/config/config.service.js +6 -2
- package/dist/core/events/event-bus.service.js +7 -3
- package/dist/core/feature/feature.service.js +6 -2
- package/dist/core/lifecycle/lifecycle-runner.singleton.js +10 -5
- package/dist/core/lifecycle/lifecycle-state-machine.js +5 -1
- package/dist/core/logging/log-ring-buffer.js +5 -1
- package/dist/core/logging/logging.service.js +9 -5
- package/dist/core/logging/scoped-logger.js +5 -1
- package/dist/core/moleculer/agent-readiness-pull.js +4 -1
- package/dist/core/moleculer/agent-registration-request.js +7 -3
- package/dist/core/moleculer/cap-call-fn.js +7 -4
- package/dist/core/moleculer/cap-route-authority.js +8 -3
- package/dist/core/moleculer/hub-mesh-guard.js +9 -5
- package/dist/core/moleculer/moleculer.service.js +86 -79
- package/dist/core/network/network-quality.service.js +6 -2
- package/dist/core/notification/notification-wrapper.service.js +7 -3
- package/dist/core/notification/toast-wrapper.service.js +7 -3
- package/dist/core/provider/provider.tokens.js +4 -1
- package/dist/core/repl/repl-engine.service.js +8 -4
- package/dist/core/runner-health/runner-boot-check.js +15 -7
- package/dist/core/runner-health/update-preflight.js +14 -9
- package/dist/core/server-update/server-update.service.js +49 -12
- package/dist/core/server-update/system-apply-closure-patches.js +42 -6
- package/dist/core/server-update/system-ensure-prebuilds.js +6 -3
- package/dist/core/server-update/system-exec-npm.js +5 -2
- package/dist/core/storage/fs-storage-backend.js +5 -1
- package/dist/core/storage/storage-location-manager.js +5 -1
- package/dist/core/storage/storage.service.js +6 -2
- package/dist/core/streaming/ffmpeg-stream-info.js +8 -3
- package/dist/core/streaming/stream-probe.service.js +16 -12
- package/dist/core/streaming/ttl-cache.js +5 -1
- package/dist/core/topology/topology-emitter.service.js +23 -19
- package/dist/core/update-availability-emitter.js +7 -3
- package/dist/core/update-availability-store.js +45 -8
- package/dist/core/updates/agent-installed-packages.js +9 -5
- package/dist/core/updates/restart-marker.js +8 -3
- package/dist/core/updates/update-check-scheduler.js +18 -13
- package/dist/core/updates/wrapper-update-checker.js +7 -3
- package/dist/first-boot-addon-plan.js +6 -2
- package/dist/framework-nodepath.js +38 -2
- package/dist/i18n/request-lang.js +11 -7
- package/dist/launcher.js +65 -30
- package/dist/lifecycle-journal-path.js +38 -2
- package/dist/main.js +324 -286
- package/dist/manual-boot.js +156 -117
- package/dist/node-role.js +4 -1
- package/dist/package-inventory.js +48 -7
- package/dist/pending-restore.js +43 -7
- package/dist/single-copy-cleanup-runner.js +45 -9
- package/dist/single-copy-cleanup.js +15 -7
- package/dist/sqlite-restore.js +48 -9
- package/dist/starter.js +39 -4
- package/dist/ui-serving/lazy-ui-serving.js +39 -3
- package/package.json +7 -7
|
@@ -1,28 +1,33 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.toWireReason = toWireReason;
|
|
4
|
+
exports.refusal = refusal;
|
|
5
|
+
exports.createDeviceCertProcedures = createDeviceCertProcedures;
|
|
6
|
+
const system_1 = require("@camstack/system");
|
|
7
|
+
const types_1 = require("@camstack/types");
|
|
8
|
+
const server_1 = require("@trpc/server");
|
|
9
|
+
const zod_1 = require("zod");
|
|
10
|
+
const session_mint_js_1 = require("../../auth/session-mint.js");
|
|
11
|
+
const trpc_middleware_js_1 = require("../trpc/trpc.middleware.js");
|
|
12
|
+
const auth_reauth_procedures_js_1 = require("./auth-reauth.procedures.js");
|
|
13
|
+
const user_error_js_1 = require("../trpc/user-error.js");
|
|
14
|
+
const DeviceLoginResultSchema = zod_1.z.discriminatedUnion('ok', [
|
|
15
|
+
zod_1.z.object({
|
|
16
|
+
ok: zod_1.z.literal(true),
|
|
17
|
+
token: zod_1.z.string(),
|
|
18
|
+
user: zod_1.z.object({ id: zod_1.z.string(), username: zod_1.z.string(), isAdmin: zod_1.z.boolean() }),
|
|
19
|
+
expiresAt: zod_1.z.number().optional(),
|
|
20
|
+
sessionId: zod_1.z.string().optional(),
|
|
21
|
+
sessionExpiresAt: zod_1.z.number().optional(),
|
|
22
|
+
refreshToken: zod_1.z.string().optional(),
|
|
23
|
+
credentialId: zod_1.z.string(),
|
|
19
24
|
/** The certificate's expiry (epoch ms): the app renews when < 30 days remain. */
|
|
20
|
-
certNotAfter: z.number(),
|
|
25
|
+
certNotAfter: zod_1.z.number(),
|
|
21
26
|
}),
|
|
22
|
-
DeviceRefusalSchema,
|
|
27
|
+
types_1.DeviceRefusalSchema,
|
|
23
28
|
]);
|
|
24
29
|
/** Internal manager reason → the wire reason an app switches on. */
|
|
25
|
-
|
|
30
|
+
function toWireReason(reason) {
|
|
26
31
|
switch (reason) {
|
|
27
32
|
case 'revoked':
|
|
28
33
|
case 'unknown-credential':
|
|
@@ -53,11 +58,11 @@ export function toWireReason(reason) {
|
|
|
53
58
|
return 'superseded';
|
|
54
59
|
}
|
|
55
60
|
}
|
|
56
|
-
|
|
61
|
+
function refusal(reason, retryAfterSeconds) {
|
|
57
62
|
return {
|
|
58
63
|
ok: false,
|
|
59
64
|
reason,
|
|
60
|
-
definitive: isDefinitiveDeviceRefusal(reason),
|
|
65
|
+
definitive: (0, types_1.isDefinitiveDeviceRefusal)(reason),
|
|
61
66
|
...(retryAfterSeconds !== undefined ? { retryAfterSeconds } : {}),
|
|
62
67
|
};
|
|
63
68
|
}
|
|
@@ -65,13 +70,13 @@ function fromAddress(req, network) {
|
|
|
65
70
|
const ip = network.clientAddress(req).ip;
|
|
66
71
|
return ip ?? undefined;
|
|
67
72
|
}
|
|
68
|
-
|
|
73
|
+
function createDeviceCertProcedures(deps) {
|
|
69
74
|
const internal = () => {
|
|
70
75
|
const found = deps.registry === null
|
|
71
76
|
? null
|
|
72
|
-
: getUserManagementInternal(deps.registry.getSingleton('user-management'));
|
|
77
|
+
: (0, system_1.getUserManagementInternal)(deps.registry.getSingleton('user-management'));
|
|
73
78
|
if (found === null) {
|
|
74
|
-
throw trpcUserError('PRECONDITION_FAILED', 'device.certificates-unavailable', undefined, 'Device certificates are not available');
|
|
79
|
+
throw (0, user_error_js_1.trpcUserError)('PRECONDITION_FAILED', 'device.certificates-unavailable', undefined, 'Device certificates are not available');
|
|
75
80
|
}
|
|
76
81
|
return found;
|
|
77
82
|
};
|
|
@@ -106,13 +111,13 @@ export function createDeviceCertProcedures(deps) {
|
|
|
106
111
|
};
|
|
107
112
|
const assertPerson = (user) => {
|
|
108
113
|
if (user.isScoped === true || user.shareView !== undefined || user.adminTokenId !== undefined) {
|
|
109
|
-
throw trpcUserError('FORBIDDEN', 'auth.session-required', undefined, 'Requires a signed-in user session');
|
|
114
|
+
throw (0, user_error_js_1.trpcUserError)('FORBIDDEN', 'auth.session-required', undefined, 'Requires a signed-in user session');
|
|
110
115
|
}
|
|
111
116
|
};
|
|
112
117
|
const selfLimit = (userId, path) => {
|
|
113
118
|
const verdict = deps.selfLimiter.check(`${path}:${userId}`);
|
|
114
119
|
if (!verdict.allowed) {
|
|
115
|
-
throw new TRPCError({
|
|
120
|
+
throw new server_1.TRPCError({
|
|
116
121
|
code: 'TOO_MANY_REQUESTS',
|
|
117
122
|
message: `Too many attempts — retry in ${verdict.retryAfterSeconds}s`,
|
|
118
123
|
});
|
|
@@ -121,8 +126,8 @@ export function createDeviceCertProcedures(deps) {
|
|
|
121
126
|
return {
|
|
122
127
|
/** PUBLIC — redeem a QR enrollment code with the device's PUBLIC key; answers the certificate. */
|
|
123
128
|
completeEnrollment: deps.publicProcedure
|
|
124
|
-
.input(CompleteEnrollmentInputSchema)
|
|
125
|
-
.output(CompleteEnrollmentResultSchema)
|
|
129
|
+
.input(types_1.CompleteEnrollmentInputSchema)
|
|
130
|
+
.output(types_1.CompleteEnrollmentResultSchema)
|
|
126
131
|
.mutation(async ({ input, ctx }) => {
|
|
127
132
|
const slow = limited(deps.credentialLimiter, 'completeEnrollment', ctx.req);
|
|
128
133
|
if (slow !== null)
|
|
@@ -145,8 +150,8 @@ export function createDeviceCertProcedures(deps) {
|
|
|
145
150
|
}),
|
|
146
151
|
/** PUBLIC — a single-use nonce (60 s), bound to the hub origin this request is entitled to. */
|
|
147
152
|
getDeviceChallenge: deps.publicProcedure
|
|
148
|
-
.input(z.void())
|
|
149
|
-
.output(DeviceChallengeResultSchema)
|
|
153
|
+
.input(zod_1.z.void())
|
|
154
|
+
.output(types_1.DeviceChallengeResultSchema)
|
|
150
155
|
.mutation(async ({ ctx }) => {
|
|
151
156
|
const slow = limited(deps.ceremonyLimiter, 'getDeviceChallenge', ctx.req);
|
|
152
157
|
if (slow !== null)
|
|
@@ -162,7 +167,7 @@ export function createDeviceCertProcedures(deps) {
|
|
|
162
167
|
if (origin === null)
|
|
163
168
|
return refusal('unavailable');
|
|
164
169
|
const { nonce, expiresAt } = internal().deviceCredentials.issueNonce(origin, partition);
|
|
165
|
-
return { ok: true, nonce, origin, expiresAt, signature: DEVICE_CERT_SIGNATURE };
|
|
170
|
+
return { ok: true, nonce, origin, expiresAt, signature: types_1.DEVICE_CERT_SIGNATURE };
|
|
166
171
|
}),
|
|
167
172
|
/**
|
|
168
173
|
* PUBLIC — sign in with the device certificate: chain to the device CA, not
|
|
@@ -170,7 +175,7 @@ export function createDeviceCertProcedures(deps) {
|
|
|
170
175
|
* Opens the SAME session a password login opens, carrying the credential id.
|
|
171
176
|
*/
|
|
172
177
|
loginWithDeviceCertificate: deps.publicProcedure
|
|
173
|
-
.input(DeviceProofInputSchema.extend({ refreshable: z.boolean().optional() }))
|
|
178
|
+
.input(types_1.DeviceProofInputSchema.extend({ refreshable: zod_1.z.boolean().optional() }))
|
|
174
179
|
.output(DeviceLoginResultSchema)
|
|
175
180
|
.mutation(async ({ input, ctx }) => {
|
|
176
181
|
const slow = limited(deps.credentialLimiter, 'loginWithDeviceCertificate', ctx.req);
|
|
@@ -215,7 +220,7 @@ export function createDeviceCertProcedures(deps) {
|
|
|
215
220
|
};
|
|
216
221
|
}
|
|
217
222
|
catch (error) {
|
|
218
|
-
if (error instanceof SessionUserGoneError) {
|
|
223
|
+
if (error instanceof session_mint_js_1.SessionUserGoneError) {
|
|
219
224
|
deps.logger?.warn('Device certificate login refused: user-gone', {
|
|
220
225
|
tags: { credentialId: credential.id, userId: credential.userId },
|
|
221
226
|
});
|
|
@@ -226,8 +231,8 @@ export function createDeviceCertProcedures(deps) {
|
|
|
226
231
|
}),
|
|
227
232
|
/** PUBLIC — a new leaf for the SAME public key, while the certificate is valid and not revoked. */
|
|
228
233
|
renewDeviceCertificate: deps.publicProcedure
|
|
229
|
-
.input(DeviceProofInputSchema)
|
|
230
|
-
.output(RenewDeviceCertificateResultSchema)
|
|
234
|
+
.input(types_1.DeviceProofInputSchema)
|
|
235
|
+
.output(types_1.RenewDeviceCertificateResultSchema)
|
|
231
236
|
.mutation(async ({ input, ctx }) => {
|
|
232
237
|
const slow = limited(deps.credentialLimiter, 'renewDeviceCertificate', ctx.req);
|
|
233
238
|
if (slow !== null)
|
|
@@ -256,8 +261,8 @@ export function createDeviceCertProcedures(deps) {
|
|
|
256
261
|
* purpose): works with no session. Ends every session the credential opened.
|
|
257
262
|
*/
|
|
258
263
|
revokeOwnDeviceCertificate: deps.publicProcedure
|
|
259
|
-
.input(DeviceProofInputSchema)
|
|
260
|
-
.output(RevokeOwnDeviceCertificateResultSchema)
|
|
264
|
+
.input(types_1.DeviceProofInputSchema)
|
|
265
|
+
.output(types_1.RevokeOwnDeviceCertificateResultSchema)
|
|
261
266
|
.mutation(async ({ input, ctx }) => {
|
|
262
267
|
const slow = limited(deps.credentialLimiter, 'revokeOwnDeviceCertificate', ctx.req);
|
|
263
268
|
if (slow !== null)
|
|
@@ -271,7 +276,7 @@ export function createDeviceCertProcedures(deps) {
|
|
|
271
276
|
if (!checked.ok)
|
|
272
277
|
return refusal(toWireReason(checked.reason));
|
|
273
278
|
const id = checked.credential.id;
|
|
274
|
-
await internal().deviceCredentials.revoke(id, formatDeviceRevoker({ kind: 'device' }));
|
|
279
|
+
await internal().deviceCredentials.revoke(id, (0, types_1.formatDeviceRevoker)({ kind: 'device' }));
|
|
275
280
|
const sessionsEnded = await internal().sessions.revokeByCredential(id);
|
|
276
281
|
await audit('device-credential-revoked', checked.credential.userId, ctx.req, {
|
|
277
282
|
credentialId: id,
|
|
@@ -282,9 +287,9 @@ export function createDeviceCertProcedures(deps) {
|
|
|
282
287
|
}),
|
|
283
288
|
// ── Signed-in legs ───────────────────────────────────────────────
|
|
284
289
|
/** The caller's own device credentials (live ones, and revoked when asked). */
|
|
285
|
-
listOwnDeviceCredentials: protectedProcedure
|
|
286
|
-
.input(z.object({ includeRevoked: z.boolean().optional() }).optional())
|
|
287
|
-
.output(z.array(DeviceCredentialSchema))
|
|
290
|
+
listOwnDeviceCredentials: trpc_middleware_js_1.protectedProcedure
|
|
291
|
+
.input(zod_1.z.object({ includeRevoked: zod_1.z.boolean().optional() }).optional())
|
|
292
|
+
.output(zod_1.z.array(types_1.DeviceCredentialSchema))
|
|
288
293
|
.query(({ ctx, input }) => {
|
|
289
294
|
assertPerson(ctx.user);
|
|
290
295
|
const manager = internal().deviceCredentials;
|
|
@@ -312,9 +317,9 @@ export function createDeviceCertProcedures(deps) {
|
|
|
312
317
|
}));
|
|
313
318
|
}),
|
|
314
319
|
/** Revoke one of the caller's OWN credentials by id, from a session (e.g. a lost phone). */
|
|
315
|
-
revokeOwnDeviceCredential: protectedProcedure
|
|
316
|
-
.input(z.object({ id: z.string().min(1) }))
|
|
317
|
-
.output(z.object({ revoked: z.boolean(), sessionsEnded: z.number().int() }))
|
|
320
|
+
revokeOwnDeviceCredential: trpc_middleware_js_1.protectedProcedure
|
|
321
|
+
.input(zod_1.z.object({ id: zod_1.z.string().min(1) }))
|
|
322
|
+
.output(zod_1.z.object({ revoked: zod_1.z.boolean(), sessionsEnded: zod_1.z.number().int() }))
|
|
318
323
|
.mutation(async ({ ctx, input }) => {
|
|
319
324
|
assertPerson(ctx.user);
|
|
320
325
|
selfLimit(ctx.user.id, 'revokeOwnDeviceCredential');
|
|
@@ -326,7 +331,7 @@ export function createDeviceCertProcedures(deps) {
|
|
|
326
331
|
});
|
|
327
332
|
return { revoked: false, sessionsEnded: 0 };
|
|
328
333
|
}
|
|
329
|
-
const revoked = await internal().deviceCredentials.revoke(input.id, formatDeviceRevoker({ kind: 'user', userId: ctx.user.id }));
|
|
334
|
+
const revoked = await internal().deviceCredentials.revoke(input.id, (0, types_1.formatDeviceRevoker)({ kind: 'user', userId: ctx.user.id }));
|
|
330
335
|
const sessionsEnded = await internal().sessions.revokeByCredential(input.id);
|
|
331
336
|
if (revoked) {
|
|
332
337
|
await audit('device-credential-revoked', ctx.user.id, ctx.req, {
|
|
@@ -338,16 +343,16 @@ export function createDeviceCertProcedures(deps) {
|
|
|
338
343
|
return { revoked, sessionsEnded };
|
|
339
344
|
}),
|
|
340
345
|
/** Step-up, password / passkey: turn a fresh `reauthenticate` proof into a single-use device-enroll proof. */
|
|
341
|
-
deviceEnrollProofFromReauth: protectedProcedure
|
|
342
|
-
.input(z.object({ reauthToken: ReauthTokenInput }))
|
|
343
|
-
.output(DeviceEnrollProofSchema)
|
|
346
|
+
deviceEnrollProofFromReauth: trpc_middleware_js_1.protectedProcedure
|
|
347
|
+
.input(zod_1.z.object({ reauthToken: auth_reauth_procedures_js_1.ReauthTokenInput }))
|
|
348
|
+
.output(types_1.DeviceEnrollProofSchema)
|
|
344
349
|
.mutation(({ ctx, input }) => {
|
|
345
350
|
assertPerson(ctx.user);
|
|
346
351
|
selfLimit(ctx.user.id, 'deviceEnrollProofFromReauth');
|
|
347
|
-
assertReauthenticated(deps.auth, ctx.user, input.reauthToken);
|
|
352
|
+
(0, auth_reauth_procedures_js_1.assertReauthenticated)(deps.auth, ctx.user, input.reauthToken);
|
|
348
353
|
// One reauthentication, one proof (or one step-up handoff code).
|
|
349
354
|
if (!deps.enrollProofs.consumeReauth(input.reauthToken)) {
|
|
350
|
-
throw new TRPCError({
|
|
355
|
+
throw new server_1.TRPCError({
|
|
351
356
|
code: 'FORBIDDEN',
|
|
352
357
|
message: 'This re-authentication was already used - confirm it is you again',
|
|
353
358
|
});
|
|
@@ -359,13 +364,13 @@ export function createDeviceCertProcedures(deps) {
|
|
|
359
364
|
* `code` is a handoff code minted with purpose `step-up` by a browser sign-in
|
|
360
365
|
* of the SAME user. Opens no session and replaces none.
|
|
361
366
|
*/
|
|
362
|
-
deviceEnrollProofFromHandoff: protectedProcedure
|
|
363
|
-
.input(z.object({
|
|
364
|
-
code: z.string().min(1).max(256),
|
|
365
|
-
codeVerifier: z.string().min(1).max(256).optional(),
|
|
366
|
-
state: z.string().min(1).max(256).optional(),
|
|
367
|
+
deviceEnrollProofFromHandoff: trpc_middleware_js_1.protectedProcedure
|
|
368
|
+
.input(zod_1.z.object({
|
|
369
|
+
code: zod_1.z.string().min(1).max(256),
|
|
370
|
+
codeVerifier: zod_1.z.string().min(1).max(256).optional(),
|
|
371
|
+
state: zod_1.z.string().min(1).max(256).optional(),
|
|
367
372
|
}))
|
|
368
|
-
.output(DeviceEnrollProofSchema)
|
|
373
|
+
.output(types_1.DeviceEnrollProofSchema)
|
|
369
374
|
.mutation(({ ctx, input }) => {
|
|
370
375
|
assertPerson(ctx.user);
|
|
371
376
|
selfLimit(ctx.user.id, 'deviceEnrollProofFromHandoff');
|
|
@@ -381,7 +386,7 @@ export function createDeviceCertProcedures(deps) {
|
|
|
381
386
|
reason: reason ?? (grant?.purpose !== 'step-up' ? 'wrong-purpose' : 'other-user'),
|
|
382
387
|
},
|
|
383
388
|
});
|
|
384
|
-
throw new TRPCError({
|
|
389
|
+
throw new server_1.TRPCError({
|
|
385
390
|
code: 'UNAUTHORIZED',
|
|
386
391
|
message: 'Invalid, expired or mismatched step-up code — sign in again',
|
|
387
392
|
});
|
|
@@ -394,9 +399,9 @@ export function createDeviceCertProcedures(deps) {
|
|
|
394
399
|
* a merely refreshed session can never mint a permanent credential.
|
|
395
400
|
* Hub-wide `deviceCertificates.selfEnrollment = disabled` refuses.
|
|
396
401
|
*/
|
|
397
|
-
enrollSelf: protectedProcedure
|
|
398
|
-
.input(EnrollSelfInputSchema)
|
|
399
|
-
.output(CompleteEnrollmentResultSchema)
|
|
402
|
+
enrollSelf: trpc_middleware_js_1.protectedProcedure
|
|
403
|
+
.input(types_1.EnrollSelfInputSchema)
|
|
404
|
+
.output(types_1.CompleteEnrollmentResultSchema)
|
|
400
405
|
.mutation(async ({ ctx, input }) => {
|
|
401
406
|
assertPerson(ctx.user);
|
|
402
407
|
selfLimit(ctx.user.id, 'enrollSelf');
|
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PROCESS_AUTH_LIMITERS = void 0;
|
|
4
|
+
exports.createAuthLimiters = createAuthLimiters;
|
|
5
|
+
exports.tooMany = tooMany;
|
|
6
|
+
exports.limitByClient = limitByClient;
|
|
7
|
+
exports.assertAccountNotLocked = assertAccountNotLocked;
|
|
8
|
+
exports.recordAccountFailure = recordAccountFailure;
|
|
9
|
+
exports.limitByUser = limitByUser;
|
|
1
10
|
/**
|
|
2
11
|
* Rate limits of the public auth surface, in one place.
|
|
3
12
|
*
|
|
@@ -20,29 +29,29 @@
|
|
|
20
29
|
*
|
|
21
30
|
* Mesh-internal calls (no originating request) are never limited.
|
|
22
31
|
*/
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
32
|
+
const server_1 = require("@trpc/server");
|
|
33
|
+
const auth_network_policy_js_1 = require("../../auth/auth-network-policy.js");
|
|
34
|
+
const auth_rate_limit_js_1 = require("../../auth/auth-rate-limit.js");
|
|
35
|
+
function createAuthLimiters(now) {
|
|
27
36
|
const clock = now !== undefined ? { now } : {};
|
|
28
37
|
return {
|
|
29
|
-
credential: createRateLimiter({ windowMs: 60_000, max: 10, ...clock }),
|
|
30
|
-
ceremony: createRateLimiter({ windowMs: 60_000, max: 30, ...clock }),
|
|
31
|
-
accountFailures: createRateLimiter({ windowMs: 15 * 60_000, max: 20, ...clock }),
|
|
32
|
-
link: createRateLimiter({ windowMs: 60_000, max: 5, ...clock }),
|
|
33
|
-
reauth: createRateLimiter({ windowMs: 60_000, max: 10, ...clock }),
|
|
38
|
+
credential: (0, auth_rate_limit_js_1.createRateLimiter)({ windowMs: 60_000, max: 10, ...clock }),
|
|
39
|
+
ceremony: (0, auth_rate_limit_js_1.createRateLimiter)({ windowMs: 60_000, max: 30, ...clock }),
|
|
40
|
+
accountFailures: (0, auth_rate_limit_js_1.createRateLimiter)({ windowMs: 15 * 60_000, max: 20, ...clock }),
|
|
41
|
+
link: (0, auth_rate_limit_js_1.createRateLimiter)({ windowMs: 60_000, max: 5, ...clock }),
|
|
42
|
+
reauth: (0, auth_rate_limit_js_1.createRateLimiter)({ windowMs: 60_000, max: 10, ...clock }),
|
|
34
43
|
};
|
|
35
44
|
}
|
|
36
45
|
/** The process-wide limiters: router rebuilds must not reset an attacker's budget. */
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
return new TRPCError({
|
|
46
|
+
exports.PROCESS_AUTH_LIMITERS = createAuthLimiters();
|
|
47
|
+
function tooMany(retryAfterSeconds) {
|
|
48
|
+
return new server_1.TRPCError({
|
|
40
49
|
code: 'TOO_MANY_REQUESTS',
|
|
41
50
|
message: `Too many attempts — retry in ${retryAfterSeconds}s`,
|
|
42
51
|
});
|
|
43
52
|
}
|
|
44
53
|
/** Count one attempt from the request's client address; throws when over. */
|
|
45
|
-
|
|
54
|
+
function limitByClient(limiter, bucket, req, network = auth_network_policy_js_1.DEFAULT_AUTH_NETWORK_POLICY) {
|
|
46
55
|
const ip = network.clientAddress(req).ip;
|
|
47
56
|
if (ip === null)
|
|
48
57
|
return;
|
|
@@ -51,17 +60,17 @@ export function limitByClient(limiter, bucket, req, network = DEFAULT_AUTH_NETWO
|
|
|
51
60
|
throw tooMany(verdict.retryAfterSeconds);
|
|
52
61
|
}
|
|
53
62
|
/** Refuse before checking a secret when `account` already failed too often. */
|
|
54
|
-
|
|
63
|
+
function assertAccountNotLocked(limiters, account) {
|
|
55
64
|
const verdict = limiters.accountFailures.peek(`account:${account.toLowerCase()}`);
|
|
56
65
|
if (!verdict.allowed)
|
|
57
66
|
throw tooMany(verdict.retryAfterSeconds);
|
|
58
67
|
}
|
|
59
68
|
/** Count one failed secret against `account`. */
|
|
60
|
-
|
|
69
|
+
function recordAccountFailure(limiters, account) {
|
|
61
70
|
limiters.accountFailures.check(`account:${account.toLowerCase()}`);
|
|
62
71
|
}
|
|
63
72
|
/** Count one call of an authenticated leg against the user; throws when over. */
|
|
64
|
-
|
|
73
|
+
function limitByUser(limiter, bucket, userId) {
|
|
65
74
|
const verdict = limiter.check(`${bucket}:${userId}`);
|
|
66
75
|
if (!verdict.allowed)
|
|
67
76
|
throw tooMany(verdict.retryAfterSeconds);
|
|
@@ -1,23 +1,28 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.REAUTH_REQUIRED_MESSAGE = exports.ReauthTokenInput = void 0;
|
|
4
|
+
exports.assertReauthenticated = assertReauthenticated;
|
|
5
|
+
exports.createReauthProcedures = createReauthProcedures;
|
|
6
|
+
const server_1 = require("@trpc/server");
|
|
7
|
+
const zod_1 = require("zod");
|
|
8
|
+
const passkey_provider_js_1 = require("../../auth/passkey-provider.js");
|
|
9
|
+
const trpc_middleware_js_1 = require("../trpc/trpc.middleware.js");
|
|
10
|
+
const auth_limits_js_1 = require("./auth-limits.js");
|
|
11
|
+
const user_error_js_1 = require("../trpc/user-error.js");
|
|
7
12
|
/** The input field a re-auth-gated procedure takes. */
|
|
8
|
-
|
|
9
|
-
|
|
13
|
+
exports.ReauthTokenInput = zod_1.z.string().min(1).max(4096);
|
|
14
|
+
exports.REAUTH_REQUIRED_MESSAGE = 'Confirm it is you first: re-enter your password or use a passkey (update the app if it cannot ask).';
|
|
10
15
|
/**
|
|
11
16
|
* Throw unless `reauthToken` is a live proof by THIS user, made in THIS
|
|
12
17
|
* session. A missing token is the same refusal: the action needs one.
|
|
13
18
|
*/
|
|
14
|
-
|
|
19
|
+
function assertReauthenticated(auth, user, reauthToken) {
|
|
15
20
|
const claims = reauthToken === undefined ? null : auth.verifyReauthToken(reauthToken);
|
|
16
21
|
const valid = claims !== null &&
|
|
17
22
|
claims.userId === user.id &&
|
|
18
23
|
(user.sessionId === undefined || claims.sessionId === user.sessionId);
|
|
19
24
|
if (!valid)
|
|
20
|
-
throw new TRPCError({ code: 'FORBIDDEN', message: REAUTH_REQUIRED_MESSAGE });
|
|
25
|
+
throw new server_1.TRPCError({ code: 'FORBIDDEN', message: exports.REAUTH_REQUIRED_MESSAGE });
|
|
21
26
|
}
|
|
22
27
|
function isPasswordChecker(value) {
|
|
23
28
|
return (value !== null &&
|
|
@@ -27,19 +32,19 @@ function isPasswordChecker(value) {
|
|
|
27
32
|
/** Real user sessions only: a scoped or share token cannot prove a password. */
|
|
28
33
|
function assertPerson(user) {
|
|
29
34
|
if (user.isScoped === true || user.shareView !== undefined || user.adminTokenId !== undefined) {
|
|
30
|
-
throw trpcUserError('FORBIDDEN', 'auth.session-required', undefined, 'Requires a signed-in user session');
|
|
35
|
+
throw (0, user_error_js_1.trpcUserError)('FORBIDDEN', 'auth.session-required', undefined, 'Requires a signed-in user session');
|
|
31
36
|
}
|
|
32
37
|
}
|
|
33
|
-
|
|
38
|
+
function createReauthProcedures(deps) {
|
|
34
39
|
return {
|
|
35
40
|
/** Passkey re-authentication, step 1: assertion options for the caller's own credentials. */
|
|
36
|
-
reauthBeginPasskey: protectedProcedure
|
|
37
|
-
.input(z.void())
|
|
38
|
-
.output(OptionsJsonSchema)
|
|
41
|
+
reauthBeginPasskey: trpc_middleware_js_1.protectedProcedure
|
|
42
|
+
.input(zod_1.z.void())
|
|
43
|
+
.output(passkey_provider_js_1.OptionsJsonSchema)
|
|
39
44
|
.mutation(async ({ ctx }) => {
|
|
40
45
|
assertPerson(ctx.user);
|
|
41
|
-
limitByUser(deps.limiters.reauth, 'auth.reauthBeginPasskey', ctx.user.id);
|
|
42
|
-
const provider = resolvePasskeyProvider(deps.registry, deps.moleculer);
|
|
46
|
+
(0, auth_limits_js_1.limitByUser)(deps.limiters.reauth, 'auth.reauthBeginPasskey', ctx.user.id);
|
|
47
|
+
const provider = (0, passkey_provider_js_1.resolvePasskeyProvider)(deps.registry, deps.moleculer);
|
|
43
48
|
if (!provider)
|
|
44
49
|
throw new Error('Passkey authentication is not available');
|
|
45
50
|
return provider.beginAuthentication({ userId: ctx.user.id });
|
|
@@ -49,20 +54,20 @@ export function createReauthProcedures(deps) {
|
|
|
49
54
|
* `reauthBeginPasskey`. Answers a 5-minute proof for the re-auth-gated
|
|
50
55
|
* actions, valid only for this user in this session.
|
|
51
56
|
*/
|
|
52
|
-
reauthenticate: protectedProcedure
|
|
53
|
-
.input(z.union([
|
|
54
|
-
z.object({ password: z.string().min(1).max(1024) }),
|
|
55
|
-
z.object({ passkeyResponse: z.record(z.string(), z.unknown()) }),
|
|
57
|
+
reauthenticate: trpc_middleware_js_1.protectedProcedure
|
|
58
|
+
.input(zod_1.z.union([
|
|
59
|
+
zod_1.z.object({ password: zod_1.z.string().min(1).max(1024) }),
|
|
60
|
+
zod_1.z.object({ passkeyResponse: zod_1.z.record(zod_1.z.string(), zod_1.z.unknown()) }),
|
|
56
61
|
]))
|
|
57
|
-
.output(z.object({
|
|
58
|
-
reauthToken: z.string(),
|
|
59
|
-
expiresAt: z.number(),
|
|
60
|
-
method: z.enum(['password', 'passkey']),
|
|
62
|
+
.output(zod_1.z.object({
|
|
63
|
+
reauthToken: zod_1.z.string(),
|
|
64
|
+
expiresAt: zod_1.z.number(),
|
|
65
|
+
method: zod_1.z.enum(['password', 'passkey']),
|
|
61
66
|
}))
|
|
62
67
|
.mutation(async ({ ctx, input }) => {
|
|
63
68
|
assertPerson(ctx.user);
|
|
64
|
-
limitByUser(deps.limiters.reauth, 'auth.reauthenticate', ctx.user.id);
|
|
65
|
-
assertAccountNotLocked(deps.limiters, ctx.user.username);
|
|
69
|
+
(0, auth_limits_js_1.limitByUser)(deps.limiters.reauth, 'auth.reauthenticate', ctx.user.id);
|
|
70
|
+
(0, auth_limits_js_1.assertAccountNotLocked)(deps.limiters, ctx.user.username);
|
|
66
71
|
let method;
|
|
67
72
|
if ('password' in input) {
|
|
68
73
|
const userMgmt = deps.registry?.getSingleton('user-management');
|
|
@@ -74,13 +79,13 @@ export function createReauthProcedures(deps) {
|
|
|
74
79
|
password: input.password,
|
|
75
80
|
});
|
|
76
81
|
if (record === null || record.id !== ctx.user.id) {
|
|
77
|
-
recordAccountFailure(deps.limiters, ctx.user.username);
|
|
78
|
-
throw trpcUserError('UNAUTHORIZED', 'auth.password-incorrect', undefined, 'Password is incorrect');
|
|
82
|
+
(0, auth_limits_js_1.recordAccountFailure)(deps.limiters, ctx.user.username);
|
|
83
|
+
throw (0, user_error_js_1.trpcUserError)('UNAUTHORIZED', 'auth.password-incorrect', undefined, 'Password is incorrect');
|
|
79
84
|
}
|
|
80
85
|
method = 'password';
|
|
81
86
|
}
|
|
82
87
|
else {
|
|
83
|
-
const provider = resolvePasskeyProvider(deps.registry, deps.moleculer);
|
|
88
|
+
const provider = (0, passkey_provider_js_1.resolvePasskeyProvider)(deps.registry, deps.moleculer);
|
|
84
89
|
if (!provider)
|
|
85
90
|
throw new Error('Passkey authentication is not available');
|
|
86
91
|
const result = await provider.finishAuthentication({
|
|
@@ -88,8 +93,8 @@ export function createReauthProcedures(deps) {
|
|
|
88
93
|
response: input.passkeyResponse,
|
|
89
94
|
});
|
|
90
95
|
if (!result.verified) {
|
|
91
|
-
recordAccountFailure(deps.limiters, ctx.user.username);
|
|
92
|
-
throw trpcUserError('UNAUTHORIZED', 'auth.passkey-verification-failed', undefined, 'Passkey verification failed');
|
|
96
|
+
(0, auth_limits_js_1.recordAccountFailure)(deps.limiters, ctx.user.username);
|
|
97
|
+
throw (0, user_error_js_1.trpcUserError)('UNAUTHORIZED', 'auth.passkey-verification-failed', undefined, 'Passkey verification failed');
|
|
93
98
|
}
|
|
94
99
|
method = 'passkey';
|
|
95
100
|
}
|
|
@@ -1,32 +1,36 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RevokeSkipReasonSchema = exports.RevocableCredentialKindSchema = exports.REVOKE_CREDENTIALS_MAX = void 0;
|
|
4
|
+
exports.createRevokeCredentialsProcedures = createRevokeCredentialsProcedures;
|
|
5
|
+
const server_1 = require("@trpc/server");
|
|
6
|
+
const zod_1 = require("zod");
|
|
7
|
+
const hub_sessions_js_1 = require("../../auth/hub-sessions.js");
|
|
8
|
+
const trpc_middleware_js_1 = require("../trpc/trpc.middleware.js");
|
|
5
9
|
/** Upper bound of one bulk call — a page of a table, several times over. */
|
|
6
|
-
|
|
10
|
+
exports.REVOKE_CREDENTIALS_MAX = 500;
|
|
7
11
|
/** What a bulk revoke can target. Each has a per-row revoke on the server. */
|
|
8
|
-
|
|
12
|
+
exports.RevocableCredentialKindSchema = zod_1.z.enum([
|
|
9
13
|
'share-link',
|
|
10
14
|
'admin-token',
|
|
11
15
|
'login-session',
|
|
12
16
|
'scoped-token',
|
|
13
17
|
'oauth-session',
|
|
14
18
|
]);
|
|
15
|
-
|
|
16
|
-
const RevokeCredentialsInputSchema = z
|
|
19
|
+
exports.RevokeSkipReasonSchema = zod_1.z.enum(['current-session', 'not-active']);
|
|
20
|
+
const RevokeCredentialsInputSchema = zod_1.z
|
|
17
21
|
.object({
|
|
18
|
-
kind: RevocableCredentialKindSchema,
|
|
19
|
-
ids: z.array(z.string().min(1)).min(1).max(REVOKE_CREDENTIALS_MAX),
|
|
22
|
+
kind: exports.RevocableCredentialKindSchema,
|
|
23
|
+
ids: zod_1.z.array(zod_1.z.string().min(1)).min(1).max(exports.REVOKE_CREDENTIALS_MAX),
|
|
20
24
|
/** The count the operator confirmed. Must equal `ids.length`. */
|
|
21
|
-
confirmCount: z.number().int().min(1),
|
|
25
|
+
confirmCount: zod_1.z.number().int().min(1),
|
|
22
26
|
})
|
|
23
27
|
.refine((input) => new Set(input.ids).size === input.ids.length, {
|
|
24
28
|
message: 'Each id may be listed once',
|
|
25
29
|
path: ['ids'],
|
|
26
30
|
});
|
|
27
|
-
const RevokeCredentialsOutputSchema = z.object({
|
|
28
|
-
revoked: z.array(z.string()),
|
|
29
|
-
skipped: z.array(z.object({ id: z.string(), reason: RevokeSkipReasonSchema })),
|
|
31
|
+
const RevokeCredentialsOutputSchema = zod_1.z.object({
|
|
32
|
+
revoked: zod_1.z.array(zod_1.z.string()),
|
|
33
|
+
skipped: zod_1.z.array(zod_1.z.object({ id: zod_1.z.string(), reason: exports.RevokeSkipReasonSchema })),
|
|
30
34
|
});
|
|
31
35
|
/** Keep the audit row bounded; the ids beyond this are counted, not listed. */
|
|
32
36
|
const AUDIT_IDS_MAX_CHARS = 4000;
|
|
@@ -35,16 +39,16 @@ function assertSignedInAdminSession(user) {
|
|
|
35
39
|
user.shareView !== undefined ||
|
|
36
40
|
user.credential !== undefined ||
|
|
37
41
|
user.adminTokenId !== undefined) {
|
|
38
|
-
throw new TRPCError({
|
|
42
|
+
throw new server_1.TRPCError({
|
|
39
43
|
code: 'FORBIDDEN',
|
|
40
44
|
message: 'Revoking credentials in bulk needs a signed-in admin session',
|
|
41
45
|
});
|
|
42
46
|
}
|
|
43
47
|
}
|
|
44
48
|
function requireInternal(registry) {
|
|
45
|
-
const internal = registry === null ? null : userManagementInternal(registry);
|
|
49
|
+
const internal = registry === null ? null : (0, hub_sessions_js_1.userManagementInternal)(registry);
|
|
46
50
|
if (internal === null) {
|
|
47
|
-
throw new TRPCError({
|
|
51
|
+
throw new server_1.TRPCError({
|
|
48
52
|
code: 'PRECONDITION_FAILED',
|
|
49
53
|
message: 'User management is not available',
|
|
50
54
|
});
|
|
@@ -56,7 +60,7 @@ function revokerFor(kind, caller, deps) {
|
|
|
56
60
|
if (kind === 'share-link') {
|
|
57
61
|
const service = deps.shareTokens;
|
|
58
62
|
if (service === null) {
|
|
59
|
-
throw new TRPCError({ code: 'PRECONDITION_FAILED', message: 'Share links are not available' });
|
|
63
|
+
throw new server_1.TRPCError({ code: 'PRECONDITION_FAILED', message: 'Share links are not available' });
|
|
60
64
|
}
|
|
61
65
|
return async (id) => (await service.revoke({ id, callerUserId: caller.id, callerIsAdmin: true }))
|
|
62
66
|
? null
|
|
@@ -84,16 +88,16 @@ function auditIds(ids) {
|
|
|
84
88
|
const joined = ids.join(',');
|
|
85
89
|
return joined.length <= AUDIT_IDS_MAX_CHARS ? joined : `${joined.slice(0, AUDIT_IDS_MAX_CHARS)}…`;
|
|
86
90
|
}
|
|
87
|
-
|
|
91
|
+
function createRevokeCredentialsProcedures(deps) {
|
|
88
92
|
return {
|
|
89
93
|
/** ADMIN — revoke exactly `ids` of one kind; see the module header. */
|
|
90
|
-
revokeCredentials: adminProcedure
|
|
94
|
+
revokeCredentials: trpc_middleware_js_1.adminProcedure
|
|
91
95
|
.input(RevokeCredentialsInputSchema)
|
|
92
96
|
.output(RevokeCredentialsOutputSchema)
|
|
93
97
|
.mutation(async ({ ctx, input }) => {
|
|
94
98
|
assertSignedInAdminSession(ctx.user);
|
|
95
99
|
if (input.confirmCount !== input.ids.length) {
|
|
96
|
-
throw new TRPCError({
|
|
100
|
+
throw new server_1.TRPCError({
|
|
97
101
|
code: 'BAD_REQUEST',
|
|
98
102
|
message: `Confirmed ${input.confirmCount} but ${input.ids.length} were listed — nothing was revoked`,
|
|
99
103
|
});
|
|
@@ -110,7 +114,7 @@ export function createRevokeCredentialsProcedures(deps) {
|
|
|
110
114
|
else
|
|
111
115
|
skipped.push({ id, reason });
|
|
112
116
|
}
|
|
113
|
-
const internal = deps.registry === null ? null : userManagementInternal(deps.registry);
|
|
117
|
+
const internal = deps.registry === null ? null : (0, hub_sessions_js_1.userManagementInternal)(deps.registry);
|
|
114
118
|
if (internal !== null) {
|
|
115
119
|
const ip = deps.network.clientAddress(ctx.req).ip;
|
|
116
120
|
await internal.audit.record({
|