@broberg/sso 0.20.0 → 0.21.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 +23 -0
- package/dist/{chunk-KEBSZBHL.cjs → chunk-2XMKZWC6.cjs} +297 -5
- package/dist/chunk-2XMKZWC6.cjs.map +1 -0
- package/dist/{chunk-ZDNBHM2G.js → chunk-UDUPP35Z.js} +285 -7
- package/dist/chunk-UDUPP35Z.js.map +1 -0
- package/dist/hono.cjs +38 -37
- package/dist/hono.cjs.map +1 -1
- package/dist/hono.d.cts +7 -1
- package/dist/hono.d.ts +7 -1
- package/dist/hono.js +3 -2
- package/dist/hono.js.map +1 -1
- package/dist/index.cjs +112 -325
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +4 -260
- package/dist/index.js.map +1 -1
- package/dist/{session-AgUJ6U_7.d.cts → session-DnotfVNd.d.cts} +17 -1
- package/dist/{session-AgUJ6U_7.d.ts → session-DnotfVNd.d.ts} +17 -1
- package/package.json +1 -1
- package/dist/chunk-KEBSZBHL.cjs.map +0 -1
- package/dist/chunk-ZDNBHM2G.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -1,264 +1,8 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
4
|
-
var fs = require('fs');
|
|
5
|
-
var http = require('http');
|
|
3
|
+
var chunk2XMKZWC6_cjs = require('./chunk-2XMKZWC6.cjs');
|
|
6
4
|
var jose = require('jose');
|
|
7
5
|
|
|
8
|
-
async function createDpopKey() {
|
|
9
|
-
const { privateKey, publicKey } = await jose.generateKeyPair("ES256", { extractable: false });
|
|
10
|
-
const jwk = await jose.exportJWK(publicKey);
|
|
11
|
-
const publicJwk = { kty: jwk.kty, crv: jwk.crv, x: jwk.x, y: jwk.y };
|
|
12
|
-
return { privateKey, publicJwk, jkt: await jose.calculateJwkThumbprint(publicJwk) };
|
|
13
|
-
}
|
|
14
|
-
var enc = new TextEncoder();
|
|
15
|
-
var b64url = (bytes) => btoa(String.fromCharCode(...new Uint8Array(bytes))).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
16
|
-
async function accessTokenHash(ticket) {
|
|
17
|
-
return b64url(await crypto.subtle.digest("SHA-256", enc.encode(ticket)));
|
|
18
|
-
}
|
|
19
|
-
function normalizeHtu(url) {
|
|
20
|
-
const u = new URL(url);
|
|
21
|
-
return `${u.protocol}//${u.host}${u.pathname}`;
|
|
22
|
-
}
|
|
23
|
-
async function createDpopProof(key, opts) {
|
|
24
|
-
const payload = {
|
|
25
|
-
jti: crypto.randomUUID(),
|
|
26
|
-
htm: opts.method.toUpperCase(),
|
|
27
|
-
htu: normalizeHtu(opts.url),
|
|
28
|
-
iat: Math.floor((opts.now?.() ?? Date.now()) / 1e3)
|
|
29
|
-
};
|
|
30
|
-
if (opts.ticket !== void 0) payload.ath = await accessTokenHash(opts.ticket);
|
|
31
|
-
return new jose.SignJWT(payload).setProtectedHeader({ typ: "dpop+jwt", alg: "ES256", jwk: key.publicJwk }).sign(key.privateKey);
|
|
32
|
-
}
|
|
33
|
-
function createMemoryReplayStore(now = () => Date.now()) {
|
|
34
|
-
const entries = /* @__PURE__ */ new Map();
|
|
35
|
-
return {
|
|
36
|
-
async seen(key, ttlMs) {
|
|
37
|
-
const t = now();
|
|
38
|
-
for (const [k, until] of entries) if (until <= t) entries.delete(k);
|
|
39
|
-
if (entries.has(key)) return true;
|
|
40
|
-
entries.set(key, t + ttlMs);
|
|
41
|
-
return false;
|
|
42
|
-
}
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
|
-
var ASYMMETRIC = /* @__PURE__ */ new Set(["ES256", "ES384", "ES512", "EdDSA", "PS256", "PS384", "PS512", "RS256", "RS384", "RS512"]);
|
|
46
|
-
async function verifyDpopProof(proof, o) {
|
|
47
|
-
let header;
|
|
48
|
-
try {
|
|
49
|
-
header = jose.decodeProtectedHeader(proof);
|
|
50
|
-
} catch {
|
|
51
|
-
throw new chunkKEBSZBHL_cjs.SsoError("DPoP proof is not a JWS");
|
|
52
|
-
}
|
|
53
|
-
if (header.typ !== "dpop+jwt") throw new chunkKEBSZBHL_cjs.SsoError(`DPoP proof has typ ${JSON.stringify(header.typ)}, not "dpop+jwt"`);
|
|
54
|
-
if (!header.alg || !ASYMMETRIC.has(header.alg)) throw new chunkKEBSZBHL_cjs.SsoError(`DPoP proof alg ${JSON.stringify(header.alg)} is not an accepted asymmetric algorithm`);
|
|
55
|
-
const jwk = header.jwk;
|
|
56
|
-
if (!jwk || typeof jwk !== "object") throw new chunkKEBSZBHL_cjs.SsoError("DPoP proof carries no jwk");
|
|
57
|
-
if ("d" in jwk) throw new chunkKEBSZBHL_cjs.SsoError("DPoP proof jwk contains a private key");
|
|
58
|
-
let payload;
|
|
59
|
-
try {
|
|
60
|
-
const key = await jose.importJWK(jwk, header.alg);
|
|
61
|
-
({ payload } = await jose.jwtVerify(proof, key, { typ: "dpop+jwt", algorithms: [header.alg] }));
|
|
62
|
-
} catch (e) {
|
|
63
|
-
throw new chunkKEBSZBHL_cjs.SsoError(`DPoP proof signature rejected: ${e instanceof Error ? e.message : String(e)}`);
|
|
64
|
-
}
|
|
65
|
-
if (await jose.calculateJwkThumbprint(jwk) !== o.jkt) throw new chunkKEBSZBHL_cjs.SsoError("DPoP proof key does not match the ticket's cnf.jkt");
|
|
66
|
-
if (payload.htm !== o.method.toUpperCase()) throw new chunkKEBSZBHL_cjs.SsoError(`DPoP proof is for ${JSON.stringify(payload.htm)}, the request is ${o.method.toUpperCase()}`);
|
|
67
|
-
let htu;
|
|
68
|
-
try {
|
|
69
|
-
htu = normalizeHtu(String(payload.htu));
|
|
70
|
-
} catch {
|
|
71
|
-
throw new chunkKEBSZBHL_cjs.SsoError("DPoP proof htu is not a URL");
|
|
72
|
-
}
|
|
73
|
-
if (htu !== normalizeHtu(o.url)) throw new chunkKEBSZBHL_cjs.SsoError(`DPoP proof is for ${htu}, the request is ${normalizeHtu(o.url)}`);
|
|
74
|
-
const nowSec = Math.floor(o.now() / 1e3);
|
|
75
|
-
if (typeof payload.iat !== "number" || Math.abs(nowSec - payload.iat) > o.iatWindowSec) {
|
|
76
|
-
throw new chunkKEBSZBHL_cjs.SsoError(`DPoP proof iat is outside \xB1${o.iatWindowSec}s`);
|
|
77
|
-
}
|
|
78
|
-
if (payload.ath !== await accessTokenHash(o.ticket)) throw new chunkKEBSZBHL_cjs.SsoError("DPoP proof ath does not match this ticket");
|
|
79
|
-
if (typeof payload.jti !== "string" || payload.jti === "") throw new chunkKEBSZBHL_cjs.SsoError("DPoP proof has no jti");
|
|
80
|
-
if (await o.replay.seen(`${o.jkt}:${payload.jti}`, o.iatWindowSec * 2 * 1e3)) {
|
|
81
|
-
throw new chunkKEBSZBHL_cjs.SsoError("DPoP proof was already used (jti replay)");
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
// src/fetch-ticket.ts
|
|
86
|
-
var DEFAULT_ISSUER = "https://id.broberg.ai";
|
|
87
|
-
var FLY_SOCKET = "/.fly/api";
|
|
88
|
-
var NoWorkloadIdentityError = class extends Error {
|
|
89
|
-
constructor(message) {
|
|
90
|
-
super(message);
|
|
91
|
-
this.name = "NoWorkloadIdentityError";
|
|
92
|
-
}
|
|
93
|
-
};
|
|
94
|
-
var TicketExchangeError = class extends Error {
|
|
95
|
-
constructor(message, code, status) {
|
|
96
|
-
super(message);
|
|
97
|
-
this.code = code;
|
|
98
|
-
this.status = status;
|
|
99
|
-
this.name = "TicketExchangeError";
|
|
100
|
-
}
|
|
101
|
-
code;
|
|
102
|
-
status;
|
|
103
|
-
};
|
|
104
|
-
var TicketUnavailableError = class extends Error {
|
|
105
|
-
constructor(message) {
|
|
106
|
-
super(message);
|
|
107
|
-
this.name = "TicketUnavailableError";
|
|
108
|
-
}
|
|
109
|
-
};
|
|
110
|
-
function flyOidcOverSocket(aud) {
|
|
111
|
-
return new Promise((resolve, reject) => {
|
|
112
|
-
const body = JSON.stringify({ aud });
|
|
113
|
-
const req = http.request(
|
|
114
|
-
{ socketPath: FLY_SOCKET, path: "/v1/tokens/oidc", method: "POST", headers: { "content-type": "application/json", "content-length": Buffer.byteLength(body) } },
|
|
115
|
-
(res) => {
|
|
116
|
-
let data = "";
|
|
117
|
-
res.setEncoding("utf8");
|
|
118
|
-
res.on("data", (c) => data += c);
|
|
119
|
-
res.on("end", () => {
|
|
120
|
-
if ((res.statusCode ?? 0) >= 200 && (res.statusCode ?? 0) < 300) resolve(data.trim());
|
|
121
|
-
else reject(new TicketUnavailableError(`Fly OIDC answered ${res.statusCode}`));
|
|
122
|
-
});
|
|
123
|
-
}
|
|
124
|
-
);
|
|
125
|
-
req.on("error", (e) => reject(new TicketUnavailableError(`Fly OIDC socket: ${e.message}`)));
|
|
126
|
-
req.end(body);
|
|
127
|
-
});
|
|
128
|
-
}
|
|
129
|
-
function jwtExp(token) {
|
|
130
|
-
try {
|
|
131
|
-
const payload = JSON.parse(Buffer.from(token.split(".")[1] ?? "", "base64url").toString("utf8"));
|
|
132
|
-
return typeof payload.exp === "number" ? payload.exp : null;
|
|
133
|
-
} catch {
|
|
134
|
-
return null;
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
async function readTicketResponse(res, issuer, target, now) {
|
|
138
|
-
let body = {};
|
|
139
|
-
try {
|
|
140
|
-
body = await res.json();
|
|
141
|
-
} catch {
|
|
142
|
-
}
|
|
143
|
-
if (res.status >= 500) throw new TicketUnavailableError(`${issuer} answered ${res.status}`);
|
|
144
|
-
if (!res.ok || !body.access_token) {
|
|
145
|
-
const desc = body.error_description ?? "";
|
|
146
|
-
const code = desc.includes(":") ? desc.slice(0, desc.indexOf(":")).trim() : body.error ?? "exchange_refused";
|
|
147
|
-
throw new TicketExchangeError(`BID refused the ticket for ${target}: ${desc || body.error || res.status}`, code, res.status);
|
|
148
|
-
}
|
|
149
|
-
const nowSec = Math.floor(now() / 1e3);
|
|
150
|
-
const exp = jwtExp(body.access_token) ?? nowSec + (body.expires_in ?? 300);
|
|
151
|
-
return { token: body.access_token, exp };
|
|
152
|
-
}
|
|
153
|
-
function createTicketClient(options = {}) {
|
|
154
|
-
const issuer = (options.issuer ?? DEFAULT_ISSUER).replace(/\/+$/, "");
|
|
155
|
-
const renewBeforeSec = options.renewBeforeSec ?? 30;
|
|
156
|
-
const env = options.env ?? process.env;
|
|
157
|
-
const fetchImpl = options.fetchImpl ?? fetch;
|
|
158
|
-
const now = options.now ?? (() => Date.now());
|
|
159
|
-
const flyOidc = options.flyOidc ?? flyOidcOverSocket;
|
|
160
|
-
const onFly = options.onFly ?? (() => !!env.FLY_APP_NAME && fs.existsSync(FLY_SOCKET));
|
|
161
|
-
const cache = /* @__PURE__ */ new Map();
|
|
162
|
-
const inFlight = /* @__PURE__ */ new Map();
|
|
163
|
-
let dpopKey = null;
|
|
164
|
-
const ownKey = () => dpopKey ??= createDpopKey();
|
|
165
|
-
async function platformToken() {
|
|
166
|
-
if (onFly()) return flyOidc(issuer);
|
|
167
|
-
const url = env.ACTIONS_ID_TOKEN_REQUEST_URL;
|
|
168
|
-
const bearer = env.ACTIONS_ID_TOKEN_REQUEST_TOKEN;
|
|
169
|
-
if (url && bearer) {
|
|
170
|
-
let res;
|
|
171
|
-
try {
|
|
172
|
-
res = await fetchImpl(`${url}&audience=${encodeURIComponent(issuer)}`, { headers: { authorization: `Bearer ${bearer}` } });
|
|
173
|
-
} catch (e) {
|
|
174
|
-
throw new TicketUnavailableError(`GitHub OIDC endpoint unreachable: ${e instanceof Error ? e.message : String(e)}`);
|
|
175
|
-
}
|
|
176
|
-
if (!res.ok) throw new TicketUnavailableError(`GitHub OIDC endpoint answered ${res.status}`);
|
|
177
|
-
const { value } = await res.json();
|
|
178
|
-
if (!value) throw new TicketUnavailableError("GitHub OIDC endpoint returned no token");
|
|
179
|
-
return value;
|
|
180
|
-
}
|
|
181
|
-
throw new NoWorkloadIdentityError(
|
|
182
|
-
"No workload identity to exchange for a ticket: not on a Fly machine (FLY_APP_NAME + /.fly/api) and not in a GitHub Actions job (ACTIONS_ID_TOKEN_REQUEST_URL/_TOKEN \u2014 add `permissions: id-token: write`). A Mac session keeps using its session key."
|
|
183
|
-
);
|
|
184
|
-
}
|
|
185
|
-
async function exchange(audience, scope) {
|
|
186
|
-
const subject = await platformToken();
|
|
187
|
-
const form = new URLSearchParams({
|
|
188
|
-
grant_type: "urn:ietf:params:oauth:grant-type:token-exchange",
|
|
189
|
-
subject_token: subject,
|
|
190
|
-
subject_token_type: "urn:ietf:params:oauth:token-type:jwt",
|
|
191
|
-
audience
|
|
192
|
-
});
|
|
193
|
-
if (scope) form.set("scope", scope);
|
|
194
|
-
let res;
|
|
195
|
-
try {
|
|
196
|
-
const tokenUrl = `${issuer}/oauth2/token`;
|
|
197
|
-
const headers = { "content-type": "application/x-www-form-urlencoded" };
|
|
198
|
-
if (options.dpop) headers.dpop = await createDpopProof(await ownKey(), { method: "POST", url: tokenUrl, now });
|
|
199
|
-
res = await fetchImpl(tokenUrl, { method: "POST", headers, body: form });
|
|
200
|
-
} catch (e) {
|
|
201
|
-
throw new TicketUnavailableError(`${issuer} unreachable: ${e instanceof Error ? e.message : String(e)}`);
|
|
202
|
-
}
|
|
203
|
-
return readTicketResponse(res, issuer, audience, now);
|
|
204
|
-
}
|
|
205
|
-
const client = {
|
|
206
|
-
async authHeaders({ audience, scope, method, url }) {
|
|
207
|
-
const ticket = await client.get({ audience, scope });
|
|
208
|
-
if (!options.dpop) return { authorization: `Bearer ${ticket}` };
|
|
209
|
-
return { authorization: `DPoP ${ticket}`, dpop: await createDpopProof(await ownKey(), { method, url, ticket, now }) };
|
|
210
|
-
},
|
|
211
|
-
async get({ audience, scope }) {
|
|
212
|
-
const key = `${audience} ${scope ?? ""}`;
|
|
213
|
-
const hit = cache.get(key);
|
|
214
|
-
if (hit && hit.exp - renewBeforeSec > now() / 1e3) return hit.token;
|
|
215
|
-
const pending = inFlight.get(key);
|
|
216
|
-
if (pending) return pending;
|
|
217
|
-
const p = exchange(audience, scope).then((t) => {
|
|
218
|
-
cache.set(key, t);
|
|
219
|
-
return t.token;
|
|
220
|
-
}).finally(() => inFlight.delete(key));
|
|
221
|
-
inFlight.set(key, p);
|
|
222
|
-
return p;
|
|
223
|
-
}
|
|
224
|
-
};
|
|
225
|
-
return client;
|
|
226
|
-
}
|
|
227
|
-
var shared = null;
|
|
228
|
-
function fetchTicket(opts) {
|
|
229
|
-
shared ??= createTicketClient();
|
|
230
|
-
return shared.get(opts);
|
|
231
|
-
}
|
|
232
|
-
async function exchangeUserToken(opts) {
|
|
233
|
-
const issuer = (opts.issuer ?? DEFAULT_ISSUER).replace(/\/+$/, "");
|
|
234
|
-
const target = opts.resource ?? opts.audience;
|
|
235
|
-
if (!target) throw new TicketExchangeError("exchangeUserToken needs a receiver: resource (its URL) or audience (BID's name for it)", "malformed_request", 0);
|
|
236
|
-
const fetchImpl = opts.fetchImpl ?? fetch;
|
|
237
|
-
const now = opts.now ?? (() => Date.now());
|
|
238
|
-
const form = new URLSearchParams({
|
|
239
|
-
grant_type: "urn:ietf:params:oauth:grant-type:token-exchange",
|
|
240
|
-
subject_token: opts.subjectToken,
|
|
241
|
-
subject_token_type: "urn:ietf:params:oauth:token-type:access_token"
|
|
242
|
-
});
|
|
243
|
-
if (opts.resource) form.set("resource", opts.resource);
|
|
244
|
-
if (opts.audience) form.set("audience", opts.audience);
|
|
245
|
-
if (opts.scope) form.set("scope", opts.scope);
|
|
246
|
-
const tokenUrl = `${issuer}/oauth2/token`;
|
|
247
|
-
const headers = { "content-type": "application/x-www-form-urlencoded" };
|
|
248
|
-
if (opts.clientSecret) {
|
|
249
|
-
headers.authorization = `Basic ${btoa(`${encodeURIComponent(opts.clientId)}:${encodeURIComponent(opts.clientSecret)}`)}`;
|
|
250
|
-
} else {
|
|
251
|
-
form.set("client_id", opts.clientId);
|
|
252
|
-
}
|
|
253
|
-
if (opts.dpopKey) headers.dpop = await createDpopProof(opts.dpopKey, { method: "POST", url: tokenUrl, now });
|
|
254
|
-
let res;
|
|
255
|
-
try {
|
|
256
|
-
res = await fetchImpl(tokenUrl, { method: "POST", headers, body: form });
|
|
257
|
-
} catch (e) {
|
|
258
|
-
throw new TicketUnavailableError(`${issuer} unreachable: ${e instanceof Error ? e.message : String(e)}`);
|
|
259
|
-
}
|
|
260
|
-
return readTicketResponse(res, issuer, target, now);
|
|
261
|
-
}
|
|
262
6
|
var PRINCIPAL_TYPES = ["human", "service", "agent", "user"];
|
|
263
7
|
var TICKET_FORMAT_VERSION = 1;
|
|
264
8
|
var MAX_ACT_DEPTH = 5;
|
|
@@ -273,11 +17,11 @@ function createTicketVerifier(options) {
|
|
|
273
17
|
dpopIatWindowSec = 60,
|
|
274
18
|
now = () => Date.now()
|
|
275
19
|
} = options;
|
|
276
|
-
const replayStore = options.replayStore ?? createMemoryReplayStore(now);
|
|
277
|
-
if (!issuer) throw new
|
|
278
|
-
if (!audience) throw new
|
|
20
|
+
const replayStore = options.replayStore ?? chunk2XMKZWC6_cjs.createMemoryReplayStore(now);
|
|
21
|
+
if (!issuer) throw new chunk2XMKZWC6_cjs.SsoError("createTicketVerifier needs an issuer");
|
|
22
|
+
if (!audience) throw new chunk2XMKZWC6_cjs.SsoError("createTicketVerifier needs this receiver's audience");
|
|
279
23
|
let cachePromise = null;
|
|
280
|
-
const cacheFor = (jwksUri) =>
|
|
24
|
+
const cacheFor = (jwksUri) => chunk2XMKZWC6_cjs.createJwksCache({
|
|
281
25
|
jwksUri,
|
|
282
26
|
fetchImpl,
|
|
283
27
|
...options.warmUp ? { warmUp: true } : {},
|
|
@@ -293,12 +37,12 @@ function createTicketVerifier(options) {
|
|
|
293
37
|
if (!res.ok) throw new Error(`answered ${res.status}`);
|
|
294
38
|
doc = await res.json();
|
|
295
39
|
} catch (cause) {
|
|
296
|
-
throw new
|
|
40
|
+
throw new chunk2XMKZWC6_cjs.JwksUnavailableError(`${url}: ${cause instanceof Error ? cause.message : String(cause)}`);
|
|
297
41
|
}
|
|
298
42
|
if (doc.issuer !== issuer) {
|
|
299
|
-
throw new
|
|
43
|
+
throw new chunk2XMKZWC6_cjs.SsoError(`issuer is ${issuer} but ${url} says ${doc.issuer} \u2014 they must match exactly`);
|
|
300
44
|
}
|
|
301
|
-
if (!doc.jwks_uri) throw new
|
|
45
|
+
if (!doc.jwks_uri) throw new chunk2XMKZWC6_cjs.SsoError(`${url} names no jwks_uri`);
|
|
302
46
|
return cacheFor(doc.jwks_uri);
|
|
303
47
|
})().catch((err) => {
|
|
304
48
|
cachePromise = null;
|
|
@@ -314,52 +58,52 @@ function createTicketVerifier(options) {
|
|
|
314
58
|
try {
|
|
315
59
|
header = jose.decodeProtectedHeader(token);
|
|
316
60
|
} catch {
|
|
317
|
-
throw new
|
|
61
|
+
throw new chunk2XMKZWC6_cjs.SsoError("ticket is not a JWS");
|
|
318
62
|
}
|
|
319
|
-
if (!header.kid) throw new
|
|
320
|
-
if (header.typ === "logout+jwt") throw new
|
|
63
|
+
if (!header.kid) throw new chunk2XMKZWC6_cjs.SsoError("ticket has no kid \u2014 cannot pick a signing key");
|
|
64
|
+
if (header.typ === "logout+jwt") throw new chunk2XMKZWC6_cjs.SsoError("a logout token is not a ticket");
|
|
321
65
|
const cache = await keys();
|
|
322
66
|
let payload;
|
|
323
67
|
try {
|
|
324
68
|
({ payload } = await jose.jwtVerify(token, async () => cache.getKey(header.kid, header.alg ?? "EdDSA"), {
|
|
325
69
|
issuer,
|
|
326
70
|
audience,
|
|
327
|
-
algorithms:
|
|
71
|
+
algorithms: chunk2XMKZWC6_cjs.ALLOWED_ALGS,
|
|
328
72
|
clockTolerance: clockToleranceSec,
|
|
329
73
|
requiredClaims: ["exp", "iat", "jti", "sub"]
|
|
330
74
|
}));
|
|
331
75
|
} catch (e) {
|
|
332
|
-
if (e instanceof
|
|
333
|
-
throw new
|
|
76
|
+
if (e instanceof chunk2XMKZWC6_cjs.JwksUnavailableError) throw e;
|
|
77
|
+
throw new chunk2XMKZWC6_cjs.SsoError(`ticket rejected: ${e instanceof Error ? e.message : String(e)}`);
|
|
334
78
|
}
|
|
335
|
-
if ("events" in payload) throw new
|
|
336
|
-
if ("nonce" in payload) throw new
|
|
79
|
+
if ("events" in payload) throw new chunk2XMKZWC6_cjs.SsoError("a logout token is not a ticket");
|
|
80
|
+
if ("nonce" in payload) throw new chunk2XMKZWC6_cjs.SsoError("an ID token is not a ticket");
|
|
337
81
|
const type = payload.principal_type;
|
|
338
82
|
if (typeof type !== "string" || !PRINCIPAL_TYPES.includes(type)) {
|
|
339
|
-
throw new
|
|
83
|
+
throw new chunk2XMKZWC6_cjs.SsoError(`ticket has no valid principal_type (got ${JSON.stringify(type)})`);
|
|
340
84
|
}
|
|
341
85
|
const lifetime = payload.exp - payload.iat;
|
|
342
86
|
if (lifetime > maxLifetimeSec) {
|
|
343
|
-
throw new
|
|
87
|
+
throw new chunk2XMKZWC6_cjs.SsoError(`ticket claims a ${lifetime}s lifetime; at most ${maxLifetimeSec}s is accepted`);
|
|
344
88
|
}
|
|
345
|
-
if (typeof payload.sub !== "string" || payload.sub === "") throw new
|
|
89
|
+
if (typeof payload.sub !== "string" || payload.sub === "") throw new chunk2XMKZWC6_cjs.SsoError("ticket has no sub");
|
|
346
90
|
const scopes = typeof payload.scope === "string" ? payload.scope.split(" ").filter(Boolean) : [];
|
|
347
91
|
const required = opts.scope === void 0 ? [] : Array.isArray(opts.scope) ? opts.scope : [opts.scope];
|
|
348
92
|
const missing = required.filter((s) => !scopes.includes(s));
|
|
349
|
-
if (missing.length) throw new
|
|
93
|
+
if (missing.length) throw new chunk2XMKZWC6_cjs.SsoError(`ticket lacks scope ${missing.join(", ")}`);
|
|
350
94
|
const ver = payload.ver === void 0 ? 0 : payload.ver;
|
|
351
95
|
if (typeof ver !== "number" || !Number.isInteger(ver) || ver < 0) {
|
|
352
|
-
throw new
|
|
96
|
+
throw new chunk2XMKZWC6_cjs.SsoError(`ticket has an invalid ver (got ${JSON.stringify(payload.ver)})`);
|
|
353
97
|
}
|
|
354
98
|
if (ver > TICKET_FORMAT_VERSION) {
|
|
355
|
-
throw new
|
|
99
|
+
throw new chunk2XMKZWC6_cjs.SsoError(`ticket format version ${ver} is newer than this @broberg/sso understands (${TICKET_FORMAT_VERSION}) \u2014 upgrade @broberg/sso`);
|
|
356
100
|
}
|
|
357
101
|
const act = payload.act === void 0 ? null : readActor(payload.act, 1);
|
|
358
102
|
const cnfRaw = payload.cnf;
|
|
359
103
|
const cnf = cnfRaw && typeof cnfRaw.jkt === "string" && cnfRaw.jkt ? { jkt: cnfRaw.jkt } : null;
|
|
360
104
|
if (cnf) {
|
|
361
|
-
if (!opts.dpop?.proof) throw new
|
|
362
|
-
await verifyDpopProof(opts.dpop.proof, {
|
|
105
|
+
if (!opts.dpop?.proof) throw new chunk2XMKZWC6_cjs.SsoError("ticket is DPoP-bound (cnf.jkt) but no DPoP proof was presented");
|
|
106
|
+
await chunk2XMKZWC6_cjs.verifyDpopProof(opts.dpop.proof, {
|
|
363
107
|
method: opts.dpop.method,
|
|
364
108
|
url: opts.dpop.url,
|
|
365
109
|
ticket: token,
|
|
@@ -369,11 +113,11 @@ function createTicketVerifier(options) {
|
|
|
369
113
|
now
|
|
370
114
|
});
|
|
371
115
|
} else if (requireDpop) {
|
|
372
|
-
throw new
|
|
116
|
+
throw new chunk2XMKZWC6_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)");
|
|
373
117
|
}
|
|
374
118
|
const named = (v) => typeof v === "string" && v ? v : null;
|
|
375
119
|
const clientId = type === "user" ? named(payload.client_id) ?? named(payload.azp) : named(payload.client_id) ?? payload.sub;
|
|
376
|
-
if (clientId === null) throw new
|
|
120
|
+
if (clientId === null) throw new chunk2XMKZWC6_cjs.SsoError("a user ticket names no app (no client_id or azp)");
|
|
377
121
|
return {
|
|
378
122
|
principal: payload.sub,
|
|
379
123
|
type,
|
|
@@ -390,141 +134,184 @@ function createTicketVerifier(options) {
|
|
|
390
134
|
};
|
|
391
135
|
}
|
|
392
136
|
function readActor(raw, depth) {
|
|
393
|
-
if (depth > MAX_ACT_DEPTH) throw new
|
|
137
|
+
if (depth > MAX_ACT_DEPTH) throw new chunk2XMKZWC6_cjs.SsoError(`ticket's delegation chain is deeper than ${MAX_ACT_DEPTH}`);
|
|
394
138
|
const link = raw;
|
|
395
139
|
if (!link || typeof link !== "object" || typeof link.sub !== "string" || link.sub === "") {
|
|
396
|
-
throw new
|
|
140
|
+
throw new chunk2XMKZWC6_cjs.SsoError(`ticket's act (link ${depth}) has no string sub`);
|
|
397
141
|
}
|
|
398
142
|
return link.act === void 0 ? { sub: link.sub } : { sub: link.sub, act: readActor(link.act, depth + 1) };
|
|
399
143
|
}
|
|
400
144
|
|
|
401
145
|
Object.defineProperty(exports, "AVATAR_TYPES", {
|
|
402
146
|
enumerable: true,
|
|
403
|
-
get: function () { return
|
|
147
|
+
get: function () { return chunk2XMKZWC6_cjs.AVATAR_TYPES; }
|
|
148
|
+
});
|
|
149
|
+
Object.defineProperty(exports, "DEFAULT_ISSUER", {
|
|
150
|
+
enumerable: true,
|
|
151
|
+
get: function () { return chunk2XMKZWC6_cjs.DEFAULT_ISSUER; }
|
|
404
152
|
});
|
|
405
153
|
Object.defineProperty(exports, "DEFAULT_SESSION_MAX_AGE", {
|
|
406
154
|
enumerable: true,
|
|
407
|
-
get: function () { return
|
|
155
|
+
get: function () { return chunk2XMKZWC6_cjs.DEFAULT_SESSION_MAX_AGE; }
|
|
408
156
|
});
|
|
409
157
|
Object.defineProperty(exports, "EMBEDDED_FLOW_MAX_AGE", {
|
|
410
158
|
enumerable: true,
|
|
411
|
-
get: function () { return
|
|
159
|
+
get: function () { return chunk2XMKZWC6_cjs.EMBEDDED_FLOW_MAX_AGE; }
|
|
160
|
+
});
|
|
161
|
+
Object.defineProperty(exports, "FLY_CLIENT_ASSERTION_TYPE", {
|
|
162
|
+
enumerable: true,
|
|
163
|
+
get: function () { return chunk2XMKZWC6_cjs.FLY_CLIENT_ASSERTION_TYPE; }
|
|
412
164
|
});
|
|
413
165
|
Object.defineProperty(exports, "JwksError", {
|
|
414
166
|
enumerable: true,
|
|
415
|
-
get: function () { return
|
|
167
|
+
get: function () { return chunk2XMKZWC6_cjs.JwksError; }
|
|
416
168
|
});
|
|
417
169
|
Object.defineProperty(exports, "JwksUnavailableError", {
|
|
418
170
|
enumerable: true,
|
|
419
|
-
get: function () { return
|
|
171
|
+
get: function () { return chunk2XMKZWC6_cjs.JwksUnavailableError; }
|
|
420
172
|
});
|
|
421
173
|
Object.defineProperty(exports, "JwksUnknownKeyError", {
|
|
422
174
|
enumerable: true,
|
|
423
|
-
get: function () { return
|
|
175
|
+
get: function () { return chunk2XMKZWC6_cjs.JwksUnknownKeyError; }
|
|
424
176
|
});
|
|
425
177
|
Object.defineProperty(exports, "MAX_AVATAR_BYTES", {
|
|
426
178
|
enumerable: true,
|
|
427
|
-
get: function () { return
|
|
179
|
+
get: function () { return chunk2XMKZWC6_cjs.MAX_AVATAR_BYTES; }
|
|
428
180
|
});
|
|
429
181
|
Object.defineProperty(exports, "MAX_INVITATIONS", {
|
|
430
182
|
enumerable: true,
|
|
431
|
-
get: function () { return
|
|
183
|
+
get: function () { return chunk2XMKZWC6_cjs.MAX_INVITATIONS; }
|
|
184
|
+
});
|
|
185
|
+
Object.defineProperty(exports, "NoWorkloadIdentityError", {
|
|
186
|
+
enumerable: true,
|
|
187
|
+
get: function () { return chunk2XMKZWC6_cjs.NoWorkloadIdentityError; }
|
|
432
188
|
});
|
|
433
189
|
Object.defineProperty(exports, "SsoAppApiError", {
|
|
434
190
|
enumerable: true,
|
|
435
|
-
get: function () { return
|
|
191
|
+
get: function () { return chunk2XMKZWC6_cjs.SsoAppApiError; }
|
|
436
192
|
});
|
|
437
193
|
Object.defineProperty(exports, "SsoAvatarRejectedError", {
|
|
438
194
|
enumerable: true,
|
|
439
|
-
get: function () { return
|
|
195
|
+
get: function () { return chunk2XMKZWC6_cjs.SsoAvatarRejectedError; }
|
|
440
196
|
});
|
|
441
197
|
Object.defineProperty(exports, "SsoConfigError", {
|
|
442
198
|
enumerable: true,
|
|
443
|
-
get: function () { return
|
|
199
|
+
get: function () { return chunk2XMKZWC6_cjs.SsoConfigError; }
|
|
444
200
|
});
|
|
445
201
|
Object.defineProperty(exports, "SsoError", {
|
|
446
202
|
enumerable: true,
|
|
447
|
-
get: function () { return
|
|
203
|
+
get: function () { return chunk2XMKZWC6_cjs.SsoError; }
|
|
448
204
|
});
|
|
449
205
|
Object.defineProperty(exports, "SsoInsufficientScopeError", {
|
|
450
206
|
enumerable: true,
|
|
451
|
-
get: function () { return
|
|
207
|
+
get: function () { return chunk2XMKZWC6_cjs.SsoInsufficientScopeError; }
|
|
452
208
|
});
|
|
453
209
|
Object.defineProperty(exports, "SsoIntrospectionUnavailableError", {
|
|
454
210
|
enumerable: true,
|
|
455
|
-
get: function () { return
|
|
211
|
+
get: function () { return chunk2XMKZWC6_cjs.SsoIntrospectionUnavailableError; }
|
|
456
212
|
});
|
|
457
213
|
Object.defineProperty(exports, "SsoReauthError", {
|
|
458
214
|
enumerable: true,
|
|
459
|
-
get: function () { return
|
|
215
|
+
get: function () { return chunk2XMKZWC6_cjs.SsoReauthError; }
|
|
460
216
|
});
|
|
461
217
|
Object.defineProperty(exports, "SsoTokenInactiveError", {
|
|
462
218
|
enumerable: true,
|
|
463
|
-
get: function () { return
|
|
219
|
+
get: function () { return chunk2XMKZWC6_cjs.SsoTokenInactiveError; }
|
|
220
|
+
});
|
|
221
|
+
Object.defineProperty(exports, "TicketExchangeError", {
|
|
222
|
+
enumerable: true,
|
|
223
|
+
get: function () { return chunk2XMKZWC6_cjs.TicketExchangeError; }
|
|
224
|
+
});
|
|
225
|
+
Object.defineProperty(exports, "TicketUnavailableError", {
|
|
226
|
+
enumerable: true,
|
|
227
|
+
get: function () { return chunk2XMKZWC6_cjs.TicketUnavailableError; }
|
|
228
|
+
});
|
|
229
|
+
Object.defineProperty(exports, "accessTokenHash", {
|
|
230
|
+
enumerable: true,
|
|
231
|
+
get: function () { return chunk2XMKZWC6_cjs.accessTokenHash; }
|
|
464
232
|
});
|
|
465
233
|
Object.defineProperty(exports, "cookieHeader", {
|
|
466
234
|
enumerable: true,
|
|
467
|
-
get: function () { return
|
|
235
|
+
get: function () { return chunk2XMKZWC6_cjs.cookieHeader; }
|
|
236
|
+
});
|
|
237
|
+
Object.defineProperty(exports, "createDpopKey", {
|
|
238
|
+
enumerable: true,
|
|
239
|
+
get: function () { return chunk2XMKZWC6_cjs.createDpopKey; }
|
|
240
|
+
});
|
|
241
|
+
Object.defineProperty(exports, "createDpopProof", {
|
|
242
|
+
enumerable: true,
|
|
243
|
+
get: function () { return chunk2XMKZWC6_cjs.createDpopProof; }
|
|
468
244
|
});
|
|
469
245
|
Object.defineProperty(exports, "createJwksCache", {
|
|
470
246
|
enumerable: true,
|
|
471
|
-
get: function () { return
|
|
247
|
+
get: function () { return chunk2XMKZWC6_cjs.createJwksCache; }
|
|
248
|
+
});
|
|
249
|
+
Object.defineProperty(exports, "createMemoryReplayStore", {
|
|
250
|
+
enumerable: true,
|
|
251
|
+
get: function () { return chunk2XMKZWC6_cjs.createMemoryReplayStore; }
|
|
472
252
|
});
|
|
473
253
|
Object.defineProperty(exports, "createSsoClient", {
|
|
474
254
|
enumerable: true,
|
|
475
|
-
get: function () { return
|
|
255
|
+
get: function () { return chunk2XMKZWC6_cjs.createSsoClient; }
|
|
256
|
+
});
|
|
257
|
+
Object.defineProperty(exports, "createTicketClient", {
|
|
258
|
+
enumerable: true,
|
|
259
|
+
get: function () { return chunk2XMKZWC6_cjs.createTicketClient; }
|
|
260
|
+
});
|
|
261
|
+
Object.defineProperty(exports, "exchangeUserToken", {
|
|
262
|
+
enumerable: true,
|
|
263
|
+
get: function () { return chunk2XMKZWC6_cjs.exchangeUserToken; }
|
|
264
|
+
});
|
|
265
|
+
Object.defineProperty(exports, "fetchTicket", {
|
|
266
|
+
enumerable: true,
|
|
267
|
+
get: function () { return chunk2XMKZWC6_cjs.fetchTicket; }
|
|
476
268
|
});
|
|
477
269
|
Object.defineProperty(exports, "loadSsoConfig", {
|
|
478
270
|
enumerable: true,
|
|
479
|
-
get: function () { return
|
|
271
|
+
get: function () { return chunk2XMKZWC6_cjs.loadSsoConfig; }
|
|
480
272
|
});
|
|
481
273
|
Object.defineProperty(exports, "memoryTokenStore", {
|
|
482
274
|
enumerable: true,
|
|
483
|
-
get: function () { return
|
|
275
|
+
get: function () { return chunk2XMKZWC6_cjs.memoryTokenStore; }
|
|
276
|
+
});
|
|
277
|
+
Object.defineProperty(exports, "normalizeHtu", {
|
|
278
|
+
enumerable: true,
|
|
279
|
+
get: function () { return chunk2XMKZWC6_cjs.normalizeHtu; }
|
|
484
280
|
});
|
|
485
281
|
Object.defineProperty(exports, "openEmbeddedFlow", {
|
|
486
282
|
enumerable: true,
|
|
487
|
-
get: function () { return
|
|
283
|
+
get: function () { return chunk2XMKZWC6_cjs.openEmbeddedFlow; }
|
|
488
284
|
});
|
|
489
285
|
Object.defineProperty(exports, "readCookie", {
|
|
490
286
|
enumerable: true,
|
|
491
|
-
get: function () { return
|
|
287
|
+
get: function () { return chunk2XMKZWC6_cjs.readCookie; }
|
|
492
288
|
});
|
|
493
289
|
Object.defineProperty(exports, "sealEmbeddedFlow", {
|
|
494
290
|
enumerable: true,
|
|
495
|
-
get: function () { return
|
|
291
|
+
get: function () { return chunk2XMKZWC6_cjs.sealEmbeddedFlow; }
|
|
496
292
|
});
|
|
497
293
|
Object.defineProperty(exports, "signSession", {
|
|
498
294
|
enumerable: true,
|
|
499
|
-
get: function () { return
|
|
295
|
+
get: function () { return chunk2XMKZWC6_cjs.signSession; }
|
|
500
296
|
});
|
|
501
297
|
Object.defineProperty(exports, "signValue", {
|
|
502
298
|
enumerable: true,
|
|
503
|
-
get: function () { return
|
|
299
|
+
get: function () { return chunk2XMKZWC6_cjs.signValue; }
|
|
300
|
+
});
|
|
301
|
+
Object.defineProperty(exports, "verifyDpopProof", {
|
|
302
|
+
enumerable: true,
|
|
303
|
+
get: function () { return chunk2XMKZWC6_cjs.verifyDpopProof; }
|
|
504
304
|
});
|
|
505
305
|
Object.defineProperty(exports, "verifySession", {
|
|
506
306
|
enumerable: true,
|
|
507
|
-
get: function () { return
|
|
307
|
+
get: function () { return chunk2XMKZWC6_cjs.verifySession; }
|
|
508
308
|
});
|
|
509
309
|
Object.defineProperty(exports, "verifyValue", {
|
|
510
310
|
enumerable: true,
|
|
511
|
-
get: function () { return
|
|
311
|
+
get: function () { return chunk2XMKZWC6_cjs.verifyValue; }
|
|
512
312
|
});
|
|
513
|
-
exports.DEFAULT_ISSUER = DEFAULT_ISSUER;
|
|
514
313
|
exports.MAX_ACT_DEPTH = MAX_ACT_DEPTH;
|
|
515
|
-
exports.NoWorkloadIdentityError = NoWorkloadIdentityError;
|
|
516
314
|
exports.TICKET_FORMAT_VERSION = TICKET_FORMAT_VERSION;
|
|
517
|
-
exports.TicketExchangeError = TicketExchangeError;
|
|
518
|
-
exports.TicketUnavailableError = TicketUnavailableError;
|
|
519
|
-
exports.accessTokenHash = accessTokenHash;
|
|
520
|
-
exports.createDpopKey = createDpopKey;
|
|
521
|
-
exports.createDpopProof = createDpopProof;
|
|
522
|
-
exports.createMemoryReplayStore = createMemoryReplayStore;
|
|
523
|
-
exports.createTicketClient = createTicketClient;
|
|
524
315
|
exports.createTicketVerifier = createTicketVerifier;
|
|
525
|
-
exports.exchangeUserToken = exchangeUserToken;
|
|
526
|
-
exports.fetchTicket = fetchTicket;
|
|
527
|
-
exports.normalizeHtu = normalizeHtu;
|
|
528
|
-
exports.verifyDpopProof = verifyDpopProof;
|
|
529
316
|
//# sourceMappingURL=index.cjs.map
|
|
530
317
|
//# sourceMappingURL=index.cjs.map
|