@camstack/server 1.2.592 → 1.2.594
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 +11 -50
- package/dist/agent/addon-scan.js +6 -44
- package/dist/agent/agent-addon-runtime.js +3 -8
- package/dist/agent/agent-addon-updates.js +12 -52
- package/dist/agent/agent-cap-dispatch-service.js +3 -5
- package/dist/agent/agent-config.js +7 -42
- package/dist/agent/agent-deploy-swap.js +5 -41
- package/dist/agent/agent-group-runner.js +8 -47
- package/dist/agent/agent-http-auth.js +10 -22
- package/dist/agent/agent-http.js +69 -115
- package/dist/agent/agent-hub-addon-update.js +10 -16
- package/dist/agent/agent-hub-registration.js +4 -7
- package/dist/agent/agent-infra-capabilities.js +5 -9
- package/dist/agent/agent-json-file.js +5 -42
- package/dist/agent/agent-mesh-gate.js +5 -8
- package/dist/agent/agent-node-certificates.js +25 -28
- package/dist/agent/agent-pairing-local.js +10 -47
- package/dist/agent/agent-pairing-routes.js +16 -20
- package/dist/agent/agent-pairing-store.js +18 -58
- package/dist/agent/agent-reconnect.js +1 -4
- package/dist/agent/agent-service.js +42 -78
- package/dist/agent/agent-sniffing-listener.js +11 -48
- package/dist/agent/agent-undeploy.js +5 -41
- package/dist/agent/agent-update-routes.js +14 -19
- package/dist/agent/agent-update-service.js +38 -80
- package/dist/agent/apply-model-distribution.js +1 -4
- package/dist/agent/apply-stage-dev-uploads.js +6 -9
- package/dist/agent/derive-hub-url.js +3 -8
- package/dist/agent/fetch-bundle-from-hub.js +6 -9
- package/dist/agent/hubs-found.js +6 -43
- package/dist/agent/infra-boot-guard.js +1 -4
- package/dist/agent/infra-boot-plan.js +2 -6
- package/dist/agent/main.js +158 -186
- package/dist/agent/register-agent-cap-dispatch.js +3 -6
- package/dist/agent/serial-queue.js +2 -7
- package/dist/agent-status-page.js +1 -4
- package/dist/api/addon-upload.js +22 -61
- package/dist/api/agent-addon-delivery.js +6 -11
- package/dist/api/auth-whoami.js +1 -4
- package/dist/api/backup-download-pump.js +5 -9
- package/dist/api/backup-download.js +7 -10
- package/dist/api/core/addon-settings.router.js +16 -19
- package/dist/api/core/agent-pairing.router.js +76 -79
- package/dist/api/core/agents.router.js +21 -24
- package/dist/api/core/auth-admin-tokens.procedures.js +48 -52
- package/dist/api/core/auth-device-cert.procedures.js +62 -67
- package/dist/api/core/auth-limits.js +16 -25
- package/dist/api/core/auth-reauth.procedures.js +32 -37
- package/dist/api/core/auth-revoke-credentials.procedures.js +22 -26
- package/dist/api/core/auth-sessions.procedures.js +52 -56
- package/dist/api/core/auth.router.js +300 -304
- package/dist/api/core/cap-providers.js +74 -121
- package/dist/api/core/capabilities.router.js +34 -37
- package/dist/api/core/capability-preference-writes.js +10 -17
- package/dist/api/core/certificates.router.js +106 -109
- package/dist/api/core/cluster-nodes.router.js +15 -18
- package/dist/api/core/collection-preference.js +2 -6
- package/dist/api/core/event-bus-proxy.router.js +34 -37
- package/dist/api/core/failure-contributions.js +4 -8
- package/dist/api/core/hwaccel.router.js +17 -20
- package/dist/api/core/integration-secrets.js +8 -12
- package/dist/api/core/lifecycle-job-runner.js +13 -49
- package/dist/api/core/live-events.router.js +29 -32
- package/dist/api/core/load-contributions.js +4 -8
- package/dist/api/core/logging-settings.js +30 -43
- package/dist/api/core/logs.router.js +40 -43
- package/dist/api/core/notifications.router.js +15 -18
- package/dist/api/core/public-event-filter.js +4 -8
- package/dist/api/core/repl.router.js +18 -21
- package/dist/api/core/request-census-settings.js +11 -16
- package/dist/api/core/server-management.provider.js +1 -4
- package/dist/api/core/server-name.js +9 -17
- package/dist/api/core/site-location.js +16 -24
- package/dist/api/core/stream-probe.router.js +21 -24
- package/dist/api/core/system-events.router.js +23 -26
- package/dist/api/cors/cors-policy.js +20 -29
- package/dist/api/cors/register-cors-policy.js +9 -14
- package/dist/api/deploy-stage-registry.js +6 -10
- package/dist/api/dev-uploads-bundle.js +12 -50
- package/dist/api/health/adaptive-probe.routes.js +9 -13
- package/dist/api/health/health.routes.js +8 -13
- package/dist/api/hub-cluster-url.js +3 -9
- package/dist/api/model-distributor.js +12 -50
- package/dist/api/oauth2/consent-page.js +10 -13
- package/dist/api/oauth2/oauth2-routes.js +17 -25
- package/dist/api/oauth2/private-host.js +1 -4
- package/dist/api/server-upload-propagate.js +15 -51
- package/dist/api/server-upload.js +13 -50
- package/dist/api/sse-no-compression.js +1 -4
- package/dist/api/static/frame-policy.js +4 -9
- package/dist/api/static/precompressed-asset.js +15 -57
- package/dist/api/static/spa-mount.js +8 -47
- package/dist/api/static/spa-static.js +5 -12
- package/dist/api/tarball-manifest.js +7 -44
- package/dist/api/trpc/cap-mount-helpers.js +7 -12
- package/dist/api/trpc/cap-route-error-formatter.js +10 -13
- package/dist/api/trpc/cap-router-runtime.js +40 -51
- package/dist/api/trpc/client-ip.js +9 -20
- package/dist/api/trpc/core-cap-bridge.js +32 -42
- package/dist/api/trpc/device-collection-dispatch.js +23 -27
- package/dist/api/trpc/device-config-secret-redaction.js +24 -40
- package/dist/api/trpc/generated-cap-mounts.js +219 -221
- package/dist/api/trpc/generated-cap-routers.js +4878 -4726
- package/dist/api/trpc/http-request-census-hook.js +8 -11
- package/dist/api/trpc/http-request-census.js +34 -41
- package/dist/api/trpc/http-request-census.singleton.js +4 -8
- package/dist/api/trpc/localized-schema-paths.js +1 -4
- package/dist/api/trpc/principal-visibility.js +14 -22
- package/dist/api/trpc/remote-addon-update-grant.js +1 -4
- package/dist/api/trpc/scope-access.js +10 -15
- package/dist/api/trpc/server-provided-caps.js +3 -7
- package/dist/api/trpc/share-view-access.js +6 -14
- package/dist/api/trpc/trpc-error-device-tags.js +3 -6
- package/dist/api/trpc/trpc-error-principal.js +2 -6
- package/dist/api/trpc/trpc.context.js +20 -25
- package/dist/api/trpc/trpc.middleware.js +38 -46
- package/dist/api/trpc/trpc.router.js +58 -99
- package/dist/api/trpc/user-error.js +5 -8
- package/dist/api/trpc/ws-request-census.js +17 -24
- package/dist/api/trpc/ws-slow-consumer-guard.js +11 -16
- package/dist/api/upload-auth.js +5 -9
- package/dist/auth/addon-route-jwt-gate.js +4 -7
- package/dist/auth/addon-route-share-gate.js +7 -12
- package/dist/auth/app-confirm-page.js +17 -22
- package/dist/auth/app-confirm-routes.js +26 -31
- package/dist/auth/app-confirmations.js +7 -44
- package/dist/auth/app-page-shell.js +17 -30
- package/dist/auth/app-page-strings.js +11 -17
- package/dist/auth/app-passkey-page.js +16 -21
- package/dist/auth/app-passkey-routes.js +37 -41
- package/dist/auth/app-refused-route.js +13 -18
- package/dist/auth/auth-network-policy.js +11 -15
- package/dist/auth/auth-provider-gate.js +4 -9
- package/dist/auth/auth-rate-limit.js +3 -7
- package/dist/auth/client-address.js +6 -9
- package/dist/auth/device-enroll-proofs.js +6 -43
- package/dist/auth/external-link-intents.js +3 -7
- package/dist/auth/hub-sessions.js +6 -12
- package/dist/auth/native-link-completions.js +7 -11
- package/dist/auth/native-return.js +9 -13
- package/dist/auth/passkey-provider.js +26 -32
- package/dist/auth/second-factors.js +6 -10
- package/dist/auth/session-connections.js +6 -12
- package/dist/auth/session-cookie.js +12 -23
- package/dist/auth/session-lifetimes.js +8 -12
- package/dist/auth/session-mint.js +4 -11
- package/dist/auth/sso-finish-app.js +13 -16
- package/dist/auth/sso-finish-deps.js +21 -27
- package/dist/auth/sso-finish.js +28 -38
- package/dist/auth/sso-routes.js +22 -25
- package/dist/auth/used-bridge-ids.js +1 -5
- package/dist/boot/boot-config.js +16 -55
- package/dist/boot/init-sequence.js +1 -4
- package/dist/boot/integration-id-backfill.js +3 -8
- package/dist/boot/platform-keychain-key.js +5 -42
- package/dist/boot/post-boot.service.js +14 -18
- package/dist/boot/reconcile-lifecycle-jobs.js +3 -6
- package/dist/boot/retired-public-url-env.js +4 -8
- package/dist/bootstrap-packages.js +1 -4
- package/dist/closure-node-modules.js +2 -38
- package/dist/closure-patches/patch-reach.json +6 -2
- package/dist/core/addon/addon-call-gateway.js +1 -5
- package/dist/core/addon/addon-package.service.js +83 -123
- package/dist/core/addon/addon-process-view.js +2 -6
- package/dist/core/addon/addon-realign-plan.js +1 -4
- package/dist/core/addon/addon-registry.service.js +254 -197
- package/dist/core/addon/addon-row-manifest.js +1 -4
- package/dist/core/addon/addon-search.service.js +3 -7
- package/dist/core/addon/addon-settings-provider.js +1 -3
- package/dist/core/addon/addon-uninstall.js +4 -11
- package/dist/core/addon/addon.tokens.js +2 -5
- package/dist/core/addon/device-meta-mirror.js +8 -13
- package/dist/core/addon/forked-custom-actions.js +6 -11
- package/dist/core/addon/integration-visibility.js +3 -6
- package/dist/core/addon/materialize-route-path.js +1 -4
- package/dist/core/addon/package-dir-utils.js +8 -45
- package/dist/core/addon/prune-misplaced-addons.js +5 -43
- package/dist/core/addon/runner-convergence.js +1 -4
- package/dist/core/addon/runner-serialization.js +1 -4
- package/dist/core/addon/runner-spawn-fanout.js +3 -8
- package/dist/core/addon/settings-door-wiring.js +5 -8
- package/dist/core/addon/single-flight-refresh.js +1 -4
- package/dist/core/addon-bridge/addon-bridge.service.js +7 -44
- package/dist/core/addon-pages/addon-pages.service.js +3 -40
- package/dist/core/addon-widgets/addon-widgets.service.js +4 -42
- package/dist/core/agent/agent-addon-backfill.js +9 -17
- package/dist/core/agent/agent-delivery-ledger.js +3 -7
- package/dist/core/agent/agent-liveness-event.js +2 -6
- package/dist/core/agent/agent-pairing-http.js +41 -45
- package/dist/core/agent/agent-registry.service.js +53 -92
- package/dist/core/agent/cluster-node-history-mirror.js +3 -7
- package/dist/core/agent/cluster-node-history-store.js +113 -58
- package/dist/core/agent/hub-agent-pairing.js +1 -5
- package/dist/core/agent/node-certificates-client.js +3 -6
- package/dist/core/agent/node-directory.js +1 -4
- package/dist/core/app-links/well-known-app-links.js +2 -6
- package/dist/core/auth/auth.service.js +3 -8
- package/dist/core/auth/handoff-code.service.js +11 -49
- package/dist/core/auth/record-kinds.js +35 -0
- package/dist/core/auth/share-token.service.js +174 -122
- package/dist/core/capability/capability.service.js +1 -5
- package/dist/core/config/config.schema.js +1 -6
- package/dist/core/config/config.service.js +2 -6
- package/dist/core/events/event-bus.service.js +3 -7
- package/dist/core/feature/feature.service.js +2 -6
- package/dist/core/lifecycle/lifecycle-runner.singleton.js +5 -10
- package/dist/core/lifecycle/lifecycle-state-machine.js +1 -5
- package/dist/core/logging/log-ring-buffer.js +1 -5
- package/dist/core/logging/logging.service.js +5 -9
- package/dist/core/logging/scoped-logger.js +1 -5
- package/dist/core/moleculer/agent-readiness-pull.js +1 -4
- package/dist/core/moleculer/agent-registration-request.js +3 -7
- package/dist/core/moleculer/cap-call-fn.js +39 -5
- package/dist/core/moleculer/cap-route-authority.js +3 -8
- package/dist/core/moleculer/hub-mesh-guard.js +5 -9
- package/dist/core/moleculer/moleculer.service.js +108 -119
- package/dist/core/network/network-quality.service.js +2 -6
- package/dist/core/notification/notification-wrapper.service.js +3 -7
- package/dist/core/notification/toast-wrapper.service.js +3 -7
- package/dist/core/provider/provider.tokens.js +1 -4
- package/dist/core/repl/repl-engine.service.js +4 -8
- package/dist/core/runner-health/runner-boot-check.js +7 -15
- package/dist/core/runner-health/update-preflight.js +9 -14
- package/dist/core/server-update/server-update.service.js +12 -49
- package/dist/core/server-update/system-apply-closure-patches.js +6 -42
- package/dist/core/server-update/system-ensure-prebuilds.js +3 -6
- package/dist/core/server-update/system-exec-npm.js +2 -5
- package/dist/core/storage/fs-storage-backend.js +1 -5
- package/dist/core/storage/storage-location-manager.js +1 -5
- package/dist/core/storage/storage.service.js +2 -6
- package/dist/core/streaming/ffmpeg-stream-info.js +3 -8
- package/dist/core/streaming/stream-probe.service.js +12 -16
- package/dist/core/streaming/ttl-cache.js +1 -5
- package/dist/core/topology/topology-emitter.service.js +19 -23
- package/dist/core/update-availability-emitter.js +3 -7
- package/dist/core/update-availability-store.js +8 -45
- package/dist/core/updates/agent-installed-packages.js +5 -9
- package/dist/core/updates/restart-marker.js +3 -8
- package/dist/core/updates/update-check-scheduler.js +13 -18
- package/dist/core/updates/wrapper-update-checker.js +3 -7
- package/dist/first-boot-addon-plan.js +2 -6
- package/dist/framework-nodepath.js +2 -38
- package/dist/i18n/request-lang.js +7 -11
- package/dist/launcher.js +30 -65
- package/dist/lifecycle-journal-path.js +2 -38
- package/dist/main.js +287 -324
- package/dist/manual-boot.js +120 -157
- package/dist/node-role.js +1 -4
- package/dist/package-inventory.js +7 -48
- package/dist/pending-restore.js +7 -43
- package/dist/single-copy-cleanup-runner.js +9 -45
- package/dist/single-copy-cleanup.js +7 -15
- package/dist/sqlite-restore.js +9 -48
- package/dist/starter.js +4 -39
- package/dist/ui-serving/lazy-ui-serving.js +3 -39
- package/package.json +13 -13
|
@@ -1,40 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.DeviceEnrollProofs = exports.DEVICE_ENROLL_PROOF_TTL_MS = void 0;
|
|
37
|
-
const types_1 = require("@camstack/types");
|
|
1
|
+
import { userError } from '@camstack/types';
|
|
38
2
|
/**
|
|
39
3
|
* Step-up proofs for `auth.enrollSelf` (D855).
|
|
40
4
|
*
|
|
@@ -46,15 +10,15 @@ const types_1 = require("@camstack/types");
|
|
|
46
10
|
* `device-enroll`, bound to the user, 5 minutes, single use, kept only as a
|
|
47
11
|
* hash. It replaces no session.
|
|
48
12
|
*/
|
|
49
|
-
|
|
50
|
-
|
|
13
|
+
import * as crypto from 'node:crypto';
|
|
14
|
+
export const DEVICE_ENROLL_PROOF_TTL_MS = 5 * 60_000;
|
|
51
15
|
const MAX_PENDING = 1_000;
|
|
52
16
|
function hash(proof) {
|
|
53
17
|
return crypto.createHash('sha256').update(proof).digest('hex');
|
|
54
18
|
}
|
|
55
19
|
/** How long a spent reauth token / claimed session is remembered (past the token's own 5 min). */
|
|
56
20
|
const SPENT_MEMORY_MS = 6 * 60_000;
|
|
57
|
-
class DeviceEnrollProofs {
|
|
21
|
+
export class DeviceEnrollProofs {
|
|
58
22
|
now;
|
|
59
23
|
logger;
|
|
60
24
|
pending = new Map();
|
|
@@ -75,9 +39,9 @@ class DeviceEnrollProofs {
|
|
|
75
39
|
mint(userId) {
|
|
76
40
|
this.prune();
|
|
77
41
|
if (this.pending.size >= MAX_PENDING)
|
|
78
|
-
throw
|
|
42
|
+
throw userError('server.step-up-busy', undefined, 'Too many pending step-up proofs');
|
|
79
43
|
const proof = `cdp_${crypto.randomBytes(32).toString('base64url')}`;
|
|
80
|
-
const expiresAt = this.now() +
|
|
44
|
+
const expiresAt = this.now() + DEVICE_ENROLL_PROOF_TTL_MS;
|
|
81
45
|
this.pending.set(hash(proof), { userId, expiresAt });
|
|
82
46
|
return { proof, expiresAt };
|
|
83
47
|
}
|
|
@@ -146,4 +110,3 @@ class DeviceEnrollProofs {
|
|
|
146
110
|
this.pending.delete(k);
|
|
147
111
|
}
|
|
148
112
|
}
|
|
149
|
-
exports.DeviceEnrollProofs = DeviceEnrollProofs;
|
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ExternalLinkIntents = void 0;
|
|
4
1
|
/**
|
|
5
2
|
* Pending "link this sign-in account" requests (D782).
|
|
6
3
|
*
|
|
@@ -15,12 +12,12 @@ exports.ExternalLinkIntents = void 0;
|
|
|
15
12
|
* Hub-local and in memory on purpose: an intent lives minutes, and a hub
|
|
16
13
|
* restart mid-link costs the user one more click.
|
|
17
14
|
*/
|
|
18
|
-
|
|
15
|
+
import { randomBytes } from 'node:crypto';
|
|
19
16
|
/** Long enough for an IdP login with a password manager; short enough to be useless later. */
|
|
20
17
|
const DEFAULT_TTL_MS = 10 * 60_000;
|
|
21
18
|
/** Bounded: a session minting intents in a loop cannot grow this without limit. */
|
|
22
19
|
const DEFAULT_MAX_ENTRIES = 1_000;
|
|
23
|
-
class ExternalLinkIntents {
|
|
20
|
+
export class ExternalLinkIntents {
|
|
24
21
|
intents = new Map();
|
|
25
22
|
now;
|
|
26
23
|
ttlMs;
|
|
@@ -38,7 +35,7 @@ class ExternalLinkIntents {
|
|
|
38
35
|
break;
|
|
39
36
|
this.intents.delete(oldest.value);
|
|
40
37
|
}
|
|
41
|
-
const handle = `li_${
|
|
38
|
+
const handle = `li_${randomBytes(24).toString('base64url')}`;
|
|
42
39
|
this.intents.set(handle, {
|
|
43
40
|
userId,
|
|
44
41
|
provider,
|
|
@@ -69,4 +66,3 @@ class ExternalLinkIntents {
|
|
|
69
66
|
}
|
|
70
67
|
}
|
|
71
68
|
}
|
|
72
|
-
exports.ExternalLinkIntents = ExternalLinkIntents;
|
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.userManagementInternal = userManagementInternal;
|
|
4
|
-
exports.createSessionBacking = createSessionBacking;
|
|
5
|
-
exports.sessionBackingFrom = sessionBackingFrom;
|
|
6
|
-
exports.createSessionTokenCheck = createSessionTokenCheck;
|
|
7
1
|
/**
|
|
8
2
|
* The hub's two uses of the login-session table, which `local-auth` owns and
|
|
9
3
|
* reaches only through its hub-internal surface (`UserManagementInternal`):
|
|
@@ -16,12 +10,12 @@ exports.createSessionTokenCheck = createSessionTokenCheck;
|
|
|
16
10
|
* Both resolve `user-management` per call: a hub whose local-auth is not up
|
|
17
11
|
* mints nothing and accepts no session (fail closed).
|
|
18
12
|
*/
|
|
19
|
-
|
|
13
|
+
import { getUserManagementInternal, } from '@camstack/system';
|
|
20
14
|
/** The hub-internal user-management surface right now, or `null`. */
|
|
21
|
-
function userManagementInternal(registry) {
|
|
22
|
-
return
|
|
15
|
+
export function userManagementInternal(registry) {
|
|
16
|
+
return getUserManagementInternal(registry.getSingleton('user-management'));
|
|
23
17
|
}
|
|
24
|
-
function createSessionBacking(sessions, lifetimes) {
|
|
18
|
+
export function createSessionBacking(sessions, lifetimes) {
|
|
25
19
|
return {
|
|
26
20
|
open: async (input) => {
|
|
27
21
|
const { session, refreshToken } = await sessions.create({
|
|
@@ -43,7 +37,7 @@ function createSessionBacking(sessions, lifetimes) {
|
|
|
43
37
|
};
|
|
44
38
|
}
|
|
45
39
|
/** The session backing behind `registry`'s user-management, or `null` while it is not up. */
|
|
46
|
-
function sessionBackingFrom(registry, lifetimes) {
|
|
40
|
+
export function sessionBackingFrom(registry, lifetimes) {
|
|
47
41
|
return () => {
|
|
48
42
|
const internal = userManagementInternal(registry);
|
|
49
43
|
return internal === null ? null : createSessionBacking(internal.sessions, lifetimes);
|
|
@@ -56,7 +50,7 @@ function sessionBackingFrom(registry, lifetimes) {
|
|
|
56
50
|
* release unless it was issued before the user's cutoff (logout everywhere,
|
|
57
51
|
* a password change, deletion).
|
|
58
52
|
*/
|
|
59
|
-
function createSessionTokenCheck(registry) {
|
|
53
|
+
export function createSessionTokenCheck(registry) {
|
|
60
54
|
return (payload) => {
|
|
61
55
|
const internal = userManagementInternal(registry);
|
|
62
56
|
if (internal === null)
|
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.NativeLinkCompletions = exports.NATIVE_LINK_CODE_TTL_MS = exports.NATIVE_LINK_CODE_PREFIX = void 0;
|
|
4
1
|
/**
|
|
5
2
|
* Native-app account links waiting for the app to claim them (D801).
|
|
6
3
|
*
|
|
@@ -17,20 +14,20 @@ exports.NativeLinkCompletions = exports.NATIVE_LINK_CODE_TTL_MS = exports.NATIVE
|
|
|
17
14
|
* outcome, and the store is bounded. In memory: a hub restart mid-link costs
|
|
18
15
|
* one more tap.
|
|
19
16
|
*/
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
17
|
+
import { createHash, randomBytes } from 'node:crypto';
|
|
18
|
+
export const NATIVE_LINK_CODE_PREFIX = 'nlc_';
|
|
19
|
+
export const NATIVE_LINK_CODE_TTL_MS = 60_000;
|
|
23
20
|
const MAX_PENDING = 1_000;
|
|
24
21
|
function hash(code) {
|
|
25
|
-
return
|
|
22
|
+
return createHash('sha256').update(code).digest('hex');
|
|
26
23
|
}
|
|
27
|
-
class NativeLinkCompletions {
|
|
24
|
+
export class NativeLinkCompletions {
|
|
28
25
|
pending = new Map();
|
|
29
26
|
now;
|
|
30
27
|
ttlMs;
|
|
31
28
|
constructor(opts = {}) {
|
|
32
29
|
this.now = opts.now ?? Date.now;
|
|
33
|
-
this.ttlMs = opts.ttlMs ??
|
|
30
|
+
this.ttlMs = opts.ttlMs ?? NATIVE_LINK_CODE_TTL_MS;
|
|
34
31
|
}
|
|
35
32
|
/** Park a verified link; returns the raw one-time code. */
|
|
36
33
|
park(link) {
|
|
@@ -41,7 +38,7 @@ class NativeLinkCompletions {
|
|
|
41
38
|
break;
|
|
42
39
|
this.pending.delete(oldest.value);
|
|
43
40
|
}
|
|
44
|
-
const code = `${
|
|
41
|
+
const code = `${NATIVE_LINK_CODE_PREFIX}${randomBytes(32).toString('base64url')}`;
|
|
45
42
|
this.pending.set(hash(code), { ...link, expiresAt: this.now() + this.ttlMs });
|
|
46
43
|
return code;
|
|
47
44
|
}
|
|
@@ -65,4 +62,3 @@ class NativeLinkCompletions {
|
|
|
65
62
|
}
|
|
66
63
|
}
|
|
67
64
|
}
|
|
68
|
-
exports.NativeLinkCompletions = NativeLinkCompletions;
|
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ssoReturnLocation = ssoReturnLocation;
|
|
4
|
-
exports.browserHandoffLocation = browserHandoffLocation;
|
|
5
1
|
/**
|
|
6
2
|
* Where the hub's auth routes send a browser, as `Location` values (D801).
|
|
7
3
|
*
|
|
@@ -11,23 +7,23 @@ exports.browserHandoffLocation = browserHandoffLocation;
|
|
|
11
7
|
* and must never be prefixed;
|
|
12
8
|
* - a browser handoff lands on the account page and nowhere else.
|
|
13
9
|
*/
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
function ssoReturnLocation(mount, location) {
|
|
17
|
-
return
|
|
10
|
+
import { hasUrlScheme } from '@camstack/types';
|
|
11
|
+
import { SSO_LOGIN_PATH, SSO_PROFILE_PATH, sessionFragmentParams } from './sso-finish.js';
|
|
12
|
+
export function ssoReturnLocation(mount, location) {
|
|
13
|
+
return hasUrlScheme(location) ? location : `${mount}${location}`;
|
|
18
14
|
}
|
|
19
15
|
/**
|
|
20
16
|
* The account page, signed in by a session in the FRAGMENT that admin-ui's
|
|
21
17
|
* auth boot consumes (`#token=…&next=…`, as an SSO login return). Without a
|
|
22
18
|
* session — unknown, used or expired code — the plain login page.
|
|
23
19
|
*/
|
|
24
|
-
function browserHandoffLocation(mount, session) {
|
|
20
|
+
export function browserHandoffLocation(mount, session) {
|
|
25
21
|
if (session === null)
|
|
26
|
-
return `${mount}${
|
|
22
|
+
return `${mount}${SSO_LOGIN_PATH}`;
|
|
27
23
|
const fragment = new URLSearchParams({
|
|
28
|
-
...
|
|
29
|
-
next:
|
|
24
|
+
...sessionFragmentParams(session),
|
|
25
|
+
next: SSO_PROFILE_PATH,
|
|
30
26
|
provider: 'browser-handoff',
|
|
31
27
|
});
|
|
32
|
-
return `${mount}${
|
|
28
|
+
return `${mount}${SSO_LOGIN_PATH}#${fragment.toString()}`;
|
|
33
29
|
}
|
|
@@ -1,11 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
exports.SetSecondFactorPreferenceSchema = exports.SecondFactorPreferenceSchema = exports.RemovePasskeySchema = exports.FinishDiscoverableAuthenticationSchema = exports.FinishAuthenticationSchema = exports.FinishRegistrationSchema = exports.OptionsJsonSchema = void 0;
|
|
4
|
-
exports.resolvePasskeyProvider = resolvePasskeyProvider;
|
|
5
|
-
exports.userHasPasskey = userHasPasskey;
|
|
6
|
-
exports.passkeySecondFactorOptedIn = passkeySecondFactorOptedIn;
|
|
7
|
-
const types_1 = require("@camstack/types");
|
|
8
|
-
const zod_1 = require("zod");
|
|
1
|
+
import { PasskeySummarySchema } from '@camstack/types';
|
|
2
|
+
import { z } from 'zod';
|
|
9
3
|
// ── Passkey (user-passkeys cap) resolution ───────────────────────────
|
|
10
4
|
//
|
|
11
5
|
// Cap-agnostic: the passkey provider is resolved through the
|
|
@@ -18,19 +12,19 @@ const zod_1 = require("zod");
|
|
|
18
12
|
// The proxy is loosely typed (`Record<method, (params)=>Promise<unknown>>`);
|
|
19
13
|
// its outputs are narrowed through the cap's own Zod schemas rather than
|
|
20
14
|
// cast to the provider type (repo rule: no `as` on cross-boundary values).
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
success:
|
|
24
|
-
credentialId:
|
|
15
|
+
export const OptionsJsonSchema = z.object({ optionsJSON: z.record(z.string(), z.unknown()) });
|
|
16
|
+
export const FinishRegistrationSchema = z.object({
|
|
17
|
+
success: z.literal(true),
|
|
18
|
+
credentialId: z.string(),
|
|
25
19
|
});
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
verified:
|
|
29
|
-
userId:
|
|
20
|
+
export const FinishAuthenticationSchema = z.object({ verified: z.boolean() });
|
|
21
|
+
export const FinishDiscoverableAuthenticationSchema = z.object({
|
|
22
|
+
verified: z.boolean(),
|
|
23
|
+
userId: z.string().nullable(),
|
|
30
24
|
});
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
25
|
+
export const RemovePasskeySchema = z.object({ success: z.literal(true) });
|
|
26
|
+
export const SecondFactorPreferenceSchema = z.object({ enabled: z.boolean() });
|
|
27
|
+
export const SetSecondFactorPreferenceSchema = z.object({ success: z.literal(true) });
|
|
34
28
|
function proxyPasskeyProvider(proxy) {
|
|
35
29
|
const call = async (method, params) => {
|
|
36
30
|
const fn = proxy[method];
|
|
@@ -39,19 +33,19 @@ function proxyPasskeyProvider(proxy) {
|
|
|
39
33
|
return fn(params);
|
|
40
34
|
};
|
|
41
35
|
return {
|
|
42
|
-
beginRegistration: async (input) =>
|
|
43
|
-
finishRegistration: async (input) =>
|
|
44
|
-
beginAuthentication: async (input) =>
|
|
45
|
-
finishAuthentication: async (input) =>
|
|
46
|
-
beginDiscoverableAuthentication: async (input) =>
|
|
47
|
-
finishDiscoverableAuthentication: async (input) =>
|
|
48
|
-
listPasskeys: async (input) =>
|
|
49
|
-
removePasskey: async (input) =>
|
|
50
|
-
getSecondFactorPreference: async (input) =>
|
|
51
|
-
setSecondFactorPreference: async (input) =>
|
|
36
|
+
beginRegistration: async (input) => OptionsJsonSchema.parse(await call('beginRegistration', input)),
|
|
37
|
+
finishRegistration: async (input) => FinishRegistrationSchema.parse(await call('finishRegistration', input)),
|
|
38
|
+
beginAuthentication: async (input) => OptionsJsonSchema.parse(await call('beginAuthentication', input)),
|
|
39
|
+
finishAuthentication: async (input) => FinishAuthenticationSchema.parse(await call('finishAuthentication', input)),
|
|
40
|
+
beginDiscoverableAuthentication: async (input) => OptionsJsonSchema.parse(await call('beginDiscoverableAuthentication', input)),
|
|
41
|
+
finishDiscoverableAuthentication: async (input) => FinishDiscoverableAuthenticationSchema.parse(await call('finishDiscoverableAuthentication', input)),
|
|
42
|
+
listPasskeys: async (input) => z.array(PasskeySummarySchema).parse(await call('listPasskeys', input)),
|
|
43
|
+
removePasskey: async (input) => RemovePasskeySchema.parse(await call('removePasskey', input)),
|
|
44
|
+
getSecondFactorPreference: async (input) => SecondFactorPreferenceSchema.parse(await call('getSecondFactorPreference', input)),
|
|
45
|
+
setSecondFactorPreference: async (input) => SetSecondFactorPreferenceSchema.parse(await call('setSecondFactorPreference', input)),
|
|
52
46
|
};
|
|
53
47
|
}
|
|
54
|
-
function resolvePasskeyProvider(registry, moleculer) {
|
|
48
|
+
export function resolvePasskeyProvider(registry, moleculer) {
|
|
55
49
|
const local = registry?.getCollection('user-passkeys')?.[0] ?? null;
|
|
56
50
|
if (local)
|
|
57
51
|
return local;
|
|
@@ -63,7 +57,7 @@ function resolvePasskeyProvider(registry, moleculer) {
|
|
|
63
57
|
* missing provider or a transport hiccup degrades to `false` so the
|
|
64
58
|
* login flow is never blocked by passkey introspection.
|
|
65
59
|
*/
|
|
66
|
-
async function userHasPasskey(userId, provider) {
|
|
60
|
+
export async function userHasPasskey(userId, provider) {
|
|
67
61
|
if (!provider)
|
|
68
62
|
return false;
|
|
69
63
|
try {
|
|
@@ -83,7 +77,7 @@ async function userHasPasskey(userId, provider) {
|
|
|
83
77
|
* hiccup all read as `false` so the preference lookup can never lock a
|
|
84
78
|
* user out of the password flow.
|
|
85
79
|
*/
|
|
86
|
-
async function passkeySecondFactorOptedIn(userId, provider) {
|
|
80
|
+
export async function passkeySecondFactorOptedIn(userId, provider) {
|
|
87
81
|
if (!provider || typeof provider.getSecondFactorPreference !== 'function')
|
|
88
82
|
return false;
|
|
89
83
|
try {
|
|
@@ -1,19 +1,15 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const zod_1 = require("zod");
|
|
6
|
-
const passkey_provider_js_1 = require("./passkey-provider.js");
|
|
7
|
-
exports.SecondFactorSchema = zod_1.z.enum(['totp', 'passkey']);
|
|
8
|
-
async function collectSecondFactors(deps, userId, primary) {
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { passkeySecondFactorOptedIn, userHasPasskey } from './passkey-provider.js';
|
|
3
|
+
export const SecondFactorSchema = z.enum(['totp', 'passkey']);
|
|
4
|
+
export async function collectSecondFactors(deps, userId, primary) {
|
|
9
5
|
const totpStatus = deps.getTotpStatus ? await deps.getTotpStatus({ userId }) : { enabled: false };
|
|
10
6
|
const factors = [];
|
|
11
7
|
if (totpStatus.enabled)
|
|
12
8
|
factors.push('totp');
|
|
13
9
|
if (primary !== 'passkey') {
|
|
14
10
|
const provider = deps.passkeyProvider();
|
|
15
|
-
const enrolled = await
|
|
16
|
-
if (enrolled && (await
|
|
11
|
+
const enrolled = await userHasPasskey(userId, provider);
|
|
12
|
+
if (enrolled && (await passkeySecondFactorOptedIn(userId, provider))) {
|
|
17
13
|
factors.push('passkey');
|
|
18
14
|
}
|
|
19
15
|
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
/**
|
|
3
2
|
* What a login session still HOLDS on the hub — its open WebSockets and its
|
|
4
3
|
* live media sessions — so that revoking the session ends them.
|
|
@@ -16,16 +15,12 @@
|
|
|
16
15
|
* the session (the WebRTC `closeSession`); recorded playback rides the same
|
|
17
16
|
* cap, so the same hold covers it.
|
|
18
17
|
*/
|
|
19
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
-
exports.SessionConnections = exports.SESSION_ENDED_CLOSE_REASON = exports.SESSION_ENDED_CLOSE_CODE = void 0;
|
|
21
|
-
exports.getSessionConnections = getSessionConnections;
|
|
22
|
-
exports.__resetSessionConnectionsForTests = __resetSessionConnectionsForTests;
|
|
23
18
|
/** WebSocket close code a client recognises as "the hub ended this session". */
|
|
24
|
-
|
|
25
|
-
|
|
19
|
+
export const SESSION_ENDED_CLOSE_CODE = 4401;
|
|
20
|
+
export const SESSION_ENDED_CLOSE_REASON = 'session-ended';
|
|
26
21
|
/** Bound on live media holds kept per login session (oldest dropped past it). */
|
|
27
22
|
const MAX_MEDIA_HOLDS_PER_SESSION = 256;
|
|
28
|
-
class SessionConnections {
|
|
23
|
+
export class SessionConnections {
|
|
29
24
|
sockets = new Set();
|
|
30
25
|
media = new Map();
|
|
31
26
|
logger = null;
|
|
@@ -77,7 +72,7 @@ class SessionConnections {
|
|
|
77
72
|
continue;
|
|
78
73
|
this.sockets.delete(tracked);
|
|
79
74
|
try {
|
|
80
|
-
tracked.socket.close(
|
|
75
|
+
tracked.socket.close(SESSION_ENDED_CLOSE_CODE, SESSION_ENDED_CLOSE_REASON);
|
|
81
76
|
sockets += 1;
|
|
82
77
|
}
|
|
83
78
|
catch (error) {
|
|
@@ -123,14 +118,13 @@ class SessionConnections {
|
|
|
123
118
|
}
|
|
124
119
|
}
|
|
125
120
|
}
|
|
126
|
-
exports.SessionConnections = SessionConnections;
|
|
127
121
|
let instance = null;
|
|
128
122
|
/** The process-wide registry (the WS factory, the WebRTC wrapper and the revoke listener share it). */
|
|
129
|
-
function getSessionConnections() {
|
|
123
|
+
export function getSessionConnections() {
|
|
130
124
|
instance ??= new SessionConnections();
|
|
131
125
|
return instance;
|
|
132
126
|
}
|
|
133
127
|
/** Test-only. */
|
|
134
|
-
function __resetSessionConnectionsForTests() {
|
|
128
|
+
export function __resetSessionConnectionsForTests() {
|
|
135
129
|
instance = null;
|
|
136
130
|
}
|
|
@@ -1,21 +1,10 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.SESSION_COOKIE = void 0;
|
|
4
|
-
exports.buildSessionCookie = buildSessionCookie;
|
|
5
|
-
exports.clearSessionCookie = clearSessionCookie;
|
|
6
|
-
exports.readSessionCookieFromHeader = readSessionCookieFromHeader;
|
|
7
|
-
exports.shouldRedirectToLogin = shouldRedirectToLogin;
|
|
8
|
-
exports.loginRedirectUrl = loginRedirectUrl;
|
|
9
|
-
exports.isEmbedRedirectTarget = isEmbedRedirectTarget;
|
|
10
|
-
exports.isSessionGradeJwtPayload = isSessionGradeJwtPayload;
|
|
11
|
-
exports.sessionCookieIsSecure = sessionCookieIsSecure;
|
|
12
1
|
/** Browser session cookie carrying the hub JWT. Set by POST /api/auth/session
|
|
13
2
|
* after a tRPC login; read by the addon-route catch-all for `authenticated`
|
|
14
3
|
* routes hit by a plain browser navigation. */
|
|
15
|
-
|
|
16
|
-
function buildSessionCookie(token, ttlSec, opts = {}) {
|
|
4
|
+
export const SESSION_COOKIE = 'camstack_session';
|
|
5
|
+
export function buildSessionCookie(token, ttlSec, opts = {}) {
|
|
17
6
|
return {
|
|
18
|
-
name:
|
|
7
|
+
name: SESSION_COOKIE,
|
|
19
8
|
value: token,
|
|
20
9
|
options: {
|
|
21
10
|
httpOnly: true,
|
|
@@ -28,9 +17,9 @@ function buildSessionCookie(token, ttlSec, opts = {}) {
|
|
|
28
17
|
},
|
|
29
18
|
};
|
|
30
19
|
}
|
|
31
|
-
function clearSessionCookie(opts = {}) {
|
|
20
|
+
export function clearSessionCookie(opts = {}) {
|
|
32
21
|
return {
|
|
33
|
-
name:
|
|
22
|
+
name: SESSION_COOKIE,
|
|
34
23
|
value: '',
|
|
35
24
|
options: { httpOnly: true, sameSite: 'lax', secure: opts.secure ?? true, path: '/', maxAge: 0 },
|
|
36
25
|
};
|
|
@@ -41,7 +30,7 @@ function clearSessionCookie(opts = {}) {
|
|
|
41
30
|
* so callers don't depend on `@fastify/cookie` decoration order. Returns
|
|
42
31
|
* `null` when the header is absent or carries no `camstack_session` pair.
|
|
43
32
|
*/
|
|
44
|
-
function readSessionCookieFromHeader(header) {
|
|
33
|
+
export function readSessionCookieFromHeader(header) {
|
|
45
34
|
if (!header)
|
|
46
35
|
return null;
|
|
47
36
|
for (const pair of header.split(';')) {
|
|
@@ -49,7 +38,7 @@ function readSessionCookieFromHeader(header) {
|
|
|
49
38
|
if (eq === -1)
|
|
50
39
|
continue;
|
|
51
40
|
const name = pair.slice(0, eq).trim();
|
|
52
|
-
if (name !==
|
|
41
|
+
if (name !== SESSION_COOKIE)
|
|
53
42
|
continue;
|
|
54
43
|
const raw = pair.slice(eq + 1).trim();
|
|
55
44
|
if (raw === '')
|
|
@@ -66,7 +55,7 @@ function readSessionCookieFromHeader(header) {
|
|
|
66
55
|
/** A browser navigation we can bounce to the login page: a top-level GET
|
|
67
56
|
* that wants HTML. Anything else (API call, POST, non-HTML) keeps the
|
|
68
57
|
* 401 behavior so programmatic clients get a clean error. */
|
|
69
|
-
function shouldRedirectToLogin(method, accept) {
|
|
58
|
+
export function shouldRedirectToLogin(method, accept) {
|
|
70
59
|
return method === 'GET' && typeof accept === 'string' && accept.includes('text/html');
|
|
71
60
|
}
|
|
72
61
|
/**
|
|
@@ -78,14 +67,14 @@ function shouldRedirectToLogin(method, accept) {
|
|
|
78
67
|
* the SPA re-mounts it itself (`hubPath`) — so one value serves both the
|
|
79
68
|
* relayed and the direct case.
|
|
80
69
|
*/
|
|
81
|
-
function loginRedirectUrl(originalUrl, mountPrefix = null) {
|
|
70
|
+
export function loginRedirectUrl(originalUrl, mountPrefix = null) {
|
|
82
71
|
return `${mountPrefix ?? ''}/login?next=${encodeURIComponent(originalUrl)}`;
|
|
83
72
|
}
|
|
84
73
|
/** Allowed redirect target for `GET /api/embed-auth`: a same-origin RELATIVE
|
|
85
74
|
* path to a stream-broker embed page. Defeats open-redirects — the endpoint
|
|
86
75
|
* sets the session cookie from a Bearer token, so the `next` must be safe to
|
|
87
76
|
* bounce to. Rejects absolute/protocol-relative URLs, backslashes, and `..`. */
|
|
88
|
-
function isEmbedRedirectTarget(next) {
|
|
77
|
+
export function isEmbedRedirectTarget(next) {
|
|
89
78
|
if (!next.startsWith('/addon/stream-broker/embed/'))
|
|
90
79
|
return false;
|
|
91
80
|
if (next.includes('\\') || next.includes('://') || next.includes('..'))
|
|
@@ -100,7 +89,7 @@ function isEmbedRedirectTarget(next) {
|
|
|
100
89
|
* password-only attacker skip the second factor on every cookie-gated
|
|
101
90
|
* surface. Shared by `POST /api/auth/session` and `auth.exchangeSession`.
|
|
102
91
|
*/
|
|
103
|
-
function isSessionGradeJwtPayload(payload) {
|
|
92
|
+
export function isSessionGradeJwtPayload(payload) {
|
|
104
93
|
if (payload === null || typeof payload !== 'object')
|
|
105
94
|
return false;
|
|
106
95
|
const p = payload;
|
|
@@ -132,7 +121,7 @@ function isSessionGradeJwtPayload(payload) {
|
|
|
132
121
|
* which is set by the proxy and by nothing else on a direct browser hit. A
|
|
133
122
|
* direct caller inventing the proto header alone changes nothing.
|
|
134
123
|
*/
|
|
135
|
-
function sessionCookieIsSecure(requestProtocol, headers, forwardedPrefixHeader) {
|
|
124
|
+
export function sessionCookieIsSecure(requestProtocol, headers, forwardedPrefixHeader) {
|
|
136
125
|
const secureLeg = requestProtocol === 'https';
|
|
137
126
|
if (!secureLeg)
|
|
138
127
|
return false;
|
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.DEFAULT_ACCESS_TOKEN_EXPIRY = exports.SESSION_LIFETIME_KEY = exports.ACCESS_TOKEN_EXPIRY_KEY = void 0;
|
|
4
|
-
exports.readSessionLifetimes = readSessionLifetimes;
|
|
5
1
|
/**
|
|
6
2
|
* The two lifetimes of a login (System → Authentication):
|
|
7
3
|
*
|
|
@@ -15,10 +11,10 @@ exports.readSessionLifetimes = readSessionLifetimes;
|
|
|
15
11
|
* applies to the next login or refresh. A value that does not parse falls
|
|
16
12
|
* back to the default — never to something longer.
|
|
17
13
|
*/
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
14
|
+
import { parseTokenExpiry } from '@camstack/system';
|
|
15
|
+
export const ACCESS_TOKEN_EXPIRY_KEY = 'auth.accessTokenExpiry';
|
|
16
|
+
export const SESSION_LIFETIME_KEY = 'auth.tokenExpiry';
|
|
17
|
+
export const DEFAULT_ACCESS_TOKEN_EXPIRY = '2h';
|
|
22
18
|
const DEFAULT_ACCESS_SEC = 2 * 3600;
|
|
23
19
|
const MIN_ACCESS_SEC = 5 * 60;
|
|
24
20
|
const MAX_ACCESS_SEC = 24 * 3600;
|
|
@@ -30,12 +26,12 @@ function safeRead(read, key) {
|
|
|
30
26
|
return undefined;
|
|
31
27
|
}
|
|
32
28
|
}
|
|
33
|
-
function readSessionLifetimes(read) {
|
|
34
|
-
const session =
|
|
35
|
-
const configuredAccess = safeRead(read,
|
|
29
|
+
export function readSessionLifetimes(read) {
|
|
30
|
+
const session = parseTokenExpiry(safeRead(read, SESSION_LIFETIME_KEY));
|
|
31
|
+
const configuredAccess = safeRead(read, ACCESS_TOKEN_EXPIRY_KEY);
|
|
36
32
|
const access = configuredAccess === undefined || configuredAccess === null || configuredAccess === ''
|
|
37
33
|
? null
|
|
38
|
-
:
|
|
34
|
+
: parseTokenExpiry(configuredAccess);
|
|
39
35
|
const accessSec = access !== null && access.ok
|
|
40
36
|
? Math.min(MAX_ACCESS_SEC, Math.max(MIN_ACCESS_SEC, access.seconds))
|
|
41
37
|
: DEFAULT_ACCESS_SEC;
|
|
@@ -1,9 +1,5 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.SessionRefreshRefusedError = exports.SessionDirectoryUnavailableError = exports.SessionUserGoneError = void 0;
|
|
4
|
-
exports.createSessionMinter = createSessionMinter;
|
|
5
1
|
/** The user a credential names is not in `user-management` (deleted, or never existed). */
|
|
6
|
-
class SessionUserGoneError extends Error {
|
|
2
|
+
export class SessionUserGoneError extends Error {
|
|
7
3
|
userId;
|
|
8
4
|
constructor(userId) {
|
|
9
5
|
super('User no longer exists');
|
|
@@ -11,17 +7,15 @@ class SessionUserGoneError extends Error {
|
|
|
11
7
|
this.name = 'SessionUserGoneError';
|
|
12
8
|
}
|
|
13
9
|
}
|
|
14
|
-
exports.SessionUserGoneError = SessionUserGoneError;
|
|
15
10
|
/** `user-management` is not registered: an infrastructure failure, never a 401. */
|
|
16
|
-
class SessionDirectoryUnavailableError extends Error {
|
|
11
|
+
export class SessionDirectoryUnavailableError extends Error {
|
|
17
12
|
constructor() {
|
|
18
13
|
super('Login unavailable — `user-management` capability not registered');
|
|
19
14
|
this.name = 'SessionDirectoryUnavailableError';
|
|
20
15
|
}
|
|
21
16
|
}
|
|
22
|
-
exports.SessionDirectoryUnavailableError = SessionDirectoryUnavailableError;
|
|
23
17
|
/** A refresh token that does not continue a live session. */
|
|
24
|
-
class SessionRefreshRefusedError extends Error {
|
|
18
|
+
export class SessionRefreshRefusedError extends Error {
|
|
25
19
|
reason;
|
|
26
20
|
constructor(reason) {
|
|
27
21
|
super('Session expired — sign in again');
|
|
@@ -29,8 +23,7 @@ class SessionRefreshRefusedError extends Error {
|
|
|
29
23
|
this.name = 'SessionRefreshRefusedError';
|
|
30
24
|
}
|
|
31
25
|
}
|
|
32
|
-
|
|
33
|
-
function createSessionMinter(deps) {
|
|
26
|
+
export function createSessionMinter(deps) {
|
|
34
27
|
const findUserOrNull = async (userId) => {
|
|
35
28
|
const directory = deps.directory();
|
|
36
29
|
if (!directory)
|