@camstack/system 1.2.441 → 1.2.443
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-runner.js +72 -2
- package/dist/addon-runner.mjs +72 -2
- package/dist/addon-utils.js +1 -1
- package/dist/addon-utils.mjs +1 -1
- package/dist/auth/totp-manager.d.ts +46 -1
- package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +1 -1
- package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +1 -1
- package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +1 -1
- package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +1 -1
- package/dist/builtins/alerts/alerts.addon.d.ts +41 -1
- package/dist/builtins/alerts/alerts.addon.js +638 -26
- package/dist/builtins/alerts/alerts.addon.mjs +638 -26
- package/dist/builtins/alerts/critical-push.d.ts +86 -0
- package/dist/builtins/alerts/runner-alert.d.ts +71 -0
- package/dist/builtins/autotrack/index.js +1 -1
- package/dist/builtins/autotrack/index.mjs +1 -1
- package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +2 -2
- package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +2 -2
- package/dist/builtins/calendars/calendars.addon.js +1 -1
- package/dist/builtins/calendars/calendars.addon.mjs +1 -1
- package/dist/builtins/camera-grid/index.js +1 -1
- package/dist/builtins/camera-grid/index.mjs +1 -1
- package/dist/builtins/composer/composer.addon.js +2 -2
- package/dist/builtins/composer/composer.addon.mjs +2 -2
- package/dist/builtins/console-logging/index.js +1 -1
- package/dist/builtins/console-logging/index.mjs +1 -1
- package/dist/builtins/core-blocks/block-package.d.ts +4 -2
- package/dist/builtins/core-blocks/block-settings-store.d.ts +65 -19
- package/dist/builtins/core-blocks/core-blocks.addon.js +365 -38
- package/dist/builtins/core-blocks/core-blocks.addon.mjs +365 -38
- package/dist/builtins/device-manager/device-manager.addon.js +1 -1
- package/dist/builtins/device-manager/device-manager.addon.mjs +1 -1
- package/dist/builtins/hub-forwarder/index.js +1 -1
- package/dist/builtins/hub-forwarder/index.mjs +1 -1
- package/dist/builtins/keychain/index.js +3 -3
- package/dist/builtins/keychain/index.mjs +3 -3
- package/dist/builtins/liveness-monitor/liveness-monitor.addon.js +1 -1
- package/dist/builtins/liveness-monitor/liveness-monitor.addon.mjs +1 -1
- package/dist/builtins/local-auth/auth-schema.d.ts +1 -1
- package/dist/builtins/local-auth/local-auth.addon.js +226 -17
- package/dist/builtins/local-auth/local-auth.addon.mjs +226 -17
- package/dist/builtins/local-network/local-network.addon.js +2 -2
- package/dist/builtins/local-network/local-network.addon.mjs +2 -2
- package/dist/builtins/loki-logging/index.js +1 -1
- package/dist/builtins/loki-logging/index.mjs +1 -1
- package/dist/builtins/native-metrics/native-metrics.addon.js +2 -2
- package/dist/builtins/native-metrics/native-metrics.addon.mjs +2 -2
- package/dist/builtins/platform-probe/index.js +2 -2
- package/dist/builtins/platform-probe/index.mjs +2 -2
- package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
- package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
- package/dist/builtins/snapshot/index.js +1 -1
- package/dist/builtins/snapshot/index.mjs +1 -1
- package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
- package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
- package/dist/builtins/sqlite-storage/index.js +1 -1
- package/dist/builtins/sqlite-storage/index.mjs +1 -1
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +2 -2
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +2 -2
- package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +1 -1
- package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +1 -1
- package/dist/builtins/system-config/system-config.addon.js +2 -2
- package/dist/builtins/system-config/system-config.addon.mjs +2 -2
- package/dist/builtins/winston-logging/index.js +1 -1
- package/dist/builtins/winston-logging/index.mjs +1 -1
- package/dist/{composition-sources-BG-W2U0d.js → composition-sources-BUXX1e8j.js} +1 -1
- package/dist/{composition-sources-ByNISOnW.mjs → composition-sources-C8tUG2uL.mjs} +1 -1
- package/dist/{constants-DJILnUSb.mjs → constants-CGwVaAV3.mjs} +1 -1
- package/dist/{constants-UojhAJcm.js → constants-DWmm7_of.js} +1 -1
- package/dist/{device-ca-D8Ri9TVz.mjs → device-ca-DBfiJ_SB.mjs} +1 -1
- package/dist/{device-ca-196DAEZT.js → device-ca-DGayRmC0.js} +1 -1
- package/dist/{dist-BWvkuc31.mjs → dist-Bxt_S-9a.mjs} +180 -13
- package/dist/{dist-BiTCcaf-.js → dist-D9qT3jHr.js} +203 -12
- package/dist/file-host-worker.js +1 -1
- package/dist/file-host-worker.mjs +1 -1
- package/dist/{hub-mdns-ciao-DOBTq8qr.js → hub-mdns-ciao-B-wk9nzx.js} +1 -1
- package/dist/{hub-mdns-ciao-Cnd0bTB9.mjs → hub-mdns-ciao-DpPcQDg-.mjs} +1 -1
- package/dist/index.js +145 -16
- package/dist/index.mjs +140 -17
- package/dist/kernel/addon-refused-error.d.ts +30 -0
- package/dist/kernel/index.d.ts +3 -0
- package/dist/kernel/legacy-blob-guard.d.ts +6 -1
- package/dist/kernel/moleculer/process-service.d.ts +16 -1
- package/dist/kernel/moleculer/runner-refusal.d.ts +27 -0
- package/dist/{restore-archive-4ziluTU3.mjs → restore-archive-9CGgeBzD.mjs} +1 -1
- package/dist/{restore-archive-CfSSjKnA.js → restore-archive-BMNXwWWD.js} +1 -1
- package/dist/{wal-maintenance-CgZTNWAz.js → wal-maintenance-cRHR4rUY.js} +0 -0
- package/dist/{wal-maintenance-B57SAp4a.mjs → wal-maintenance-sIB32ONp.mjs} +0 -0
- package/package.json +1 -1
- package/dist/{custom-action-registry-BKmoaqG0.js → custom-action-registry-COyb49zm.js} +1 -1
- package/dist/{custom-action-registry-CyUuhATc.mjs → custom-action-registry-stpC3Zy-.mjs} +1 -1
|
@@ -3,10 +3,10 @@ Object.defineProperties(exports, {
|
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
5
|
const require_chunk = require("../../chunk-Cek0wNdY.js");
|
|
6
|
-
const require_dist = require("../../dist-
|
|
6
|
+
const require_dist = require("../../dist-D9qT3jHr.js");
|
|
7
7
|
const require_safe_buffer$1 = require("../../safe-buffer-D3aoa-7X.js");
|
|
8
8
|
const require_ms$1 = require("../../ms-LB3giGoU.js");
|
|
9
|
-
const require_device_ca = require("../../device-ca-
|
|
9
|
+
const require_device_ca = require("../../device-ca-DGayRmC0.js");
|
|
10
10
|
let node_crypto = require("node:crypto");
|
|
11
11
|
node_crypto = require_chunk.__toESM(node_crypto);
|
|
12
12
|
let crypto$1 = require("crypto");
|
|
@@ -8293,7 +8293,7 @@ var OAUTH_SESSIONS_COLLECTION = "oauth_sessions";
|
|
|
8293
8293
|
* = null`) is INACTIVE — `verifyTotp` returns false until confirmation.
|
|
8294
8294
|
*
|
|
8295
8295
|
* @durable class=config owner=local-auth
|
|
8296
|
-
* write="`setup` inserts a
|
|
8296
|
+
* write="`setup` inserts a keychain marker for the user's seed (D878; deleting any pending row
|
|
8297
8297
|
* first); `confirm` flips `confirmedAt` once a valid code proves possession"
|
|
8298
8298
|
* retention="`disable` deletes the row, and `setup` deletes before re-inserting.
|
|
8299
8299
|
* Nothing else prunes — deleting a USER does not cascade here, so their
|
|
@@ -10912,6 +10912,16 @@ function d(t) {
|
|
|
10912
10912
|
* we do NOT reuse the old secret on re-enroll (avoids the case where a
|
|
10913
10913
|
* leaked secret stays valid after a "rotation").
|
|
10914
10914
|
*
|
|
10915
|
+
* ## Where the seed lives (D878)
|
|
10916
|
+
*
|
|
10917
|
+
* The seed is a keychain secret (owner `system`, ref
|
|
10918
|
+
* `secret://system/row/user_totp/<userId>/secret`); the row's `secret` column
|
|
10919
|
+
* holds the JSON keychain marker, never the seed. A row written before D878
|
|
10920
|
+
* still holds the seed inline and keeps working until the operator's
|
|
10921
|
+
* `migrateRawSecrets` moves it (nothing migrates at boot). A seed the keychain
|
|
10922
|
+
* cannot serve makes `confirm`/`verify` THROW {@link TotpSecretUnavailableError}:
|
|
10923
|
+
* 2FA fails closed, never accepts a code and never reads as "not enrolled".
|
|
10924
|
+
*
|
|
10915
10925
|
* `otplib` v13's functional API (`generateSync`/`verifySync`) uses RFC 6238
|
|
10916
10926
|
* TOTP with a default 30-second period. We pass a per-call `epochTolerance`
|
|
10917
10927
|
* of one step in either direction (±30 s) — operator clock skew is the usual
|
|
@@ -10937,13 +10947,52 @@ function parseRow(data) {
|
|
|
10937
10947
|
createdAt
|
|
10938
10948
|
};
|
|
10939
10949
|
}
|
|
10950
|
+
/**
|
|
10951
|
+
* The seed cannot be used right now. The message is what a not-yet-logged-in
|
|
10952
|
+
* caller sees, so it is generic; `reason` (keychain detail) is for the server log only.
|
|
10953
|
+
*/
|
|
10954
|
+
var TotpSecretUnavailableError = class extends Error {
|
|
10955
|
+
reason;
|
|
10956
|
+
constructor(reason) {
|
|
10957
|
+
super("two-factor verification is temporarily unavailable; try again later or ask an administrator");
|
|
10958
|
+
this.reason = reason;
|
|
10959
|
+
this.name = "TotpSecretUnavailableError";
|
|
10960
|
+
}
|
|
10961
|
+
};
|
|
10962
|
+
/** A seed cannot be stored: setup is refused, never written as plaintext. */
|
|
10963
|
+
var TotpSecretStorageUnavailableError = class extends Error {
|
|
10964
|
+
constructor(reason) {
|
|
10965
|
+
super(`two-factor setup is unavailable: the TOTP seed cannot be stored in the keychain (${reason}).`);
|
|
10966
|
+
this.name = "TotpSecretStorageUnavailableError";
|
|
10967
|
+
}
|
|
10968
|
+
};
|
|
10969
|
+
/** What the stored column is: a keychain marker, a pre-D878 inline seed, or garbage (never an inline seed). */
|
|
10970
|
+
function classify(column) {
|
|
10971
|
+
if (column.length === 0) return { kind: "invalid" };
|
|
10972
|
+
if (!column.startsWith("{")) return { kind: "inline" };
|
|
10973
|
+
try {
|
|
10974
|
+
const parsed = JSON.parse(column);
|
|
10975
|
+
return require_dist.isSecretMarker(parsed) ? {
|
|
10976
|
+
kind: "marker",
|
|
10977
|
+
ref: parsed.$secret
|
|
10978
|
+
} : { kind: "invalid" };
|
|
10979
|
+
} catch {
|
|
10980
|
+
return { kind: "invalid" };
|
|
10981
|
+
}
|
|
10982
|
+
}
|
|
10940
10983
|
var TotpManager = class {
|
|
10941
10984
|
store;
|
|
10942
10985
|
/** ±N steps of clock-skew tolerance, expressed in seconds for otplib v13's
|
|
10943
10986
|
* per-call `epochTolerance`. Default window 1 = ±30 s. */
|
|
10944
10987
|
epochTolerance;
|
|
10988
|
+
secrets;
|
|
10989
|
+
logger;
|
|
10990
|
+
/** Per-user queue: setup / confirm / disable / migration never interleave on one user. */
|
|
10991
|
+
chains = /* @__PURE__ */ new Map();
|
|
10945
10992
|
constructor(store, opts = {}) {
|
|
10946
10993
|
this.store = store;
|
|
10994
|
+
this.secrets = opts.secrets ?? null;
|
|
10995
|
+
this.logger = opts.logger ?? null;
|
|
10947
10996
|
this.epochTolerance = (opts.window ?? 1) * TOTP_PERIOD_SECONDS;
|
|
10948
10997
|
}
|
|
10949
10998
|
/**
|
|
@@ -10955,23 +11004,35 @@ var TotpManager = class {
|
|
|
10955
11004
|
* the account name; we use the supplied `username` for human-readable
|
|
10956
11005
|
* recognition. `ISSUER` is the CamStack brand.
|
|
10957
11006
|
*/
|
|
10958
|
-
|
|
11007
|
+
setup(userId, username) {
|
|
11008
|
+
return this.serialize(userId, () => this.setupLocked(userId, username));
|
|
11009
|
+
}
|
|
11010
|
+
async setupLocked(userId, username) {
|
|
10959
11011
|
const secret = I();
|
|
10960
11012
|
const otpauthUrl = T({
|
|
10961
11013
|
secret,
|
|
10962
11014
|
label: username,
|
|
10963
11015
|
issuer: ISSUER
|
|
10964
11016
|
});
|
|
11017
|
+
const ref = this.refFor(userId);
|
|
11018
|
+
if (this.secrets === null || ref === null) throw new TotpSecretStorageUnavailableError("this process has no keychain door");
|
|
11019
|
+
const state = await this.secrets.state().catch(() => null);
|
|
11020
|
+
if (state === null || state.state !== "ready") throw new TotpSecretStorageUnavailableError("the keychain is locked");
|
|
11021
|
+
await this.store.delete.mutate({
|
|
11022
|
+
collection: TOTP_COLLECTION,
|
|
11023
|
+
key: userId
|
|
11024
|
+
});
|
|
11025
|
+
try {
|
|
11026
|
+
await this.secrets.put(ref, secret);
|
|
11027
|
+
} catch (err) {
|
|
11028
|
+
throw new TotpSecretStorageUnavailableError(err instanceof Error ? err.message : String(err));
|
|
11029
|
+
}
|
|
10965
11030
|
const row = {
|
|
10966
11031
|
userId,
|
|
10967
|
-
secret,
|
|
11032
|
+
secret: JSON.stringify(require_dist.makeSecretMarker(ref)),
|
|
10968
11033
|
confirmedAt: null,
|
|
10969
11034
|
createdAt: Date.now()
|
|
10970
11035
|
};
|
|
10971
|
-
await this.store.delete.mutate({
|
|
10972
|
-
collection: TOTP_COLLECTION,
|
|
10973
|
-
key: userId
|
|
10974
|
-
});
|
|
10975
11036
|
await this.store.insert.mutate({
|
|
10976
11037
|
collection: TOTP_COLLECTION,
|
|
10977
11038
|
record: {
|
|
@@ -10991,12 +11052,15 @@ var TotpManager = class {
|
|
|
10991
11052
|
* rows: re-confirming a valid code just succeeds with no state
|
|
10992
11053
|
* change.
|
|
10993
11054
|
*/
|
|
10994
|
-
|
|
11055
|
+
confirm(userId, code) {
|
|
11056
|
+
return this.serialize(userId, () => this.confirmLocked(userId, code));
|
|
11057
|
+
}
|
|
11058
|
+
async confirmLocked(userId, code) {
|
|
10995
11059
|
const row = await this.find(userId);
|
|
10996
11060
|
if (!row) return false;
|
|
10997
11061
|
if (!d({
|
|
10998
11062
|
token: code,
|
|
10999
|
-
secret: row
|
|
11063
|
+
secret: await this.seedOf(row),
|
|
11000
11064
|
epochTolerance: this.epochTolerance
|
|
11001
11065
|
}).valid) return false;
|
|
11002
11066
|
if (row.confirmedAt === null) {
|
|
@@ -11015,11 +11079,23 @@ var TotpManager = class {
|
|
|
11015
11079
|
/**
|
|
11016
11080
|
* Remove enrollment. Idempotent — no error if the user had no row.
|
|
11017
11081
|
*/
|
|
11018
|
-
|
|
11019
|
-
|
|
11020
|
-
|
|
11021
|
-
|
|
11082
|
+
disable(userId) {
|
|
11083
|
+
return this.serialize(userId, async () => {
|
|
11084
|
+
await this.store.delete.mutate({
|
|
11085
|
+
collection: TOTP_COLLECTION,
|
|
11086
|
+
key: userId
|
|
11087
|
+
});
|
|
11088
|
+
await this.dropSeed(userId);
|
|
11089
|
+
});
|
|
11090
|
+
}
|
|
11091
|
+
serialize(userId, fn) {
|
|
11092
|
+
const run = (this.chains.get(userId) ?? Promise.resolve()).then(fn, fn);
|
|
11093
|
+
const tail = run.catch(() => void 0);
|
|
11094
|
+
this.chains.set(userId, tail);
|
|
11095
|
+
tail.then(() => {
|
|
11096
|
+
if (this.chains.get(userId) === tail) this.chains.delete(userId);
|
|
11022
11097
|
});
|
|
11098
|
+
return run;
|
|
11023
11099
|
}
|
|
11024
11100
|
/**
|
|
11025
11101
|
* Verify a code against the confirmed secret. Returns `false` when:
|
|
@@ -11033,7 +11109,7 @@ var TotpManager = class {
|
|
|
11033
11109
|
if (row.confirmedAt === null) return false;
|
|
11034
11110
|
return d({
|
|
11035
11111
|
token: code,
|
|
11036
|
-
secret: row
|
|
11112
|
+
secret: await this.seedOf(row),
|
|
11037
11113
|
epochTolerance: this.epochTolerance
|
|
11038
11114
|
}).valid;
|
|
11039
11115
|
}
|
|
@@ -11052,6 +11128,132 @@ var TotpManager = class {
|
|
|
11052
11128
|
confirmedAt: row.confirmedAt
|
|
11053
11129
|
};
|
|
11054
11130
|
}
|
|
11131
|
+
refFor(userId) {
|
|
11132
|
+
return this.secrets === null ? null : this.secrets.ref({
|
|
11133
|
+
kind: "row",
|
|
11134
|
+
collection: TOTP_COLLECTION,
|
|
11135
|
+
rowId: userId
|
|
11136
|
+
}, "secret");
|
|
11137
|
+
}
|
|
11138
|
+
/** A refused read: generic to the caller, the keychain detail to the server log. */
|
|
11139
|
+
unavailable(userId, reason) {
|
|
11140
|
+
this.logger?.warn("TOTP seed unavailable", {
|
|
11141
|
+
tags: { userId },
|
|
11142
|
+
meta: { reason }
|
|
11143
|
+
});
|
|
11144
|
+
return new TotpSecretUnavailableError(reason);
|
|
11145
|
+
}
|
|
11146
|
+
/** The seed in clear: from the keychain for a marker row, inline for a pre-D878 row. Throws when unreadable. */
|
|
11147
|
+
async seedOf(row) {
|
|
11148
|
+
const column = classify(row.secret);
|
|
11149
|
+
if (column.kind === "inline") return row.secret;
|
|
11150
|
+
if (column.kind === "invalid") throw this.unavailable(row.userId, "malformed seed column");
|
|
11151
|
+
if (this.secrets === null || column.ref !== this.refFor(row.userId)) throw this.unavailable(row.userId, "marker does not belong to this user");
|
|
11152
|
+
let answer;
|
|
11153
|
+
try {
|
|
11154
|
+
answer = (await this.secrets.resolve([column.ref]))[column.ref];
|
|
11155
|
+
} catch (err) {
|
|
11156
|
+
throw this.unavailable(row.userId, err instanceof Error ? err.message : String(err));
|
|
11157
|
+
}
|
|
11158
|
+
if (answer?.kind === "value") return answer.value;
|
|
11159
|
+
throw this.unavailable(row.userId, answer?.kind === "unavailable" ? answer.reason : answer?.kind ?? "no-answer");
|
|
11160
|
+
}
|
|
11161
|
+
/** Remove a user's seed from the keychain; a miss leaves an orphan the Secrets page shows. */
|
|
11162
|
+
async dropSeed(userId) {
|
|
11163
|
+
const ref = this.refFor(userId);
|
|
11164
|
+
if (ref === null) return;
|
|
11165
|
+
try {
|
|
11166
|
+
await this.secrets?.delete([ref]);
|
|
11167
|
+
} catch {}
|
|
11168
|
+
}
|
|
11169
|
+
/**
|
|
11170
|
+
* D878: move every inline seed into the keychain. Dry run unless `apply`.
|
|
11171
|
+
* Per row: put -> read back and compare -> only then rewrite the row with the
|
|
11172
|
+
* marker, so the inline seed is cleared by the verified move and nowhere
|
|
11173
|
+
* else. Counts only; idempotent.
|
|
11174
|
+
*/
|
|
11175
|
+
async migrateRawSecrets(apply) {
|
|
11176
|
+
const rows = await this.readAll();
|
|
11177
|
+
let raw = 0;
|
|
11178
|
+
let markers = 0;
|
|
11179
|
+
let migrated = 0;
|
|
11180
|
+
let failed = 0;
|
|
11181
|
+
for (const row of rows) {
|
|
11182
|
+
const kind = classify(row.secret).kind;
|
|
11183
|
+
if (kind === "marker") {
|
|
11184
|
+
markers += 1;
|
|
11185
|
+
continue;
|
|
11186
|
+
}
|
|
11187
|
+
if (kind !== "inline") continue;
|
|
11188
|
+
raw += 1;
|
|
11189
|
+
if (!apply) continue;
|
|
11190
|
+
if (await this.moveRow(row)) migrated += 1;
|
|
11191
|
+
else failed += 1;
|
|
11192
|
+
}
|
|
11193
|
+
let remainingRaw = 0;
|
|
11194
|
+
let unresolvedMarkers = 0;
|
|
11195
|
+
for (const row of await this.readAll()) {
|
|
11196
|
+
if (classify(row.secret).kind === "inline") {
|
|
11197
|
+
remainingRaw += 1;
|
|
11198
|
+
continue;
|
|
11199
|
+
}
|
|
11200
|
+
try {
|
|
11201
|
+
await this.seedOf(row);
|
|
11202
|
+
} catch {
|
|
11203
|
+
unresolvedMarkers += 1;
|
|
11204
|
+
}
|
|
11205
|
+
}
|
|
11206
|
+
return {
|
|
11207
|
+
apply,
|
|
11208
|
+
rows: rows.length,
|
|
11209
|
+
raw,
|
|
11210
|
+
markers,
|
|
11211
|
+
migrated,
|
|
11212
|
+
failed,
|
|
11213
|
+
remainingRaw,
|
|
11214
|
+
unresolvedMarkers,
|
|
11215
|
+
verified: remainingRaw === 0 && unresolvedMarkers === 0
|
|
11216
|
+
};
|
|
11217
|
+
}
|
|
11218
|
+
/**
|
|
11219
|
+
* Compare-and-set under the user's queue: the row is re-read and moved only if
|
|
11220
|
+
* it still holds the very inline seed we planned to move, so a concurrent
|
|
11221
|
+
* `setup` (its seed and marker) is never overwritten. The ref is always the
|
|
11222
|
+
* user's own, never taken from the row.
|
|
11223
|
+
*/
|
|
11224
|
+
moveRow(planned) {
|
|
11225
|
+
return this.serialize(planned.userId, async () => {
|
|
11226
|
+
const ref = this.refFor(planned.userId);
|
|
11227
|
+
if (this.secrets === null || ref === null) return false;
|
|
11228
|
+
try {
|
|
11229
|
+
const current = await this.find(planned.userId);
|
|
11230
|
+
if (current === null || current.secret !== planned.secret) return false;
|
|
11231
|
+
await this.secrets.put(ref, planned.secret);
|
|
11232
|
+
const back = (await this.secrets.resolve([ref]))[ref];
|
|
11233
|
+
if (back?.kind !== "value" || back.value !== planned.secret) return false;
|
|
11234
|
+
const next = {
|
|
11235
|
+
...current,
|
|
11236
|
+
secret: JSON.stringify(require_dist.makeSecretMarker(ref))
|
|
11237
|
+
};
|
|
11238
|
+
await this.store.update.mutate({
|
|
11239
|
+
collection: TOTP_COLLECTION,
|
|
11240
|
+
id: planned.userId,
|
|
11241
|
+
data: { ...next }
|
|
11242
|
+
});
|
|
11243
|
+
return true;
|
|
11244
|
+
} catch {
|
|
11245
|
+
return false;
|
|
11246
|
+
}
|
|
11247
|
+
});
|
|
11248
|
+
}
|
|
11249
|
+
async readAll() {
|
|
11250
|
+
const results = await this.store.query.query({ collection: TOTP_COLLECTION });
|
|
11251
|
+
const out = [];
|
|
11252
|
+
for (const r of results) try {
|
|
11253
|
+
out.push(parseRow(r.data));
|
|
11254
|
+
} catch {}
|
|
11255
|
+
return out;
|
|
11256
|
+
}
|
|
11055
11257
|
async find(userId) {
|
|
11056
11258
|
const results = await this.store.query.query({
|
|
11057
11259
|
collection: TOTP_COLLECTION,
|
|
@@ -11549,7 +11751,10 @@ var LocalAuthAddon = class extends require_dist.BaseAddon {
|
|
|
11549
11751
|
this.apiKeyManager = new ApiKeyManager(storageAccess);
|
|
11550
11752
|
this.scopedTokenManager = new ScopedTokenManager(store);
|
|
11551
11753
|
this.oauthSessionManager = new OauthSessionManager(store);
|
|
11552
|
-
this.totpManager = new TotpManager(store
|
|
11754
|
+
this.totpManager = new TotpManager(store, {
|
|
11755
|
+
secrets: forOwner === null ? null : forOwner("system"),
|
|
11756
|
+
logger: this.ctx.logger
|
|
11757
|
+
});
|
|
11553
11758
|
this.externalIdentities = new ExternalIdentityManager(store);
|
|
11554
11759
|
this.loginSessions = new LoginSessionManager(store, { logger: this.ctx.logger });
|
|
11555
11760
|
await this.loginSessions.load();
|
|
@@ -11791,6 +11996,10 @@ var LocalAuthAddon = class extends require_dist.BaseAddon {
|
|
|
11791
11996
|
};
|
|
11792
11997
|
return this.totpManager.getStatus(input.userId);
|
|
11793
11998
|
},
|
|
11999
|
+
migrateRawSecrets: async (input) => {
|
|
12000
|
+
if (!this.totpManager) throw new Error("TOTP management not available");
|
|
12001
|
+
return this.totpManager.migrateRawSecrets(input.apply);
|
|
12002
|
+
},
|
|
11794
12003
|
verifyTotp: async (input) => {
|
|
11795
12004
|
if (!this.totpManager) return { valid: false };
|
|
11796
12005
|
return { valid: await this.totpManager.verify(input.userId, input.code) };
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { i as __require, o as __toESM, t as __commonJSMin } from "../../chunk-CNf5ZN-e.mjs";
|
|
2
|
-
import { E as DEVICE_NONCE_TTL_MS, It as deviceCredentialsCapability, Q as SsoBridgeClaimsSchema, T as DEVICE_ENROLLMENT_TTL_MS, X as ScopedTokenSchema, Zt as formatDeviceRevoker, a as ApiKeyRecordSchema, cr as userManagementCapability, ct as authProviderCapability, jr as BaseAddon, mt as buildDeviceSignatureMessage,
|
|
2
|
+
import { E as DEVICE_NONCE_TTL_MS, It as deviceCredentialsCapability, Q as SsoBridgeClaimsSchema, T as DEVICE_ENROLLMENT_TTL_MS, Ti as userError, X as ScopedTokenSchema, Zt as formatDeviceRevoker, a as ApiKeyRecordSchema, cr as userManagementCapability, ct as authProviderCapability, jr as BaseAddon, li as makeSecretMarker, mt as buildDeviceSignatureMessage, oi as isSecretMarker, pt as buildDeviceEnrollmentLink, rt as UserRecordSchema } from "../../dist-Bxt_S-9a.mjs";
|
|
3
3
|
import { t as require_safe_buffer } from "../../safe-buffer-B1OjL1I9.mjs";
|
|
4
4
|
import { t as require_ms } from "../../ms-CgH2OFlD.mjs";
|
|
5
|
-
import { _ as setProcessJwtSigningKey, a as importDevicePublicKey, c as rebuildDeviceCaCertificate, d as JWT_SIGNING_KEY_REF, g as processJwtSigningKey, h as loadJwtSigningKey, i as generateDeviceCa, l as verifyDeviceLeaf, m as getProcessJwtSigningKeyState, n as DEVICE_CA_KEY_REF, o as issueDeviceLeaf, r as DEVICE_CA_SETTINGS_SECTION, s as loadDeviceCa, t as DEVICE_CA_FIELD, u as verifyDeviceSignature } from "../../device-ca-
|
|
5
|
+
import { _ as setProcessJwtSigningKey, a as importDevicePublicKey, c as rebuildDeviceCaCertificate, d as JWT_SIGNING_KEY_REF, g as processJwtSigningKey, h as loadJwtSigningKey, i as generateDeviceCa, l as verifyDeviceLeaf, m as getProcessJwtSigningKeyState, n as DEVICE_CA_KEY_REF, o as issueDeviceLeaf, r as DEVICE_CA_SETTINGS_SECTION, s as loadDeviceCa, t as DEVICE_CA_FIELD, u as verifyDeviceSignature } from "../../device-ca-DBfiJ_SB.mjs";
|
|
6
6
|
import * as crypto$1 from "node:crypto";
|
|
7
7
|
import { X509Certificate, createHash, randomUUID, timingSafeEqual } from "node:crypto";
|
|
8
8
|
import nodeCrypto from "crypto";
|
|
@@ -8283,7 +8283,7 @@ var OAUTH_SESSIONS_COLLECTION = "oauth_sessions";
|
|
|
8283
8283
|
* = null`) is INACTIVE — `verifyTotp` returns false until confirmation.
|
|
8284
8284
|
*
|
|
8285
8285
|
* @durable class=config owner=local-auth
|
|
8286
|
-
* write="`setup` inserts a
|
|
8286
|
+
* write="`setup` inserts a keychain marker for the user's seed (D878; deleting any pending row
|
|
8287
8287
|
* first); `confirm` flips `confirmedAt` once a valid code proves possession"
|
|
8288
8288
|
* retention="`disable` deletes the row, and `setup` deletes before re-inserting.
|
|
8289
8289
|
* Nothing else prunes — deleting a USER does not cascade here, so their
|
|
@@ -10902,6 +10902,16 @@ function d(t) {
|
|
|
10902
10902
|
* we do NOT reuse the old secret on re-enroll (avoids the case where a
|
|
10903
10903
|
* leaked secret stays valid after a "rotation").
|
|
10904
10904
|
*
|
|
10905
|
+
* ## Where the seed lives (D878)
|
|
10906
|
+
*
|
|
10907
|
+
* The seed is a keychain secret (owner `system`, ref
|
|
10908
|
+
* `secret://system/row/user_totp/<userId>/secret`); the row's `secret` column
|
|
10909
|
+
* holds the JSON keychain marker, never the seed. A row written before D878
|
|
10910
|
+
* still holds the seed inline and keeps working until the operator's
|
|
10911
|
+
* `migrateRawSecrets` moves it (nothing migrates at boot). A seed the keychain
|
|
10912
|
+
* cannot serve makes `confirm`/`verify` THROW {@link TotpSecretUnavailableError}:
|
|
10913
|
+
* 2FA fails closed, never accepts a code and never reads as "not enrolled".
|
|
10914
|
+
*
|
|
10905
10915
|
* `otplib` v13's functional API (`generateSync`/`verifySync`) uses RFC 6238
|
|
10906
10916
|
* TOTP with a default 30-second period. We pass a per-call `epochTolerance`
|
|
10907
10917
|
* of one step in either direction (±30 s) — operator clock skew is the usual
|
|
@@ -10927,13 +10937,52 @@ function parseRow(data) {
|
|
|
10927
10937
|
createdAt
|
|
10928
10938
|
};
|
|
10929
10939
|
}
|
|
10940
|
+
/**
|
|
10941
|
+
* The seed cannot be used right now. The message is what a not-yet-logged-in
|
|
10942
|
+
* caller sees, so it is generic; `reason` (keychain detail) is for the server log only.
|
|
10943
|
+
*/
|
|
10944
|
+
var TotpSecretUnavailableError = class extends Error {
|
|
10945
|
+
reason;
|
|
10946
|
+
constructor(reason) {
|
|
10947
|
+
super("two-factor verification is temporarily unavailable; try again later or ask an administrator");
|
|
10948
|
+
this.reason = reason;
|
|
10949
|
+
this.name = "TotpSecretUnavailableError";
|
|
10950
|
+
}
|
|
10951
|
+
};
|
|
10952
|
+
/** A seed cannot be stored: setup is refused, never written as plaintext. */
|
|
10953
|
+
var TotpSecretStorageUnavailableError = class extends Error {
|
|
10954
|
+
constructor(reason) {
|
|
10955
|
+
super(`two-factor setup is unavailable: the TOTP seed cannot be stored in the keychain (${reason}).`);
|
|
10956
|
+
this.name = "TotpSecretStorageUnavailableError";
|
|
10957
|
+
}
|
|
10958
|
+
};
|
|
10959
|
+
/** What the stored column is: a keychain marker, a pre-D878 inline seed, or garbage (never an inline seed). */
|
|
10960
|
+
function classify(column) {
|
|
10961
|
+
if (column.length === 0) return { kind: "invalid" };
|
|
10962
|
+
if (!column.startsWith("{")) return { kind: "inline" };
|
|
10963
|
+
try {
|
|
10964
|
+
const parsed = JSON.parse(column);
|
|
10965
|
+
return isSecretMarker(parsed) ? {
|
|
10966
|
+
kind: "marker",
|
|
10967
|
+
ref: parsed.$secret
|
|
10968
|
+
} : { kind: "invalid" };
|
|
10969
|
+
} catch {
|
|
10970
|
+
return { kind: "invalid" };
|
|
10971
|
+
}
|
|
10972
|
+
}
|
|
10930
10973
|
var TotpManager = class {
|
|
10931
10974
|
store;
|
|
10932
10975
|
/** ±N steps of clock-skew tolerance, expressed in seconds for otplib v13's
|
|
10933
10976
|
* per-call `epochTolerance`. Default window 1 = ±30 s. */
|
|
10934
10977
|
epochTolerance;
|
|
10978
|
+
secrets;
|
|
10979
|
+
logger;
|
|
10980
|
+
/** Per-user queue: setup / confirm / disable / migration never interleave on one user. */
|
|
10981
|
+
chains = /* @__PURE__ */ new Map();
|
|
10935
10982
|
constructor(store, opts = {}) {
|
|
10936
10983
|
this.store = store;
|
|
10984
|
+
this.secrets = opts.secrets ?? null;
|
|
10985
|
+
this.logger = opts.logger ?? null;
|
|
10937
10986
|
this.epochTolerance = (opts.window ?? 1) * TOTP_PERIOD_SECONDS;
|
|
10938
10987
|
}
|
|
10939
10988
|
/**
|
|
@@ -10945,23 +10994,35 @@ var TotpManager = class {
|
|
|
10945
10994
|
* the account name; we use the supplied `username` for human-readable
|
|
10946
10995
|
* recognition. `ISSUER` is the CamStack brand.
|
|
10947
10996
|
*/
|
|
10948
|
-
|
|
10997
|
+
setup(userId, username) {
|
|
10998
|
+
return this.serialize(userId, () => this.setupLocked(userId, username));
|
|
10999
|
+
}
|
|
11000
|
+
async setupLocked(userId, username) {
|
|
10949
11001
|
const secret = I();
|
|
10950
11002
|
const otpauthUrl = T({
|
|
10951
11003
|
secret,
|
|
10952
11004
|
label: username,
|
|
10953
11005
|
issuer: ISSUER
|
|
10954
11006
|
});
|
|
11007
|
+
const ref = this.refFor(userId);
|
|
11008
|
+
if (this.secrets === null || ref === null) throw new TotpSecretStorageUnavailableError("this process has no keychain door");
|
|
11009
|
+
const state = await this.secrets.state().catch(() => null);
|
|
11010
|
+
if (state === null || state.state !== "ready") throw new TotpSecretStorageUnavailableError("the keychain is locked");
|
|
11011
|
+
await this.store.delete.mutate({
|
|
11012
|
+
collection: TOTP_COLLECTION,
|
|
11013
|
+
key: userId
|
|
11014
|
+
});
|
|
11015
|
+
try {
|
|
11016
|
+
await this.secrets.put(ref, secret);
|
|
11017
|
+
} catch (err) {
|
|
11018
|
+
throw new TotpSecretStorageUnavailableError(err instanceof Error ? err.message : String(err));
|
|
11019
|
+
}
|
|
10955
11020
|
const row = {
|
|
10956
11021
|
userId,
|
|
10957
|
-
secret,
|
|
11022
|
+
secret: JSON.stringify(makeSecretMarker(ref)),
|
|
10958
11023
|
confirmedAt: null,
|
|
10959
11024
|
createdAt: Date.now()
|
|
10960
11025
|
};
|
|
10961
|
-
await this.store.delete.mutate({
|
|
10962
|
-
collection: TOTP_COLLECTION,
|
|
10963
|
-
key: userId
|
|
10964
|
-
});
|
|
10965
11026
|
await this.store.insert.mutate({
|
|
10966
11027
|
collection: TOTP_COLLECTION,
|
|
10967
11028
|
record: {
|
|
@@ -10981,12 +11042,15 @@ var TotpManager = class {
|
|
|
10981
11042
|
* rows: re-confirming a valid code just succeeds with no state
|
|
10982
11043
|
* change.
|
|
10983
11044
|
*/
|
|
10984
|
-
|
|
11045
|
+
confirm(userId, code) {
|
|
11046
|
+
return this.serialize(userId, () => this.confirmLocked(userId, code));
|
|
11047
|
+
}
|
|
11048
|
+
async confirmLocked(userId, code) {
|
|
10985
11049
|
const row = await this.find(userId);
|
|
10986
11050
|
if (!row) return false;
|
|
10987
11051
|
if (!d({
|
|
10988
11052
|
token: code,
|
|
10989
|
-
secret: row
|
|
11053
|
+
secret: await this.seedOf(row),
|
|
10990
11054
|
epochTolerance: this.epochTolerance
|
|
10991
11055
|
}).valid) return false;
|
|
10992
11056
|
if (row.confirmedAt === null) {
|
|
@@ -11005,11 +11069,23 @@ var TotpManager = class {
|
|
|
11005
11069
|
/**
|
|
11006
11070
|
* Remove enrollment. Idempotent — no error if the user had no row.
|
|
11007
11071
|
*/
|
|
11008
|
-
|
|
11009
|
-
|
|
11010
|
-
|
|
11011
|
-
|
|
11072
|
+
disable(userId) {
|
|
11073
|
+
return this.serialize(userId, async () => {
|
|
11074
|
+
await this.store.delete.mutate({
|
|
11075
|
+
collection: TOTP_COLLECTION,
|
|
11076
|
+
key: userId
|
|
11077
|
+
});
|
|
11078
|
+
await this.dropSeed(userId);
|
|
11079
|
+
});
|
|
11080
|
+
}
|
|
11081
|
+
serialize(userId, fn) {
|
|
11082
|
+
const run = (this.chains.get(userId) ?? Promise.resolve()).then(fn, fn);
|
|
11083
|
+
const tail = run.catch(() => void 0);
|
|
11084
|
+
this.chains.set(userId, tail);
|
|
11085
|
+
tail.then(() => {
|
|
11086
|
+
if (this.chains.get(userId) === tail) this.chains.delete(userId);
|
|
11012
11087
|
});
|
|
11088
|
+
return run;
|
|
11013
11089
|
}
|
|
11014
11090
|
/**
|
|
11015
11091
|
* Verify a code against the confirmed secret. Returns `false` when:
|
|
@@ -11023,7 +11099,7 @@ var TotpManager = class {
|
|
|
11023
11099
|
if (row.confirmedAt === null) return false;
|
|
11024
11100
|
return d({
|
|
11025
11101
|
token: code,
|
|
11026
|
-
secret: row
|
|
11102
|
+
secret: await this.seedOf(row),
|
|
11027
11103
|
epochTolerance: this.epochTolerance
|
|
11028
11104
|
}).valid;
|
|
11029
11105
|
}
|
|
@@ -11042,6 +11118,132 @@ var TotpManager = class {
|
|
|
11042
11118
|
confirmedAt: row.confirmedAt
|
|
11043
11119
|
};
|
|
11044
11120
|
}
|
|
11121
|
+
refFor(userId) {
|
|
11122
|
+
return this.secrets === null ? null : this.secrets.ref({
|
|
11123
|
+
kind: "row",
|
|
11124
|
+
collection: TOTP_COLLECTION,
|
|
11125
|
+
rowId: userId
|
|
11126
|
+
}, "secret");
|
|
11127
|
+
}
|
|
11128
|
+
/** A refused read: generic to the caller, the keychain detail to the server log. */
|
|
11129
|
+
unavailable(userId, reason) {
|
|
11130
|
+
this.logger?.warn("TOTP seed unavailable", {
|
|
11131
|
+
tags: { userId },
|
|
11132
|
+
meta: { reason }
|
|
11133
|
+
});
|
|
11134
|
+
return new TotpSecretUnavailableError(reason);
|
|
11135
|
+
}
|
|
11136
|
+
/** The seed in clear: from the keychain for a marker row, inline for a pre-D878 row. Throws when unreadable. */
|
|
11137
|
+
async seedOf(row) {
|
|
11138
|
+
const column = classify(row.secret);
|
|
11139
|
+
if (column.kind === "inline") return row.secret;
|
|
11140
|
+
if (column.kind === "invalid") throw this.unavailable(row.userId, "malformed seed column");
|
|
11141
|
+
if (this.secrets === null || column.ref !== this.refFor(row.userId)) throw this.unavailable(row.userId, "marker does not belong to this user");
|
|
11142
|
+
let answer;
|
|
11143
|
+
try {
|
|
11144
|
+
answer = (await this.secrets.resolve([column.ref]))[column.ref];
|
|
11145
|
+
} catch (err) {
|
|
11146
|
+
throw this.unavailable(row.userId, err instanceof Error ? err.message : String(err));
|
|
11147
|
+
}
|
|
11148
|
+
if (answer?.kind === "value") return answer.value;
|
|
11149
|
+
throw this.unavailable(row.userId, answer?.kind === "unavailable" ? answer.reason : answer?.kind ?? "no-answer");
|
|
11150
|
+
}
|
|
11151
|
+
/** Remove a user's seed from the keychain; a miss leaves an orphan the Secrets page shows. */
|
|
11152
|
+
async dropSeed(userId) {
|
|
11153
|
+
const ref = this.refFor(userId);
|
|
11154
|
+
if (ref === null) return;
|
|
11155
|
+
try {
|
|
11156
|
+
await this.secrets?.delete([ref]);
|
|
11157
|
+
} catch {}
|
|
11158
|
+
}
|
|
11159
|
+
/**
|
|
11160
|
+
* D878: move every inline seed into the keychain. Dry run unless `apply`.
|
|
11161
|
+
* Per row: put -> read back and compare -> only then rewrite the row with the
|
|
11162
|
+
* marker, so the inline seed is cleared by the verified move and nowhere
|
|
11163
|
+
* else. Counts only; idempotent.
|
|
11164
|
+
*/
|
|
11165
|
+
async migrateRawSecrets(apply) {
|
|
11166
|
+
const rows = await this.readAll();
|
|
11167
|
+
let raw = 0;
|
|
11168
|
+
let markers = 0;
|
|
11169
|
+
let migrated = 0;
|
|
11170
|
+
let failed = 0;
|
|
11171
|
+
for (const row of rows) {
|
|
11172
|
+
const kind = classify(row.secret).kind;
|
|
11173
|
+
if (kind === "marker") {
|
|
11174
|
+
markers += 1;
|
|
11175
|
+
continue;
|
|
11176
|
+
}
|
|
11177
|
+
if (kind !== "inline") continue;
|
|
11178
|
+
raw += 1;
|
|
11179
|
+
if (!apply) continue;
|
|
11180
|
+
if (await this.moveRow(row)) migrated += 1;
|
|
11181
|
+
else failed += 1;
|
|
11182
|
+
}
|
|
11183
|
+
let remainingRaw = 0;
|
|
11184
|
+
let unresolvedMarkers = 0;
|
|
11185
|
+
for (const row of await this.readAll()) {
|
|
11186
|
+
if (classify(row.secret).kind === "inline") {
|
|
11187
|
+
remainingRaw += 1;
|
|
11188
|
+
continue;
|
|
11189
|
+
}
|
|
11190
|
+
try {
|
|
11191
|
+
await this.seedOf(row);
|
|
11192
|
+
} catch {
|
|
11193
|
+
unresolvedMarkers += 1;
|
|
11194
|
+
}
|
|
11195
|
+
}
|
|
11196
|
+
return {
|
|
11197
|
+
apply,
|
|
11198
|
+
rows: rows.length,
|
|
11199
|
+
raw,
|
|
11200
|
+
markers,
|
|
11201
|
+
migrated,
|
|
11202
|
+
failed,
|
|
11203
|
+
remainingRaw,
|
|
11204
|
+
unresolvedMarkers,
|
|
11205
|
+
verified: remainingRaw === 0 && unresolvedMarkers === 0
|
|
11206
|
+
};
|
|
11207
|
+
}
|
|
11208
|
+
/**
|
|
11209
|
+
* Compare-and-set under the user's queue: the row is re-read and moved only if
|
|
11210
|
+
* it still holds the very inline seed we planned to move, so a concurrent
|
|
11211
|
+
* `setup` (its seed and marker) is never overwritten. The ref is always the
|
|
11212
|
+
* user's own, never taken from the row.
|
|
11213
|
+
*/
|
|
11214
|
+
moveRow(planned) {
|
|
11215
|
+
return this.serialize(planned.userId, async () => {
|
|
11216
|
+
const ref = this.refFor(planned.userId);
|
|
11217
|
+
if (this.secrets === null || ref === null) return false;
|
|
11218
|
+
try {
|
|
11219
|
+
const current = await this.find(planned.userId);
|
|
11220
|
+
if (current === null || current.secret !== planned.secret) return false;
|
|
11221
|
+
await this.secrets.put(ref, planned.secret);
|
|
11222
|
+
const back = (await this.secrets.resolve([ref]))[ref];
|
|
11223
|
+
if (back?.kind !== "value" || back.value !== planned.secret) return false;
|
|
11224
|
+
const next = {
|
|
11225
|
+
...current,
|
|
11226
|
+
secret: JSON.stringify(makeSecretMarker(ref))
|
|
11227
|
+
};
|
|
11228
|
+
await this.store.update.mutate({
|
|
11229
|
+
collection: TOTP_COLLECTION,
|
|
11230
|
+
id: planned.userId,
|
|
11231
|
+
data: { ...next }
|
|
11232
|
+
});
|
|
11233
|
+
return true;
|
|
11234
|
+
} catch {
|
|
11235
|
+
return false;
|
|
11236
|
+
}
|
|
11237
|
+
});
|
|
11238
|
+
}
|
|
11239
|
+
async readAll() {
|
|
11240
|
+
const results = await this.store.query.query({ collection: TOTP_COLLECTION });
|
|
11241
|
+
const out = [];
|
|
11242
|
+
for (const r of results) try {
|
|
11243
|
+
out.push(parseRow(r.data));
|
|
11244
|
+
} catch {}
|
|
11245
|
+
return out;
|
|
11246
|
+
}
|
|
11045
11247
|
async find(userId) {
|
|
11046
11248
|
const results = await this.store.query.query({
|
|
11047
11249
|
collection: TOTP_COLLECTION,
|
|
@@ -11539,7 +11741,10 @@ var LocalAuthAddon = class extends BaseAddon {
|
|
|
11539
11741
|
this.apiKeyManager = new ApiKeyManager(storageAccess);
|
|
11540
11742
|
this.scopedTokenManager = new ScopedTokenManager(store);
|
|
11541
11743
|
this.oauthSessionManager = new OauthSessionManager(store);
|
|
11542
|
-
this.totpManager = new TotpManager(store
|
|
11744
|
+
this.totpManager = new TotpManager(store, {
|
|
11745
|
+
secrets: forOwner === null ? null : forOwner("system"),
|
|
11746
|
+
logger: this.ctx.logger
|
|
11747
|
+
});
|
|
11543
11748
|
this.externalIdentities = new ExternalIdentityManager(store);
|
|
11544
11749
|
this.loginSessions = new LoginSessionManager(store, { logger: this.ctx.logger });
|
|
11545
11750
|
await this.loginSessions.load();
|
|
@@ -11781,6 +11986,10 @@ var LocalAuthAddon = class extends BaseAddon {
|
|
|
11781
11986
|
};
|
|
11782
11987
|
return this.totpManager.getStatus(input.userId);
|
|
11783
11988
|
},
|
|
11989
|
+
migrateRawSecrets: async (input) => {
|
|
11990
|
+
if (!this.totpManager) throw new Error("TOTP management not available");
|
|
11991
|
+
return this.totpManager.migrateRawSecrets(input.apply);
|
|
11992
|
+
},
|
|
11784
11993
|
verifyTotp: async (input) => {
|
|
11785
11994
|
if (!this.totpManager) return { valid: false };
|
|
11786
11995
|
return { valid: await this.totpManager.verify(input.userId, input.code) };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
const require_chunk = require("../../chunk-Cek0wNdY.js");
|
|
3
|
-
const require_dist = require("../../dist-
|
|
4
|
-
const require_hub_mdns_ciao = require("../../hub-mdns-ciao-
|
|
3
|
+
const require_dist = require("../../dist-D9qT3jHr.js");
|
|
4
|
+
const require_hub_mdns_ciao = require("../../hub-mdns-ciao-B-wk9nzx.js");
|
|
5
5
|
let zod = require("zod");
|
|
6
6
|
let node_net = require("node:net");
|
|
7
7
|
let node_os = require("node:os");
|