
























@@ -8,6 +8,7 @@ import {
88type DeviceBootstrapProfile,
99type DeviceBootstrapProfileInput,
1010} from "../shared/device-bootstrap-profile.js";
11+import { asDateTimestampMs, resolveExpiresAtMsFromDurationMs } from "../shared/number-coercion.js";
1112import { roleScopesAllow } from "../shared/operator-scope-compat.js";
1213import { normalizeDevicePublicKeyBase64Url } from "./device-identity.js";
1314import { resolvePairingPaths } from "./pairing-files.js";
@@ -201,7 +202,7 @@ async function loadState(baseDir?: string): Promise<DeviceBootstrapStateFile> {
201202const record = entry as Partial<DeviceBootstrapTokenRecord>;
202203const token =
203204typeof record.token === "string" && record.token.trim().length > 0 ? record.token : tokenKey;
204-const issuedAtMs = typeof record.issuedAtMs === "number" ? record.issuedAtMs : 0;
205+const issuedAtMs = asDateTimestampMs(record.issuedAtMs) ?? 0;
205206const profile = resolvePersistedBootstrapProfile(record);
206207const pendingProfile = resolvePersistedPendingProfile(record);
207208state[tokenKey] = {
@@ -212,11 +213,11 @@ async function loadState(baseDir?: string): Promise<DeviceBootstrapStateFile> {
212213deviceId: typeof record.deviceId === "string" ? record.deviceId : undefined,
213214publicKey: typeof record.publicKey === "string" ? record.publicKey : undefined,
214215 issuedAtMs,
215-ts: typeof record.ts === "number" ? record.ts : issuedAtMs,
216+ts: asDateTimestampMs(record.ts) ?? issuedAtMs,
216217lastUsedAtMs: typeof record.lastUsedAtMs === "number" ? record.lastUsedAtMs : undefined,
217218};
218219}
219-pruneExpiredPending(state, Date.now(), DEVICE_BOOTSTRAP_TOKEN_TTL_MS);
220+pruneExpiredPending(state, asDateTimestampMs(Date.now()) ?? 0, DEVICE_BOOTSTRAP_TOKEN_TTL_MS);
220221return state;
221222}
222223@@ -236,7 +237,14 @@ export async function issueDeviceBootstrapToken(
236237return await withLock(async () => {
237238const state = await loadState(params.baseDir);
238239const token = generatePairingToken();
239-const issuedAtMs = Date.now();
240+const issuedAtMs = asDateTimestampMs(Date.now());
241+const expiresAtMs =
242+issuedAtMs === undefined
243+ ? undefined
244+ : resolveExpiresAtMsFromDurationMs(DEVICE_BOOTSTRAP_TOKEN_TTL_MS, { nowMs: issuedAtMs });
245+if (issuedAtMs === undefined || expiresAtMs === undefined) {
246+throw new Error("Device bootstrap token expiry could not be resolved.");
247+}
240248const profileInput = resolveIssuedBootstrapProfileInput(params);
241249const profile = resolveIssuedBootstrapProfile(params);
242250warnIfIssuedBootstrapScopesWereStripped({ input: profileInput, profile });
@@ -248,7 +256,7 @@ export async function issueDeviceBootstrapToken(
248256 issuedAtMs,
249257};
250258await persistState(state, params.baseDir);
251-return { token, expiresAtMs: issuedAtMs + DEVICE_BOOTSTRAP_TOKEN_TTL_MS };
259+return { token, expiresAtMs };
252260});
253261}
254262此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。