@broberg/sso 0.18.3 → 0.19.0
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/README.md +25 -0
- package/dist/{chunk-CHRCHUZ3.cjs → chunk-MZE4TPFU.cjs} +254 -134
- package/dist/chunk-MZE4TPFU.cjs.map +1 -0
- package/dist/{chunk-XKKKS2RO.js → chunk-PQTT74BA.js} +252 -135
- package/dist/chunk-PQTT74BA.js.map +1 -0
- package/dist/hono.cjs +37 -37
- package/dist/hono.d.cts +1 -1
- package/dist/hono.d.ts +1 -1
- package/dist/hono.js +1 -1
- package/dist/index.cjs +77 -65
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -2
- package/dist/{session-eya6Bon5.d.cts → session-qvhMg3ID.d.cts} +106 -1
- package/dist/{session-eya6Bon5.d.ts → session-qvhMg3ID.d.ts} +106 -1
- package/package.json +1 -1
- package/dist/chunk-CHRCHUZ3.cjs.map +0 -1
- package/dist/chunk-XKKKS2RO.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunkMZE4TPFU_cjs = require('./chunk-MZE4TPFU.cjs');
|
|
4
4
|
var fs = require('fs');
|
|
5
5
|
var http = require('http');
|
|
6
6
|
var jose = require('jose');
|
|
@@ -48,37 +48,37 @@ async function verifyDpopProof(proof, o) {
|
|
|
48
48
|
try {
|
|
49
49
|
header = jose.decodeProtectedHeader(proof);
|
|
50
50
|
} catch {
|
|
51
|
-
throw new
|
|
51
|
+
throw new chunkMZE4TPFU_cjs.SsoError("DPoP proof is not a JWS");
|
|
52
52
|
}
|
|
53
|
-
if (header.typ !== "dpop+jwt") throw new
|
|
54
|
-
if (!header.alg || !ASYMMETRIC.has(header.alg)) throw new
|
|
53
|
+
if (header.typ !== "dpop+jwt") throw new chunkMZE4TPFU_cjs.SsoError(`DPoP proof has typ ${JSON.stringify(header.typ)}, not "dpop+jwt"`);
|
|
54
|
+
if (!header.alg || !ASYMMETRIC.has(header.alg)) throw new chunkMZE4TPFU_cjs.SsoError(`DPoP proof alg ${JSON.stringify(header.alg)} is not an accepted asymmetric algorithm`);
|
|
55
55
|
const jwk = header.jwk;
|
|
56
|
-
if (!jwk || typeof jwk !== "object") throw new
|
|
57
|
-
if ("d" in jwk) throw new
|
|
56
|
+
if (!jwk || typeof jwk !== "object") throw new chunkMZE4TPFU_cjs.SsoError("DPoP proof carries no jwk");
|
|
57
|
+
if ("d" in jwk) throw new chunkMZE4TPFU_cjs.SsoError("DPoP proof jwk contains a private key");
|
|
58
58
|
let payload;
|
|
59
59
|
try {
|
|
60
60
|
const key = await jose.importJWK(jwk, header.alg);
|
|
61
61
|
({ payload } = await jose.jwtVerify(proof, key, { typ: "dpop+jwt", algorithms: [header.alg] }));
|
|
62
62
|
} catch (e) {
|
|
63
|
-
throw new
|
|
63
|
+
throw new chunkMZE4TPFU_cjs.SsoError(`DPoP proof signature rejected: ${e instanceof Error ? e.message : String(e)}`);
|
|
64
64
|
}
|
|
65
|
-
if (await jose.calculateJwkThumbprint(jwk) !== o.jkt) throw new
|
|
66
|
-
if (payload.htm !== o.method.toUpperCase()) throw new
|
|
65
|
+
if (await jose.calculateJwkThumbprint(jwk) !== o.jkt) throw new chunkMZE4TPFU_cjs.SsoError("DPoP proof key does not match the ticket's cnf.jkt");
|
|
66
|
+
if (payload.htm !== o.method.toUpperCase()) throw new chunkMZE4TPFU_cjs.SsoError(`DPoP proof is for ${JSON.stringify(payload.htm)}, the request is ${o.method.toUpperCase()}`);
|
|
67
67
|
let htu;
|
|
68
68
|
try {
|
|
69
69
|
htu = normalizeHtu(String(payload.htu));
|
|
70
70
|
} catch {
|
|
71
|
-
throw new
|
|
71
|
+
throw new chunkMZE4TPFU_cjs.SsoError("DPoP proof htu is not a URL");
|
|
72
72
|
}
|
|
73
|
-
if (htu !== normalizeHtu(o.url)) throw new
|
|
73
|
+
if (htu !== normalizeHtu(o.url)) throw new chunkMZE4TPFU_cjs.SsoError(`DPoP proof is for ${htu}, the request is ${normalizeHtu(o.url)}`);
|
|
74
74
|
const nowSec = Math.floor(o.now() / 1e3);
|
|
75
75
|
if (typeof payload.iat !== "number" || Math.abs(nowSec - payload.iat) > o.iatWindowSec) {
|
|
76
|
-
throw new
|
|
76
|
+
throw new chunkMZE4TPFU_cjs.SsoError(`DPoP proof iat is outside \xB1${o.iatWindowSec}s`);
|
|
77
77
|
}
|
|
78
|
-
if (payload.ath !== await accessTokenHash(o.ticket)) throw new
|
|
79
|
-
if (typeof payload.jti !== "string" || payload.jti === "") throw new
|
|
78
|
+
if (payload.ath !== await accessTokenHash(o.ticket)) throw new chunkMZE4TPFU_cjs.SsoError("DPoP proof ath does not match this ticket");
|
|
79
|
+
if (typeof payload.jti !== "string" || payload.jti === "") throw new chunkMZE4TPFU_cjs.SsoError("DPoP proof has no jti");
|
|
80
80
|
if (await o.replay.seen(`${o.jkt}:${payload.jti}`, o.iatWindowSec * 2 * 1e3)) {
|
|
81
|
-
throw new
|
|
81
|
+
throw new chunkMZE4TPFU_cjs.SsoError("DPoP proof was already used (jti replay)");
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
84
|
|
|
@@ -241,10 +241,10 @@ function createTicketVerifier(options) {
|
|
|
241
241
|
now = () => Date.now()
|
|
242
242
|
} = options;
|
|
243
243
|
const replayStore = options.replayStore ?? createMemoryReplayStore(now);
|
|
244
|
-
if (!issuer) throw new
|
|
245
|
-
if (!audience) throw new
|
|
244
|
+
if (!issuer) throw new chunkMZE4TPFU_cjs.SsoError("createTicketVerifier needs an issuer");
|
|
245
|
+
if (!audience) throw new chunkMZE4TPFU_cjs.SsoError("createTicketVerifier needs this receiver's audience");
|
|
246
246
|
let cachePromise = null;
|
|
247
|
-
const cacheFor = (jwksUri) =>
|
|
247
|
+
const cacheFor = (jwksUri) => chunkMZE4TPFU_cjs.createJwksCache({
|
|
248
248
|
jwksUri,
|
|
249
249
|
fetchImpl,
|
|
250
250
|
...options.warmUp ? { warmUp: true } : {},
|
|
@@ -260,12 +260,12 @@ function createTicketVerifier(options) {
|
|
|
260
260
|
if (!res.ok) throw new Error(`answered ${res.status}`);
|
|
261
261
|
doc = await res.json();
|
|
262
262
|
} catch (cause) {
|
|
263
|
-
throw new
|
|
263
|
+
throw new chunkMZE4TPFU_cjs.JwksUnavailableError(`${url}: ${cause instanceof Error ? cause.message : String(cause)}`);
|
|
264
264
|
}
|
|
265
265
|
if (doc.issuer !== issuer) {
|
|
266
|
-
throw new
|
|
266
|
+
throw new chunkMZE4TPFU_cjs.SsoError(`issuer is ${issuer} but ${url} says ${doc.issuer} \u2014 they must match exactly`);
|
|
267
267
|
}
|
|
268
|
-
if (!doc.jwks_uri) throw new
|
|
268
|
+
if (!doc.jwks_uri) throw new chunkMZE4TPFU_cjs.SsoError(`${url} names no jwks_uri`);
|
|
269
269
|
return cacheFor(doc.jwks_uri);
|
|
270
270
|
})().catch((err) => {
|
|
271
271
|
cachePromise = null;
|
|
@@ -281,51 +281,51 @@ function createTicketVerifier(options) {
|
|
|
281
281
|
try {
|
|
282
282
|
header = jose.decodeProtectedHeader(token);
|
|
283
283
|
} catch {
|
|
284
|
-
throw new
|
|
284
|
+
throw new chunkMZE4TPFU_cjs.SsoError("ticket is not a JWS");
|
|
285
285
|
}
|
|
286
|
-
if (!header.kid) throw new
|
|
287
|
-
if (header.typ === "logout+jwt") throw new
|
|
286
|
+
if (!header.kid) throw new chunkMZE4TPFU_cjs.SsoError("ticket has no kid \u2014 cannot pick a signing key");
|
|
287
|
+
if (header.typ === "logout+jwt") throw new chunkMZE4TPFU_cjs.SsoError("a logout token is not a ticket");
|
|
288
288
|
const cache = await keys();
|
|
289
289
|
let payload;
|
|
290
290
|
try {
|
|
291
291
|
({ payload } = await jose.jwtVerify(token, async () => cache.getKey(header.kid, header.alg ?? "EdDSA"), {
|
|
292
292
|
issuer,
|
|
293
293
|
audience,
|
|
294
|
-
algorithms:
|
|
294
|
+
algorithms: chunkMZE4TPFU_cjs.ALLOWED_ALGS,
|
|
295
295
|
clockTolerance: clockToleranceSec,
|
|
296
296
|
requiredClaims: ["exp", "iat", "jti", "sub"]
|
|
297
297
|
}));
|
|
298
298
|
} catch (e) {
|
|
299
|
-
if (e instanceof
|
|
300
|
-
throw new
|
|
299
|
+
if (e instanceof chunkMZE4TPFU_cjs.JwksUnavailableError) throw e;
|
|
300
|
+
throw new chunkMZE4TPFU_cjs.SsoError(`ticket rejected: ${e instanceof Error ? e.message : String(e)}`);
|
|
301
301
|
}
|
|
302
|
-
if ("events" in payload) throw new
|
|
303
|
-
if ("nonce" in payload) throw new
|
|
302
|
+
if ("events" in payload) throw new chunkMZE4TPFU_cjs.SsoError("a logout token is not a ticket");
|
|
303
|
+
if ("nonce" in payload) throw new chunkMZE4TPFU_cjs.SsoError("an ID token is not a ticket");
|
|
304
304
|
const type = payload.principal_type;
|
|
305
305
|
if (typeof type !== "string" || !PRINCIPAL_TYPES.includes(type)) {
|
|
306
|
-
throw new
|
|
306
|
+
throw new chunkMZE4TPFU_cjs.SsoError(`ticket has no valid principal_type (got ${JSON.stringify(type)})`);
|
|
307
307
|
}
|
|
308
308
|
const lifetime = payload.exp - payload.iat;
|
|
309
309
|
if (lifetime > maxLifetimeSec) {
|
|
310
|
-
throw new
|
|
310
|
+
throw new chunkMZE4TPFU_cjs.SsoError(`ticket claims a ${lifetime}s lifetime; at most ${maxLifetimeSec}s is accepted`);
|
|
311
311
|
}
|
|
312
|
-
if (typeof payload.sub !== "string" || payload.sub === "") throw new
|
|
312
|
+
if (typeof payload.sub !== "string" || payload.sub === "") throw new chunkMZE4TPFU_cjs.SsoError("ticket has no sub");
|
|
313
313
|
const scopes = typeof payload.scope === "string" ? payload.scope.split(" ").filter(Boolean) : [];
|
|
314
314
|
const required = opts.scope === void 0 ? [] : Array.isArray(opts.scope) ? opts.scope : [opts.scope];
|
|
315
315
|
const missing = required.filter((s) => !scopes.includes(s));
|
|
316
|
-
if (missing.length) throw new
|
|
316
|
+
if (missing.length) throw new chunkMZE4TPFU_cjs.SsoError(`ticket lacks scope ${missing.join(", ")}`);
|
|
317
317
|
const ver = payload.ver === void 0 ? 0 : payload.ver;
|
|
318
318
|
if (typeof ver !== "number" || !Number.isInteger(ver) || ver < 0) {
|
|
319
|
-
throw new
|
|
319
|
+
throw new chunkMZE4TPFU_cjs.SsoError(`ticket has an invalid ver (got ${JSON.stringify(payload.ver)})`);
|
|
320
320
|
}
|
|
321
321
|
if (ver > TICKET_FORMAT_VERSION) {
|
|
322
|
-
throw new
|
|
322
|
+
throw new chunkMZE4TPFU_cjs.SsoError(`ticket format version ${ver} is newer than this @broberg/sso understands (${TICKET_FORMAT_VERSION}) \u2014 upgrade @broberg/sso`);
|
|
323
323
|
}
|
|
324
324
|
const act = payload.act === void 0 ? null : readActor(payload.act, 1);
|
|
325
325
|
const cnfRaw = payload.cnf;
|
|
326
326
|
const cnf = cnfRaw && typeof cnfRaw.jkt === "string" && cnfRaw.jkt ? { jkt: cnfRaw.jkt } : null;
|
|
327
327
|
if (cnf) {
|
|
328
|
-
if (!opts.dpop?.proof) throw new
|
|
328
|
+
if (!opts.dpop?.proof) throw new chunkMZE4TPFU_cjs.SsoError("ticket is DPoP-bound (cnf.jkt) but no DPoP proof was presented");
|
|
329
329
|
await verifyDpopProof(opts.dpop.proof, {
|
|
330
330
|
method: opts.dpop.method,
|
|
331
331
|
url: opts.dpop.url,
|
|
@@ -336,11 +336,11 @@ function createTicketVerifier(options) {
|
|
|
336
336
|
now
|
|
337
337
|
});
|
|
338
338
|
} else if (requireDpop) {
|
|
339
|
-
throw new
|
|
339
|
+
throw new chunkMZE4TPFU_cjs.SsoError(cnfRaw !== void 0 ? "ticket has a cnf without a string jkt \u2014 DPoP is required here" : "this receiver requires DPoP; the ticket is not bound (no cnf.jkt)");
|
|
340
340
|
}
|
|
341
341
|
const named = (v) => typeof v === "string" && v ? v : null;
|
|
342
342
|
const clientId = type === "user" ? named(payload.client_id) ?? named(payload.azp) : named(payload.client_id) ?? payload.sub;
|
|
343
|
-
if (clientId === null) throw new
|
|
343
|
+
if (clientId === null) throw new chunkMZE4TPFU_cjs.SsoError("a user ticket names no app (no client_id or azp)");
|
|
344
344
|
return {
|
|
345
345
|
principal: payload.sub,
|
|
346
346
|
type,
|
|
@@ -357,113 +357,125 @@ function createTicketVerifier(options) {
|
|
|
357
357
|
};
|
|
358
358
|
}
|
|
359
359
|
function readActor(raw, depth) {
|
|
360
|
-
if (depth > MAX_ACT_DEPTH) throw new
|
|
360
|
+
if (depth > MAX_ACT_DEPTH) throw new chunkMZE4TPFU_cjs.SsoError(`ticket's delegation chain is deeper than ${MAX_ACT_DEPTH}`);
|
|
361
361
|
const link = raw;
|
|
362
362
|
if (!link || typeof link !== "object" || typeof link.sub !== "string" || link.sub === "") {
|
|
363
|
-
throw new
|
|
363
|
+
throw new chunkMZE4TPFU_cjs.SsoError(`ticket's act (link ${depth}) has no string sub`);
|
|
364
364
|
}
|
|
365
365
|
return link.act === void 0 ? { sub: link.sub } : { sub: link.sub, act: readActor(link.act, depth + 1) };
|
|
366
366
|
}
|
|
367
367
|
|
|
368
368
|
Object.defineProperty(exports, "AVATAR_TYPES", {
|
|
369
369
|
enumerable: true,
|
|
370
|
-
get: function () { return
|
|
370
|
+
get: function () { return chunkMZE4TPFU_cjs.AVATAR_TYPES; }
|
|
371
371
|
});
|
|
372
372
|
Object.defineProperty(exports, "DEFAULT_SESSION_MAX_AGE", {
|
|
373
373
|
enumerable: true,
|
|
374
|
-
get: function () { return
|
|
374
|
+
get: function () { return chunkMZE4TPFU_cjs.DEFAULT_SESSION_MAX_AGE; }
|
|
375
|
+
});
|
|
376
|
+
Object.defineProperty(exports, "EMBEDDED_FLOW_MAX_AGE", {
|
|
377
|
+
enumerable: true,
|
|
378
|
+
get: function () { return chunkMZE4TPFU_cjs.EMBEDDED_FLOW_MAX_AGE; }
|
|
375
379
|
});
|
|
376
380
|
Object.defineProperty(exports, "JwksError", {
|
|
377
381
|
enumerable: true,
|
|
378
|
-
get: function () { return
|
|
382
|
+
get: function () { return chunkMZE4TPFU_cjs.JwksError; }
|
|
379
383
|
});
|
|
380
384
|
Object.defineProperty(exports, "JwksUnavailableError", {
|
|
381
385
|
enumerable: true,
|
|
382
|
-
get: function () { return
|
|
386
|
+
get: function () { return chunkMZE4TPFU_cjs.JwksUnavailableError; }
|
|
383
387
|
});
|
|
384
388
|
Object.defineProperty(exports, "JwksUnknownKeyError", {
|
|
385
389
|
enumerable: true,
|
|
386
|
-
get: function () { return
|
|
390
|
+
get: function () { return chunkMZE4TPFU_cjs.JwksUnknownKeyError; }
|
|
387
391
|
});
|
|
388
392
|
Object.defineProperty(exports, "MAX_AVATAR_BYTES", {
|
|
389
393
|
enumerable: true,
|
|
390
|
-
get: function () { return
|
|
394
|
+
get: function () { return chunkMZE4TPFU_cjs.MAX_AVATAR_BYTES; }
|
|
391
395
|
});
|
|
392
396
|
Object.defineProperty(exports, "MAX_INVITATIONS", {
|
|
393
397
|
enumerable: true,
|
|
394
|
-
get: function () { return
|
|
398
|
+
get: function () { return chunkMZE4TPFU_cjs.MAX_INVITATIONS; }
|
|
395
399
|
});
|
|
396
400
|
Object.defineProperty(exports, "SsoAppApiError", {
|
|
397
401
|
enumerable: true,
|
|
398
|
-
get: function () { return
|
|
402
|
+
get: function () { return chunkMZE4TPFU_cjs.SsoAppApiError; }
|
|
399
403
|
});
|
|
400
404
|
Object.defineProperty(exports, "SsoAvatarRejectedError", {
|
|
401
405
|
enumerable: true,
|
|
402
|
-
get: function () { return
|
|
406
|
+
get: function () { return chunkMZE4TPFU_cjs.SsoAvatarRejectedError; }
|
|
403
407
|
});
|
|
404
408
|
Object.defineProperty(exports, "SsoConfigError", {
|
|
405
409
|
enumerable: true,
|
|
406
|
-
get: function () { return
|
|
410
|
+
get: function () { return chunkMZE4TPFU_cjs.SsoConfigError; }
|
|
407
411
|
});
|
|
408
412
|
Object.defineProperty(exports, "SsoError", {
|
|
409
413
|
enumerable: true,
|
|
410
|
-
get: function () { return
|
|
414
|
+
get: function () { return chunkMZE4TPFU_cjs.SsoError; }
|
|
411
415
|
});
|
|
412
416
|
Object.defineProperty(exports, "SsoInsufficientScopeError", {
|
|
413
417
|
enumerable: true,
|
|
414
|
-
get: function () { return
|
|
418
|
+
get: function () { return chunkMZE4TPFU_cjs.SsoInsufficientScopeError; }
|
|
415
419
|
});
|
|
416
420
|
Object.defineProperty(exports, "SsoIntrospectionUnavailableError", {
|
|
417
421
|
enumerable: true,
|
|
418
|
-
get: function () { return
|
|
422
|
+
get: function () { return chunkMZE4TPFU_cjs.SsoIntrospectionUnavailableError; }
|
|
419
423
|
});
|
|
420
424
|
Object.defineProperty(exports, "SsoReauthError", {
|
|
421
425
|
enumerable: true,
|
|
422
|
-
get: function () { return
|
|
426
|
+
get: function () { return chunkMZE4TPFU_cjs.SsoReauthError; }
|
|
423
427
|
});
|
|
424
428
|
Object.defineProperty(exports, "SsoTokenInactiveError", {
|
|
425
429
|
enumerable: true,
|
|
426
|
-
get: function () { return
|
|
430
|
+
get: function () { return chunkMZE4TPFU_cjs.SsoTokenInactiveError; }
|
|
427
431
|
});
|
|
428
432
|
Object.defineProperty(exports, "cookieHeader", {
|
|
429
433
|
enumerable: true,
|
|
430
|
-
get: function () { return
|
|
434
|
+
get: function () { return chunkMZE4TPFU_cjs.cookieHeader; }
|
|
431
435
|
});
|
|
432
436
|
Object.defineProperty(exports, "createJwksCache", {
|
|
433
437
|
enumerable: true,
|
|
434
|
-
get: function () { return
|
|
438
|
+
get: function () { return chunkMZE4TPFU_cjs.createJwksCache; }
|
|
435
439
|
});
|
|
436
440
|
Object.defineProperty(exports, "createSsoClient", {
|
|
437
441
|
enumerable: true,
|
|
438
|
-
get: function () { return
|
|
442
|
+
get: function () { return chunkMZE4TPFU_cjs.createSsoClient; }
|
|
439
443
|
});
|
|
440
444
|
Object.defineProperty(exports, "loadSsoConfig", {
|
|
441
445
|
enumerable: true,
|
|
442
|
-
get: function () { return
|
|
446
|
+
get: function () { return chunkMZE4TPFU_cjs.loadSsoConfig; }
|
|
443
447
|
});
|
|
444
448
|
Object.defineProperty(exports, "memoryTokenStore", {
|
|
445
449
|
enumerable: true,
|
|
446
|
-
get: function () { return
|
|
450
|
+
get: function () { return chunkMZE4TPFU_cjs.memoryTokenStore; }
|
|
451
|
+
});
|
|
452
|
+
Object.defineProperty(exports, "openEmbeddedFlow", {
|
|
453
|
+
enumerable: true,
|
|
454
|
+
get: function () { return chunkMZE4TPFU_cjs.openEmbeddedFlow; }
|
|
447
455
|
});
|
|
448
456
|
Object.defineProperty(exports, "readCookie", {
|
|
449
457
|
enumerable: true,
|
|
450
|
-
get: function () { return
|
|
458
|
+
get: function () { return chunkMZE4TPFU_cjs.readCookie; }
|
|
459
|
+
});
|
|
460
|
+
Object.defineProperty(exports, "sealEmbeddedFlow", {
|
|
461
|
+
enumerable: true,
|
|
462
|
+
get: function () { return chunkMZE4TPFU_cjs.sealEmbeddedFlow; }
|
|
451
463
|
});
|
|
452
464
|
Object.defineProperty(exports, "signSession", {
|
|
453
465
|
enumerable: true,
|
|
454
|
-
get: function () { return
|
|
466
|
+
get: function () { return chunkMZE4TPFU_cjs.signSession; }
|
|
455
467
|
});
|
|
456
468
|
Object.defineProperty(exports, "signValue", {
|
|
457
469
|
enumerable: true,
|
|
458
|
-
get: function () { return
|
|
470
|
+
get: function () { return chunkMZE4TPFU_cjs.signValue; }
|
|
459
471
|
});
|
|
460
472
|
Object.defineProperty(exports, "verifySession", {
|
|
461
473
|
enumerable: true,
|
|
462
|
-
get: function () { return
|
|
474
|
+
get: function () { return chunkMZE4TPFU_cjs.verifySession; }
|
|
463
475
|
});
|
|
464
476
|
Object.defineProperty(exports, "verifyValue", {
|
|
465
477
|
enumerable: true,
|
|
466
|
-
get: function () { return
|
|
478
|
+
get: function () { return chunkMZE4TPFU_cjs.verifyValue; }
|
|
467
479
|
});
|
|
468
480
|
exports.DEFAULT_ISSUER = DEFAULT_ISSUER;
|
|
469
481
|
exports.MAX_ACT_DEPTH = MAX_ACT_DEPTH;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { A as AVATAR_TYPES, c as AddressOwnership, B as BeginLoginOptions, d as BidProfile, C as CreateSsoClientOptions, D as DEFAULT_SESSION_MAX_AGE, e as Discovery, I as IntrospectOptions,
|
|
1
|
+
export { A as AVATAR_TYPES, c as AddressOwnership, B as BeginLoginOptions, d as BidProfile, C as CreateSsoClientOptions, D as DEFAULT_SESSION_MAX_AGE, e as Discovery, E as EMBEDDED_FLOW_MAX_AGE, f as EmbeddedClient, g as EmbeddedError, h as EmbeddedFlow, i as EmbeddedStart, j as EmbeddedStartInput, k as EmbeddedStep, l as EmbeddedTerms, I as IntrospectOptions, m as IntrospectedToken, n as InvitationOutcome, o as InvitationResult, p as InviteUsersInput, J as JwksCache, q as JwksCacheOptions, r as JwksError, s as JwksUnavailableError, t as JwksUnknownKeyError, L as LoginResult, u as LoginStart, v as LogoutToken, M as MAX_AVATAR_BYTES, w as MAX_INVITATIONS, x as MigrationState, y as MigrationStatus, b as SessionPayload, z as SsoAppApiError, F as SsoAvatarRejectedError, G as SsoClaims, S as SsoClient, a as SsoConfig, H as SsoConfigError, K as SsoError, N as SsoInsufficientScopeError, O as SsoIntrospectionUnavailableError, P as SsoReauthError, Q as SsoTokenInactiveError, R as TokenSet, T as TokenStore, U as cookieHeader, V as createJwksCache, W as createSsoClient, X as loadSsoConfig, Y as memoryTokenStore, Z as openEmbeddedFlow, _ as readCookie, $ as sealEmbeddedFlow, a0 as signSession, a1 as signValue, a2 as verifySession, a3 as verifyValue } from './session-qvhMg3ID.cjs';
|
|
2
2
|
import { JWK } from 'jose';
|
|
3
3
|
|
|
4
4
|
declare const DEFAULT_ISSUER = "https://id.broberg.ai";
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { A as AVATAR_TYPES, c as AddressOwnership, B as BeginLoginOptions, d as BidProfile, C as CreateSsoClientOptions, D as DEFAULT_SESSION_MAX_AGE, e as Discovery, I as IntrospectOptions,
|
|
1
|
+
export { A as AVATAR_TYPES, c as AddressOwnership, B as BeginLoginOptions, d as BidProfile, C as CreateSsoClientOptions, D as DEFAULT_SESSION_MAX_AGE, e as Discovery, E as EMBEDDED_FLOW_MAX_AGE, f as EmbeddedClient, g as EmbeddedError, h as EmbeddedFlow, i as EmbeddedStart, j as EmbeddedStartInput, k as EmbeddedStep, l as EmbeddedTerms, I as IntrospectOptions, m as IntrospectedToken, n as InvitationOutcome, o as InvitationResult, p as InviteUsersInput, J as JwksCache, q as JwksCacheOptions, r as JwksError, s as JwksUnavailableError, t as JwksUnknownKeyError, L as LoginResult, u as LoginStart, v as LogoutToken, M as MAX_AVATAR_BYTES, w as MAX_INVITATIONS, x as MigrationState, y as MigrationStatus, b as SessionPayload, z as SsoAppApiError, F as SsoAvatarRejectedError, G as SsoClaims, S as SsoClient, a as SsoConfig, H as SsoConfigError, K as SsoError, N as SsoInsufficientScopeError, O as SsoIntrospectionUnavailableError, P as SsoReauthError, Q as SsoTokenInactiveError, R as TokenSet, T as TokenStore, U as cookieHeader, V as createJwksCache, W as createSsoClient, X as loadSsoConfig, Y as memoryTokenStore, Z as openEmbeddedFlow, _ as readCookie, $ as sealEmbeddedFlow, a0 as signSession, a1 as signValue, a2 as verifySession, a3 as verifyValue } from './session-qvhMg3ID.js';
|
|
2
2
|
import { JWK } from 'jose';
|
|
3
3
|
|
|
4
4
|
declare const DEFAULT_ISSUER = "https://id.broberg.ai";
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { SsoError, ALLOWED_ALGS, JwksUnavailableError, createJwksCache } from './chunk-
|
|
2
|
-
export { AVATAR_TYPES, DEFAULT_SESSION_MAX_AGE, JwksError, JwksUnavailableError, JwksUnknownKeyError, MAX_AVATAR_BYTES, MAX_INVITATIONS, SsoAppApiError, SsoAvatarRejectedError, SsoConfigError, SsoError, SsoInsufficientScopeError, SsoIntrospectionUnavailableError, SsoReauthError, SsoTokenInactiveError, cookieHeader, createJwksCache, createSsoClient, loadSsoConfig, memoryTokenStore, readCookie, signSession, signValue, verifySession, verifyValue } from './chunk-
|
|
1
|
+
import { SsoError, ALLOWED_ALGS, JwksUnavailableError, createJwksCache } from './chunk-PQTT74BA.js';
|
|
2
|
+
export { AVATAR_TYPES, DEFAULT_SESSION_MAX_AGE, EMBEDDED_FLOW_MAX_AGE, JwksError, JwksUnavailableError, JwksUnknownKeyError, MAX_AVATAR_BYTES, MAX_INVITATIONS, SsoAppApiError, SsoAvatarRejectedError, SsoConfigError, SsoError, SsoInsufficientScopeError, SsoIntrospectionUnavailableError, SsoReauthError, SsoTokenInactiveError, cookieHeader, createJwksCache, createSsoClient, loadSsoConfig, memoryTokenStore, openEmbeddedFlow, readCookie, sealEmbeddedFlow, signSession, signValue, verifySession, verifyValue } from './chunk-PQTT74BA.js';
|
|
3
3
|
import { existsSync } from 'fs';
|
|
4
4
|
import { request } from 'http';
|
|
5
5
|
import { generateKeyPair, exportJWK, calculateJwkThumbprint, SignJWT, decodeProtectedHeader, importJWK, jwtVerify } from 'jose';
|
|
@@ -57,6 +57,105 @@ interface SsoConfig {
|
|
|
57
57
|
declare const DEFAULT_SESSION_MAX_AGE: number;
|
|
58
58
|
declare function loadSsoConfig(env?: NodeJS.ProcessEnv): SsoConfig;
|
|
59
59
|
|
|
60
|
+
/** What a step can answer, as data the app can word itself (the contract's error table). */
|
|
61
|
+
type EmbeddedError = {
|
|
62
|
+
kind: "invalid_code";
|
|
63
|
+
attemptsLeft?: number;
|
|
64
|
+
} | {
|
|
65
|
+
kind: "expired";
|
|
66
|
+
} | {
|
|
67
|
+
kind: "locked";
|
|
68
|
+
retryAfter?: number;
|
|
69
|
+
} | {
|
|
70
|
+
kind: "too_many_requests";
|
|
71
|
+
retryAfter?: number;
|
|
72
|
+
} | {
|
|
73
|
+
kind: "not_embedded";
|
|
74
|
+
} | {
|
|
75
|
+
kind: "signup_not_enabled";
|
|
76
|
+
} | {
|
|
77
|
+
kind: "terms_outdated";
|
|
78
|
+
version?: string;
|
|
79
|
+
} | {
|
|
80
|
+
kind: "invalid_request";
|
|
81
|
+
} | {
|
|
82
|
+
kind: "unavailable";
|
|
83
|
+
message: string;
|
|
84
|
+
};
|
|
85
|
+
/** The server-side state of one embedded login: keep it in a signed cookie (sealEmbeddedFlow). */
|
|
86
|
+
interface EmbeddedFlow {
|
|
87
|
+
flowId: string;
|
|
88
|
+
codeVerifier: string;
|
|
89
|
+
nonce: string;
|
|
90
|
+
email: string;
|
|
91
|
+
}
|
|
92
|
+
interface EmbeddedStartInput {
|
|
93
|
+
email: string;
|
|
94
|
+
/** "signup" creates the account at verify (BID F084.167); needs `name` and the current `termsVersion`. */
|
|
95
|
+
intent?: "login" | "signup";
|
|
96
|
+
name?: string;
|
|
97
|
+
termsVersion?: string;
|
|
98
|
+
/** The user's IP, for BID's per-IP limit. Without it, BID counts your server's IP. */
|
|
99
|
+
endUserIp?: string;
|
|
100
|
+
}
|
|
101
|
+
type EmbeddedStart = {
|
|
102
|
+
ok: true;
|
|
103
|
+
flow: EmbeddedFlow;
|
|
104
|
+
expiresIn: number;
|
|
105
|
+
} | {
|
|
106
|
+
ok: false;
|
|
107
|
+
error: EmbeddedError;
|
|
108
|
+
};
|
|
109
|
+
/** After a code: signed in (the same result as completeLogin), a second factor, or an error. */
|
|
110
|
+
type EmbeddedStep = {
|
|
111
|
+
kind: "done";
|
|
112
|
+
result: LoginResult;
|
|
113
|
+
} | {
|
|
114
|
+
kind: "second_factor";
|
|
115
|
+
methods: ("totp" | "sms")[];
|
|
116
|
+
} | {
|
|
117
|
+
kind: "error";
|
|
118
|
+
error: EmbeddedError;
|
|
119
|
+
};
|
|
120
|
+
interface EmbeddedTerms {
|
|
121
|
+
version: string;
|
|
122
|
+
/** Show it VERBATIM at the button — BID owns the text. */
|
|
123
|
+
text: string;
|
|
124
|
+
links: {
|
|
125
|
+
label: string;
|
|
126
|
+
url: string;
|
|
127
|
+
}[];
|
|
128
|
+
}
|
|
129
|
+
interface EmbeddedClient {
|
|
130
|
+
start(input: EmbeddedStartInput): Promise<EmbeddedStart>;
|
|
131
|
+
verify(flow: EmbeddedFlow, code: string): Promise<EmbeddedStep>;
|
|
132
|
+
sendSms(flow: EmbeddedFlow): Promise<{
|
|
133
|
+
ok: true;
|
|
134
|
+
} | {
|
|
135
|
+
ok: false;
|
|
136
|
+
error: EmbeddedError;
|
|
137
|
+
}>;
|
|
138
|
+
verifySecondFactor(flow: EmbeddedFlow, method: "totp" | "sms", code: string): Promise<EmbeddedStep>;
|
|
139
|
+
/** BID's consent text for signup, current version. Throws SsoError if BID does not answer it. */
|
|
140
|
+
terms(lang: "da" | "en"): Promise<EmbeddedTerms>;
|
|
141
|
+
}
|
|
142
|
+
/** A flow lives as long as BID's code does (10 min). */
|
|
143
|
+
declare const EMBEDDED_FLOW_MAX_AGE = 600;
|
|
144
|
+
/** Seal the flow for a signed, httpOnly cookie (same signing as the login transaction). */
|
|
145
|
+
declare function sealEmbeddedFlow(flow: EmbeddedFlow, secret: string, options?: {
|
|
146
|
+
now?: () => number;
|
|
147
|
+
}): Promise<string>;
|
|
148
|
+
/** Read it back: the flow, or why not — "absent" (no cookie), "expired" (too old), "unreadable" (not ours / tampered). */
|
|
149
|
+
declare function openEmbeddedFlow(raw: string | undefined | null, secret: string, options?: {
|
|
150
|
+
now?: () => number;
|
|
151
|
+
}): Promise<{
|
|
152
|
+
ok: true;
|
|
153
|
+
flow: EmbeddedFlow;
|
|
154
|
+
} | {
|
|
155
|
+
ok: false;
|
|
156
|
+
reason: "absent" | "expired" | "unreadable";
|
|
157
|
+
}>;
|
|
158
|
+
|
|
60
159
|
/**
|
|
61
160
|
* The signing-key cache.
|
|
62
161
|
*
|
|
@@ -357,6 +456,12 @@ interface SsoClient {
|
|
|
357
456
|
verifyIdToken(idToken: string, options?: {
|
|
358
457
|
nonce?: string;
|
|
359
458
|
}): Promise<SsoClaims>;
|
|
459
|
+
/**
|
|
460
|
+
* components-F084.165 — the embedded login and signup (BID F084.166 v1.1 + F084.167), called
|
|
461
|
+
* from YOUR SERVER as a confidential client (needs `clientSecret`). Ends in the same
|
|
462
|
+
* LoginResult as completeLogin. Keep the returned flow in a cookie with sealEmbeddedFlow.
|
|
463
|
+
*/
|
|
464
|
+
embedded: EmbeddedClient;
|
|
360
465
|
logoutUrl(options?: {
|
|
361
466
|
idTokenHint?: string;
|
|
362
467
|
postLogoutRedirectUri?: string;
|
|
@@ -623,4 +728,4 @@ declare function cookieHeader(name: string, value: string, opts: {
|
|
|
623
728
|
}): string;
|
|
624
729
|
declare function readCookie(header: string | null | undefined, name: string): string | undefined;
|
|
625
730
|
|
|
626
|
-
export { AVATAR_TYPES as A, type BeginLoginOptions as B, type CreateSsoClientOptions as C, DEFAULT_SESSION_MAX_AGE as D,
|
|
731
|
+
export { sealEmbeddedFlow as $, AVATAR_TYPES as A, type BeginLoginOptions as B, type CreateSsoClientOptions as C, DEFAULT_SESSION_MAX_AGE as D, EMBEDDED_FLOW_MAX_AGE as E, SsoAvatarRejectedError as F, type SsoClaims as G, SsoConfigError as H, type IntrospectOptions as I, type JwksCache as J, SsoError as K, type LoginResult as L, MAX_AVATAR_BYTES as M, SsoInsufficientScopeError as N, SsoIntrospectionUnavailableError as O, SsoReauthError as P, SsoTokenInactiveError as Q, type TokenSet as R, type SsoClient as S, type TokenStore as T, cookieHeader as U, createJwksCache as V, createSsoClient as W, loadSsoConfig as X, memoryTokenStore as Y, openEmbeddedFlow as Z, readCookie as _, type SsoConfig as a, signSession as a0, signValue as a1, verifySession as a2, verifyValue as a3, type SessionPayload as b, type AddressOwnership as c, type BidProfile as d, type Discovery as e, type EmbeddedClient as f, type EmbeddedError as g, type EmbeddedFlow as h, type EmbeddedStart as i, type EmbeddedStartInput as j, type EmbeddedStep as k, type EmbeddedTerms as l, type IntrospectedToken as m, type InvitationOutcome as n, type InvitationResult as o, type InviteUsersInput as p, type JwksCacheOptions as q, JwksError as r, JwksUnavailableError as s, JwksUnknownKeyError as t, type LoginStart as u, type LogoutToken as v, MAX_INVITATIONS as w, type MigrationState as x, type MigrationStatus as y, SsoAppApiError as z };
|
|
@@ -57,6 +57,105 @@ interface SsoConfig {
|
|
|
57
57
|
declare const DEFAULT_SESSION_MAX_AGE: number;
|
|
58
58
|
declare function loadSsoConfig(env?: NodeJS.ProcessEnv): SsoConfig;
|
|
59
59
|
|
|
60
|
+
/** What a step can answer, as data the app can word itself (the contract's error table). */
|
|
61
|
+
type EmbeddedError = {
|
|
62
|
+
kind: "invalid_code";
|
|
63
|
+
attemptsLeft?: number;
|
|
64
|
+
} | {
|
|
65
|
+
kind: "expired";
|
|
66
|
+
} | {
|
|
67
|
+
kind: "locked";
|
|
68
|
+
retryAfter?: number;
|
|
69
|
+
} | {
|
|
70
|
+
kind: "too_many_requests";
|
|
71
|
+
retryAfter?: number;
|
|
72
|
+
} | {
|
|
73
|
+
kind: "not_embedded";
|
|
74
|
+
} | {
|
|
75
|
+
kind: "signup_not_enabled";
|
|
76
|
+
} | {
|
|
77
|
+
kind: "terms_outdated";
|
|
78
|
+
version?: string;
|
|
79
|
+
} | {
|
|
80
|
+
kind: "invalid_request";
|
|
81
|
+
} | {
|
|
82
|
+
kind: "unavailable";
|
|
83
|
+
message: string;
|
|
84
|
+
};
|
|
85
|
+
/** The server-side state of one embedded login: keep it in a signed cookie (sealEmbeddedFlow). */
|
|
86
|
+
interface EmbeddedFlow {
|
|
87
|
+
flowId: string;
|
|
88
|
+
codeVerifier: string;
|
|
89
|
+
nonce: string;
|
|
90
|
+
email: string;
|
|
91
|
+
}
|
|
92
|
+
interface EmbeddedStartInput {
|
|
93
|
+
email: string;
|
|
94
|
+
/** "signup" creates the account at verify (BID F084.167); needs `name` and the current `termsVersion`. */
|
|
95
|
+
intent?: "login" | "signup";
|
|
96
|
+
name?: string;
|
|
97
|
+
termsVersion?: string;
|
|
98
|
+
/** The user's IP, for BID's per-IP limit. Without it, BID counts your server's IP. */
|
|
99
|
+
endUserIp?: string;
|
|
100
|
+
}
|
|
101
|
+
type EmbeddedStart = {
|
|
102
|
+
ok: true;
|
|
103
|
+
flow: EmbeddedFlow;
|
|
104
|
+
expiresIn: number;
|
|
105
|
+
} | {
|
|
106
|
+
ok: false;
|
|
107
|
+
error: EmbeddedError;
|
|
108
|
+
};
|
|
109
|
+
/** After a code: signed in (the same result as completeLogin), a second factor, or an error. */
|
|
110
|
+
type EmbeddedStep = {
|
|
111
|
+
kind: "done";
|
|
112
|
+
result: LoginResult;
|
|
113
|
+
} | {
|
|
114
|
+
kind: "second_factor";
|
|
115
|
+
methods: ("totp" | "sms")[];
|
|
116
|
+
} | {
|
|
117
|
+
kind: "error";
|
|
118
|
+
error: EmbeddedError;
|
|
119
|
+
};
|
|
120
|
+
interface EmbeddedTerms {
|
|
121
|
+
version: string;
|
|
122
|
+
/** Show it VERBATIM at the button — BID owns the text. */
|
|
123
|
+
text: string;
|
|
124
|
+
links: {
|
|
125
|
+
label: string;
|
|
126
|
+
url: string;
|
|
127
|
+
}[];
|
|
128
|
+
}
|
|
129
|
+
interface EmbeddedClient {
|
|
130
|
+
start(input: EmbeddedStartInput): Promise<EmbeddedStart>;
|
|
131
|
+
verify(flow: EmbeddedFlow, code: string): Promise<EmbeddedStep>;
|
|
132
|
+
sendSms(flow: EmbeddedFlow): Promise<{
|
|
133
|
+
ok: true;
|
|
134
|
+
} | {
|
|
135
|
+
ok: false;
|
|
136
|
+
error: EmbeddedError;
|
|
137
|
+
}>;
|
|
138
|
+
verifySecondFactor(flow: EmbeddedFlow, method: "totp" | "sms", code: string): Promise<EmbeddedStep>;
|
|
139
|
+
/** BID's consent text for signup, current version. Throws SsoError if BID does not answer it. */
|
|
140
|
+
terms(lang: "da" | "en"): Promise<EmbeddedTerms>;
|
|
141
|
+
}
|
|
142
|
+
/** A flow lives as long as BID's code does (10 min). */
|
|
143
|
+
declare const EMBEDDED_FLOW_MAX_AGE = 600;
|
|
144
|
+
/** Seal the flow for a signed, httpOnly cookie (same signing as the login transaction). */
|
|
145
|
+
declare function sealEmbeddedFlow(flow: EmbeddedFlow, secret: string, options?: {
|
|
146
|
+
now?: () => number;
|
|
147
|
+
}): Promise<string>;
|
|
148
|
+
/** Read it back: the flow, or why not — "absent" (no cookie), "expired" (too old), "unreadable" (not ours / tampered). */
|
|
149
|
+
declare function openEmbeddedFlow(raw: string | undefined | null, secret: string, options?: {
|
|
150
|
+
now?: () => number;
|
|
151
|
+
}): Promise<{
|
|
152
|
+
ok: true;
|
|
153
|
+
flow: EmbeddedFlow;
|
|
154
|
+
} | {
|
|
155
|
+
ok: false;
|
|
156
|
+
reason: "absent" | "expired" | "unreadable";
|
|
157
|
+
}>;
|
|
158
|
+
|
|
60
159
|
/**
|
|
61
160
|
* The signing-key cache.
|
|
62
161
|
*
|
|
@@ -357,6 +456,12 @@ interface SsoClient {
|
|
|
357
456
|
verifyIdToken(idToken: string, options?: {
|
|
358
457
|
nonce?: string;
|
|
359
458
|
}): Promise<SsoClaims>;
|
|
459
|
+
/**
|
|
460
|
+
* components-F084.165 — the embedded login and signup (BID F084.166 v1.1 + F084.167), called
|
|
461
|
+
* from YOUR SERVER as a confidential client (needs `clientSecret`). Ends in the same
|
|
462
|
+
* LoginResult as completeLogin. Keep the returned flow in a cookie with sealEmbeddedFlow.
|
|
463
|
+
*/
|
|
464
|
+
embedded: EmbeddedClient;
|
|
360
465
|
logoutUrl(options?: {
|
|
361
466
|
idTokenHint?: string;
|
|
362
467
|
postLogoutRedirectUri?: string;
|
|
@@ -623,4 +728,4 @@ declare function cookieHeader(name: string, value: string, opts: {
|
|
|
623
728
|
}): string;
|
|
624
729
|
declare function readCookie(header: string | null | undefined, name: string): string | undefined;
|
|
625
730
|
|
|
626
|
-
export { AVATAR_TYPES as A, type BeginLoginOptions as B, type CreateSsoClientOptions as C, DEFAULT_SESSION_MAX_AGE as D,
|
|
731
|
+
export { sealEmbeddedFlow as $, AVATAR_TYPES as A, type BeginLoginOptions as B, type CreateSsoClientOptions as C, DEFAULT_SESSION_MAX_AGE as D, EMBEDDED_FLOW_MAX_AGE as E, SsoAvatarRejectedError as F, type SsoClaims as G, SsoConfigError as H, type IntrospectOptions as I, type JwksCache as J, SsoError as K, type LoginResult as L, MAX_AVATAR_BYTES as M, SsoInsufficientScopeError as N, SsoIntrospectionUnavailableError as O, SsoReauthError as P, SsoTokenInactiveError as Q, type TokenSet as R, type SsoClient as S, type TokenStore as T, cookieHeader as U, createJwksCache as V, createSsoClient as W, loadSsoConfig as X, memoryTokenStore as Y, openEmbeddedFlow as Z, readCookie as _, type SsoConfig as a, signSession as a0, signValue as a1, verifySession as a2, verifyValue as a3, type SessionPayload as b, type AddressOwnership as c, type BidProfile as d, type Discovery as e, type EmbeddedClient as f, type EmbeddedError as g, type EmbeddedFlow as h, type EmbeddedStart as i, type EmbeddedStartInput as j, type EmbeddedStep as k, type EmbeddedTerms as l, type IntrospectedToken as m, type InvitationOutcome as n, type InvitationResult as o, type InviteUsersInput as p, type JwksCacheOptions as q, JwksError as r, JwksUnavailableError as s, JwksUnknownKeyError as t, type LoginStart as u, type LogoutToken as v, MAX_INVITATIONS as w, type MigrationState as x, type MigrationStatus as y, SsoAppApiError as z };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@broberg/sso",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.19.0",
|
|
4
4
|
"description": "The thin client for BrobergID — send a user to central login, verify the ID token against JWKS, keep a local session. Public client + PKCE only: no passwords, no passkeys, no client secret can live in an app.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|