@skrr-ai/cli 0.1.79 → 0.1.80
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/base-command.js +9 -2
- package/dist/commands/inbox/index.js +4 -2
- package/dist/commands/login.d.ts +8 -3
- package/dist/commands/login.js +14 -4
- package/dist/commands/spaces/workflows/events.js +9 -2
- package/dist/lib/agentic-stream.js +13 -4
- package/dist/lib/api-fetch.js +6 -2
- package/dist/lib/brokered-dpop.d.ts +34 -0
- package/dist/lib/brokered-dpop.js +89 -0
- package/dist/lib/daemonBroker.d.ts +71 -2
- package/dist/lib/daemonBroker.js +203 -36
- package/dist/lib/daemonBrokerRefusal.d.ts +26 -0
- package/dist/lib/daemonBrokerRefusal.js +83 -4
- package/dist/lib/daemonRestartWait.d.ts +39 -0
- package/dist/lib/daemonRestartWait.js +181 -0
- package/dist/lib/dedicated-service.js +10 -5
- package/dist/lib/dedicated-ssh.js +6 -3
- package/dist/lib/dedicated-terminal.js +6 -3
- package/dist/lib/dpop-auth.d.ts +35 -0
- package/dist/lib/dpop-auth.js +57 -0
- package/dist/lib/login.d.ts +6 -0
- package/dist/lib/login.js +13 -4
- package/dist/lib/node-adapter.js +4 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/cliHandoffWire.d.ts +42 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/cliHandoffWire.js +35 -3
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/credentialSession.d.ts +49 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/credentialSession.js +47 -9
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/deviceKey.d.ts +56 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/deviceKey.js +42 -20
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/dpop.d.ts +101 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/dpop.js +179 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.d.ts +1 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.js +19 -3
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/cliHandoffWire.d.ts +42 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/cliHandoffWire.js +32 -2
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/credentialSession.d.ts +49 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/credentialSession.js +47 -9
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/deviceKey.d.ts +56 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/deviceKey.js +39 -20
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/dpop.d.ts +101 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/dpop.js +138 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.d.ts +1 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.js +4 -0
- package/dist/node_modules/@skrr-ai/auth-core/package.json +1 -1
- package/dist/node_modules/@skrr-ai/data-provider/index.js +3422 -3407
- package/oclif.manifest.json +31930 -31930
- package/package.json +2 -2
|
@@ -14,6 +14,20 @@
|
|
|
14
14
|
* Deliberately thin: no persistence, no retry policy, no logging. Callers
|
|
15
15
|
* own their store and their error surfaces; this owns the cache/single-
|
|
16
16
|
* flight/invalidate invariants so they cannot drift between surfaces.
|
|
17
|
+
*
|
|
18
|
+
* Two policies are options because mechanisms genuinely need different ones,
|
|
19
|
+
* and both default to the original behaviour:
|
|
20
|
+
*
|
|
21
|
+
* - `forcedAcquire: 'join'` — a forced acquire joins whatever is in flight,
|
|
22
|
+
* for a mechanism where a second concurrent renewal is itself the hazard
|
|
23
|
+
* (a refresh-family rotation or a family mint);
|
|
24
|
+
* - `classifyAcquireError` — stale-on-degrade: a TRANSIENT failure serves the
|
|
25
|
+
* still-unexpired cached credential instead of failing, a PERMANENT one
|
|
26
|
+
* drops it.
|
|
27
|
+
*
|
|
28
|
+
* Mechanism-specific limits — how often a new family may be minted, which
|
|
29
|
+
* failures retire it — stay in the mechanism. The daemon's task token broker
|
|
30
|
+
* (`daemon/src/task/token-broker.ts`) is the consumer that uses both options.
|
|
17
31
|
*/
|
|
18
32
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
33
|
exports.createCredentialSession = createCredentialSession;
|
|
@@ -21,6 +35,8 @@ const DEFAULT_SKEW_MS = 30_000;
|
|
|
21
35
|
function createCredentialSession(opts) {
|
|
22
36
|
const skewMs = opts.skewMs ?? DEFAULT_SKEW_MS;
|
|
23
37
|
const now = opts.now ?? Date.now;
|
|
38
|
+
const forcedJoins = opts.forcedAcquire === 'join';
|
|
39
|
+
const classify = opts.classifyAcquireError;
|
|
24
40
|
let cached = null;
|
|
25
41
|
let inFlight = null;
|
|
26
42
|
let inFlightForced = false;
|
|
@@ -29,17 +45,26 @@ function createCredentialSession(opts) {
|
|
|
29
45
|
// result back over the newer one.
|
|
30
46
|
let generation = 0;
|
|
31
47
|
const fresh = (t) => t.expiresAtMs == null || now() < t.expiresAtMs - skewMs;
|
|
48
|
+
/** Not yet actually expired — the bar for serving a stale credential. */
|
|
49
|
+
const unexpired = (t) => t.expiresAtMs == null || now() < t.expiresAtMs;
|
|
32
50
|
const acquireShared = (force) => {
|
|
33
|
-
//
|
|
34
|
-
// serve the very credential a 401 just invalidated. A
|
|
35
|
-
// DOES satisfy non-forced waiters: it is already renewing.
|
|
36
|
-
|
|
51
|
+
// Under 'supersede', a forced acquire must not join a non-forced in-flight
|
|
52
|
+
// one — that would serve the very credential a 401 just invalidated. A
|
|
53
|
+
// forced in-flight DOES satisfy non-forced waiters: it is already renewing.
|
|
54
|
+
// Under 'join', everything joins (see `forcedAcquire`).
|
|
55
|
+
if (inFlight && (forcedJoins || inFlightForced || !force))
|
|
37
56
|
return inFlight;
|
|
38
57
|
const gen = ++generation;
|
|
39
58
|
const p = opts.acquire(force).then((t) => {
|
|
40
59
|
if (gen === generation)
|
|
41
60
|
cached = t;
|
|
42
61
|
return t;
|
|
62
|
+
}, (err) => {
|
|
63
|
+
// Settled before any waiter resumes, so a waiter's stale check below
|
|
64
|
+
// already sees a permanent failure's dropped cache.
|
|
65
|
+
if (gen === generation && classify?.(err) === 'permanent')
|
|
66
|
+
cached = null;
|
|
67
|
+
throw err;
|
|
43
68
|
});
|
|
44
69
|
inFlight = p;
|
|
45
70
|
inFlightForced = force;
|
|
@@ -54,13 +79,26 @@ function createCredentialSession(opts) {
|
|
|
54
79
|
p.then(cleanup, cleanup);
|
|
55
80
|
return p;
|
|
56
81
|
};
|
|
82
|
+
const getCredential = async (forceRefresh = false) => {
|
|
83
|
+
if (forceRefresh)
|
|
84
|
+
cached = null;
|
|
85
|
+
if (cached && fresh(cached))
|
|
86
|
+
return cached;
|
|
87
|
+
try {
|
|
88
|
+
return await acquireShared(forceRefresh);
|
|
89
|
+
}
|
|
90
|
+
catch (err) {
|
|
91
|
+
// Stale-on-degrade: read `cached` AFTER the failure, so an invalidate()
|
|
92
|
+
// or a forced discard that happened meanwhile is honoured.
|
|
93
|
+
if (classify?.(err) === 'transient' && cached && unexpired(cached))
|
|
94
|
+
return cached;
|
|
95
|
+
throw err;
|
|
96
|
+
}
|
|
97
|
+
};
|
|
57
98
|
return {
|
|
99
|
+
getCredential,
|
|
58
100
|
async getToken(forceRefresh = false) {
|
|
59
|
-
|
|
60
|
-
cached = null;
|
|
61
|
-
if (cached && fresh(cached))
|
|
62
|
-
return cached.accessToken;
|
|
63
|
-
return (await acquireShared(forceRefresh)).accessToken;
|
|
101
|
+
return (await getCredential(forceRefresh)).accessToken;
|
|
64
102
|
},
|
|
65
103
|
invalidate() {
|
|
66
104
|
cached = null;
|
|
@@ -1,3 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* deviceKey.ts — L11 Phase 1: Ed25519 device identity + DPoP-style proofs.
|
|
3
|
+
*
|
|
4
|
+
* The load-bearing recommendation in `docs/daemon-auth-cc-deep-comparison-
|
|
5
|
+
* 2026-04-24.md` §L11. Two-axis identity:
|
|
6
|
+
*
|
|
7
|
+
* user identity = OAuth refresh token (server-rotated, scope-pinned, reuse-detected)
|
|
8
|
+
* device identity = Ed25519 keypair (device-generated, server-revocable)
|
|
9
|
+
*
|
|
10
|
+
* Every daemon→server request carries a JWS signature over
|
|
11
|
+
* `{htm, htu, iat, jti, rth?, ath?}` using the device key. Server validates
|
|
12
|
+
* against the public key registered for this daemon.
|
|
13
|
+
*
|
|
14
|
+
* Stolen credentials require BOTH halves:
|
|
15
|
+
* - refresh token alone is useless without the device key
|
|
16
|
+
* - device key alone is useless without the refresh token
|
|
17
|
+
*
|
|
18
|
+
* Why DPoP-shaped (RFC 9449) rather than rolling our own JWS:
|
|
19
|
+
* - The htm/htu/iat/jti/ath claim set is well-studied. Server-side
|
|
20
|
+
* replay defenses, clock-skew handling, and audit-trail formats all
|
|
21
|
+
* have a body of prior art to copy from.
|
|
22
|
+
* - Future interop: if we ever expose OverSky as a third-party OAuth
|
|
23
|
+
* resource server, DPoP-bearer is the way clients will already
|
|
24
|
+
* understand.
|
|
25
|
+
*
|
|
26
|
+
* Why we extend with `rth` (refresh-token-hash):
|
|
27
|
+
* - RFC 9449 only mints `ath` (access-token-hash). The whole point of
|
|
28
|
+
* L11 is to bind the *refresh* token to the device, so the proof
|
|
29
|
+
* accompanying a refresh request must commit to the refresh token.
|
|
30
|
+
* `rth` is structurally identical to `ath`; the name disambiguates.
|
|
31
|
+
*
|
|
32
|
+
* What this module is NOT responsible for:
|
|
33
|
+
* - Persistence (lives in `daemon/src/deviceIdentity.ts`).
|
|
34
|
+
* - Server-side public-key registration (separate API in a later PR).
|
|
35
|
+
* - Wire-up to the daemon's outgoing HTTP path (Phase 2 of L11).
|
|
36
|
+
*/
|
|
37
|
+
import crypto from 'node:crypto';
|
|
1
38
|
/**
|
|
2
39
|
* Ed25519 JSON Web Key — RFC 8037 §2 ("Key Type 'OKP'").
|
|
3
40
|
*
|
|
@@ -123,6 +160,18 @@ export interface BuildProofOptions {
|
|
|
123
160
|
* not a silent fallback.
|
|
124
161
|
*/
|
|
125
162
|
export declare function buildProof(privateJwk: Ed25519PrivateJwk, publicJwk: Ed25519PublicJwk, opts: BuildProofOptions): string;
|
|
163
|
+
/** The header `typ` of an OverSky device-identity proof. */
|
|
164
|
+
export declare const DEVICE_PROOF_TYP = "oversky-dpop+jwt";
|
|
165
|
+
/**
|
|
166
|
+
* Sign a DPoP-shaped proof with an Ed25519 key under the given header `typ`.
|
|
167
|
+
*
|
|
168
|
+
* @internal Shared by the device-identity proof (`oversky-dpop+jwt`, above)
|
|
169
|
+
* and the RFC 9449 access-token proof (`dpop+jwt`, `dpop.ts`). `typ` is the
|
|
170
|
+
* only thing on the wire that separates the two protocols, which is why it is
|
|
171
|
+
* a required argument rather than a default: a proof signed for one must
|
|
172
|
+
* never verify as the other.
|
|
173
|
+
*/
|
|
174
|
+
export declare function signEd25519Proof(privateKey: crypto.KeyObject, publicJwk: Ed25519PublicJwk, typ: string, opts: BuildProofOptions): string;
|
|
126
175
|
export type VerifyFailureReason = 'malformed' | 'bad_alg' | 'bad_typ' | 'bad_jwk' | 'sig_invalid' | 'iat_old' | 'iat_future' | 'htm_mismatch' | 'htu_mismatch' | 'rth_mismatch' | 'ath_mismatch' | 'pin_mismatch';
|
|
127
176
|
export interface VerifySuccess {
|
|
128
177
|
valid: true;
|
|
@@ -165,3 +214,10 @@ export interface VerifyOptions {
|
|
|
165
214
|
* `sig_invalid` to keep the failure surface narrow.
|
|
166
215
|
*/
|
|
167
216
|
export declare function verifyProof(jws: string, opts?: VerifyOptions): VerifySuccess | VerifyFailure;
|
|
217
|
+
/**
|
|
218
|
+
* Verify a DPoP-shaped Ed25519 proof whose header `typ` must equal `typ`.
|
|
219
|
+
*
|
|
220
|
+
* @internal See `signEd25519Proof`. `verifyProof` (device identity) and
|
|
221
|
+
* `verifyDpopProof` (`dpop.ts`, RFC 9449) are the two public doors.
|
|
222
|
+
*/
|
|
223
|
+
export declare function verifyEd25519Proof(jws: string, opts: VerifyOptions, typ: string): VerifySuccess | VerifyFailure;
|
|
@@ -3,13 +3,16 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.DEVICE_PROOF_TYP = void 0;
|
|
6
7
|
exports.generateDeviceKeyPair = generateDeviceKeyPair;
|
|
7
8
|
exports.jwkThumbprint = jwkThumbprint;
|
|
8
9
|
exports.publicJwkFromX = publicJwkFromX;
|
|
9
10
|
exports.tokenHash = tokenHash;
|
|
10
11
|
exports.normalizeHtu = normalizeHtu;
|
|
11
12
|
exports.buildProof = buildProof;
|
|
13
|
+
exports.signEd25519Proof = signEd25519Proof;
|
|
12
14
|
exports.verifyProof = verifyProof;
|
|
15
|
+
exports.verifyEd25519Proof = verifyEd25519Proof;
|
|
13
16
|
/**
|
|
14
17
|
* deviceKey.ts — L11 Phase 1: Ed25519 device identity + DPoP-style proofs.
|
|
15
18
|
*
|
|
@@ -164,17 +167,40 @@ function normalizeHtu(url) {
|
|
|
164
167
|
* not a silent fallback.
|
|
165
168
|
*/
|
|
166
169
|
function buildProof(privateJwk, publicJwk, opts) {
|
|
170
|
+
if (publicJwk.x !== privateJwk.x) {
|
|
171
|
+
// Defensive — public and private must come from the same keypair.
|
|
172
|
+
// A mismatch indicates a corrupted in-memory state.
|
|
173
|
+
throw new Error('buildProof: public/private JWK x mismatch (corrupted keypair)');
|
|
174
|
+
}
|
|
175
|
+
// Cast to crypto's `JsonWebKey` shape — that type requires an index
|
|
176
|
+
// signature for arbitrary string keys; our narrow OKP shape doesn't
|
|
177
|
+
// declare one because the strict shape is more useful at our API
|
|
178
|
+
// boundary. Cast through `unknown` to silence the structural mismatch
|
|
179
|
+
// without losing the rest of the type safety on this surface.
|
|
180
|
+
const privateKey = node_crypto_1.default.createPrivateKey({
|
|
181
|
+
format: 'jwk',
|
|
182
|
+
key: privateJwk,
|
|
183
|
+
});
|
|
184
|
+
return signEd25519Proof(privateKey, publicJwk, exports.DEVICE_PROOF_TYP, opts);
|
|
185
|
+
}
|
|
186
|
+
/** The header `typ` of an OverSky device-identity proof. */
|
|
187
|
+
exports.DEVICE_PROOF_TYP = 'oversky-dpop+jwt';
|
|
188
|
+
/**
|
|
189
|
+
* Sign a DPoP-shaped proof with an Ed25519 key under the given header `typ`.
|
|
190
|
+
*
|
|
191
|
+
* @internal Shared by the device-identity proof (`oversky-dpop+jwt`, above)
|
|
192
|
+
* and the RFC 9449 access-token proof (`dpop+jwt`, `dpop.ts`). `typ` is the
|
|
193
|
+
* only thing on the wire that separates the two protocols, which is why it is
|
|
194
|
+
* a required argument rather than a default: a proof signed for one must
|
|
195
|
+
* never verify as the other.
|
|
196
|
+
*/
|
|
197
|
+
function signEd25519Proof(privateKey, publicJwk, typ, opts) {
|
|
167
198
|
if (typeof opts.method !== 'string' || opts.method.length === 0) {
|
|
168
199
|
throw new Error('buildProof: method must be a non-empty string');
|
|
169
200
|
}
|
|
170
201
|
if (typeof opts.url !== 'string' || opts.url.length === 0) {
|
|
171
202
|
throw new Error('buildProof: url must be a non-empty string');
|
|
172
203
|
}
|
|
173
|
-
if (publicJwk.x !== privateJwk.x) {
|
|
174
|
-
// Defensive — public and private must come from the same keypair.
|
|
175
|
-
// A mismatch indicates a corrupted in-memory state.
|
|
176
|
-
throw new Error('buildProof: public/private JWK x mismatch (corrupted keypair)');
|
|
177
|
-
}
|
|
178
204
|
const claims = {
|
|
179
205
|
htm: opts.method.toUpperCase(),
|
|
180
206
|
htu: normalizeHtu(opts.url),
|
|
@@ -185,23 +211,10 @@ function buildProof(privateJwk, publicJwk, opts) {
|
|
|
185
211
|
claims.rth = tokenHash(opts.refreshToken);
|
|
186
212
|
if (opts.accessToken !== undefined)
|
|
187
213
|
claims.ath = tokenHash(opts.accessToken);
|
|
188
|
-
const header = {
|
|
189
|
-
alg: 'EdDSA',
|
|
190
|
-
typ: 'oversky-dpop+jwt',
|
|
191
|
-
jwk: publicJwk,
|
|
192
|
-
};
|
|
214
|
+
const header = { alg: 'EdDSA', typ, jwk: publicJwk };
|
|
193
215
|
const headerB64 = b64u(Buffer.from(JSON.stringify(header), 'utf-8'));
|
|
194
216
|
const payloadB64 = b64u(Buffer.from(JSON.stringify(claims), 'utf-8'));
|
|
195
217
|
const signingInput = `${headerB64}.${payloadB64}`;
|
|
196
|
-
// Cast to crypto's `JsonWebKey` shape — that type requires an index
|
|
197
|
-
// signature for arbitrary string keys; our narrow OKP shape doesn't
|
|
198
|
-
// declare one because the strict shape is more useful at our API
|
|
199
|
-
// boundary. Cast through `unknown` to silence the structural mismatch
|
|
200
|
-
// without losing the rest of the type safety on this surface.
|
|
201
|
-
const privateKey = node_crypto_1.default.createPrivateKey({
|
|
202
|
-
format: 'jwk',
|
|
203
|
-
key: privateJwk,
|
|
204
|
-
});
|
|
205
218
|
// EdDSA over Ed25519 — Node's crypto.sign expects `null` as the
|
|
206
219
|
// digest algorithm because Ed25519 hashes the message internally.
|
|
207
220
|
const signature = node_crypto_1.default.sign(null, Buffer.from(signingInput, 'utf-8'), privateKey);
|
|
@@ -217,6 +230,15 @@ function buildProof(privateJwk, publicJwk, opts) {
|
|
|
217
230
|
* `sig_invalid` to keep the failure surface narrow.
|
|
218
231
|
*/
|
|
219
232
|
function verifyProof(jws, opts = {}) {
|
|
233
|
+
return verifyEd25519Proof(jws, opts, exports.DEVICE_PROOF_TYP);
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Verify a DPoP-shaped Ed25519 proof whose header `typ` must equal `typ`.
|
|
237
|
+
*
|
|
238
|
+
* @internal See `signEd25519Proof`. `verifyProof` (device identity) and
|
|
239
|
+
* `verifyDpopProof` (`dpop.ts`, RFC 9449) are the two public doors.
|
|
240
|
+
*/
|
|
241
|
+
function verifyEd25519Proof(jws, opts, typ) {
|
|
220
242
|
if (typeof jws !== 'string' || jws.length === 0) {
|
|
221
243
|
return { valid: false, reason: 'malformed' };
|
|
222
244
|
}
|
|
@@ -237,7 +259,7 @@ function verifyProof(jws, opts = {}) {
|
|
|
237
259
|
if (!header || typeof header !== 'object' || header.alg !== 'EdDSA') {
|
|
238
260
|
return { valid: false, reason: 'bad_alg' };
|
|
239
261
|
}
|
|
240
|
-
if (header.typ !==
|
|
262
|
+
if (header.typ !== typ) {
|
|
241
263
|
return { valid: false, reason: 'bad_typ' };
|
|
242
264
|
}
|
|
243
265
|
const headerJwk = header.jwk;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dpop.ts — RFC 9449 proof-of-possession for ACCESS tokens.
|
|
3
|
+
*
|
|
4
|
+
* Sibling of `deviceKey.ts`, and deliberately a different protocol on the
|
|
5
|
+
* wire. A device-identity proof (`typ: oversky-dpop+jwt`, header
|
|
6
|
+
* `OverSky-DPoP`) proves a daemon holds its ENROLLED key and authorizes
|
|
7
|
+
* minting/rotating that daemon's families. An access-token proof
|
|
8
|
+
* (`typ: dpop+jwt`, header `DPoP`) proves the presenter of an access token
|
|
9
|
+
* holds the key the token is bound to (`cnf.jkt`). The `typ` keeps either
|
|
10
|
+
* proof from ever verifying as the other.
|
|
11
|
+
*
|
|
12
|
+
* Why a SEPARATE key, never the device key (OSK-12020): the access-token key
|
|
13
|
+
* signs whatever `{htm, htu, ath}` a local caller asks of the daemon's proof
|
|
14
|
+
* oracle (`/v1/auth/cli-handoff-dpop-proof`). Handing that oracle the device
|
|
15
|
+
* key would turn it into a signer for the credential that mints families —
|
|
16
|
+
* one confused-deputy step from the workload asking for a device proof over
|
|
17
|
+
* `POST /api/daemons/cli-handoff`. A dedicated key carries no authority but
|
|
18
|
+
* the tokens bound to it, lives only in daemon memory (`createDpopKey` never
|
|
19
|
+
* exposes the private half), and dies with the family it binds.
|
|
20
|
+
*
|
|
21
|
+
* Algorithm: EdDSA / Ed25519, the same primitive as the device key. RFC 9449
|
|
22
|
+
* lets the server advertise `dpop_signing_alg_values_supported`; ours is
|
|
23
|
+
* exactly `["EdDSA"]`, and the verifier refuses anything else.
|
|
24
|
+
*/
|
|
25
|
+
import { type DeviceProofClaims, type Ed25519PublicJwk, type VerifyFailureReason } from './deviceKey.js';
|
|
26
|
+
/** RFC 9449 §4.2 — the proof's JOSE header `typ`. */
|
|
27
|
+
export declare const DPOP_PROOF_TYP = "dpop+jwt";
|
|
28
|
+
/** RFC 9449 §4.1 — the request header carrying the proof. */
|
|
29
|
+
export declare const DPOP_HEADER = "DPoP";
|
|
30
|
+
/** Default freshness window, matching the device-proof verifier. */
|
|
31
|
+
export declare const DPOP_MAX_AGE_SECONDS = 60;
|
|
32
|
+
/** Server error codes for a bound token presented without a usable proof. */
|
|
33
|
+
export declare const DPOP_PROOF_REQUIRED = "DPOP_PROOF_REQUIRED";
|
|
34
|
+
export declare const DPOP_PROOF_INVALID = "DPOP_PROOF_INVALID";
|
|
35
|
+
export declare const DPOP_PROOF_VERIFY_UNAVAILABLE = "DPOP_PROOF_VERIFY_UNAVAILABLE";
|
|
36
|
+
/** A proof's claims: RFC 9449's `{htm, htu, iat, jti, ath?}`. */
|
|
37
|
+
export type DpopProofClaims = Pick<DeviceProofClaims, 'htm' | 'htu' | 'iat' | 'jti' | 'ath'>;
|
|
38
|
+
export interface DpopProofRequest {
|
|
39
|
+
/** HTTP method of the request the proof will accompany. */
|
|
40
|
+
htm: string;
|
|
41
|
+
/** Target URI; query and fragment are stripped (RFC 9449 §4.2). */
|
|
42
|
+
htu: string;
|
|
43
|
+
/** The access token the proof commits to (hashed into `ath`). Omitted only
|
|
44
|
+
* at the token endpoint, where the token does not exist yet. */
|
|
45
|
+
accessToken?: string;
|
|
46
|
+
/** Pre-computed `ath`, for a signer that never sees the token itself. */
|
|
47
|
+
ath?: string;
|
|
48
|
+
/** Test hooks. */
|
|
49
|
+
iat?: number;
|
|
50
|
+
jti?: string;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* An in-memory DPoP key. The private half lives in this closure and nowhere
|
|
54
|
+
* else: there is no accessor, no serializer, and it is never written to disk.
|
|
55
|
+
*/
|
|
56
|
+
export interface DpopKey {
|
|
57
|
+
readonly publicJwk: Ed25519PublicJwk;
|
|
58
|
+
/** RFC 7638 thumbprint — the value a bound token carries as `cnf.jkt`. */
|
|
59
|
+
readonly thumbprint: string;
|
|
60
|
+
sign(req: DpopProofRequest): string;
|
|
61
|
+
}
|
|
62
|
+
export declare function createDpopKey(): DpopKey;
|
|
63
|
+
/** RFC 9449 §4.2 htu normalization — identical to the device proof's. */
|
|
64
|
+
export declare function normalizeDpopHtu(url: string): string;
|
|
65
|
+
/** `ath` for an access token: base64url(SHA-256(ascii(token))), RFC 9449 §4.2. */
|
|
66
|
+
export declare function accessTokenHash(accessToken: string): string;
|
|
67
|
+
/** True for a value shaped like an `ath` / `jkt` (43-char base64url SHA-256). */
|
|
68
|
+
export declare function isAth(value: unknown): value is string;
|
|
69
|
+
/** Same shape test, named for the `cnf.jkt` call sites. */
|
|
70
|
+
export declare const isJwkThumbprint: typeof isAth;
|
|
71
|
+
export type DpopVerifyFailureReason = Exclude<VerifyFailureReason, 'rth_mismatch' | 'pin_mismatch'> | 'ath_missing' | 'jkt_mismatch';
|
|
72
|
+
export interface DpopVerifyOptions {
|
|
73
|
+
expectedHtm: string;
|
|
74
|
+
expectedHtu: string;
|
|
75
|
+
/** `ath` the proof must carry. Required for a resource request. */
|
|
76
|
+
expectedAth?: string;
|
|
77
|
+
/** Thumbprint the proof's key must have — the token's `cnf.jkt`. */
|
|
78
|
+
expectedJkt?: string;
|
|
79
|
+
maxAgeSeconds?: number;
|
|
80
|
+
clockSkewSeconds?: number;
|
|
81
|
+
now?: number;
|
|
82
|
+
}
|
|
83
|
+
export type DpopVerifyResult = {
|
|
84
|
+
valid: true;
|
|
85
|
+
claims: DpopProofClaims;
|
|
86
|
+
thumbprint: string;
|
|
87
|
+
publicJwk: Ed25519PublicJwk;
|
|
88
|
+
} | {
|
|
89
|
+
valid: false;
|
|
90
|
+
reason: DpopVerifyFailureReason;
|
|
91
|
+
};
|
|
92
|
+
/**
|
|
93
|
+
* Verify an RFC 9449 proof: `typ` dpop+jwt, `alg` EdDSA, a well-formed inline
|
|
94
|
+
* JWK, the signature, iat window, htm, htu, `ath` (when expected) and the
|
|
95
|
+
* key's thumbprint against `expectedJkt` (when given).
|
|
96
|
+
*
|
|
97
|
+
* Replay (`jti`) is NOT checked here — it needs shared state, and the caller
|
|
98
|
+
* owns that (the API's Redis jti cache). A verifier that claimed to do it in
|
|
99
|
+
* process memory would be wrong on every multi-replica deployment.
|
|
100
|
+
*/
|
|
101
|
+
export declare function verifyDpopProof(jws: string, opts: DpopVerifyOptions): DpopVerifyResult;
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* dpop.ts — RFC 9449 proof-of-possession for ACCESS tokens.
|
|
4
|
+
*
|
|
5
|
+
* Sibling of `deviceKey.ts`, and deliberately a different protocol on the
|
|
6
|
+
* wire. A device-identity proof (`typ: oversky-dpop+jwt`, header
|
|
7
|
+
* `OverSky-DPoP`) proves a daemon holds its ENROLLED key and authorizes
|
|
8
|
+
* minting/rotating that daemon's families. An access-token proof
|
|
9
|
+
* (`typ: dpop+jwt`, header `DPoP`) proves the presenter of an access token
|
|
10
|
+
* holds the key the token is bound to (`cnf.jkt`). The `typ` keeps either
|
|
11
|
+
* proof from ever verifying as the other.
|
|
12
|
+
*
|
|
13
|
+
* Why a SEPARATE key, never the device key (OSK-12020): the access-token key
|
|
14
|
+
* signs whatever `{htm, htu, ath}` a local caller asks of the daemon's proof
|
|
15
|
+
* oracle (`/v1/auth/cli-handoff-dpop-proof`). Handing that oracle the device
|
|
16
|
+
* key would turn it into a signer for the credential that mints families —
|
|
17
|
+
* one confused-deputy step from the workload asking for a device proof over
|
|
18
|
+
* `POST /api/daemons/cli-handoff`. A dedicated key carries no authority but
|
|
19
|
+
* the tokens bound to it, lives only in daemon memory (`createDpopKey` never
|
|
20
|
+
* exposes the private half), and dies with the family it binds.
|
|
21
|
+
*
|
|
22
|
+
* Algorithm: EdDSA / Ed25519, the same primitive as the device key. RFC 9449
|
|
23
|
+
* lets the server advertise `dpop_signing_alg_values_supported`; ours is
|
|
24
|
+
* exactly `["EdDSA"]`, and the verifier refuses anything else.
|
|
25
|
+
*/
|
|
26
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
27
|
+
if (k2 === undefined) k2 = k;
|
|
28
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
29
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
30
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
31
|
+
}
|
|
32
|
+
Object.defineProperty(o, k2, desc);
|
|
33
|
+
}) : (function(o, m, k, k2) {
|
|
34
|
+
if (k2 === undefined) k2 = k;
|
|
35
|
+
o[k2] = m[k];
|
|
36
|
+
}));
|
|
37
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
38
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
39
|
+
}) : function(o, v) {
|
|
40
|
+
o["default"] = v;
|
|
41
|
+
});
|
|
42
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
43
|
+
var ownKeys = function(o) {
|
|
44
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
45
|
+
var ar = [];
|
|
46
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
47
|
+
return ar;
|
|
48
|
+
};
|
|
49
|
+
return ownKeys(o);
|
|
50
|
+
};
|
|
51
|
+
return function (mod) {
|
|
52
|
+
if (mod && mod.__esModule) return mod;
|
|
53
|
+
var result = {};
|
|
54
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
55
|
+
__setModuleDefault(result, mod);
|
|
56
|
+
return result;
|
|
57
|
+
};
|
|
58
|
+
})();
|
|
59
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
60
|
+
exports.isJwkThumbprint = exports.DPOP_PROOF_VERIFY_UNAVAILABLE = exports.DPOP_PROOF_INVALID = exports.DPOP_PROOF_REQUIRED = exports.DPOP_MAX_AGE_SECONDS = exports.DPOP_HEADER = exports.DPOP_PROOF_TYP = void 0;
|
|
61
|
+
exports.createDpopKey = createDpopKey;
|
|
62
|
+
exports.normalizeDpopHtu = normalizeDpopHtu;
|
|
63
|
+
exports.accessTokenHash = accessTokenHash;
|
|
64
|
+
exports.isAth = isAth;
|
|
65
|
+
exports.verifyDpopProof = verifyDpopProof;
|
|
66
|
+
const crypto = __importStar(require("node:crypto"));
|
|
67
|
+
const deviceKey_js_1 = require("./deviceKey.js");
|
|
68
|
+
/** RFC 9449 §4.2 — the proof's JOSE header `typ`. */
|
|
69
|
+
exports.DPOP_PROOF_TYP = 'dpop+jwt';
|
|
70
|
+
/** RFC 9449 §4.1 — the request header carrying the proof. */
|
|
71
|
+
exports.DPOP_HEADER = 'DPoP';
|
|
72
|
+
/** Default freshness window, matching the device-proof verifier. */
|
|
73
|
+
exports.DPOP_MAX_AGE_SECONDS = 60;
|
|
74
|
+
/** Server error codes for a bound token presented without a usable proof. */
|
|
75
|
+
exports.DPOP_PROOF_REQUIRED = 'DPOP_PROOF_REQUIRED';
|
|
76
|
+
exports.DPOP_PROOF_INVALID = 'DPOP_PROOF_INVALID';
|
|
77
|
+
exports.DPOP_PROOF_VERIFY_UNAVAILABLE = 'DPOP_PROOF_VERIFY_UNAVAILABLE';
|
|
78
|
+
function createDpopKey() {
|
|
79
|
+
const { privateKey, publicKey } = crypto.generateKeyPairSync('ed25519');
|
|
80
|
+
const exported = publicKey.export({ format: 'jwk' });
|
|
81
|
+
if (exported.kty !== 'OKP' || exported.crv !== 'Ed25519' || typeof exported.x !== 'string') {
|
|
82
|
+
throw new Error('createDpopKey: unexpected public JWK shape');
|
|
83
|
+
}
|
|
84
|
+
const publicJwk = Object.freeze({ kty: 'OKP', crv: 'Ed25519', x: exported.x });
|
|
85
|
+
const thumbprint = (0, deviceKey_js_1.jwkThumbprint)(publicJwk);
|
|
86
|
+
return Object.freeze({
|
|
87
|
+
publicJwk,
|
|
88
|
+
thumbprint,
|
|
89
|
+
sign(req) {
|
|
90
|
+
return buildDpopProofWithKey(privateKey, publicJwk, req);
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
function b64u(buf) {
|
|
95
|
+
return buf.toString('base64').replace(/=+$/, '').replace(/\+/g, '-').replace(/\//g, '_');
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Sign one proof. `ath` comes from the token when the signer holds it, or is
|
|
99
|
+
* taken as given — the daemon's proof oracle is handed the hash and never the
|
|
100
|
+
* token, which is the point of the oracle.
|
|
101
|
+
*/
|
|
102
|
+
function buildDpopProofWithKey(privateKey, publicJwk, req) {
|
|
103
|
+
if (typeof req.htm !== 'string' || !/^[A-Za-z]{3,10}$/.test(req.htm)) {
|
|
104
|
+
throw new Error('dpop: htm must be an HTTP method');
|
|
105
|
+
}
|
|
106
|
+
if (typeof req.htu !== 'string' || req.htu.length === 0) {
|
|
107
|
+
throw new Error('dpop: htu must be a URL');
|
|
108
|
+
}
|
|
109
|
+
const ath = req.accessToken !== undefined ? (0, deviceKey_js_1.tokenHash)(req.accessToken) : req.ath;
|
|
110
|
+
if (ath !== undefined && !isAth(ath)) {
|
|
111
|
+
throw new Error('dpop: ath must be a base64url SHA-256 digest');
|
|
112
|
+
}
|
|
113
|
+
const header = { alg: 'EdDSA', typ: exports.DPOP_PROOF_TYP, jwk: publicJwk };
|
|
114
|
+
const claims = {
|
|
115
|
+
htm: req.htm.toUpperCase(),
|
|
116
|
+
htu: normalizeDpopHtu(req.htu),
|
|
117
|
+
iat: req.iat ?? Math.floor(Date.now() / 1000),
|
|
118
|
+
jti: req.jti ?? b64u(crypto.randomBytes(16)),
|
|
119
|
+
...(ath !== undefined ? { ath } : {}),
|
|
120
|
+
};
|
|
121
|
+
const signingInput = `${b64u(Buffer.from(JSON.stringify(header), 'utf-8'))}.${b64u(Buffer.from(JSON.stringify(claims), 'utf-8'))}`;
|
|
122
|
+
const signature = crypto.sign(null, Buffer.from(signingInput, 'utf-8'), privateKey);
|
|
123
|
+
return `${signingInput}.${b64u(signature)}`;
|
|
124
|
+
}
|
|
125
|
+
/** RFC 9449 §4.2 htu normalization — identical to the device proof's. */
|
|
126
|
+
function normalizeDpopHtu(url) {
|
|
127
|
+
return (0, deviceKey_js_1.normalizeHtu)(url);
|
|
128
|
+
}
|
|
129
|
+
/** `ath` for an access token: base64url(SHA-256(ascii(token))), RFC 9449 §4.2. */
|
|
130
|
+
function accessTokenHash(accessToken) {
|
|
131
|
+
return (0, deviceKey_js_1.tokenHash)(accessToken);
|
|
132
|
+
}
|
|
133
|
+
/** True for a value shaped like an `ath` / `jkt` (43-char base64url SHA-256). */
|
|
134
|
+
function isAth(value) {
|
|
135
|
+
return typeof value === 'string' && /^[A-Za-z0-9_-]{43}$/.test(value);
|
|
136
|
+
}
|
|
137
|
+
/** Same shape test, named for the `cnf.jkt` call sites. */
|
|
138
|
+
exports.isJwkThumbprint = isAth;
|
|
139
|
+
/**
|
|
140
|
+
* Verify an RFC 9449 proof: `typ` dpop+jwt, `alg` EdDSA, a well-formed inline
|
|
141
|
+
* JWK, the signature, iat window, htm, htu, `ath` (when expected) and the
|
|
142
|
+
* key's thumbprint against `expectedJkt` (when given).
|
|
143
|
+
*
|
|
144
|
+
* Replay (`jti`) is NOT checked here — it needs shared state, and the caller
|
|
145
|
+
* owns that (the API's Redis jti cache). A verifier that claimed to do it in
|
|
146
|
+
* process memory would be wrong on every multi-replica deployment.
|
|
147
|
+
*/
|
|
148
|
+
function verifyDpopProof(jws, opts) {
|
|
149
|
+
const result = (0, deviceKey_js_1.verifyEd25519Proof)(jws, {
|
|
150
|
+
expectedHtm: opts.expectedHtm,
|
|
151
|
+
expectedHtu: opts.expectedHtu,
|
|
152
|
+
...(opts.maxAgeSeconds !== undefined ? { maxAgeSeconds: opts.maxAgeSeconds } : {}),
|
|
153
|
+
...(opts.clockSkewSeconds !== undefined ? { clockSkewSeconds: opts.clockSkewSeconds } : {}),
|
|
154
|
+
...(opts.now !== undefined ? { now: opts.now } : {}),
|
|
155
|
+
}, exports.DPOP_PROOF_TYP);
|
|
156
|
+
if (!result.valid) {
|
|
157
|
+
// rth/pin are device-proof-only checks the options above never request.
|
|
158
|
+
return { valid: false, reason: result.reason };
|
|
159
|
+
}
|
|
160
|
+
// An empty jti cannot be claimed in a replay cache, so it is not a nonce.
|
|
161
|
+
if (result.claims.jti.length === 0)
|
|
162
|
+
return { valid: false, reason: 'malformed' };
|
|
163
|
+
if (opts.expectedAth !== undefined) {
|
|
164
|
+
if (typeof result.claims.ath !== 'string')
|
|
165
|
+
return { valid: false, reason: 'ath_missing' };
|
|
166
|
+
if (result.claims.ath !== opts.expectedAth)
|
|
167
|
+
return { valid: false, reason: 'ath_mismatch' };
|
|
168
|
+
}
|
|
169
|
+
if (opts.expectedJkt !== undefined && result.thumbprint !== opts.expectedJkt) {
|
|
170
|
+
return { valid: false, reason: 'jkt_mismatch' };
|
|
171
|
+
}
|
|
172
|
+
const { htm, htu, iat, jti, ath } = result.claims;
|
|
173
|
+
return {
|
|
174
|
+
valid: true,
|
|
175
|
+
claims: { htm, htu, iat, jti, ...(typeof ath === 'string' ? { ath } : {}) },
|
|
176
|
+
thumbprint: result.thumbprint,
|
|
177
|
+
publicJwk: result.publicJwk,
|
|
178
|
+
};
|
|
179
|
+
}
|
|
@@ -27,6 +27,7 @@ export { formatRecoveryCode, parseRecoveryCode, normalizeRecoveryCode, isValidRe
|
|
|
27
27
|
export { buildCredentialAad, decrypt as credEnvelopeDecrypt, deserialize as credEnvelopeDeserialize, encrypt as credEnvelopeEncrypt, ENVELOPE_MAGIC as CRED_ENVELOPE_MAGIC, generateDek, isEnvelopeString as isCredEnvelopeString, KEY_LEN as CRED_KEY_LEN, openBuffer as credOpenBuffer, sealBuffer as credSealBuffer, serialize as credEnvelopeSerialize, unwrap as credEnvelopeUnwrap, wrap as credEnvelopeWrap, type SealedBlob as CredSealedBlob, } from './credentialEnvelope.js';
|
|
28
28
|
export { __setKekStrategyForTest, getKekStrategy, InMemoryKek, UnavailableKek, KekUnavailableError, LinuxKek, MacosKek, registerZeroizeHook, WindowsKek, zeroizeKekCaches, type KekStrategy, } from './kek/index.js';
|
|
29
29
|
export { buildProof as buildDeviceProof, generateDeviceKeyPair, jwkThumbprint, normalizeHtu, publicJwkFromX, tokenHash, verifyProof as verifyDeviceProof, type DeviceKeyPair, type DeviceProofClaims, type DeviceProofHeader, type Ed25519PrivateJwk, type Ed25519PublicJwk, type VerifyFailureReason as DeviceProofVerifyFailureReason, type VerifyFailure as DeviceProofVerifyFailure, type VerifyOptions as DeviceProofVerifyOptions, type VerifySuccess as DeviceProofVerifySuccess, } from './deviceKey.js';
|
|
30
|
+
export { DPOP_HEADER, DPOP_MAX_AGE_SECONDS, DPOP_PROOF_INVALID, DPOP_PROOF_REQUIRED, DPOP_PROOF_TYP, DPOP_PROOF_VERIFY_UNAVAILABLE, accessTokenHash, createDpopKey, isAth, isJwkThumbprint, normalizeDpopHtu, verifyDpopProof, type DpopKey, type DpopProofClaims, type DpopProofRequest, type DpopVerifyFailureReason, type DpopVerifyOptions, type DpopVerifyResult, } from './dpop.js';
|
|
30
31
|
export { initCredEnvelope, resetCredEnvelope, shutdownCredEnvelope, isCredEnvelopeActive, hasCredEnvelopePathConflict, describeCredEnvelopeState, splitKeyConvergencePolicy, maybeEncryptForWrite, maybeDecryptOnRead, DEFAULT_UNREADABLE_CREDENTIAL_HINT, type DecryptOnReadOptions, __wrappedDekFilePathForTest, __setStateForTest as __setCredEnvelopeStateForTest, __getStateForTest as __getCredEnvelopeStateForTest, __resetShutdownGuardForTest, type ReadTransformResult, } from './credentialEnvelopeBridge.js';
|
|
31
32
|
export { initDeviceIdentity, resetDeviceIdentity, signServerRequest, enrollWithServer, clearEnrollmentMarker, isDeviceIdentityActive, getDevicePublicKey, describeDeviceIdentityState, describeDeviceKeySplit, __publicKeyPathForTest, __privateKeyPathForTest, __enrollmentMarkerPathForTest, __enrollmentBackoffPathForTest, __setStateForTest as __setDeviceIdentityStateForTest, __getStateForTest as __getDeviceIdentityStateForTest, type EnrollmentResult, } from './deviceIdentityBridge.js';
|
|
32
33
|
export { getMachineUuid, hostedRuntimeIdOverride, HOSTED_RUNTIME_MACHINE_SOURCES, __resetMachineUuidCacheForTest, } from './machineUuid.js';
|
|
@@ -24,9 +24,9 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
24
24
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
25
|
exports.generateVerifier = exports.invalidateHeadersHelperCache = exports.runHeadersHelper = exports.SENTINEL_TRIGGERING_FAILURES = exports.__resetAuthHelperFailureReasonForTest = exports.getLastAuthHelperFailureReason = exports.getDefaultHelperTtlMs = exports.invokeAuthHelper = exports.AuthHelperUnavailableError = exports.AuthHelperUntrustedError = exports.CI_TOKEN_PREFIX = exports.classifyTokenKind = exports.invalidateAuthHelperCache = exports.__resetCredentialResolverForTest = exports.CREDENTIAL_PRECEDENCE = exports.CredentialResolver = exports.__setFdReaderForTest = exports.readAuthFromFd = exports.createRefreshScheduler = exports.UNKNOWN_401_BUDGET = exports.__resetUnknown401StreaksForTest = exports.__refreshDiagnostics = exports.installLockExitHandlers = exports.clearReauthState = exports.needsReauthState = exports.persistReauthState = exports.withAuthLock = exports.revokeDaemonRefreshSession = exports.refreshDaemonToken = exports.classifyRefreshResponse = exports.EXIT_NEEDS_REAUTH = exports.reauthCommand = exports.formatReauthMessage = exports.handleMidStreamAuthExpiry = exports.isAuthExpiredSseEvent = exports.decodeJwtPayload = exports.decodeJwtExpiry = exports.TransientAuthFailure = exports.PermanentAuthFailure = exports.emitAuthTelemetry = exports.getAuthBinaryName = exports.getAuthKillSwitches = exports.getAuthConfigDir = exports.getAuthLogger = exports.configureAuthCore = exports.OAUTH_CLI_SCOPE = exports.isDaemonScope = exports.VALID_DAEMON_SCOPE_SET = exports.ALL_DAEMON_SCOPES = exports.SCOPES = void 0;
|
|
26
26
|
exports.publicJwkFromX = exports.normalizeHtu = exports.jwkThumbprint = exports.generateDeviceKeyPair = exports.buildDeviceProof = exports.zeroizeKekCaches = exports.WindowsKek = exports.registerZeroizeHook = exports.MacosKek = exports.LinuxKek = exports.KekUnavailableError = exports.UnavailableKek = exports.InMemoryKek = exports.getKekStrategy = exports.__setKekStrategyForTest = exports.credEnvelopeWrap = exports.credEnvelopeUnwrap = exports.credEnvelopeSerialize = exports.credSealBuffer = exports.credOpenBuffer = exports.CRED_KEY_LEN = exports.isCredEnvelopeString = exports.generateDek = exports.CRED_ENVELOPE_MAGIC = exports.credEnvelopeEncrypt = exports.credEnvelopeDeserialize = exports.credEnvelopeDecrypt = exports.buildCredentialAad = exports.isValidRecoveryCode = exports.normalizeRecoveryCode = exports.parseRecoveryCode = exports.formatRecoveryCode = exports.MAX_TTL_DAYS = exports.MIN_TTL_DAYS = exports.parseTtlDays = exports.LoginExchangeError = exports.LoginInitError = exports.loginWithLocalhost = exports.LocalCallbackMalformedError = exports.LocalCallbackDeniedError = exports.LocalCallbackStateMismatchError = exports.LocalCallbackTimeoutError = exports.LocalCallbackBindError = exports.LocalCallbackError = exports.DEFAULT_LOGIN_TIMEOUT_MS = exports.startLocalCallback = exports.verifyChallenge = exports.safeEqual = exports.generateState = exports.challenge = void 0;
|
|
27
|
-
exports.
|
|
28
|
-
exports.
|
|
29
|
-
exports.consolidateProfileState = exports.profileStateFilePathsToClear = exports.profileStateFileLocations = exports.resolveProfileStateFile = exports.resolveProfileStateDir = exports.legacyProfileStateDir = exports.profileStateDir = exports.SUPERSEDED_COPY_SUFFIX = exports.SUPERSEDED_MARKER_SUFFIX = exports.PROFILES_SEGMENT = exports.isConfigRootOverridden = exports.configRootPath = exports.resolveConfigRoot = exports.verifyManifest = exports.verifyArtifact = exports.isTrustedArtifactOrigin = exports.getDeltaFor = exports.buildManifestMessage = exports.buildArtifactMessage = exports.TRUSTED_FEED_ORIGINS = exports.MAX_SIGS = exports.OVERSKY_RELEASE_PUBLIC_KEYS = exports.resolveFirstPartyHarnessChannel = void 0;
|
|
27
|
+
exports.__resetCachedDaemonIdForTest = exports.legacyDaemonUuids = exports.ensureMachineDaemonId = exports.__resetMachineUuidCacheForTest = exports.HOSTED_RUNTIME_MACHINE_SOURCES = exports.hostedRuntimeIdOverride = exports.getMachineUuid = exports.__getDeviceIdentityStateForTest = exports.__setDeviceIdentityStateForTest = exports.__enrollmentBackoffPathForTest = exports.__enrollmentMarkerPathForTest = exports.__privateKeyPathForTest = exports.__publicKeyPathForTest = exports.describeDeviceKeySplit = exports.describeDeviceIdentityState = exports.getDevicePublicKey = exports.isDeviceIdentityActive = exports.clearEnrollmentMarker = exports.enrollWithServer = exports.signServerRequest = exports.resetDeviceIdentity = exports.initDeviceIdentity = exports.__resetShutdownGuardForTest = exports.__getCredEnvelopeStateForTest = exports.__setCredEnvelopeStateForTest = exports.__wrappedDekFilePathForTest = exports.DEFAULT_UNREADABLE_CREDENTIAL_HINT = exports.maybeDecryptOnRead = exports.maybeEncryptForWrite = exports.splitKeyConvergencePolicy = exports.describeCredEnvelopeState = exports.hasCredEnvelopePathConflict = exports.isCredEnvelopeActive = exports.shutdownCredEnvelope = exports.resetCredEnvelope = exports.initCredEnvelope = exports.verifyDpopProof = exports.normalizeDpopHtu = exports.isJwkThumbprint = exports.isAth = exports.createDpopKey = exports.accessTokenHash = exports.DPOP_PROOF_VERIFY_UNAVAILABLE = exports.DPOP_PROOF_TYP = exports.DPOP_PROOF_REQUIRED = exports.DPOP_PROOF_INVALID = exports.DPOP_MAX_AGE_SECONDS = exports.DPOP_HEADER = exports.verifyDeviceProof = exports.tokenHash = void 0;
|
|
28
|
+
exports.FIRST_PARTY_HARNESS_PROVENANCE_SUFFIX = exports.FIRST_PARTY_HARNESS_PREVIOUS_SUFFIX = exports.describeLegacyState = exports.findLegacyLocalState = exports.DEFAULT_BINARY_NAME = exports.LEGACY_NATIVE_ID_PREFIXES = exports.NATIVE_ID_PREFIX = exports.LEGACY_CONFIG_DIR_NAMES = exports.CONFIG_DIR_NAME = exports.LOOPBACK_DEFAULT_MAX_BODY_BYTES = exports.LoopbackTargetRefusedError = exports.isLoopbackAddress = exports.isLoopbackHostname = exports.assertLoopbackHttpUrl = exports.loopbackFetch = exports.withLoopbackNoProxy = exports.envHasProxy = exports.LOOPBACK_NO_PROXY_HOSTS = exports.SENSITIVE_ENV_PREFIX_EXCEPTIONS = exports.SENSITIVE_ENV_NAME_TOKENS = exports.SENSITIVE_ENV_SUFFIXES = exports.SENSITIVE_ENV_PREFIXES = exports.SENSITIVE_ENV_VARS = exports.sanitizeSpawnEnv = exports.harnessLoadsClaudeSkillsLayout = exports.claudeSkillsLayoutEvidence = exports.HARNESSES_LOADING_CLAUDE_SKILLS_LAYOUT = exports.tierPermitsConfiguredAutoMode = exports.credentialPolicyForTier = exports.getHarnessTier = exports.HARNESS_TIERS = exports.pairUint8ArrayToBase64Url = exports.pairUint8ArrayToBase64 = exports.parsePairUrl = exports.parsePairPlaintext = exports.parsePairBundle = exports.openPairBundleBase64 = exports.generatePairKeyPair = exports.buildPairUrl = exports.buildPairAad = exports.pairBase64UrlToUint8Array = exports.pairBase64ToUint8Array = exports.assemblePairBundleBase64 = exports.PairBundleError = exports.PAIR_SECRET_LEN = exports.PAIR_PUBKEY_LEN = exports.PAIR_NONCE_LEN = exports.PAIR_BUNDLE_MAX_BASE64_LEN = exports.PAIR_AAD_MAX_LEN = exports.PAIR_AAD_PREFIX = void 0;
|
|
29
|
+
exports.consolidateProfileState = exports.profileStateFilePathsToClear = exports.profileStateFileLocations = exports.resolveProfileStateFile = exports.resolveProfileStateDir = exports.legacyProfileStateDir = exports.profileStateDir = exports.SUPERSEDED_COPY_SUFFIX = exports.SUPERSEDED_MARKER_SUFFIX = exports.PROFILES_SEGMENT = exports.isConfigRootOverridden = exports.configRootPath = exports.resolveConfigRoot = exports.verifyManifest = exports.verifyArtifact = exports.isTrustedArtifactOrigin = exports.getDeltaFor = exports.buildManifestMessage = exports.buildArtifactMessage = exports.TRUSTED_FEED_ORIGINS = exports.MAX_SIGS = exports.OVERSKY_RELEASE_PUBLIC_KEYS = exports.resolveFirstPartyHarnessChannel = exports.resolveChannelManifestUrl = exports.isFirstPartyHarnessChannel = exports.firstPartyHarnessFeedCandidates = exports.firstPartyHarnessFeedBase = exports.firstPartyHarnessChannelPrefix = exports.FIRST_PARTY_HARNESS_CHANNELS = exports.DEFAULT_FIRST_PARTY_HARNESS_CHANNEL = exports.migrateFirstPartyHarnessHome = exports.linkFirstPartyHarnessAliases = exports.firstPartyHarnessInstalledBinaryPath = exports.firstPartyHarnessHomeDir = exports.firstPartyHarnessBinDir = void 0;
|
|
30
30
|
// Daemon scope vocabulary — single source of truth for non-Mongoose tiers
|
|
31
31
|
// (daemon, desktop, cli). Schema-bound canonical lives in
|
|
32
32
|
// `@skrr-ai/data-schemas/common/daemonScopes`; the two are kept byte-equal
|
|
@@ -222,6 +222,22 @@ Object.defineProperty(exports, "normalizeHtu", { enumerable: true, get: function
|
|
|
222
222
|
Object.defineProperty(exports, "publicJwkFromX", { enumerable: true, get: function () { return deviceKey_js_1.publicJwkFromX; } });
|
|
223
223
|
Object.defineProperty(exports, "tokenHash", { enumerable: true, get: function () { return deviceKey_js_1.tokenHash; } });
|
|
224
224
|
Object.defineProperty(exports, "verifyDeviceProof", { enumerable: true, get: function () { return deviceKey_js_1.verifyProof; } });
|
|
225
|
+
// OSK-12020 — RFC 9449 access-token binding (`cnf.jkt` + `DPoP` proof). A
|
|
226
|
+
// different protocol from the device proof above (`typ: dpop+jwt`, header
|
|
227
|
+
// `DPoP`), signed by a dedicated in-memory key the daemon holds.
|
|
228
|
+
var dpop_js_1 = require("./dpop.js");
|
|
229
|
+
Object.defineProperty(exports, "DPOP_HEADER", { enumerable: true, get: function () { return dpop_js_1.DPOP_HEADER; } });
|
|
230
|
+
Object.defineProperty(exports, "DPOP_MAX_AGE_SECONDS", { enumerable: true, get: function () { return dpop_js_1.DPOP_MAX_AGE_SECONDS; } });
|
|
231
|
+
Object.defineProperty(exports, "DPOP_PROOF_INVALID", { enumerable: true, get: function () { return dpop_js_1.DPOP_PROOF_INVALID; } });
|
|
232
|
+
Object.defineProperty(exports, "DPOP_PROOF_REQUIRED", { enumerable: true, get: function () { return dpop_js_1.DPOP_PROOF_REQUIRED; } });
|
|
233
|
+
Object.defineProperty(exports, "DPOP_PROOF_TYP", { enumerable: true, get: function () { return dpop_js_1.DPOP_PROOF_TYP; } });
|
|
234
|
+
Object.defineProperty(exports, "DPOP_PROOF_VERIFY_UNAVAILABLE", { enumerable: true, get: function () { return dpop_js_1.DPOP_PROOF_VERIFY_UNAVAILABLE; } });
|
|
235
|
+
Object.defineProperty(exports, "accessTokenHash", { enumerable: true, get: function () { return dpop_js_1.accessTokenHash; } });
|
|
236
|
+
Object.defineProperty(exports, "createDpopKey", { enumerable: true, get: function () { return dpop_js_1.createDpopKey; } });
|
|
237
|
+
Object.defineProperty(exports, "isAth", { enumerable: true, get: function () { return dpop_js_1.isAth; } });
|
|
238
|
+
Object.defineProperty(exports, "isJwkThumbprint", { enumerable: true, get: function () { return dpop_js_1.isJwkThumbprint; } });
|
|
239
|
+
Object.defineProperty(exports, "normalizeDpopHtu", { enumerable: true, get: function () { return dpop_js_1.normalizeDpopHtu; } });
|
|
240
|
+
Object.defineProperty(exports, "verifyDpopProof", { enumerable: true, get: function () { return dpop_js_1.verifyDpopProof; } });
|
|
225
241
|
// L11/L12 — composed runtime: init, transforms, sign, enroll, locks.
|
|
226
242
|
// Daemon and CLI consume these; the underlying primitives live above.
|
|
227
243
|
var credentialEnvelopeBridge_js_1 = require("./credentialEnvelopeBridge.js");
|