fedipod 0.14.1 → 0.16.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/lib/admin.mjs +32 -4
- package/lib/embed.mjs +6 -1
- package/lib/front-core.mjs +27 -4
- package/lib/intake.mjs +21 -2
- package/lib/links.mjs +35 -0
- package/lib/remote.mjs +119 -3
- package/lib/setup.mjs +56 -0
- package/package.json +1 -1
- package/scripts/pin-solid-oidc.mjs +68 -0
- package/web/admin/setup/index.html +6 -1
- package/web/admin/setup/setup.js +34 -8
- package/web/front/admin.html +21 -17
- package/web/front/run.html +18 -15
- package/web/front/solid-oidc-client.js +6 -0
- package/web/front/solid-client-authn.bundle.js +0 -2
package/lib/admin.mjs
CHANGED
|
@@ -27,7 +27,7 @@ import { identityHomes, rootOf, tildify, defaultProfile, writeJsonAtomic } from
|
|
|
27
27
|
import { copyPrivateHalf, isCurrent, CURRENT_LAYOUT } from './migrate.mjs';
|
|
28
28
|
import { normalizeImport, IMPORT_KINDS } from './import.mjs';
|
|
29
29
|
import { insecureUrlReason } from './safefetch.mjs';
|
|
30
|
-
import { newRun, preflight, runSetup, setupInputError, hasCredential } from './setup.mjs';
|
|
30
|
+
import { newRun, preflight, runSetup, setupInputError, hasCredential, credentialPath } from './setup.mjs';
|
|
31
31
|
import { portFree, freePortFrom } from './ports.mjs';
|
|
32
32
|
import { claimDirectory, yieldDirectory } from './directory.mjs';
|
|
33
33
|
import { localFetch } from './localapi.mjs';
|
|
@@ -112,7 +112,7 @@ const AGENT_VERSION = JSON.parse(fs.readFileSync(path.join(projectRoot, 'package
|
|
|
112
112
|
// /shutdown is here because stopping an agent that was never set up is exactly
|
|
113
113
|
// the case it exists for; it is in LOCAL_ONLY_POSTS below, so it still answers
|
|
114
114
|
// only to this machine.
|
|
115
|
-
const OPEN_POSTS = new Set(['/block', '/unblock', '/setup', '/setup/check', '/shutdown']);
|
|
115
|
+
const OPEN_POSTS = new Set(['/block', '/unblock', '/setup', '/setup/check', '/setup/reset', '/shutdown']);
|
|
116
116
|
|
|
117
117
|
// The page the other server's redirect lands on. Self-contained on purpose:
|
|
118
118
|
// the browser arrives here from somewhere else, and nothing may load from
|
|
@@ -147,11 +147,11 @@ function callbackPage(ok, msg) {
|
|
|
147
147
|
// server there is no such process and no such machine: identities come from
|
|
148
148
|
// the server's own configuration, so these are not there to be found.
|
|
149
149
|
const EMBEDDED_CUT = new Set(['/profiles', '/shutdown', '/new-actor', '/start-actor',
|
|
150
|
-
'/state-move', '/setup', '/setup/check']);
|
|
150
|
+
'/state-move', '/setup', '/setup/check', '/setup/reset']);
|
|
151
151
|
// AP_ALLOWED_HOSTS may name a tailnet host or a reverse-proxy domain. The
|
|
152
152
|
// fediverse is welcome there; creating accounts and editing the record is for
|
|
153
153
|
// whoever is sitting at the machine.
|
|
154
|
-
const LOCAL_ONLY_POSTS = new Set(['/setup', '/setup/check', '/config', '/new-actor', '/start-actor', '/shutdown', '/state-move', '/atproto/connect', '/fediacct/connect', '/fediacct/disconnect', '/fediacct', '/gateway', '/alias', '/import', '/update']);
|
|
154
|
+
const LOCAL_ONLY_POSTS = new Set(['/setup', '/setup/check', '/setup/reset', '/config', '/new-actor', '/start-actor', '/shutdown', '/state-move', '/atproto/connect', '/fediacct/connect', '/fediacct/disconnect', '/fediacct', '/gateway', '/alias', '/import', '/update']);
|
|
155
155
|
// The identity itself. Changing any of these means a different actor at a
|
|
156
156
|
// different address, which is a new setup, not an edit.
|
|
157
157
|
const PERMANENT_CONFIG = ['handle', 'remotePod', 'issuer', 'root', 'kind'];
|
|
@@ -848,6 +848,34 @@ export function buildAdminSurface({ agent, gate, allowed, log = console.log,
|
|
|
848
848
|
}
|
|
849
849
|
// ---- setup, driven by the page at /admin/setup/ ----
|
|
850
850
|
case '/setup/check': return json(res, 200, preflight(body));
|
|
851
|
+
// Discard a credential that never finished setup, so the account and
|
|
852
|
+
// pod can be entered again. The credential a CSS server mints is shown
|
|
853
|
+
// once, so a setup that stops after the mint (a wrong pod answers 401
|
|
854
|
+
// to the first write) leaves the form in "finish" mode with no way to
|
|
855
|
+
// re-enter what was wrong. This removes it locally and reopens the full
|
|
856
|
+
// form. It does NOT revoke server-side — that needs the account
|
|
857
|
+
// password (`fedipod revoke-credential`); the old credential is left on
|
|
858
|
+
// the account, revocable from its dashboard.
|
|
859
|
+
case '/setup/reset': {
|
|
860
|
+
if (isCrossSiteNavigation(req)) return json(res, 403, { error: 'cross-site request' });
|
|
861
|
+
// A working identity is never swapped out this way — that is a
|
|
862
|
+
// teardown (`fedipod retire`), not a half-finished setup.
|
|
863
|
+
if (agent.configured()) {
|
|
864
|
+
return json(res, 409, { error: 'this home holds a working identity — retire it, do not reset' });
|
|
865
|
+
}
|
|
866
|
+
if (setupRun?.phase === 'running') {
|
|
867
|
+
return json(res, 409, { error: 'setup is running — let it finish or stop it first', phase: 'running' });
|
|
868
|
+
}
|
|
869
|
+
const home = agent.home;
|
|
870
|
+
if (!home) return json(res, 500, { error: 'this agent has no AP_HOME to reset' });
|
|
871
|
+
const removed = hasCredential(home);
|
|
872
|
+
if (removed) fs.rmSync(credentialPath(home), { force: true });
|
|
873
|
+
// Drop the pod handle too, so configured() cannot flicker true off a
|
|
874
|
+
// stale in-memory session while the fresh form is filled in.
|
|
875
|
+
agent.remote = null;
|
|
876
|
+
setupRun = null;
|
|
877
|
+
return json(res, 200, { ok: true, removed });
|
|
878
|
+
}
|
|
851
879
|
case '/setup': {
|
|
852
880
|
// A visited page must not be able to navigate this into existence.
|
|
853
881
|
if (isCrossSiteNavigation(req)) return json(res, 403, { error: 'cross-site request' });
|
package/lib/embed.mjs
CHANGED
|
@@ -210,5 +210,10 @@ export async function startEmbeddedAgent({
|
|
|
210
210
|
]);
|
|
211
211
|
};
|
|
212
212
|
|
|
213
|
-
|
|
213
|
+
// podHome and actorUrl are the identity's own locations on the pod. They are
|
|
214
|
+
// returned rather than rebuilt by the caller so the root name lives here.
|
|
215
|
+
return {
|
|
216
|
+
agent, handle, home, surface, host: authorities.host,
|
|
217
|
+
podHome: urls.home, actorUrl: urls.actor, stop,
|
|
218
|
+
};
|
|
214
219
|
}
|
package/lib/front-core.mjs
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
import crypto from 'node:crypto';
|
|
19
19
|
import { handleDelivery } from './gateway-core.mjs';
|
|
20
20
|
import { readCapped } from './safefetch.mjs';
|
|
21
|
+
import { linkTargets, REL } from './links.mjs';
|
|
21
22
|
|
|
22
23
|
// The one WebFinger document, spelled out here rather than imported from
|
|
23
24
|
// wire.mjs: wire drags the agent's whole HTML pipeline (sanitize-html and
|
|
@@ -48,6 +49,24 @@ async function verifyPodToken(request, pathname, verifier) {
|
|
|
48
49
|
// Is this WebID served by the claimed pod? A pod owner's WebID lives on the pod
|
|
49
50
|
// origin — that is the whole proof: a token for a WebID under podHome could
|
|
50
51
|
// only be minted by someone who controls that pod's identity provider.
|
|
52
|
+
// A pod that will not answer must not hold up the person opting in; without
|
|
53
|
+
// an answer the older check stands on its own.
|
|
54
|
+
const OWNER_LOOKUP_MS = 5_000;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Who the pod server says owns the pod. The server that hosts it is the
|
|
58
|
+
* authority on that, so when it answers, its answer decides. A server that
|
|
59
|
+
* says nothing leaves where the WebID lives as the only evidence there is.
|
|
60
|
+
*/
|
|
61
|
+
async function podOwners(podBase, fetchImpl = fetch) {
|
|
62
|
+
try {
|
|
63
|
+
const res = await fetchImpl(podBase, {
|
|
64
|
+
method: 'HEAD', signal: AbortSignal.timeout(OWNER_LOOKUP_MS),
|
|
65
|
+
});
|
|
66
|
+
return linkTargets(res?.headers?.get?.('link'), REL.owner, podBase);
|
|
67
|
+
} catch { return []; }
|
|
68
|
+
}
|
|
69
|
+
|
|
51
70
|
function webidUnderPod(webid, podHome) {
|
|
52
71
|
try { return new URL(webid).origin === new URL(podHome).origin; } catch { return false; }
|
|
53
72
|
}
|
|
@@ -285,7 +304,11 @@ export async function routeFront(request, ctx) {
|
|
|
285
304
|
} catch { return j(400, { error: 'podBase is not a URL' }); }
|
|
286
305
|
const webid = await verifyPodToken(request, pathname, ctx.verifier);
|
|
287
306
|
if (!webid) return j(401, { error: 'a Solid-OIDC token proving the pod is required' });
|
|
288
|
-
|
|
307
|
+
// The pod's own server names its owner when it can. Where it does, that is
|
|
308
|
+
// the proof; where it does not, the WebID must at least live under the pod.
|
|
309
|
+
const owners = await podOwners(podBase, ctx.fetchImpl || fetch);
|
|
310
|
+
const proven = owners.length ? owners.includes(webid) : webid.startsWith(podBase);
|
|
311
|
+
if (!proven) {
|
|
289
312
|
return j(403, { error: 'the token proves a different pod than the one you listed' });
|
|
290
313
|
}
|
|
291
314
|
if (action === 'opt-in') {
|
|
@@ -304,9 +327,9 @@ export async function routeFront(request, ctx) {
|
|
|
304
327
|
return j(400, { error: 'action must be opt-in or opt-out' });
|
|
305
328
|
}
|
|
306
329
|
|
|
307
|
-
// The vendored Solid-OIDC browser library the
|
|
308
|
-
// here because this function owns every path on the domain.
|
|
309
|
-
if (pathname === '/solid-client
|
|
330
|
+
// The vendored Solid-OIDC browser library the /run and /admin pages load —
|
|
331
|
+
// served here because this function owns every path on the domain.
|
|
332
|
+
if (pathname === '/solid-oidc-client.js') {
|
|
310
333
|
if (!ctx.authBundle) return notFound();
|
|
311
334
|
return { status: 200, headers: { 'content-type': 'text/javascript' }, body: ctx.authBundle };
|
|
312
335
|
}
|
package/lib/intake.mjs
CHANGED
|
@@ -18,6 +18,7 @@ import * as $rdf from 'rdflib';
|
|
|
18
18
|
import { USER_AGENT } from './ua.mjs';
|
|
19
19
|
import { PUBLIC } from './wire.mjs';
|
|
20
20
|
import { HTTP_TIMEOUT_MS, readCapped } from './safefetch.mjs';
|
|
21
|
+
import { linkTargets, REL } from './links.mjs';
|
|
21
22
|
import { dropFollower } from './store.mjs';
|
|
22
23
|
|
|
23
24
|
const RDF = $rdf.Namespace('http://www.w3.org/1999/02/22-rdf-syntax-ns#');
|
|
@@ -307,6 +308,24 @@ export class Intake {
|
|
|
307
308
|
}
|
|
308
309
|
}
|
|
309
310
|
|
|
311
|
+
/**
|
|
312
|
+
* Where this pod describes the services it offers. The pod says so on any
|
|
313
|
+
* response about one of its resources; the well-known path is only what a
|
|
314
|
+
* pod that says nothing has always used.
|
|
315
|
+
*/
|
|
316
|
+
async _storageDescriptionUrl() {
|
|
317
|
+
try {
|
|
318
|
+
const head = await fetch(this.urls.base, {
|
|
319
|
+
method: 'HEAD',
|
|
320
|
+
headers: { 'user-agent': USER_AGENT },
|
|
321
|
+
signal: AbortSignal.timeout(HTTP_TIMEOUT_MS),
|
|
322
|
+
});
|
|
323
|
+
const [found] = linkTargets(head.headers.get('link'), REL.storageDescription, this.urls.base);
|
|
324
|
+
if (found) return found;
|
|
325
|
+
} catch { /* the well-known path below */ }
|
|
326
|
+
return this.urls.base + '.well-known/solid';
|
|
327
|
+
}
|
|
328
|
+
|
|
310
329
|
async _subscribeOnce() {
|
|
311
330
|
// Reuse a channel we already have rather than asking for another one.
|
|
312
331
|
const saved = this.store.read(CHANNEL_DOC, null);
|
|
@@ -314,13 +333,13 @@ export class Intake {
|
|
|
314
333
|
this._openSocket(saved.receiveFrom, true);
|
|
315
334
|
return;
|
|
316
335
|
}
|
|
317
|
-
const
|
|
336
|
+
const descUrl = await this._storageDescriptionUrl();
|
|
337
|
+
const descRes = await fetch(descUrl, {
|
|
318
338
|
headers: { accept: 'text/turtle', 'user-agent': USER_AGENT },
|
|
319
339
|
signal: AbortSignal.timeout(HTTP_TIMEOUT_MS),
|
|
320
340
|
});
|
|
321
341
|
// The service description is RDF; ask rdflib which subject is the
|
|
322
342
|
// WebSocketChannel2023 service rather than pattern-matching the document.
|
|
323
|
-
const descUrl = this.urls.base + '.well-known/solid';
|
|
324
343
|
const g = $rdf.graph();
|
|
325
344
|
try { $rdf.parse(await readCapped(descRes), g, descUrl, 'text/turtle'); }
|
|
326
345
|
catch (e) { this.wsState = 'unavailable'; this.log(`service description unparsable (${e.message}) — polling only`); return; }
|
package/lib/links.mjs
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// links.mjs — reading RFC 8288 Link headers.
|
|
2
|
+
//
|
|
3
|
+
// Solid says where a resource's access control lives, where a storage
|
|
4
|
+
// describes itself, and who owns a storage, by putting a link on the response.
|
|
5
|
+
// Working any of those out from the resource's own URL instead is exactly what
|
|
6
|
+
// the specs tell clients not to do, so this is the one place that reads them.
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Every target a Link header gives for one relation, resolved against the URL
|
|
10
|
+
* the header came from. A header may carry several links, and one link may
|
|
11
|
+
* carry several relation names.
|
|
12
|
+
*/
|
|
13
|
+
export function linkTargets(headerValue, rel, baseUrl) {
|
|
14
|
+
if (!headerValue) return [];
|
|
15
|
+
const wanted = String(rel).toLowerCase();
|
|
16
|
+
const out = [];
|
|
17
|
+
// Split on the commas BETWEEN links: one inside a URI has its closing angle
|
|
18
|
+
// bracket still ahead of it, and is left alone.
|
|
19
|
+
for (const part of String(headerValue).split(/,(?![^<]*>)/u)) {
|
|
20
|
+
const link = /^\s*<([^>]*)>\s*(.*)$/u.exec(part);
|
|
21
|
+
if (!link) continue;
|
|
22
|
+
const relParam = /(?:^|;)\s*rel\s*=\s*(?:"([^"]*)"|([^;"\s]+))/iu.exec(link[2]);
|
|
23
|
+
const names = (relParam?.[1] ?? relParam?.[2] ?? '').toLowerCase().split(/\s+/u);
|
|
24
|
+
if (!names.includes(wanted)) continue;
|
|
25
|
+
try { out.push(new URL(link[1], baseUrl).href); } catch { /* not a URL we can follow */ }
|
|
26
|
+
}
|
|
27
|
+
return out;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** The relations this project follows. */
|
|
31
|
+
export const REL = {
|
|
32
|
+
acl: 'acl',
|
|
33
|
+
storageDescription: 'http://www.w3.org/ns/solid/terms#storageDescription',
|
|
34
|
+
owner: 'http://www.w3.org/ns/solid/terms#owner',
|
|
35
|
+
};
|
package/lib/remote.mjs
CHANGED
|
@@ -29,6 +29,12 @@ export { mintCredential, discoverTokenEndpoint, revokeCredentialViaAccount };
|
|
|
29
29
|
// opened — and the callers' existing retry paths take it from there.
|
|
30
30
|
// The parser is shared with the outbound path; see lib/safefetch.mjs.
|
|
31
31
|
import { retryAfterMs, readCapped } from './safefetch.mjs';
|
|
32
|
+
import { linkTargets, REL } from './links.mjs';
|
|
33
|
+
|
|
34
|
+
// A pod whose access rules are ACP policies, not WAC authorizations. This
|
|
35
|
+
// agent writes WAC; over an ACP resource that would be noise where the pod's
|
|
36
|
+
// real rules used to be, so it stops instead.
|
|
37
|
+
const ACP_NS = 'http://www.w3.org/ns/solid/acp#';
|
|
32
38
|
|
|
33
39
|
// The inbox is public-Append, so the listing's size is in other people's
|
|
34
40
|
// hands; reading it whole must still have a ceiling.
|
|
@@ -82,6 +88,11 @@ export class RemotePod {
|
|
|
82
88
|
this.pausedUntil = 0;
|
|
83
89
|
this.probeCount = 0;
|
|
84
90
|
this.log = log;
|
|
91
|
+
// Where each resource's access control lives, as the pod itself said. WAC
|
|
92
|
+
// forbids working it out from the resource's own URL, so it is asked for
|
|
93
|
+
// and remembered rather than assembled.
|
|
94
|
+
this.aclUrls = new Map();
|
|
95
|
+
this.aclFlavour = null; // null until the first write asks what this pod speaks
|
|
85
96
|
// A fronted identity advertises ids on a shared domain but writes to the
|
|
86
97
|
// pod. run-agent installs the fronted→pod mapping here, so every request
|
|
87
98
|
// built from an advertised id lands on the pod — one choke point, and
|
|
@@ -153,9 +164,63 @@ export class RemotePod {
|
|
|
153
164
|
method: 'PUT', headers: { 'content-type': contentType }, body,
|
|
154
165
|
});
|
|
155
166
|
if (res.status >= 400) throw new Error(`PUT ${url} → ${res.status}`);
|
|
167
|
+
// Writing a document is usually the step before setting its access, and
|
|
168
|
+
// the answer to the write already says where that lives. Taking it here
|
|
169
|
+
// spares the extra request the ACL write would otherwise make.
|
|
170
|
+
this.noteAclLink(url, res);
|
|
156
171
|
return res;
|
|
157
172
|
}
|
|
158
173
|
|
|
174
|
+
/** Remember an access-control location the pod volunteered on a response. */
|
|
175
|
+
noteAclLink(url, res) {
|
|
176
|
+
if (this.aclUrls.has(url)) return;
|
|
177
|
+
const [acl] = linkTargets(res?.headers?.get?.('link'), REL.acl, url);
|
|
178
|
+
if (acl) this.aclUrls.set(url, acl);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Where this resource's access control lives. The pod says so on any
|
|
183
|
+
* response about the resource; a pod that says nothing is taken to keep it
|
|
184
|
+
* at the usual suffix, which is what every server this runs against does.
|
|
185
|
+
*/
|
|
186
|
+
async aclUrlFor(targetUrl) {
|
|
187
|
+
const known = this.aclUrls.get(targetUrl);
|
|
188
|
+
if (known) return known;
|
|
189
|
+
try {
|
|
190
|
+
const res = await this.fetch(targetUrl, { method: 'HEAD' });
|
|
191
|
+
this.noteAclLink(targetUrl, res);
|
|
192
|
+
} catch { /* unreachable or no such resource yet: the suffix below */ }
|
|
193
|
+
const resolved = this.aclUrls.get(targetUrl) || targetUrl + '.acl';
|
|
194
|
+
this.aclUrls.set(targetUrl, resolved);
|
|
195
|
+
return resolved;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Whether writing a WAC document here is meaningful. Asked once per pod, on
|
|
200
|
+
* the first access-control write. A pod that answers with ACP policies is
|
|
201
|
+
* left alone: replacing them with authorizations it does not read would take
|
|
202
|
+
* away the rules actually protecting it.
|
|
203
|
+
*/
|
|
204
|
+
async aclWritable(aclUrl) {
|
|
205
|
+
if (this.aclFlavour !== null) return this.aclFlavour;
|
|
206
|
+
this.aclFlavour = true;
|
|
207
|
+
try {
|
|
208
|
+
const res = await this.fetch(aclUrl, { headers: { accept: 'text/turtle' } });
|
|
209
|
+
if (res.status < 300) {
|
|
210
|
+
const g = $rdf.graph();
|
|
211
|
+
$rdf.parse(await res.text(), g, aclUrl, 'text/turtle');
|
|
212
|
+
const acp = g.statements.some(st => st.predicate.value.startsWith(ACP_NS)
|
|
213
|
+
|| st.object.value.startsWith(ACP_NS));
|
|
214
|
+
if (acp) {
|
|
215
|
+
this.aclFlavour = false;
|
|
216
|
+
this.log('this pod states access as ACP policies, which this agent does not write — '
|
|
217
|
+
+ 'its access rules are left exactly as they are, and nothing here is published private');
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
} catch { /* absent, unreadable or unparsable: WAC is what we write */ }
|
|
221
|
+
return this.aclFlavour;
|
|
222
|
+
}
|
|
223
|
+
|
|
159
224
|
async putJson(url, obj, contentType = 'application/activity+json') {
|
|
160
225
|
return this.put(url, JSON.stringify(obj), contentType);
|
|
161
226
|
}
|
|
@@ -173,6 +238,11 @@ export class RemotePod {
|
|
|
173
238
|
|
|
174
239
|
async delete(url) {
|
|
175
240
|
protectedFromDeletion(url);
|
|
241
|
+
// The pattern list above knows the usual name for an access-control
|
|
242
|
+
// document. One the pod named itself is just as fatal to remove.
|
|
243
|
+
for (const acl of this.aclUrls.values()) {
|
|
244
|
+
if (acl === url) throw new Error(`refusing to DELETE an access-control document: ${url}`);
|
|
245
|
+
}
|
|
176
246
|
const res = await this.fetch(url, { method: 'DELETE' });
|
|
177
247
|
return res.status < 400 || res.status === 404;
|
|
178
248
|
}
|
|
@@ -238,8 +308,8 @@ export class RemotePod {
|
|
|
238
308
|
// $rdf.sym() also throws on an illegal IRI, so a pod URL with something odd
|
|
239
309
|
// in it fails here rather than silently producing a document that means
|
|
240
310
|
// something else.
|
|
241
|
-
aclDoc(targetUrl, publicModes, { appendAgents = [] } = {}) {
|
|
242
|
-
const url = targetUrl + '.acl';
|
|
311
|
+
aclDoc(targetUrl, publicModes, { appendAgents = [], aclUrl = null } = {}) {
|
|
312
|
+
const url = aclUrl || targetUrl + '.acl';
|
|
243
313
|
const doc = $rdf.sym(url);
|
|
244
314
|
const target = $rdf.sym(targetUrl);
|
|
245
315
|
const g = $rdf.graph();
|
|
@@ -263,7 +333,9 @@ export class RemotePod {
|
|
|
263
333
|
}
|
|
264
334
|
|
|
265
335
|
async setAcl(targetUrl, publicModes, opts = {}) {
|
|
266
|
-
|
|
336
|
+
const url = await this.aclUrlFor(targetUrl);
|
|
337
|
+
if (!await this.aclWritable(url)) return null;
|
|
338
|
+
return this.put(url, this.aclDoc(targetUrl, publicModes, { ...opts, aclUrl: url }), 'text/turtle');
|
|
267
339
|
}
|
|
268
340
|
|
|
269
341
|
// The WebID profile advertises the actor as an account:
|
|
@@ -295,9 +367,53 @@ export class RemotePod {
|
|
|
295
367
|
const stale = g.statementsMatching(actor, FOAF('accountName'), null, doc)
|
|
296
368
|
.filter(st => st.object.value !== accountName);
|
|
297
369
|
if (!missing.length && !stale.length) return false;
|
|
370
|
+
// A patch touches these statements and nothing else. Rewriting the whole
|
|
371
|
+
// profile re-serialises statements that are not ours — the OIDC issuer
|
|
372
|
+
// among them — and a server is entitled to refuse a write that would.
|
|
373
|
+
const deletes = stale.map(st => [ st.subject, st.predicate, st.object ]);
|
|
374
|
+
if (await this.patchDocument(docUrl, missing, deletes)) return true;
|
|
298
375
|
for (const st of stale) g.remove(st);
|
|
299
376
|
for (const [s, p, o] of missing) g.add(s, p, o, doc);
|
|
300
377
|
await this.put(docUrl, $rdf.serialize(doc, g, docUrl, 'text/turtle'), 'text/turtle');
|
|
301
378
|
return true;
|
|
302
379
|
}
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* An N3 Patch of exactly these statements, or false when the pod will not
|
|
383
|
+
* take one and the caller should write the document instead.
|
|
384
|
+
*
|
|
385
|
+
* The statements are serialised by rdflib; only the wrapper naming what is
|
|
386
|
+
* being patched is assembled here, because N3's braces have no rdflib form.
|
|
387
|
+
*/
|
|
388
|
+
n3Patch(docUrl, inserts, deletes) {
|
|
389
|
+
const block = (triples) => {
|
|
390
|
+
const g = $rdf.graph();
|
|
391
|
+
for (const [s, p, o] of triples) g.add(s, p, o);
|
|
392
|
+
return $rdf.serialize(null, g, docUrl, 'application/n-triples').trim();
|
|
393
|
+
};
|
|
394
|
+
const clauses = [];
|
|
395
|
+
if (deletes.length) clauses.push(` solid:deletes { ${block(deletes)} }`);
|
|
396
|
+
if (inserts.length) clauses.push(` solid:inserts { ${block(inserts)} }`);
|
|
397
|
+
return `@prefix solid: <http://www.w3.org/ns/solid/terms#>.\n`
|
|
398
|
+
+ `<> a solid:InsertDeletePatch;\n${clauses.join(';\n')}.\n`;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
async patchDocument(docUrl, inserts, deletes) {
|
|
402
|
+
let res;
|
|
403
|
+
try {
|
|
404
|
+
res = await this.fetch(docUrl, {
|
|
405
|
+
method: 'PATCH',
|
|
406
|
+
headers: { 'content-type': 'text/n3' },
|
|
407
|
+
body: this.n3Patch(docUrl, inserts, deletes),
|
|
408
|
+
});
|
|
409
|
+
} catch {
|
|
410
|
+
return false; // no PATCH on this transport at all
|
|
411
|
+
}
|
|
412
|
+
if (res.status < 300) return true;
|
|
413
|
+
// The pod cannot patch. Anything else — a 409 saying what we meant to
|
|
414
|
+
// remove is not there any more — is a real answer, and rewriting the whole
|
|
415
|
+
// document over the top of it would destroy whatever changed it.
|
|
416
|
+
if (res.status === 405 || res.status === 415 || res.status === 501) return false;
|
|
417
|
+
throw new Error(`PATCH ${docUrl} → ${res.status}`);
|
|
418
|
+
}
|
|
303
419
|
}
|
package/lib/setup.mjs
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
import fs from 'node:fs';
|
|
12
12
|
import path from 'node:path';
|
|
13
13
|
import { pathToFileURL } from 'node:url';
|
|
14
|
+
import * as $rdf from 'rdflib';
|
|
14
15
|
|
|
15
16
|
import { createAccountWithPod as realCreateAccount } from './account.mjs';
|
|
16
17
|
import { mintCredential as realMint } from './remote.mjs';
|
|
@@ -20,8 +21,48 @@ import { rootOf, recordLastUsed, writeJsonAtomic } from './home.mjs';
|
|
|
20
21
|
import { insecureUrlReason } from './safefetch.mjs';
|
|
21
22
|
import { CURRENT_LAYOUT, isCurrent } from './migrate.mjs';
|
|
22
23
|
|
|
24
|
+
const SOLID = $rdf.Namespace('http://www.w3.org/ns/solid/terms#');
|
|
25
|
+
|
|
23
26
|
export const STEPS = ['account', 'credential', 'bootstrap', 'connect', 'publish', 'verify'];
|
|
24
27
|
|
|
28
|
+
// Read-only pre-check on the pod behind an existing-pod setup, before a
|
|
29
|
+
// credential is minted or the first write is attempted. Two failures it turns
|
|
30
|
+
// from a bare "PUT … → 401" into a plain sentence:
|
|
31
|
+
// 1. the pod host is unreachable — no account should be built on a pod that
|
|
32
|
+
// is not there;
|
|
33
|
+
// 2. the pod's WebID document declares no OIDC issuer. A pod verifies a token
|
|
34
|
+
// by dereferencing its WebID and looking for solid:oidcIssuer; an empty or
|
|
35
|
+
// issuer-less profile makes it reject every write with 401, so setup would
|
|
36
|
+
// stall on the first one. Only judged when the document is publicly
|
|
37
|
+
// readable — a protected one cannot be read here and is left to bootstrap.
|
|
38
|
+
export async function checkPodUsable(pod, { webId, fetch: doFetch = globalThis.fetch } = {}) {
|
|
39
|
+
const base = pod.endsWith('/') ? pod : pod + '/';
|
|
40
|
+
const wid = webId || new URL('profile/card#me', base).href;
|
|
41
|
+
const cardUrl = wid.split('#')[0];
|
|
42
|
+
|
|
43
|
+
let root;
|
|
44
|
+
try { root = await doFetch(base, { headers: { accept: 'text/turtle' } }); }
|
|
45
|
+
catch (e) { return { ok: false, error: `${base} could not be reached (${e.message}). Check the address and that the server is running.` }; }
|
|
46
|
+
if (root.status === 404) return { ok: false, error: `there is no pod at ${base} — the server answered 404.` };
|
|
47
|
+
if (root.status >= 500) return { ok: false, error: `the pod host at ${base} is not responding (HTTP ${root.status}).` };
|
|
48
|
+
|
|
49
|
+
let card;
|
|
50
|
+
try { card = await doFetch(cardUrl, { headers: { accept: 'text/turtle' } }); }
|
|
51
|
+
catch (e) { return { ok: false, error: `the pod's WebID document ${cardUrl} could not be read (${e.message}).` }; }
|
|
52
|
+
if (card.status === 404) return { ok: false, error: `the pod has no WebID document at ${cardUrl} — the identity it needs is missing.` };
|
|
53
|
+
if (card.status >= 500) return { ok: false, error: `the pod host is not serving ${cardUrl} (HTTP ${card.status}).` };
|
|
54
|
+
if (card.status === 401 || card.status === 403) return { ok: true }; // cannot read unauthenticated; leave it to bootstrap
|
|
55
|
+
|
|
56
|
+
const body = await card.text().catch(() => '');
|
|
57
|
+
const g = $rdf.graph();
|
|
58
|
+
try { $rdf.parse(body, g, cardUrl, (card.headers.get('content-type') || 'text/turtle').split(';')[0].trim()); }
|
|
59
|
+
catch { return { ok: false, error: `the pod's WebID document ${cardUrl} could not be parsed as RDF.` }; }
|
|
60
|
+
if (!g.each($rdf.sym(wid), SOLID('oidcIssuer'), null).length) {
|
|
61
|
+
return { ok: false, error: `the pod's WebID document ${cardUrl} declares no OIDC issuer, so the pod will reject every write. It looks empty or incomplete — restore it, or set up with a fresh pod.` };
|
|
62
|
+
}
|
|
63
|
+
return { ok: true };
|
|
64
|
+
}
|
|
65
|
+
|
|
25
66
|
export const credentialPath = (home) => path.join(home, 'credential.json');
|
|
26
67
|
export const hasCredential = (home) => fs.existsSync(credentialPath(home));
|
|
27
68
|
|
|
@@ -118,6 +159,7 @@ export function preflight({ mode, pod, issuer, podName, handle, kind }) {
|
|
|
118
159
|
export async function runSetup({ home, agent, answers, run, deps = {}, log = () => {} }) {
|
|
119
160
|
const createAccount = deps.createAccountWithPod || realCreateAccount;
|
|
120
161
|
const mint = deps.mintCredential || realMint;
|
|
162
|
+
const checkPod = deps.checkPodUsable || checkPodUsable;
|
|
121
163
|
|
|
122
164
|
const at = (key) => run.steps.find(s => s.key === key);
|
|
123
165
|
const begin = (key) => { at(key).state = 'running'; };
|
|
@@ -166,14 +208,21 @@ export async function runSetup({ home, agent, answers, run, deps = {}, log = ()
|
|
|
166
208
|
}
|
|
167
209
|
done('account', pod);
|
|
168
210
|
} else {
|
|
211
|
+
// A pod you bring must be there and carry a usable WebID before a
|
|
212
|
+
// credential is minted against it — no account on a pod that is not
|
|
213
|
+
// reachable, and no silent 401 later on a pod whose profile is empty.
|
|
214
|
+
const usable = await checkPod(pod);
|
|
215
|
+
if (!usable.ok) throw new Error(usable.error);
|
|
169
216
|
skip('account', 'using the pod you already have');
|
|
170
217
|
}
|
|
171
218
|
|
|
172
219
|
// --- credential: the point of no return, and the durability boundary ---
|
|
220
|
+
let resumeWebId = null;
|
|
173
221
|
if (resuming) {
|
|
174
222
|
const rec = JSON.parse(fs.readFileSync(credPath, 'utf8'));
|
|
175
223
|
pod = rec.remotePod;
|
|
176
224
|
root = rec.root;
|
|
225
|
+
resumeWebId = rec.webId || null;
|
|
177
226
|
skip('credential', `already minted — ${credPath}`);
|
|
178
227
|
} else {
|
|
179
228
|
begin('credential');
|
|
@@ -212,6 +261,13 @@ export async function runSetup({ home, agent, answers, run, deps = {}, log = ()
|
|
|
212
261
|
|
|
213
262
|
// --- provision the pod and bring federation up ---
|
|
214
263
|
begin('bootstrap');
|
|
264
|
+
// Resuming skipped the checks the fresh paths ran, and the credential it
|
|
265
|
+
// carries may be the one bound to a pod whose profile is empty — the write
|
|
266
|
+
// below would 401. Catch it here, as this step, with a sentence.
|
|
267
|
+
if (resuming) {
|
|
268
|
+
const ready = await checkPod(pod, { webId: resumeWebId || undefined });
|
|
269
|
+
if (!ready.ok) throw new Error(ready.error);
|
|
270
|
+
}
|
|
215
271
|
await agent.bootstrap({ handle, name: name || handle, root, kind, approveJoins, summary, icon, gateway });
|
|
216
272
|
done('bootstrap');
|
|
217
273
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fedipod",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.0",
|
|
4
4
|
"description": "Standalone single-actor ActivityPub agent whose wire face, RDF truth and state all live on a Solid pod (CSS). Bundles a Phanpy UI and a Mastodon client-API facade.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// pin-solid-oidc.mjs — vendor a pinned copy of the browser Solid-OIDC client
|
|
3
|
+
// for the front pages (run.html, admin.html).
|
|
4
|
+
//
|
|
5
|
+
// Downloads the exact npm tarball, verifies its sha512 against the pin below,
|
|
6
|
+
// extracts the worker-free CORE build (SessionCore — no SharedWorker, right for
|
|
7
|
+
// these one-shot sign-in pages), strips the trailing sourceMappingURL, prepends
|
|
8
|
+
// a provenance banner, and writes web/front/solid-oidc-client.js.
|
|
9
|
+
//
|
|
10
|
+
// Re-pin: bump VERSION + INTEGRITY (from `npm view @uvdsl/solid-oidc-client-browser@<v> dist.integrity`)
|
|
11
|
+
// and re-run. The output filename stays unversioned so routes/tests don't churn.
|
|
12
|
+
|
|
13
|
+
import { createHash } from 'node:crypto';
|
|
14
|
+
import { writeFileSync, mkdirSync } from 'node:fs';
|
|
15
|
+
import { dirname, resolve } from 'node:path';
|
|
16
|
+
import { fileURLToPath } from 'node:url';
|
|
17
|
+
import { gunzipSync } from 'node:zlib';
|
|
18
|
+
|
|
19
|
+
const NAME = '@uvdsl/solid-oidc-client-browser';
|
|
20
|
+
const VERSION = '0.2.3';
|
|
21
|
+
const INTEGRITY = 'sha512-WzVlxv46EUSoqm7ovsWJRZq8KEI/CdpA9O1fXoiP8bihs2cNxPnet3YcqvIYWYMsTrf0zsR031l5s/BzQ9MEgA==';
|
|
22
|
+
const TARBALL = `https://registry.npmjs.org/@uvdsl/solid-oidc-client-browser/-/solid-oidc-client-browser-${VERSION}.tgz`;
|
|
23
|
+
const ENTRY = 'package/dist/esm/core/index.min.js';
|
|
24
|
+
|
|
25
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
26
|
+
const outFile = resolve(here, '..', 'web', 'front', 'solid-oidc-client.js');
|
|
27
|
+
|
|
28
|
+
function verifyIntegrity(buf, integrity) {
|
|
29
|
+
const [alg, expected] = integrity.split('-', 2);
|
|
30
|
+
const actual = createHash(alg).update(buf).digest('base64');
|
|
31
|
+
if (actual !== expected) {
|
|
32
|
+
throw new Error(`integrity mismatch: expected ${alg}-${expected}, got ${alg}-${actual}`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Minimal ustar tar reader — enough to pull one file out of the npm tarball.
|
|
37
|
+
function readTarEntry(tar, wanted) {
|
|
38
|
+
let off = 0;
|
|
39
|
+
while (off + 512 <= tar.length) {
|
|
40
|
+
const name = tar.toString('utf8', off, off + 100).replace(/\0.*$/, '');
|
|
41
|
+
if (!name) break;
|
|
42
|
+
const size = parseInt(tar.toString('utf8', off + 124, off + 136).replace(/\0.*$/, '').trim() || '0', 8);
|
|
43
|
+
const start = off + 512;
|
|
44
|
+
if (name === wanted) return tar.subarray(start, start + size);
|
|
45
|
+
off = start + Math.ceil(size / 512) * 512;
|
|
46
|
+
}
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const res = await fetch(TARBALL);
|
|
51
|
+
if (!res.ok) throw new Error(`download failed: HTTP ${res.status}`);
|
|
52
|
+
const gz = Buffer.from(await res.arrayBuffer());
|
|
53
|
+
verifyIntegrity(gz, INTEGRITY);
|
|
54
|
+
|
|
55
|
+
const tar = gunzipSync(gz);
|
|
56
|
+
const entry = readTarEntry(tar, ENTRY);
|
|
57
|
+
if (!entry) throw new Error(`entry not found in tarball: ${ENTRY}`);
|
|
58
|
+
|
|
59
|
+
let code = entry.toString('utf8').replace(/\n?\/\/# sourceMappingURL=.*$/m, '');
|
|
60
|
+
const banner =
|
|
61
|
+
`// ${NAME}@${VERSION} — vendored worker-free Solid-OIDC client (core build).\n` +
|
|
62
|
+
`// tarball ${INTEGRITY}\n` +
|
|
63
|
+
`// license MIT — https://github.com/uvdsl/solid-oidc-client-browser\n` +
|
|
64
|
+
`// Regenerate with: node scripts/pin-solid-oidc.mjs\n`;
|
|
65
|
+
|
|
66
|
+
mkdirSync(dirname(outFile), { recursive: true });
|
|
67
|
+
writeFileSync(outFile, banner + code + '\n');
|
|
68
|
+
console.log(`[pin-solid-oidc] wrote ${outFile} (${(code.length / 1024).toFixed(1)} KB)`);
|
|
@@ -94,7 +94,8 @@ button:disabled { opacity: .5; cursor: default; }
|
|
|
94
94
|
<ul class="steps" id="run-steps"></ul>
|
|
95
95
|
<p class="sr-only" id="run-say" role="status"></p>
|
|
96
96
|
<p class="err" id="run-error" role="alert"></p>
|
|
97
|
-
<p><button id="run-again" title=" Try the step that failed again" hidden>Try again</button
|
|
97
|
+
<p><button id="run-again" title=" Try the step that failed again" hidden>Try again</button>
|
|
98
|
+
<button id="run-reenter" title=" Discard the credential and enter the account and pod again" hidden>Re-enter credentials</button></p>
|
|
98
99
|
</section>
|
|
99
100
|
|
|
100
101
|
<section id="pane-form" hidden>
|
|
@@ -164,6 +165,10 @@ button:disabled { opacity: .5; cursor: default; }
|
|
|
164
165
|
</div>
|
|
165
166
|
</fieldset>
|
|
166
167
|
|
|
168
|
+
<p id="row-reenter" class="warn" hidden>Finishing a setup that already has an account credential.
|
|
169
|
+
Wrong account or pod?
|
|
170
|
+
<button type="button" id="form-reenter" title=" Discard the credential and enter the account and pod again">Re-enter credentials</button></p>
|
|
171
|
+
|
|
167
172
|
<h2>You will be</h2>
|
|
168
173
|
<p class="address" id="preview">…</p>
|
|
169
174
|
<div id="preview-notes"></div>
|
package/web/admin/setup/setup.js
CHANGED
|
@@ -78,14 +78,16 @@ function paneForm() {
|
|
|
78
78
|
// Resuming: the account exists and the credential is minted. Asking for a
|
|
79
79
|
// password again would mint a second one and orphan the first, which cannot
|
|
80
80
|
// be recovered.
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
81
|
+
// Set both ways, not just hidden-when-resuming: re-entering credentials
|
|
82
|
+
// re-renders this in the other mode, and a one-way hide would strand the
|
|
83
|
+
// account and pod fields off-screen. Resuming settles the account and the
|
|
84
|
+
// credential, so those fields go; a fresh run shows them.
|
|
85
|
+
$('row-reenter').hidden = !state.resumable;
|
|
86
|
+
$('fs-pod').hidden = state.resumable;
|
|
87
|
+
// The password field also goes when AP_PASSWORD supplied it in the environment.
|
|
88
|
+
$('row-password').hidden = state.resumable || state.passwordSupplied;
|
|
89
|
+
$('submit').textContent = state.resumable ? 'Finish setting up' : 'Create it';
|
|
90
|
+
if (state.resumable) $('form-reenter').onclick = reEnter; // the escape from a wrong credential
|
|
89
91
|
show('pane-form');
|
|
90
92
|
// The markup's autofocus was set while this pane was still hidden, so it
|
|
91
93
|
// never fired — move focus to the first field now that the pane is showing.
|
|
@@ -208,6 +210,30 @@ async function watchRun() {
|
|
|
208
210
|
state = json || state;
|
|
209
211
|
paneForm();
|
|
210
212
|
};
|
|
213
|
+
// A failure past the credential (a wrong pod answering 401 to the first
|
|
214
|
+
// write) leaves a credential that "Try again" can only re-use. Offer to
|
|
215
|
+
// discard it and re-enter the account and pod — only when there is one.
|
|
216
|
+
const { json: st } = await api('/setup/state');
|
|
217
|
+
const reenter = $('run-reenter');
|
|
218
|
+
reenter.hidden = !st?.hasCredential;
|
|
219
|
+
reenter.onclick = reEnter;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Discard the saved credential (server confirms, then the full form returns).
|
|
223
|
+
// Destructive and unrecoverable — a minted credential is shown once — so it
|
|
224
|
+
// asks first.
|
|
225
|
+
async function reEnter() {
|
|
226
|
+
if (!confirm('Discard the saved account credential and enter the account and pod again?\n\n'
|
|
227
|
+
+ 'The old credential is left on your account — revoke it from the account dashboard if you want it gone.')) return;
|
|
228
|
+
const { status, json } = await postJson('/setup/reset', {});
|
|
229
|
+
if (status !== 200) {
|
|
230
|
+
const box = $('pane-form').hidden ? $('run-error') : $('form-error');
|
|
231
|
+
box.textContent = json?.error || `could not reset (HTTP ${status})`;
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
const { json: st } = await api('/setup/state');
|
|
235
|
+
state = st || {};
|
|
236
|
+
paneForm();
|
|
211
237
|
}
|
|
212
238
|
|
|
213
239
|
function renderRun(run) {
|
package/web/front/admin.html
CHANGED
|
@@ -47,40 +47,44 @@ accounts that only use it as their gateway. Sign in as the server's admin to see
|
|
|
47
47
|
<tbody id="roster-rows"></tbody>
|
|
48
48
|
</table>
|
|
49
49
|
|
|
50
|
-
<script
|
|
51
|
-
<script>
|
|
50
|
+
<script type="module">
|
|
52
51
|
const $ = (id) => document.getElementById(id);
|
|
53
52
|
const note = (text) => { const n = $('roster-note'); n.hidden = !text; n.textContent = text || ''; };
|
|
54
53
|
|
|
54
|
+
// The Solid-OIDC client for the sign-in round trip. Constructed once; the
|
|
55
|
+
// redirect state lives in this tab's sessionStorage, so the same session
|
|
56
|
+
// finishes the login on the way back.
|
|
57
|
+
let session = null;
|
|
58
|
+
try {
|
|
59
|
+
const { SessionCore } = await import('/solid-oidc-client.js');
|
|
60
|
+
session = new SessionCore({ redirect_uris: [location.origin + '/admin'], client_name: 'FediPod admin' });
|
|
61
|
+
} catch { /* leave null — the "did not load" note fires */ }
|
|
62
|
+
|
|
55
63
|
$('roster-issuer').addEventListener('input', () => {
|
|
56
64
|
$('roster-signin').disabled = !/^https?:\/\/\S+/.test($('roster-issuer').value.trim());
|
|
57
65
|
});
|
|
58
66
|
|
|
59
67
|
$('roster-form').addEventListener('submit', (ev) => {
|
|
60
68
|
ev.preventDefault();
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
oidcIssuer: $('roster-issuer').value.trim(),
|
|
65
|
-
redirectUrl: location.origin + '/admin',
|
|
66
|
-
clientName: 'FediPod admin',
|
|
67
|
-
}).catch((e) => note('sign-in failed to start: ' + e.message));
|
|
69
|
+
if (!session) { note('the sign-in library did not load — reload and try again'); return; }
|
|
70
|
+
session.login($('roster-issuer').value.trim(), location.origin + '/admin')
|
|
71
|
+
.catch((e) => note('sign-in failed to start: ' + e.message));
|
|
68
72
|
});
|
|
69
73
|
|
|
70
74
|
// Back from the identity provider: read the roster with the proven login.
|
|
71
75
|
(async () => {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
+
if (!session) return;
|
|
77
|
+
await session.handleRedirectFromLogin().catch(() => {});
|
|
78
|
+
if (!session.isActive) return;
|
|
79
|
+
const webId = session.webId;
|
|
76
80
|
|
|
77
81
|
// Signed fetches build a DPoP proof from the URL, so it must be absolute.
|
|
78
82
|
const load = async () => {
|
|
79
83
|
let res;
|
|
80
|
-
try { res = await
|
|
84
|
+
try { res = await session.authFetch(location.origin + '/api/roster'); }
|
|
81
85
|
catch (e) { note('the roster request failed: ' + e.message); return; }
|
|
82
86
|
const d = await res.json().catch(() => ({}));
|
|
83
|
-
if (res.status === 403) { note('signed in as ' +
|
|
87
|
+
if (res.status === 403) { note('signed in as ' + webId + ', which is not this server’s admin'); return; }
|
|
84
88
|
if (res.status === 501) { note('this server names no admin — set FEDIPOD_ADMIN_WEBID and redeploy'); return; }
|
|
85
89
|
if (res.status !== 200) { note('roster unavailable: ' + (d.error || 'HTTP ' + res.status)); return; }
|
|
86
90
|
const rows = $('roster-rows');
|
|
@@ -108,7 +112,7 @@ accounts that only use it as their gateway. Sign in as the server's admin to see
|
|
|
108
112
|
if (!confirm('Remove ' + a.address + ' from this server? The name stops resolving here; nothing on their pod is touched.')) return;
|
|
109
113
|
let res;
|
|
110
114
|
try {
|
|
111
|
-
res = await
|
|
115
|
+
res = await session.authFetch(location.origin + '/api/revoke', {
|
|
112
116
|
method: 'POST', headers: { 'content-type': 'application/json' },
|
|
113
117
|
body: JSON.stringify({ handle: a.handle }),
|
|
114
118
|
});
|
|
@@ -120,7 +124,7 @@ accounts that only use it as their gateway. Sign in as the server's admin to see
|
|
|
120
124
|
note('removed ' + a.address);
|
|
121
125
|
};
|
|
122
126
|
|
|
123
|
-
note('signed in as ' +
|
|
127
|
+
note('signed in as ' + webId + ' — reading the roster…');
|
|
124
128
|
await load();
|
|
125
129
|
})();
|
|
126
130
|
</script>
|
package/web/front/run.html
CHANGED
|
@@ -45,10 +45,18 @@ you to keep running. Sign in with your pod to prove it is yours.</p>
|
|
|
45
45
|
<p class="hint" id="run-note" hidden></p>
|
|
46
46
|
</form>
|
|
47
47
|
|
|
48
|
-
<script
|
|
49
|
-
<script>
|
|
48
|
+
<script type="module">
|
|
50
49
|
const $ = (id) => document.getElementById(id);
|
|
51
50
|
|
|
51
|
+
// The Solid-OIDC client for the sign-in round trip. Constructed once; the
|
|
52
|
+
// redirect state (PKCE, csrf) lives in this tab's sessionStorage, so the same
|
|
53
|
+
// session finishes the login on the way back.
|
|
54
|
+
let session = null;
|
|
55
|
+
try {
|
|
56
|
+
const { SessionCore } = await import('/solid-oidc-client.js');
|
|
57
|
+
session = new SessionCore({ redirect_uris: [location.origin + '/run'], client_name: 'FediPod gateway' });
|
|
58
|
+
} catch { /* leave null — the "did not load" notes below fire */ }
|
|
59
|
+
|
|
52
60
|
// Offered only where this server actually runs identities. An
|
|
53
61
|
// unauthenticated probe tells the two apart: 501 = not offered here,
|
|
54
62
|
// anything else = offered (a real opt-in still needs the signed-in proof).
|
|
@@ -85,37 +93,32 @@ you to keep running. Sign in with your pod to prove it is yours.</p>
|
|
|
85
93
|
$('run-issuer').addEventListener('input', runFormCheck);
|
|
86
94
|
|
|
87
95
|
const runStart = (action) => {
|
|
88
|
-
const auth = window.solidClientAuthentication;
|
|
89
96
|
const n = $('run-note');
|
|
90
|
-
if (!
|
|
97
|
+
if (!session) { n.hidden = false; n.textContent = 'the sign-in library did not load — reload and try again'; return; }
|
|
91
98
|
const u = new URL($('run-pod-url').value.trim());
|
|
92
99
|
if (u.pathname === '') u.pathname = '/';
|
|
93
100
|
if (!u.pathname.endsWith('/')) u.pathname += '/';
|
|
94
101
|
u.search = ''; u.hash = '';
|
|
95
102
|
sessionStorage.setItem('fp-run', JSON.stringify({ podBase: u.href, action }));
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
redirectUrl: location.origin + '/run',
|
|
99
|
-
clientName: 'FediPod gateway',
|
|
100
|
-
}).catch((e) => { n.hidden = false; n.textContent = 'sign-in failed to start: ' + e.message; });
|
|
103
|
+
session.login($('run-issuer').value.trim(), location.origin + '/run')
|
|
104
|
+
.catch((e) => { n.hidden = false; n.textContent = 'sign-in failed to start: ' + e.message; });
|
|
101
105
|
};
|
|
102
106
|
$('run-continue').onclick = () => runStart('opt-in');
|
|
103
107
|
$('run-leave').onclick = () => runStart('opt-out');
|
|
104
108
|
|
|
105
109
|
// Back from the identity provider: finish the opt-in with the proven login.
|
|
106
110
|
(async () => {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
const info = await auth.handleIncomingRedirect().catch(() => null);
|
|
111
|
+
if (!session) return;
|
|
112
|
+
await session.handleRedirectFromLogin().catch(() => {});
|
|
110
113
|
const pending = sessionStorage.getItem('fp-run');
|
|
111
|
-
if (!
|
|
114
|
+
if (!session.isActive || !pending) return;
|
|
112
115
|
sessionStorage.removeItem('fp-run');
|
|
113
116
|
const p = JSON.parse(pending);
|
|
114
117
|
const n = $('run-note');
|
|
115
118
|
n.hidden = false;
|
|
116
|
-
n.textContent = 'signed in as ' +
|
|
119
|
+
n.textContent = 'signed in as ' + session.webId + ' — asking the server…';
|
|
117
120
|
// The signed fetch builds a DPoP proof from the URL, so it must be absolute.
|
|
118
|
-
const res = await
|
|
121
|
+
const res = await session.authFetch(location.origin + '/api/agent', {
|
|
119
122
|
method: 'POST', headers: { 'content-type': 'application/json' },
|
|
120
123
|
body: JSON.stringify({ action: p.action, podBase: p.podBase }),
|
|
121
124
|
}).catch(() => null);
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// @uvdsl/solid-oidc-client-browser@0.2.3 — vendored worker-free Solid-OIDC client (core build).
|
|
2
|
+
// tarball sha512-WzVlxv46EUSoqm7ovsWJRZq8KEI/CdpA9O1fXoiP8bihs2cNxPnet3YcqvIYWYMsTrf0zsR031l5s/BzQ9MEgA==
|
|
3
|
+
// license MIT — https://github.com/uvdsl/solid-oidc-client-browser
|
|
4
|
+
// Regenerate with: node scripts/pin-solid-oidc.mjs
|
|
5
|
+
var e=crypto;const t=e=>e instanceof CryptoKey;var r=async(t,r)=>{const a=`SHA-${t.slice(-3)}`;return new Uint8Array(await e.subtle.digest(a,r))};const a=new TextEncoder,n=new TextDecoder;function s(...e){const t=e.reduce(((e,{length:t})=>e+t),0),r=new Uint8Array(t);let a=0;for(const t of e)r.set(t,a),a+=t.length;return r}const i=e=>(e=>{let t=e;"string"==typeof t&&(t=a.encode(t));const r=[];for(let e=0;e<t.length;e+=32768)r.push(String.fromCharCode.apply(null,t.subarray(e,e+32768)));return btoa(r.join(""))})(e).replace(/=/g,"").replace(/\+/g,"-").replace(/\//g,"_"),o=e=>{let t=e;t instanceof Uint8Array&&(t=n.decode(t)),t=t.replace(/-/g,"+").replace(/_/g,"/").replace(/\s/g,"");try{return(e=>{const t=atob(e),r=new Uint8Array(t.length);for(let e=0;e<t.length;e++)r[e]=t.charCodeAt(e);return r})(t)}catch{throw new TypeError("The input to be decoded is not correctly encoded.")}};class c extends Error{constructor(e,t){super(e,t),this.code="ERR_JOSE_GENERIC",this.name=this.constructor.name,Error.captureStackTrace?.(this,this.constructor)}}c.code="ERR_JOSE_GENERIC";class d extends c{constructor(e,t,r="unspecified",a="unspecified"){super(e,{cause:{claim:r,reason:a,payload:t}}),this.code="ERR_JWT_CLAIM_VALIDATION_FAILED",this.claim=r,this.reason=a,this.payload=t}}d.code="ERR_JWT_CLAIM_VALIDATION_FAILED";class h extends c{constructor(e,t,r="unspecified",a="unspecified"){super(e,{cause:{claim:r,reason:a,payload:t}}),this.code="ERR_JWT_EXPIRED",this.claim=r,this.reason=a,this.payload=t}}h.code="ERR_JWT_EXPIRED";class l extends c{constructor(){super(...arguments),this.code="ERR_JOSE_ALG_NOT_ALLOWED"}}l.code="ERR_JOSE_ALG_NOT_ALLOWED";class p extends c{constructor(){super(...arguments),this.code="ERR_JOSE_NOT_SUPPORTED"}}p.code="ERR_JOSE_NOT_SUPPORTED";(class extends c{constructor(e="decryption operation failed",t){super(e,t),this.code="ERR_JWE_DECRYPTION_FAILED"}}).code="ERR_JWE_DECRYPTION_FAILED";(class extends c{constructor(){super(...arguments),this.code="ERR_JWE_INVALID"}}).code="ERR_JWE_INVALID";class u extends c{constructor(){super(...arguments),this.code="ERR_JWS_INVALID"}}u.code="ERR_JWS_INVALID";class f extends c{constructor(){super(...arguments),this.code="ERR_JWT_INVALID"}}f.code="ERR_JWT_INVALID";class w extends c{constructor(){super(...arguments),this.code="ERR_JWK_INVALID"}}w.code="ERR_JWK_INVALID";class y extends c{constructor(){super(...arguments),this.code="ERR_JWKS_INVALID"}}y.code="ERR_JWKS_INVALID";class m extends c{constructor(e="no applicable key found in the JSON Web Key Set",t){super(e,t),this.code="ERR_JWKS_NO_MATCHING_KEY"}}m.code="ERR_JWKS_NO_MATCHING_KEY";class g extends c{constructor(e="multiple matching keys found in the JSON Web Key Set",t){super(e,t),this.code="ERR_JWKS_MULTIPLE_MATCHING_KEYS"}}g.code="ERR_JWKS_MULTIPLE_MATCHING_KEYS";class _ extends c{constructor(e="request timed out",t){super(e,t),this.code="ERR_JWKS_TIMEOUT"}}_.code="ERR_JWKS_TIMEOUT";class S extends c{constructor(e="signature verification failed",t){super(e,t),this.code="ERR_JWS_SIGNATURE_VERIFICATION_FAILED"}}function E(e,t="algorithm.name"){return new TypeError(`CryptoKey does not support this operation, its ${t} must be ${e}`)}function b(e,t){return e.name===t}function k(e){return parseInt(e.name.slice(4),10)}function v(e,t,...r){switch(t){case"HS256":case"HS384":case"HS512":{if(!b(e.algorithm,"HMAC"))throw E("HMAC");const r=parseInt(t.slice(2),10);if(k(e.algorithm.hash)!==r)throw E(`SHA-${r}`,"algorithm.hash");break}case"RS256":case"RS384":case"RS512":{if(!b(e.algorithm,"RSASSA-PKCS1-v1_5"))throw E("RSASSA-PKCS1-v1_5");const r=parseInt(t.slice(2),10);if(k(e.algorithm.hash)!==r)throw E(`SHA-${r}`,"algorithm.hash");break}case"PS256":case"PS384":case"PS512":{if(!b(e.algorithm,"RSA-PSS"))throw E("RSA-PSS");const r=parseInt(t.slice(2),10);if(k(e.algorithm.hash)!==r)throw E(`SHA-${r}`,"algorithm.hash");break}case"EdDSA":if("Ed25519"!==e.algorithm.name&&"Ed448"!==e.algorithm.name)throw E("Ed25519 or Ed448");break;case"Ed25519":if(!b(e.algorithm,"Ed25519"))throw E("Ed25519");break;case"ES256":case"ES384":case"ES512":{if(!b(e.algorithm,"ECDSA"))throw E("ECDSA");const r=function(e){switch(e){case"ES256":return"P-256";case"ES384":return"P-384";case"ES512":return"P-521";default:throw new Error("unreachable")}}(t);if(e.algorithm.namedCurve!==r)throw E(r,"algorithm.namedCurve");break}default:throw new TypeError("CryptoKey does not support this operation")}!function(e,t){if(t.length&&!t.some((t=>e.usages.includes(t)))){let e="CryptoKey does not support this operation, its usages must include ";if(t.length>2){const r=t.pop();e+=`one of ${t.join(", ")}, or ${r}.`}else 2===t.length?e+=`one of ${t[0]} or ${t[1]}.`:e+=`${t[0]}.`;throw new TypeError(e)}}(e,r)}function A(e,t,...r){if((r=r.filter(Boolean)).length>2){const t=r.pop();e+=`one of type ${r.join(", ")}, or ${t}.`}else 2===r.length?e+=`one of type ${r[0]} or ${r[1]}.`:e+=`of type ${r[0]}.`;return null==t?e+=` Received ${t}`:"function"==typeof t&&t.name?e+=` Received function ${t.name}`:"object"==typeof t&&null!=t&&t.constructor?.name&&(e+=` Received an instance of ${t.constructor.name}`),e}S.code="ERR_JWS_SIGNATURE_VERIFICATION_FAILED";var T=(e,...t)=>A("Key must be ",e,...t);function P(e,t,...r){return A(`Key for the ${e} algorithm must be `,t,...r)}var R=e=>!!t(e)||"KeyObject"===e?.[Symbol.toStringTag];const I=["CryptoKey"];var C=(...e)=>{const t=e.filter(Boolean);if(0===t.length||1===t.length)return!0;let r;for(const e of t){const t=Object.keys(e);if(r&&0!==r.size)for(const e of t){if(r.has(e))return!1;r.add(e)}else r=new Set(t)}return!0};function D(e){if("object"!=typeof(t=e)||null===t||"[object Object]"!==Object.prototype.toString.call(e))return!1;var t;if(null===Object.getPrototypeOf(e))return!0;let r=e;for(;null!==Object.getPrototypeOf(r);)r=Object.getPrototypeOf(r);return Object.getPrototypeOf(e)===r}var W=(e,t)=>{if(e.startsWith("RS")||e.startsWith("PS")){const{modulusLength:r}=t.algorithm;if("number"!=typeof r||r<2048)throw new TypeError(`${e} requires key modulusLength to be 2048 bits or larger`)}};function H(e){return D(e)&&"string"==typeof e.kty}var J=async t=>{if(!t.alg)throw new TypeError('"alg" argument is required when "jwk.alg" is not present');const{algorithm:r,keyUsages:a}=function(e){let t,r;switch(e.kty){case"RSA":switch(e.alg){case"PS256":case"PS384":case"PS512":t={name:"RSA-PSS",hash:`SHA-${e.alg.slice(-3)}`},r=e.d?["sign"]:["verify"];break;case"RS256":case"RS384":case"RS512":t={name:"RSASSA-PKCS1-v1_5",hash:`SHA-${e.alg.slice(-3)}`},r=e.d?["sign"]:["verify"];break;case"RSA-OAEP":case"RSA-OAEP-256":case"RSA-OAEP-384":case"RSA-OAEP-512":t={name:"RSA-OAEP",hash:`SHA-${parseInt(e.alg.slice(-3),10)||1}`},r=e.d?["decrypt","unwrapKey"]:["encrypt","wrapKey"];break;default:throw new p('Invalid or unsupported JWK "alg" (Algorithm) Parameter value')}break;case"EC":switch(e.alg){case"ES256":t={name:"ECDSA",namedCurve:"P-256"},r=e.d?["sign"]:["verify"];break;case"ES384":t={name:"ECDSA",namedCurve:"P-384"},r=e.d?["sign"]:["verify"];break;case"ES512":t={name:"ECDSA",namedCurve:"P-521"},r=e.d?["sign"]:["verify"];break;case"ECDH-ES":case"ECDH-ES+A128KW":case"ECDH-ES+A192KW":case"ECDH-ES+A256KW":t={name:"ECDH",namedCurve:e.crv},r=e.d?["deriveBits"]:[];break;default:throw new p('Invalid or unsupported JWK "alg" (Algorithm) Parameter value')}break;case"OKP":switch(e.alg){case"Ed25519":t={name:"Ed25519"},r=e.d?["sign"]:["verify"];break;case"EdDSA":t={name:e.crv},r=e.d?["sign"]:["verify"];break;case"ECDH-ES":case"ECDH-ES+A128KW":case"ECDH-ES+A192KW":case"ECDH-ES+A256KW":t={name:e.crv},r=e.d?["deriveBits"]:[];break;default:throw new p('Invalid or unsupported JWK "alg" (Algorithm) Parameter value')}break;default:throw new p('Invalid or unsupported JWK "kty" (Key Type) Parameter value')}return{algorithm:t,keyUsages:r}}(t),n=[r,t.ext??!1,t.key_ops??a],s={...t};return delete s.alg,delete s.use,e.subtle.importKey("jwk",s,...n)};const x=e=>o(e);let K,O;const j=e=>"KeyObject"===e?.[Symbol.toStringTag],N=async(e,t,r,a,n=!1)=>{let s=e.get(t);if(s?.[a])return s[a];const i=await J({...r,alg:a});return n&&Object.freeze(t),s?s[a]=i:e.set(t,{[a]:i}),i};var U=(e,t)=>{if(j(e)){let r=e.export({format:"jwk"});return delete r.d,delete r.dp,delete r.dq,delete r.p,delete r.q,delete r.qi,r.k?x(r.k):(O||(O=new WeakMap),N(O,e,r,t))}if(H(e)){if(e.k)return o(e.k);O||(O=new WeakMap);return N(O,e,e,t,!0)}return e},$=(e,t)=>{if(j(e)){let r=e.export({format:"jwk"});return r.k?x(r.k):(K||(K=new WeakMap),N(K,e,r,t))}if(H(e)){if(e.k)return o(e.k);K||(K=new WeakMap);return N(K,e,e,t,!0)}return e};async function L(e,t){if(!D(e))throw new TypeError("JWK must be an object");switch(t||(t=e.alg),e.kty){case"oct":if("string"!=typeof e.k||!e.k)throw new TypeError('missing "k" (Key Value) Parameter value');return o(e.k);case"RSA":if("oth"in e&&void 0!==e.oth)throw new p('RSA JWK "oth" (Other Primes Info) Parameter value is not supported');case"EC":case"OKP":return J({...e,alg:t});default:throw new p('Unsupported "kty" (Key Type) Parameter value')}}const M=e=>e?.[Symbol.toStringTag],F=(e,t,r)=>{if(void 0!==t.use&&"sig"!==t.use)throw new TypeError("Invalid key for this operation, when present its use must be sig");if(void 0!==t.key_ops&&!0!==t.key_ops.includes?.(r))throw new TypeError(`Invalid key for this operation, when present its key_ops must include ${r}`);if(void 0!==t.alg&&t.alg!==e)throw new TypeError(`Invalid key for this operation, when present its alg must be ${e}`);return!0},G=(e,t,r,a)=>{if(!(t instanceof Uint8Array)){if(a&&H(t)){if(function(e){return H(e)&&"oct"===e.kty&&"string"==typeof e.k}(t)&&F(e,t,r))return;throw new TypeError('JSON Web Key for symmetric algorithms must have JWK "kty" (Key Type) equal to "oct" and the JWK "k" (Key Value) present')}if(!R(t))throw new TypeError(P(e,t,...I,"Uint8Array",a?"JSON Web Key":null));if("secret"!==t.type)throw new TypeError(`${M(t)} instances for symmetric algorithms must be of type "secret"`)}};function V(e,t,r,a){t.startsWith("HS")||"dir"===t||t.startsWith("PBES2")||/^A\d{3}(?:GCM)?KW$/.test(t)?G(t,r,a,e):((e,t,r,a)=>{if(a&&H(t))switch(r){case"sign":if(function(e){return"oct"!==e.kty&&"string"==typeof e.d}(t)&&F(e,t,r))return;throw new TypeError("JSON Web Key for this operation be a private JWK");case"verify":if(function(e){return"oct"!==e.kty&&void 0===e.d}(t)&&F(e,t,r))return;throw new TypeError("JSON Web Key for this operation be a public JWK")}if(!R(t))throw new TypeError(P(e,t,...I,a?"JSON Web Key":null));if("secret"===t.type)throw new TypeError(`${M(t)} instances for asymmetric algorithms must not be of type "secret"`);if("sign"===r&&"public"===t.type)throw new TypeError(`${M(t)} instances for asymmetric algorithm signing must be of type "private"`);if("decrypt"===r&&"public"===t.type)throw new TypeError(`${M(t)} instances for asymmetric algorithm decryption must be of type "private"`);if(t.algorithm&&"verify"===r&&"private"===t.type)throw new TypeError(`${M(t)} instances for asymmetric algorithm verifying must be of type "public"`);if(t.algorithm&&"encrypt"===r&&"private"===t.type)throw new TypeError(`${M(t)} instances for asymmetric algorithm encryption must be of type "public"`)})(t,r,a,e)}V.bind(void 0,!1);const q=V.bind(void 0,!0);function X(e,t,r,a,n){if(void 0!==n.crit&&void 0===a?.crit)throw new e('"crit" (Critical) Header Parameter MUST be integrity protected');if(!a||void 0===a.crit)return new Set;if(!Array.isArray(a.crit)||0===a.crit.length||a.crit.some((e=>"string"!=typeof e||0===e.length)))throw new e('"crit" (Critical) Header Parameter MUST be an array of non-empty strings when present');let s;s=void 0!==r?new Map([...Object.entries(r),...t.entries()]):t;for(const t of a.crit){if(!s.has(t))throw new p(`Extension Header Parameter "${t}" is not recognized`);if(void 0===n[t])throw new e(`Extension Header Parameter "${t}" is missing`);if(s.get(t)&&void 0===a[t])throw new e(`Extension Header Parameter "${t}" MUST be integrity protected`)}return new Set(a.crit)}var z=(e,t)=>{if(void 0!==t&&(!Array.isArray(t)||t.some((e=>"string"!=typeof e))))throw new TypeError(`"${e}" option must be an array of strings`);if(t)return new Set(t)};var B=async r=>{if(r instanceof Uint8Array)return{kty:"oct",k:i(r)};if(!t(r))throw new TypeError(T(r,...I,"Uint8Array"));if(!r.extractable)throw new TypeError("non-extractable CryptoKey cannot be exported as a JWK");const{ext:a,key_ops:n,alg:s,use:o,...c}=await e.subtle.exportKey("jwk",r);return c};async function Y(e){return B(e)}function Q(e,t){const r=`SHA-${e.slice(-3)}`;switch(e){case"HS256":case"HS384":case"HS512":return{hash:r,name:"HMAC"};case"PS256":case"PS384":case"PS512":return{hash:r,name:"RSA-PSS",saltLength:e.slice(-3)>>3};case"RS256":case"RS384":case"RS512":return{hash:r,name:"RSASSA-PKCS1-v1_5"};case"ES256":case"ES384":case"ES512":return{hash:r,name:"ECDSA",namedCurve:t.namedCurve};case"Ed25519":return{name:"Ed25519"};case"EdDSA":return{name:t.name};default:throw new p(`alg ${e} is not supported either by JOSE or your javascript runtime`)}}async function Z(r,a,n){if("sign"===n&&(a=await $(a,r)),"verify"===n&&(a=await U(a,r)),t(a))return v(a,r,n),a;if(a instanceof Uint8Array){if(!r.startsWith("HS"))throw new TypeError(T(a,...I));return e.subtle.importKey("raw",a,{hash:`SHA-${r.slice(-3)}`,name:"HMAC"},!1,[n])}throw new TypeError(T(a,...I,"Uint8Array","JSON Web Key"))}var ee=async(t,r,a,n)=>{const s=await Z(t,r,"verify");W(t,s);const i=Q(t,s.algorithm);try{return await e.subtle.verify(i,s,a,n)}catch{return!1}};async function te(e,t,r){if(e instanceof Uint8Array&&(e=n.decode(e)),"string"!=typeof e)throw new u("Compact JWS must be a string or Uint8Array");const{0:i,1:c,2:d,length:h}=e.split(".");if(3!==h)throw new u("Invalid Compact JWS");const p=await async function(e,t,r){if(!D(e))throw new u("Flattened JWS must be an object");if(void 0===e.protected&&void 0===e.header)throw new u('Flattened JWS must have either of the "protected" or "header" members');if(void 0!==e.protected&&"string"!=typeof e.protected)throw new u("JWS Protected Header incorrect type");if(void 0===e.payload)throw new u("JWS Payload missing");if("string"!=typeof e.signature)throw new u("JWS Signature missing or incorrect type");if(void 0!==e.header&&!D(e.header))throw new u("JWS Unprotected Header incorrect type");let i={};if(e.protected)try{const t=o(e.protected);i=JSON.parse(n.decode(t))}catch{throw new u("JWS Protected Header is invalid")}if(!C(i,e.header))throw new u("JWS Protected and JWS Unprotected Header Parameter names must be disjoint");const c={...i,...e.header};let d=!0;if(X(u,new Map([["b64",!0]]),r?.crit,i,c).has("b64")&&(d=i.b64,"boolean"!=typeof d))throw new u('The "b64" (base64url-encode payload) Header Parameter must be a boolean');const{alg:h}=c;if("string"!=typeof h||!h)throw new u('JWS "alg" (Algorithm) Header Parameter missing or invalid');const p=r&&z("algorithms",r.algorithms);if(p&&!p.has(h))throw new l('"alg" (Algorithm) Header Parameter value not allowed');if(d){if("string"!=typeof e.payload)throw new u("JWS Payload must be a string")}else if("string"!=typeof e.payload&&!(e.payload instanceof Uint8Array))throw new u("JWS Payload must be a string or an Uint8Array instance");let f=!1;"function"==typeof t?(t=await t(i,e),f=!0,q(h,t,"verify"),H(t)&&(t=await L(t,h))):q(h,t,"verify");const w=s(a.encode(e.protected??""),a.encode("."),"string"==typeof e.payload?a.encode(e.payload):e.payload);let y,m;try{y=o(e.signature)}catch{throw new u("Failed to base64url decode the signature")}if(!await ee(h,t,y,w))throw new S;if(d)try{m=o(e.payload)}catch{throw new u("Failed to base64url decode the payload")}else m="string"==typeof e.payload?a.encode(e.payload):e.payload;const g={payload:m};return void 0!==e.protected&&(g.protectedHeader=i),void 0!==e.header&&(g.unprotectedHeader=e.header),f?{...g,key:t}:g}({payload:c,protected:i,signature:d},t,r),f={payload:p.payload,protectedHeader:p.protectedHeader};return"function"==typeof t?{...f,key:p.key}:f}var re=e=>Math.floor(e.getTime()/1e3);const ae=86400,ne=/^(\+|\-)? ?(\d+|\d+\.\d+) ?(seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)(?: (ago|from now))?$/i;var se=e=>{const t=ne.exec(e);if(!t||t[4]&&t[1])throw new TypeError("Invalid time period format");const r=parseFloat(t[2]);let a;switch(t[3].toLowerCase()){case"sec":case"secs":case"second":case"seconds":case"s":a=Math.round(r);break;case"minute":case"minutes":case"min":case"mins":case"m":a=Math.round(60*r);break;case"hour":case"hours":case"hr":case"hrs":case"h":a=Math.round(3600*r);break;case"day":case"days":case"d":a=Math.round(r*ae);break;case"week":case"weeks":case"w":a=Math.round(604800*r);break;default:a=Math.round(31557600*r)}return"-"===t[1]||"ago"===t[4]?-a:a};const ie=e=>e.toLowerCase().replace(/^application\//,"");var oe=(e,t,r={})=>{let a;try{a=JSON.parse(n.decode(t))}catch{}if(!D(a))throw new f("JWT Claims Set must be a top-level JSON object");const{typ:s}=r;if(s&&("string"!=typeof e.typ||ie(e.typ)!==ie(s)))throw new d('unexpected "typ" JWT header value',a,"typ","check_failed");const{requiredClaims:i=[],issuer:o,subject:c,audience:l,maxTokenAge:p}=r,u=[...i];void 0!==p&&u.push("iat"),void 0!==l&&u.push("aud"),void 0!==c&&u.push("sub"),void 0!==o&&u.push("iss");for(const e of new Set(u.reverse()))if(!(e in a))throw new d(`missing required "${e}" claim`,a,e,"missing");if(o&&!(Array.isArray(o)?o:[o]).includes(a.iss))throw new d('unexpected "iss" claim value',a,"iss","check_failed");if(c&&a.sub!==c)throw new d('unexpected "sub" claim value',a,"sub","check_failed");if(l&&(w=a.aud,y="string"==typeof l?[l]:l,!("string"==typeof w?y.includes(w):Array.isArray(w)&&y.some(Set.prototype.has.bind(new Set(w))))))throw new d('unexpected "aud" claim value',a,"aud","check_failed");var w,y;let m;switch(typeof r.clockTolerance){case"string":m=se(r.clockTolerance);break;case"number":m=r.clockTolerance;break;case"undefined":m=0;break;default:throw new TypeError("Invalid clockTolerance option type")}const{currentDate:g}=r,_=re(g||new Date);if((void 0!==a.iat||p)&&"number"!=typeof a.iat)throw new d('"iat" claim must be a number',a,"iat","invalid");if(void 0!==a.nbf){if("number"!=typeof a.nbf)throw new d('"nbf" claim must be a number',a,"nbf","invalid");if(a.nbf>_+m)throw new d('"nbf" claim timestamp check failed',a,"nbf","check_failed")}if(void 0!==a.exp){if("number"!=typeof a.exp)throw new d('"exp" claim must be a number',a,"exp","invalid");if(a.exp<=_-m)throw new h('"exp" claim timestamp check failed',a,"exp","check_failed")}if(p){const e=_-a.iat;if(e-m>("number"==typeof p?p:se(p)))throw new h('"iat" claim timestamp check failed (too far in the past)',a,"iat","check_failed");if(e<0-m)throw new d('"iat" claim timestamp check failed (it should be in the past)',a,"iat","check_failed")}return a};async function ce(e,t,r){const a=await te(e,t,r);if(a.protectedHeader.crit?.includes("b64")&&!1===a.protectedHeader.b64)throw new f("JWTs MUST NOT use unencoded payload");const n={payload:oe(a.protectedHeader,a.payload,r),protectedHeader:a.protectedHeader};return"function"==typeof t?{...n,key:a.key}:n}var de=async(t,r,a)=>{const n=await Z(t,r,"sign");W(t,n);const s=await e.subtle.sign(Q(t,n.algorithm),n,a);return new Uint8Array(s)};class he{constructor(e){if(!(e instanceof Uint8Array))throw new TypeError("payload must be an instance of Uint8Array");this._payload=e}setProtectedHeader(e){if(this._protectedHeader)throw new TypeError("setProtectedHeader can only be called once");return this._protectedHeader=e,this}setUnprotectedHeader(e){if(this._unprotectedHeader)throw new TypeError("setUnprotectedHeader can only be called once");return this._unprotectedHeader=e,this}async sign(e,t){if(!this._protectedHeader&&!this._unprotectedHeader)throw new u("either setProtectedHeader or setUnprotectedHeader must be called before #sign()");if(!C(this._protectedHeader,this._unprotectedHeader))throw new u("JWS Protected and JWS Unprotected Header Parameter names must be disjoint");const r={...this._protectedHeader,...this._unprotectedHeader};let o=!0;if(X(u,new Map([["b64",!0]]),t?.crit,this._protectedHeader,r).has("b64")&&(o=this._protectedHeader.b64,"boolean"!=typeof o))throw new u('The "b64" (base64url-encode payload) Header Parameter must be a boolean');const{alg:c}=r;if("string"!=typeof c||!c)throw new u('JWS "alg" (Algorithm) Header Parameter missing or invalid');q(c,e,"sign");let d,h=this._payload;o&&(h=a.encode(i(h))),d=this._protectedHeader?a.encode(i(JSON.stringify(this._protectedHeader))):a.encode("");const l=s(d,a.encode("."),h),p=await de(c,e,l),f={signature:i(p),payload:""};return o&&(f.payload=n.decode(h)),this._unprotectedHeader&&(f.header=this._unprotectedHeader),this._protectedHeader&&(f.protected=n.decode(d)),f}}class le{constructor(e){this._flattened=new he(e)}setProtectedHeader(e){return this._flattened.setProtectedHeader(e),this}async sign(e,t){const r=await this._flattened.sign(e,t);if(void 0===r.payload)throw new TypeError("use the flattened module for creating JWS with b64: false");return`${r.protected}.${r.payload}.${r.signature}`}}function pe(e,t){if(!Number.isFinite(t))throw new TypeError(`Invalid ${e} input`);return t}class ue{constructor(e={}){if(!D(e))throw new TypeError("JWT Claims Set MUST be an object");this._payload=e}setIssuer(e){return this._payload={...this._payload,iss:e},this}setSubject(e){return this._payload={...this._payload,sub:e},this}setAudience(e){return this._payload={...this._payload,aud:e},this}setJti(e){return this._payload={...this._payload,jti:e},this}setNotBefore(e){return"number"==typeof e?this._payload={...this._payload,nbf:pe("setNotBefore",e)}:e instanceof Date?this._payload={...this._payload,nbf:pe("setNotBefore",re(e))}:this._payload={...this._payload,nbf:re(new Date)+se(e)},this}setExpirationTime(e){return"number"==typeof e?this._payload={...this._payload,exp:pe("setExpirationTime",e)}:e instanceof Date?this._payload={...this._payload,exp:pe("setExpirationTime",re(e))}:this._payload={...this._payload,exp:re(new Date)+se(e)},this}setIssuedAt(e){return void 0===e?this._payload={...this._payload,iat:re(new Date)}:e instanceof Date?this._payload={...this._payload,iat:pe("setIssuedAt",re(e))}:this._payload="string"==typeof e?{...this._payload,iat:pe("setIssuedAt",re(new Date)+se(e))}:{...this._payload,iat:pe("setIssuedAt",e)},this}}class fe extends ue{setProtectedHeader(e){return this._protectedHeader=e,this}async sign(e,t){const r=new le(a.encode(JSON.stringify(this._payload)));if(r.setProtectedHeader(this._protectedHeader),Array.isArray(this._protectedHeader?.crit)&&this._protectedHeader.crit.includes("b64")&&!1===this._protectedHeader.b64)throw new f("JWTs MUST NOT use unencoded payload");return r.sign(e,t)}}const we=(e,t)=>{if("string"!=typeof e||!e)throw new w(`${t} missing or invalid`)};async function ye(e,t){if(!D(e))throw new TypeError("JWK must be an object");if(t??(t="sha256"),"sha256"!==t&&"sha384"!==t&&"sha512"!==t)throw new TypeError('digestAlgorithm must one of "sha256", "sha384", or "sha512"');let n;switch(e.kty){case"EC":we(e.crv,'"crv" (Curve) Parameter'),we(e.x,'"x" (X Coordinate) Parameter'),we(e.y,'"y" (Y Coordinate) Parameter'),n={crv:e.crv,kty:e.kty,x:e.x,y:e.y};break;case"OKP":we(e.crv,'"crv" (Subtype of Key Pair) Parameter'),we(e.x,'"x" (Public Key) Parameter'),n={crv:e.crv,kty:e.kty,x:e.x};break;case"RSA":we(e.e,'"e" (Exponent) Parameter'),we(e.n,'"n" (Modulus) Parameter'),n={e:e.e,kty:e.kty,n:e.n};break;case"oct":we(e.k,'"k" (Key Value) Parameter'),n={k:e.k,kty:e.kty};break;default:throw new p('"kty" (Key Type) Parameter missing or unsupported')}const s=a.encode(JSON.stringify(n));return i(await r(t,s))}function me(e){return D(e)}function ge(e){return"function"==typeof structuredClone?structuredClone(e):JSON.parse(JSON.stringify(e))}class _e{constructor(e){if(this._cached=new WeakMap,!function(e){return e&&"object"==typeof e&&Array.isArray(e.keys)&&e.keys.every(me)}(e))throw new y("JSON Web Key Set malformed");this._jwks=ge(e)}async getKey(e,t){const{alg:r,kid:a}={...e,...t?.header},n=function(e){switch("string"==typeof e&&e.slice(0,2)){case"RS":case"PS":return"RSA";case"ES":return"EC";case"Ed":return"OKP";default:throw new p('Unsupported "alg" value for a JSON Web Key Set')}}(r),s=this._jwks.keys.filter((e=>{let t=n===e.kty;if(t&&"string"==typeof a&&(t=a===e.kid),t&&"string"==typeof e.alg&&(t=r===e.alg),t&&"string"==typeof e.use&&(t="sig"===e.use),t&&Array.isArray(e.key_ops)&&(t=e.key_ops.includes("verify")),t)switch(r){case"ES256":t="P-256"===e.crv;break;case"ES256K":t="secp256k1"===e.crv;break;case"ES384":t="P-384"===e.crv;break;case"ES512":t="P-521"===e.crv;break;case"Ed25519":t="Ed25519"===e.crv;break;case"EdDSA":t="Ed25519"===e.crv||"Ed448"===e.crv}return t})),{0:i,length:o}=s;if(0===o)throw new m;if(1!==o){const e=new g,{_cached:t}=this;throw e[Symbol.asyncIterator]=async function*(){for(const e of s)try{yield await Se(t,e,r)}catch{}},e}return Se(this._cached,i,r)}}async function Se(e,t,r){const a=e.get(t)||e.set(t,{}).get(t);if(void 0===a[r]){const e=await L({...t,ext:!0},r);if(e instanceof Uint8Array||"public"!==e.type)throw new y("JSON Web Key Set members must be public keys");a[r]=e}return a[r]}function Ee(e){const t=new _e(e),r=async(e,r)=>t.getKey(e,r);return Object.defineProperties(r,{jwks:{value:()=>ge(t._jwks),enumerable:!0,configurable:!1,writable:!1}}),r}var be=async(e,t,r)=>{let a,n,s=!1;"function"==typeof AbortController&&(a=new AbortController,n=setTimeout((()=>{s=!0,a.abort()}),t));const i=await fetch(e.href,{signal:a?a.signal:void 0,redirect:"manual",headers:r.headers}).catch((e=>{if(s)throw new _;throw e}));if(void 0!==n&&clearTimeout(n),200!==i.status)throw new c("Expected 200 OK from the JSON Web Key Set HTTP response");try{return await i.json()}catch{throw new c("Failed to parse the JSON Web Key Set HTTP response as JSON")}};let ke;if("undefined"==typeof navigator||!navigator.userAgent?.startsWith?.("Mozilla/5.0 ")){ke=`${"jose"}/${"v5.10.0"}`}const ve=Symbol();class Ae{constructor(e,t){if(!(e instanceof URL))throw new TypeError("url must be an instance of URL");var r,a;this._url=new URL(e.href),this._options={agent:t?.agent,headers:t?.headers},this._timeoutDuration="number"==typeof t?.timeoutDuration?t?.timeoutDuration:5e3,this._cooldownDuration="number"==typeof t?.cooldownDuration?t?.cooldownDuration:3e4,this._cacheMaxAge="number"==typeof t?.cacheMaxAge?t?.cacheMaxAge:6e5,void 0!==t?.[ve]&&(this._cache=t?.[ve],r=t?.[ve],a=this._cacheMaxAge,"object"==typeof r&&null!==r&&"uat"in r&&"number"==typeof r.uat&&!(Date.now()-r.uat>=a)&&"jwks"in r&&D(r.jwks)&&Array.isArray(r.jwks.keys)&&Array.prototype.every.call(r.jwks.keys,D)&&(this._jwksTimestamp=this._cache.uat,this._local=Ee(this._cache.jwks)))}coolingDown(){return"number"==typeof this._jwksTimestamp&&Date.now()<this._jwksTimestamp+this._cooldownDuration}fresh(){return"number"==typeof this._jwksTimestamp&&Date.now()<this._jwksTimestamp+this._cacheMaxAge}async getKey(e,t){this._local&&this.fresh()||await this.reload();try{return await this._local(e,t)}catch(r){if(r instanceof m&&!1===this.coolingDown())return await this.reload(),this._local(e,t);throw r}}async reload(){this._pendingFetch&&("undefined"!=typeof WebSocketPair||"undefined"!=typeof navigator&&"Cloudflare-Workers"===navigator.userAgent||"undefined"!=typeof EdgeRuntime&&"vercel"===EdgeRuntime)&&(this._pendingFetch=void 0);const e=new Headers(this._options.headers);ke&&!e.has("User-Agent")&&(e.set("User-Agent",ke),this._options.headers=Object.fromEntries(e.entries())),this._pendingFetch||(this._pendingFetch=be(this._url,this._timeoutDuration,this._options).then((e=>{this._local=Ee(e),this._cache&&(this._cache.uat=Date.now(),this._cache.jwks=e),this._jwksTimestamp=Date.now(),this._pendingFetch=void 0})).catch((e=>{throw this._pendingFetch=void 0,e}))),await this._pendingFetch}}function Te(e,t){const r=new Ae(e,t),a=async(e,t)=>r.getKey(e,t);return Object.defineProperties(a,{coolingDown:{get:()=>r.coolingDown(),enumerable:!0,configurable:!1},fresh:{get:()=>r.fresh(),enumerable:!0,configurable:!1},reload:{value:()=>r.reload(),enumerable:!0,configurable:!1,writable:!1},reloading:{get:()=>!!r._pendingFetch,enumerable:!0,configurable:!1},jwks:{value:()=>r._local?.jwks(),enumerable:!0,configurable:!1,writable:!1}}),a}const Pe=o;function Re(e){const t=e?.modulusLength??2048;if("number"!=typeof t||t<2048)throw new p("Invalid or unsupported modulusLength option provided, 2048 bits or larger keys must be used");return t}async function Ie(t,r){return async function(t,r){let a,n;switch(t){case"PS256":case"PS384":case"PS512":a={name:"RSA-PSS",hash:`SHA-${t.slice(-3)}`,publicExponent:new Uint8Array([1,0,1]),modulusLength:Re(r)},n=["sign","verify"];break;case"RS256":case"RS384":case"RS512":a={name:"RSASSA-PKCS1-v1_5",hash:`SHA-${t.slice(-3)}`,publicExponent:new Uint8Array([1,0,1]),modulusLength:Re(r)},n=["sign","verify"];break;case"RSA-OAEP":case"RSA-OAEP-256":case"RSA-OAEP-384":case"RSA-OAEP-512":a={name:"RSA-OAEP",hash:`SHA-${parseInt(t.slice(-3),10)||1}`,publicExponent:new Uint8Array([1,0,1]),modulusLength:Re(r)},n=["decrypt","unwrapKey","encrypt","wrapKey"];break;case"ES256":a={name:"ECDSA",namedCurve:"P-256"},n=["sign","verify"];break;case"ES384":a={name:"ECDSA",namedCurve:"P-384"},n=["sign","verify"];break;case"ES512":a={name:"ECDSA",namedCurve:"P-521"},n=["sign","verify"];break;case"Ed25519":a={name:"Ed25519"},n=["sign","verify"];break;case"EdDSA":{n=["sign","verify"];const e=r?.crv??"Ed25519";switch(e){case"Ed25519":case"Ed448":a={name:e};break;default:throw new p("Invalid or unsupported crv option provided")}break}case"ECDH-ES":case"ECDH-ES+A128KW":case"ECDH-ES+A192KW":case"ECDH-ES+A256KW":{n=["deriveKey","deriveBits"];const e=r?.crv??"P-256";switch(e){case"P-256":case"P-384":case"P-521":a={name:"ECDH",namedCurve:e};break;case"X25519":case"X448":a={name:e};break;default:throw new p("Invalid or unsupported crv option provided, supported values are P-256, P-384, P-521, X25519, and X448")}break}default:throw new p('Invalid or unsupported JWK "alg" (Algorithm) Parameter value')}return e.subtle.generateKey(a,r?.extractable??!1,n)}(t,r)}const Ce=async(e,t,r)=>{const a=new URL(t),n=a.origin+a.pathname+a.search,s=new URL(e).origin,i=await fetch(`${s}/.well-known/openid-configuration`).then((e=>{if(!e.ok)throw new Error(`HTTP error! Status: ${e.status}`);return e.json()})),o=i.issuer,c=e=>e.endsWith("/")?e.slice(0,-1):e;if(c(e)!==c(o))throw new Error("RFC 9207 - iss !== idp - "+o+" !== "+e);sessionStorage.setItem("idp",o),sessionStorage.setItem("token_endpoint",i.token_endpoint),sessionStorage.setItem("jwks_uri",i.jwks_uri);let d=r?.client_id;if(!d){const e=i.registration_endpoint,t=await(async(e,t)=>{const r={...t,grant_types:["authorization_code","refresh_token"],token_endpoint_auth_method:"none",application_type:"web",subject_type:"public"};return fetch(e,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(r)})})(e,r??{redirect_uris:[n]}).then((e=>{if(!e.ok)throw new Error(`HTTP error! Status: ${e.status}`);return e.json()}));d=t.client_id}try{new URL(d)}catch{sessionStorage.setItem("client_id",d)}const{pkce_code_verifier:h,pkce_code_challenge:l}=await De();sessionStorage.setItem("pkce_code_verifier",h);const p=window.crypto.randomUUID();sessionStorage.setItem("csrf_token",p);const u=i.authorization_endpoint+"?response_type=code"+`&redirect_uri=${encodeURIComponent(n)}&scope=openid offline_access webid`+`&client_id=${encodeURIComponent(d)}&code_challenge_method=S256`+`&code_challenge=${l}`+`&state=${p}&prompt=consent`;window.location.href=u},De=async()=>{const e=window.crypto.randomUUID()+"-"+window.crypto.randomUUID(),t=new Uint8Array(await window.crypto.subtle.digest("SHA-256",(new TextEncoder).encode(e)));return{pkce_code_verifier:e,pkce_code_challenge:btoa(String.fromCharCode(...t)).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}},We=async(e,t,r,a,n,s)=>{const i=await Y(s.publicKey);i.alg="ES256";const o=await new fe({htu:n,htm:"POST"}).setIssuedAt().setJti(window.crypto.randomUUID()).setProtectedHeader({alg:"ES256",typ:"dpop+jwt",jwk:i}).sign(s.privateKey);return fetch(n,{method:"POST",headers:{dpop:o,"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({grant_type:"authorization_code",code:e,code_verifier:t,redirect_uri:r,client_id:a})})},He=async(e,t,r,a)=>{const n=await Y(a.publicKey);n.alg="ES256";const s=await new fe({htu:r,htm:"POST"}).setIssuedAt().setJti(self.crypto.randomUUID()).setProtectedHeader({alg:"ES256",typ:"dpop+jwt",jwk:n}).sign(a.privateKey);return fetch(r,{method:"POST",headers:{dpop:s,"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({grant_type:"refresh_token",refresh_token:e,client_id:t})})};var Je;!function(e){e.STATE_CHANGE="sessionStateChange",e.EXPIRATION_WARNING="sessionExpirationWarning",e.EXPIRATION="sessionExpiration"}(Je||(Je={}));class xe extends EventTarget{isActive_=!1;exp_;webId_=void 0;currentAth_=void 0;information;database;refreshPromise;resolveRefresh;rejectRefresh;constructor(e,t){super(),this.authFetch=this.authFetch.bind(this),this.information={clientDetails:e},this.database=t?.database,t?.onSessionStateChange&&this.addEventListener(Je.STATE_CHANGE,(e=>t.onSessionStateChange?.(e))),t?.onSessionExpirationWarning&&this.addEventListener(Je.EXPIRATION_WARNING,(e=>t?.onSessionExpirationWarning?.(e))),t?.onSessionExpiration&&this.addEventListener(Je.EXPIRATION,(e=>t?.onSessionExpiration?.(e)))}async login(e,t){await Ce(e,t,this.information.clientDetails)}async handleRedirectFromLogin(){const e=await(async(e,t)=>{const r=new URL(window.location.href),a=r.searchParams.get("code");if(null===a)return{clientDetails:e};const n=sessionStorage.getItem("idp");if(null===n||r.searchParams.get("iss")!==n)throw new Error("RFC 9207 - iss !== idp - "+r.searchParams.get("iss")+" !== "+n);if(r.searchParams.get("state")!==sessionStorage.getItem("csrf_token"))throw new Error("RFC 6749 - state !== csrf_token - "+r.searchParams.get("state")+" !== "+sessionStorage.getItem("csrf_token"));r.searchParams.delete("iss"),r.searchParams.delete("state"),r.searchParams.delete("code"),window.history.pushState({},document.title,r.toString());const s=sessionStorage.getItem("pkce_code_verifier");if(null===s)throw new Error("Access Token Request preparation - Could not find in sessionStorage: pkce_code_verifier");const i=e?.client_id||sessionStorage.getItem("client_id");if(!i)throw new Error("Access Token Request preparation - Could not find in sessionStorage: client_id (dynamic registration)");const o=sessionStorage.getItem("token_endpoint");if(null===o)throw new Error("Access Token Request preparation - Could not find in sessionStorage: token_endpoint");const c=await Ie("ES256"),d=await We(a,s,r.toString(),i,o,c).then((e=>{if(!e.ok)throw new Error(`HTTP error! Status: ${e.status}`);return e.json()})),h=d.access_token,l=sessionStorage.getItem("jwks_uri");if(null===l)throw new Error("Access Token validation preparation - Could not find in sessionStorage: jwks_uri");const p=Te(new URL(l)),{payload:u}=await ce(h,p,{issuer:n,audience:"solid"}),f=await ye(await Y(c.publicKey));if(u.cnf.jkt!==f)throw new Error("Access Token validation failed on `jkt`: jkt !== DPoP thumbprint - "+u.cnf.jkt+" !== "+f);if(u.client_id!==i)throw new Error("Access Token validation failed on `client_id`: JWT payload !== client_id - "+u.client_id+" !== "+i);const w={...d,dpop_key_pair:c},y={idp:n,jwks_uri:l,token_endpoint:o};return e||(e={redirect_uris:[r.toString()]}),e.client_id=i,t&&(await t.init(),await Promise.all([t.setItem("idp",n),t.setItem("jwks_uri",l),t.setItem("token_endpoint",o),t.setItem("client_id",i),t.setItem("dpop_keypair",c),t.setItem("refresh_token",d.refresh_token)]),t.close()),sessionStorage.removeItem("csrf_token"),sessionStorage.removeItem("pkce_code_verifier"),sessionStorage.removeItem("idp"),sessionStorage.removeItem("jwks_uri"),sessionStorage.removeItem("token_endpoint"),sessionStorage.removeItem("client_id"),{clientDetails:e,idpDetails:y,tokenDetails:w}})(this.information.clientDetails,this.database);e.tokenDetails&&(this.information.clientDetails=e.clientDetails,this.information.idpDetails=e.idpDetails,await this.setTokenDetails(e.tokenDetails),this.dispatchStateChangeEvent())}async restore(){if(!this.database)throw new Error("Could not refresh tokens: missing database. Provide database in sessionOption.");if(this.refreshPromise)return this.refreshPromise;this.refreshPromise=new Promise(((e,t)=>{this.resolveRefresh=e,this.rejectRefresh=t}));const e=this.isActive;return(async e=>{try{await e.init();const t=await e.getItem("client_id"),r=await e.getItem("token_endpoint"),a=await e.getItem("dpop_keypair"),n=await e.getItem("refresh_token");if(null===t||null===r||null===a||null===n)throw new Error("Could not refresh tokens: details missing from database.");const s=await He(n,t,r,a).then((e=>{if(!e.ok)throw new Error(`HTTP error! Status: ${e.status}`);return e.json()})),i=s.access_token,o=await e.getItem("idp");if(null===o)throw new Error("Access Token validation preparation - Could not find in sessionDatabase: idp");const c=await e.getItem("jwks_uri");if(null===c)throw new Error("Access Token validation preparation - Could not find in sessionDatabase: jwks_uri");const d=Te(new URL(c)),{payload:h}=await ce(i,d,{issuer:o,audience:"solid"}),l=await ye(await Y(a.publicKey));if(h.cnf.jkt!==l)throw new Error("Access Token validation failed on `jkt`: jkt !== DPoP thumbprint - "+h.cnf.jkt+" !== "+l);if(h.client_id!==t)throw new Error("Access Token validation failed on `client_id`: JWT payload !== client_id - "+h.client_id+" !== "+t);return await e.setItem("refresh_token",s.refresh_token),{...s,dpop_key_pair:a}}finally{e.close()}})(this.database).then((e=>this.setTokenDetails(e))).then((()=>this.resolveRefresh())).catch((e=>{this.isActive?(this.rejectRefresh(new Error(e||"Token refresh failed")),this.isExpired()?this.dispatchExpirationEvent():this.dispatchExpirationWarningEvent()):this.rejectRefresh(new Error("No session to restore."))})).finally((()=>{this.clearRefreshPromise(),e!==this.isActive&&this.dispatchStateChangeEvent()})),this.refreshPromise}async logout(){this.isActive_=!1,this.exp_=void 0,this.webId_=void 0,this.currentAth_=void 0,this.information.idpDetails=void 0,this.information.tokenDetails=void 0,this.refreshPromise&&this.rejectRefresh&&(this.rejectRefresh(new Error("Logout during token refresh.")),this.clearRefreshPromise()),this.database&&(await this.database.init(),await this.database.clear(),this.database.close()),this.dispatchStateChangeEvent()}async authFetch(e,t,r){if(!this.isActive)return fetch(e,t);let a,n,s;e instanceof Request?(a=new URL(e.url),n=t?.method||e?.method||"GET",s=new Headers(e.headers)):(t=t||{},a=new URL(e.toString()),n=t.method||"GET",s=t.headers?new Headers(t.headers):new Headers),await this._renewTokensIfExpired(),r=r??{htu:`${a.origin}${a.pathname}`,htm:n.toUpperCase()};const i=await this._createSignedDPoPToken(r);return s.set("dpop",i),s.set("authorization",`DPoP ${this.information.tokenDetails.access_token}`),e instanceof Request?fetch(new Request(e,{...t,headers:s})):fetch(a,{...t,headers:s})}async setTokenDetails(e){this.information.tokenDetails=e,await this._updateSessionDetailsFromToken(e.access_token)}clearRefreshPromise(){this.refreshPromise=void 0,this.resolveRefresh=void 0,this.rejectRefresh=void 0}get isActive(){return this.isActive_}get webId(){return this.webId_}isExpired(){return!this.exp_||this._isTokenExpired(this.exp_)}getExpiresIn(){return this.exp_?this._getTokenTTL(this.exp_):-1}getTokenDetails(){return this.information.tokenDetails}async _renewTokensIfExpired(){this.isExpired()&&(this.refreshPromise?await this.refreshPromise:await this.restore())}async _computeAth(e){const t=(new TextEncoder).encode(e),r=await crypto.subtle.digest("SHA-256",t),a=Array.from(new Uint8Array(r));return btoa(String.fromCharCode(...a)).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}async _createSignedDPoPToken(e){if(!this.information.tokenDetails||!this.currentAth_)throw new Error("Session not established.");e.ath=this.currentAth_;const t=await Y(this.information.tokenDetails.dpop_key_pair.publicKey);return new fe(e).setIssuedAt().setJti(window.crypto.randomUUID()).setProtectedHeader({alg:"ES256",typ:"dpop+jwt",jwk:t}).sign(this.information.tokenDetails.dpop_key_pair.privateKey)}async _updateSessionDetailsFromToken(e){if(e)try{const t=function(e){if("string"!=typeof e)throw new f("JWTs must use Compact JWS serialization, JWT must be a string");const{1:t,length:r}=e.split(".");if(5===r)throw new f("Only JWTs using Compact JWS serialization can be decoded");if(3!==r)throw new f("Invalid JWT");if(!t)throw new f("JWTs must contain a payload");let a,s;try{a=Pe(t)}catch{throw new f("Failed to base64url decode the payload")}try{s=JSON.parse(n.decode(a))}catch{throw new f("Failed to parse the decoded payload as JSON")}if(!D(s))throw new f("Invalid JWT Claims Set");return s}(e),r=t.webid;if(!r)throw new Error("Missing webid claim in access token");const a=t.exp;if(!a)throw new Error("Missing exp claim in access token");this.currentAth_=await this._computeAth(e),this.webId_=r,this.exp_=a,this.isActive_=!0}catch(e){await this.logout()}else await this.logout()}_isTokenExpired(e,t=0){return!("number"==typeof e&&!isNaN(e))||this._getTokenTTL(e,t)<0}_getTokenTTL(e,t=0){return e-(Math.floor(Date.now()/1e3)+t)}dispatchStateChangeEvent(){this.dispatchEvent(new CustomEvent(Je.STATE_CHANGE,{detail:{isActive:this.isActive,webId:this.webId}}))}dispatchExpirationWarningEvent(){this.dispatchEvent(new CustomEvent(Je.EXPIRATION_WARNING,{detail:{expires_in:this.getExpiresIn()}}))}dispatchExpirationEvent(){this.dispatchEvent(new CustomEvent(Je.EXPIRATION))}}export{xe as SessionCore,Je as SessionEvents};
|
|
6
|
+
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
var solidClientAuthentication=function(e){"use strict";const t=new TextEncoder,r=new TextDecoder;function s(...e){const t=e.reduce(((e,{length:t})=>e+t),0),r=new Uint8Array(t);let s=0;for(const t of e)r.set(t,s),s+=t.length;return r}function i(e){const t=new Uint8Array(e.length);for(let r=0;r<e.length;r++){const s=e.charCodeAt(r);if(s>127)throw new TypeError("non-ASCII string encountered in encode()");t[r]=s}return t}function n(e){if(Uint8Array.fromBase64)return Uint8Array.fromBase64("string"==typeof e?e:r.decode(e),{alphabet:"base64url"});let t=e;t instanceof Uint8Array&&(t=r.decode(t)),t=t.replace(/-/g,"+").replace(/_/g,"/");try{return function(e){if(Uint8Array.fromBase64)return Uint8Array.fromBase64(e);const t=atob(e),r=new Uint8Array(t.length);for(let e=0;e<t.length;e++)r[e]=t.charCodeAt(e);return r}(t)}catch{throw new TypeError("The input to be decoded is not correctly encoded.")}}function o(e){let r=e;return"string"==typeof r&&(r=t.encode(r)),Uint8Array.prototype.toBase64?r.toBase64({alphabet:"base64url",omitPadding:!0}):function(e){if(Uint8Array.prototype.toBase64)return e.toBase64();const t=[];for(let r=0;r<e.length;r+=32768)t.push(String.fromCharCode.apply(null,e.subarray(r,r+32768)));return btoa(t.join(""))}(r).replace(/=/g,"").replace(/\+/g,"-").replace(/\//g,"_")}const a=(e,t="algorithm.name")=>new TypeError(`CryptoKey does not support this operation, its ${t} must be ${e}`),c=(e,t)=>e.name===t;function d(e,t){var r;if((r=e.hash,parseInt(r.name.slice(4),10))!==t)throw a(`SHA-${t}`,"algorithm.hash")}function l(e,t,r){switch(t){case"HS256":case"HS384":case"HS512":if(!c(e.algorithm,"HMAC"))throw a("HMAC");d(e.algorithm,parseInt(t.slice(2),10));break;case"RS256":case"RS384":case"RS512":if(!c(e.algorithm,"RSASSA-PKCS1-v1_5"))throw a("RSASSA-PKCS1-v1_5");d(e.algorithm,parseInt(t.slice(2),10));break;case"PS256":case"PS384":case"PS512":if(!c(e.algorithm,"RSA-PSS"))throw a("RSA-PSS");d(e.algorithm,parseInt(t.slice(2),10));break;case"Ed25519":case"EdDSA":if(!c(e.algorithm,"Ed25519"))throw a("Ed25519");break;case"ML-DSA-44":case"ML-DSA-65":case"ML-DSA-87":if(!c(e.algorithm,t))throw a(t);break;case"ES256":case"ES384":case"ES512":{if(!c(e.algorithm,"ECDSA"))throw a("ECDSA");const r=function(e){switch(e){case"ES256":return"P-256";case"ES384":return"P-384";case"ES512":return"P-521";default:throw new Error("unreachable")}}(t);if(e.algorithm.namedCurve!==r)throw a(r,"algorithm.namedCurve");break}default:throw new TypeError("CryptoKey does not support this operation")}!function(e,t){if(t&&!e.usages.includes(t))throw new TypeError(`CryptoKey does not support this operation, its usages must include ${t}.`)}(e,r)}function h(e,t,...r){if((r=r.filter(Boolean)).length>2){const t=r.pop();e+=`one of type ${r.join(", ")}, or ${t}.`}else 2===r.length?e+=`one of type ${r[0]} or ${r[1]}.`:e+=`of type ${r[0]}.`;return null==t?e+=` Received ${t}`:"function"==typeof t&&t.name?e+=` Received function ${t.name}`:"object"==typeof t&&null!=t&&t.constructor?.name&&(e+=` Received an instance of ${t.constructor.name}`),e}const u=(e,...t)=>h("Key must be ",e,...t),p=(e,t,...r)=>h(`Key for the ${e} algorithm must be `,t,...r);class g extends Error{static code="ERR_JOSE_GENERIC";code="ERR_JOSE_GENERIC";constructor(e,t){super(e,t),this.name=this.constructor.name,Error.captureStackTrace?.(this,this.constructor)}}class f extends g{static code="ERR_JWT_CLAIM_VALIDATION_FAILED";code="ERR_JWT_CLAIM_VALIDATION_FAILED";claim;reason;payload;constructor(e,t,r="unspecified",s="unspecified"){super(e,{cause:{claim:r,reason:s,payload:t}}),this.claim=r,this.reason=s,this.payload=t}}class y extends g{static code="ERR_JWT_EXPIRED";code="ERR_JWT_EXPIRED";claim;reason;payload;constructor(e,t,r="unspecified",s="unspecified"){super(e,{cause:{claim:r,reason:s,payload:t}}),this.claim=r,this.reason=s,this.payload=t}}class w extends g{static code="ERR_JOSE_ALG_NOT_ALLOWED";code="ERR_JOSE_ALG_NOT_ALLOWED"}class _ extends g{static code="ERR_JOSE_NOT_SUPPORTED";code="ERR_JOSE_NOT_SUPPORTED"}class m extends g{static code="ERR_JWS_INVALID";code="ERR_JWS_INVALID"}class S extends g{static code="ERR_JWT_INVALID";code="ERR_JWT_INVALID"}class v extends g{static code="ERR_JWKS_INVALID";code="ERR_JWKS_INVALID"}class b extends g{static code="ERR_JWKS_NO_MATCHING_KEY";code="ERR_JWKS_NO_MATCHING_KEY";constructor(e="no applicable key found in the JSON Web Key Set",t){super(e,t)}}class E extends g{[Symbol.asyncIterator];static code="ERR_JWKS_MULTIPLE_MATCHING_KEYS";code="ERR_JWKS_MULTIPLE_MATCHING_KEYS";constructor(e="multiple matching keys found in the JSON Web Key Set",t){super(e,t)}}class k extends g{static code="ERR_JWKS_TIMEOUT";code="ERR_JWKS_TIMEOUT";constructor(e="request timed out",t){super(e,t)}}class A extends g{static code="ERR_JWS_SIGNATURE_VERIFICATION_FAILED";code="ERR_JWS_SIGNATURE_VERIFICATION_FAILED";constructor(e="signature verification failed",t){super(e,t)}}const I=e=>{if("CryptoKey"===e?.[Symbol.toStringTag])return!0;try{return e instanceof CryptoKey}catch{return!1}},T=e=>"KeyObject"===e?.[Symbol.toStringTag],R=e=>I(e)||T(e);function U(e,t){if(e)throw new TypeError(`${t} can only be called once`)}function P(e,t,r){try{return n(e)}catch{throw new r(`Failed to base64url decode the ${t}`)}}function C(e){if("object"!=typeof(t=e)||null===t||"[object Object]"!==Object.prototype.toString.call(e))return!1;var t;if(null===Object.getPrototypeOf(e))return!0;let r=e;for(;null!==Object.getPrototypeOf(r);)r=Object.getPrototypeOf(r);return Object.getPrototypeOf(e)===r}function x(...e){const t=e.filter(Boolean);if(0===t.length||1===t.length)return!0;let r;for(const e of t){const t=Object.keys(e);if(r&&0!==r.size)for(const e of t){if(r.has(e))return!1;r.add(e)}else r=new Set(t)}return!0}const O=e=>C(e)&&"string"==typeof e.kty;function K(e,t){if(e.startsWith("RS")||e.startsWith("PS")){const{modulusLength:r}=t.algorithm;if("number"!=typeof r||r<2048)throw new TypeError(`${e} requires key modulusLength to be 2048 bits or larger`)}}function H(e,t){const r=`SHA-${e.slice(-3)}`;switch(e){case"HS256":case"HS384":case"HS512":return{hash:r,name:"HMAC"};case"PS256":case"PS384":case"PS512":return{hash:r,name:"RSA-PSS",saltLength:parseInt(e.slice(-3),10)>>3};case"RS256":case"RS384":case"RS512":return{hash:r,name:"RSASSA-PKCS1-v1_5"};case"ES256":case"ES384":case"ES512":return{hash:r,name:"ECDSA",namedCurve:t.namedCurve};case"Ed25519":case"EdDSA":return{name:"Ed25519"};case"ML-DSA-44":case"ML-DSA-65":case"ML-DSA-87":return{name:e};default:throw new _(`alg ${e} is not supported either by JOSE or your javascript runtime`)}}async function D(e,t,r){if(t instanceof Uint8Array){if(!e.startsWith("HS"))throw new TypeError(u(t,"CryptoKey","KeyObject","JSON Web Key"));return crypto.subtle.importKey("raw",t,{hash:`SHA-${e.slice(-3)}`,name:"HMAC"},!1,[r])}return l(t,e,r),t}const j='Invalid or unsupported JWK "alg" (Algorithm) Parameter value';async function L(e){if(!e.alg)throw new TypeError('"alg" argument is required when "jwk.alg" is not present');const{algorithm:t,keyUsages:r}=function(e){let t,r;switch(e.kty){case"AKP":switch(e.alg){case"ML-DSA-44":case"ML-DSA-65":case"ML-DSA-87":t={name:e.alg},r=e.priv?["sign"]:["verify"];break;default:throw new _(j)}break;case"RSA":switch(e.alg){case"PS256":case"PS384":case"PS512":t={name:"RSA-PSS",hash:`SHA-${e.alg.slice(-3)}`},r=e.d?["sign"]:["verify"];break;case"RS256":case"RS384":case"RS512":t={name:"RSASSA-PKCS1-v1_5",hash:`SHA-${e.alg.slice(-3)}`},r=e.d?["sign"]:["verify"];break;case"RSA-OAEP":case"RSA-OAEP-256":case"RSA-OAEP-384":case"RSA-OAEP-512":t={name:"RSA-OAEP",hash:`SHA-${parseInt(e.alg.slice(-3),10)||1}`},r=e.d?["decrypt","unwrapKey"]:["encrypt","wrapKey"];break;default:throw new _(j)}break;case"EC":switch(e.alg){case"ES256":case"ES384":case"ES512":t={name:"ECDSA",namedCurve:{ES256:"P-256",ES384:"P-384",ES512:"P-521"}[e.alg]},r=e.d?["sign"]:["verify"];break;case"ECDH-ES":case"ECDH-ES+A128KW":case"ECDH-ES+A192KW":case"ECDH-ES+A256KW":t={name:"ECDH",namedCurve:e.crv},r=e.d?["deriveBits"]:[];break;default:throw new _(j)}break;case"OKP":switch(e.alg){case"Ed25519":case"EdDSA":t={name:"Ed25519"},r=e.d?["sign"]:["verify"];break;case"ECDH-ES":case"ECDH-ES+A128KW":case"ECDH-ES+A192KW":case"ECDH-ES+A256KW":t={name:e.crv},r=e.d?["deriveBits"]:[];break;default:throw new _(j)}break;default:throw new _('Invalid or unsupported JWK "kty" (Key Type) Parameter value')}return{algorithm:t,keyUsages:r}}(e),s={...e};return"AKP"!==s.kty&&delete s.alg,delete s.use,crypto.subtle.importKey("jwk",s,t,e.ext??(!e.d&&!e.priv),e.key_ops??r)}const $="given KeyObject instance cannot be used for this algorithm";let N;const J=async(e,t,r,s=!1)=>{N||=new WeakMap;let i=N.get(e);if(i?.[r])return i[r];const n=await L({...t,alg:r});return s&&Object.freeze(e),i?i[r]=n:N.set(e,{[r]:n}),n};async function W(e,t){if(e instanceof Uint8Array)return e;if(I(e))return e;if(T(e)){if("secret"===e.type)return e.export();if("toCryptoKey"in e&&"function"==typeof e.toCryptoKey)try{return((e,t)=>{N||=new WeakMap;let r=N.get(e);if(r?.[t])return r[t];const s="public"===e.type,i=!!s;let n;if("x25519"===e.asymmetricKeyType){switch(t){case"ECDH-ES":case"ECDH-ES+A128KW":case"ECDH-ES+A192KW":case"ECDH-ES+A256KW":break;default:throw new TypeError($)}n=e.toCryptoKey(e.asymmetricKeyType,i,s?[]:["deriveBits"])}if("ed25519"===e.asymmetricKeyType){if("EdDSA"!==t&&"Ed25519"!==t)throw new TypeError($);n=e.toCryptoKey(e.asymmetricKeyType,i,[s?"verify":"sign"])}switch(e.asymmetricKeyType){case"ml-dsa-44":case"ml-dsa-65":case"ml-dsa-87":if(t!==e.asymmetricKeyType.toUpperCase())throw new TypeError($);n=e.toCryptoKey(e.asymmetricKeyType,i,[s?"verify":"sign"])}if("rsa"===e.asymmetricKeyType){let r;switch(t){case"RSA-OAEP":r="SHA-1";break;case"RS256":case"PS256":case"RSA-OAEP-256":r="SHA-256";break;case"RS384":case"PS384":case"RSA-OAEP-384":r="SHA-384";break;case"RS512":case"PS512":case"RSA-OAEP-512":r="SHA-512";break;default:throw new TypeError($)}if(t.startsWith("RSA-OAEP"))return e.toCryptoKey({name:"RSA-OAEP",hash:r},i,s?["encrypt"]:["decrypt"]);n=e.toCryptoKey({name:t.startsWith("PS")?"RSA-PSS":"RSASSA-PKCS1-v1_5",hash:r},i,[s?"verify":"sign"])}if("ec"===e.asymmetricKeyType){const r=new Map([["prime256v1","P-256"],["secp384r1","P-384"],["secp521r1","P-521"]]).get(e.asymmetricKeyDetails?.namedCurve);if(!r)throw new TypeError($);const o={ES256:"P-256",ES384:"P-384",ES512:"P-521"};o[t]&&r===o[t]&&(n=e.toCryptoKey({name:"ECDSA",namedCurve:r},i,[s?"verify":"sign"])),t.startsWith("ECDH-ES")&&(n=e.toCryptoKey({name:"ECDH",namedCurve:r},i,s?[]:["deriveBits"]))}if(!n)throw new TypeError($);return r?r[t]=n:N.set(e,{[t]:n}),n})(e,t)}catch(e){if(e instanceof TypeError)throw e}let r=e.export({format:"jwk"});return J(e,r,t)}if(O(e))return e.k?n(e.k):J(e,e,t,!0);throw new Error("unreachable")}async function M(e){return async function(e){if(T(e)){if("secret"!==e.type)return e.export({format:"jwk"});e=e.export()}if(e instanceof Uint8Array)return{kty:"oct",k:o(e)};if(!I(e))throw new TypeError(u(e,"CryptoKey","KeyObject","Uint8Array"));if(!e.extractable)throw new TypeError("non-extractable CryptoKey cannot be exported as a JWK");const{ext:t,key_ops:r,alg:s,use:i,...n}=await crypto.subtle.exportKey("jwk",e);return"AKP"===n.kty&&(n.alg=s),n}(e)}function F(e,t,r,s,i){if(void 0!==i.crit&&void 0===s?.crit)throw new e('"crit" (Critical) Header Parameter MUST be integrity protected');if(!s||void 0===s.crit)return new Set;if(!Array.isArray(s.crit)||0===s.crit.length||s.crit.some((e=>"string"!=typeof e||0===e.length)))throw new e('"crit" (Critical) Header Parameter MUST be an array of non-empty strings when present');let n;n=void 0!==r?new Map([...Object.entries(r),...t.entries()]):t;for(const t of s.crit){if(!n.has(t))throw new _(`Extension Header Parameter "${t}" is not recognized`);if(void 0===i[t])throw new e(`Extension Header Parameter "${t}" is missing`);if(n.get(t)&&void 0===s[t])throw new e(`Extension Header Parameter "${t}" MUST be integrity protected`)}return new Set(s.crit)}const q=e=>e?.[Symbol.toStringTag],B=(e,t,r)=>{if(void 0!==t.use){let e;switch(r){case"sign":case"verify":e="sig";break;case"encrypt":case"decrypt":e="enc"}if(t.use!==e)throw new TypeError(`Invalid key for this operation, its "use" must be "${e}" when present`)}if(void 0!==t.alg&&t.alg!==e)throw new TypeError(`Invalid key for this operation, its "alg" must be "${e}" when present`);if(Array.isArray(t.key_ops)){let s;switch(!0){case"sign"===r||"verify"===r:case"dir"===e:case e.includes("CBC-HS"):s=r;break;case e.startsWith("PBES2"):s="deriveBits";break;case/^A\d{3}(?:GCM)?(?:KW)?$/.test(e):s=!e.includes("GCM")&&e.endsWith("KW")?"encrypt"===r?"wrapKey":"unwrapKey":r;break;case"encrypt"===r&&e.startsWith("RSA"):s="wrapKey";break;case"decrypt"===r:s=e.startsWith("RSA")?"unwrapKey":"deriveBits"}if(s&&!1===t.key_ops?.includes?.(s))throw new TypeError(`Invalid key for this operation, its "key_ops" must include "${s}" when present`)}return!0};function V(e,t,r){switch(e.substring(0,2)){case"A1":case"A2":case"di":case"HS":case"PB":((e,t,r)=>{if(!(t instanceof Uint8Array)){if(O(t)){if((e=>"oct"===e.kty&&"string"==typeof e.k)(t)&&B(e,t,r))return;throw new TypeError('JSON Web Key for symmetric algorithms must have JWK "kty" (Key Type) equal to "oct" and the JWK "k" (Key Value) present')}if(!R(t))throw new TypeError(p(e,t,"CryptoKey","KeyObject","JSON Web Key","Uint8Array"));if("secret"!==t.type)throw new TypeError(`${q(t)} instances for symmetric algorithms must be of type "secret"`)}})(e,t,r);break;default:((e,t,r)=>{if(O(t))switch(r){case"decrypt":case"sign":if((e=>"oct"!==e.kty&&("AKP"===e.kty&&"string"==typeof e.priv||"string"==typeof e.d))(t)&&B(e,t,r))return;throw new TypeError("JSON Web Key for this operation must be a private JWK");case"encrypt":case"verify":if((e=>"oct"!==e.kty&&void 0===e.d&&void 0===e.priv)(t)&&B(e,t,r))return;throw new TypeError("JSON Web Key for this operation must be a public JWK")}if(!R(t))throw new TypeError(p(e,t,"CryptoKey","KeyObject","JSON Web Key"));if("secret"===t.type)throw new TypeError(`${q(t)} instances for asymmetric algorithms must not be of type "secret"`);if("public"===t.type)switch(r){case"sign":throw new TypeError(`${q(t)} instances for asymmetric algorithm signing must be of type "private"`);case"decrypt":throw new TypeError(`${q(t)} instances for asymmetric algorithm decryption must be of type "private"`)}if("private"===t.type)switch(r){case"verify":throw new TypeError(`${q(t)} instances for asymmetric algorithm verifying must be of type "public"`);case"encrypt":throw new TypeError(`${q(t)} instances for asymmetric algorithm encryption must be of type "public"`)}})(e,t,r)}}async function z(e,o,a){if(!C(e))throw new m("Flattened JWS must be an object");if(void 0===e.protected&&void 0===e.header)throw new m('Flattened JWS must have either of the "protected" or "header" members');if(void 0!==e.protected&&"string"!=typeof e.protected)throw new m("JWS Protected Header incorrect type");if(void 0===e.payload)throw new m("JWS Payload missing");if("string"!=typeof e.signature)throw new m("JWS Signature missing or incorrect type");if(void 0!==e.header&&!C(e.header))throw new m("JWS Unprotected Header incorrect type");let c={};if(e.protected)try{const t=n(e.protected);c=JSON.parse(r.decode(t))}catch{throw new m("JWS Protected Header is invalid")}if(!x(c,e.header))throw new m("JWS Protected and JWS Unprotected Header Parameter names must be disjoint");const d={...c,...e.header};let l=!0;if(F(m,new Map([["b64",!0]]),a?.crit,c,d).has("b64")&&(l=c.b64,"boolean"!=typeof l))throw new m('The "b64" (base64url-encode payload) Header Parameter must be a boolean');const{alg:h}=d;if("string"!=typeof h||!h)throw new m('JWS "alg" (Algorithm) Header Parameter missing or invalid');const u=a&&function(e,t){if(void 0!==t&&(!Array.isArray(t)||t.some((e=>"string"!=typeof e))))throw new TypeError(`"${e}" option must be an array of strings`);if(t)return new Set(t)}("algorithms",a.algorithms);if(u&&!u.has(h))throw new w('"alg" (Algorithm) Header Parameter value not allowed');if(l){if("string"!=typeof e.payload)throw new m("JWS Payload must be a string")}else if("string"!=typeof e.payload&&!(e.payload instanceof Uint8Array))throw new m("JWS Payload must be a string or an Uint8Array instance");let p=!1;"function"==typeof o&&(o=await o(c,e),p=!0),V(h,o,"verify");const g=s(void 0!==e.protected?i(e.protected):new Uint8Array,i("."),"string"==typeof e.payload?l?i(e.payload):t.encode(e.payload):e.payload),f=P(e.signature,"signature",m),y=await W(o,h),_=await async function(e,t,r,s){const i=await D(e,t,"verify");K(e,i);const n=H(e,i.algorithm);try{return await crypto.subtle.verify(n,i,r,s)}catch{return!1}}(h,y,f,g);if(!_)throw new A;let S;S=l?P(e.payload,"payload",m):"string"==typeof e.payload?t.encode(e.payload):e.payload;const v={payload:S};return void 0!==e.protected&&(v.protectedHeader=c),void 0!==e.header&&(v.unprotectedHeader=e.header),p?{...v,key:y}:v}const G=e=>Math.floor(e.getTime()/1e3),X=86400,Q=/^(\+|\-)? ?(\d+|\d+\.\d+) ?(seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)(?: (ago|from now))?$/i;function Y(e){const t=Q.exec(e);if(!t||t[4]&&t[1])throw new TypeError("Invalid time period format");const r=parseFloat(t[2]);let s;switch(t[3].toLowerCase()){case"sec":case"secs":case"second":case"seconds":case"s":s=Math.round(r);break;case"minute":case"minutes":case"min":case"mins":case"m":s=Math.round(60*r);break;case"hour":case"hours":case"hr":case"hrs":case"h":s=Math.round(3600*r);break;case"day":case"days":case"d":s=Math.round(r*X);break;case"week":case"weeks":case"w":s=Math.round(604800*r);break;default:s=Math.round(31557600*r)}return"-"===t[1]||"ago"===t[4]?-s:s}function Z(e,t){if(!Number.isFinite(t))throw new TypeError(`Invalid ${e} input`);return t}const ee=e=>e.includes("/")?e.toLowerCase():`application/${e.toLowerCase()}`;function te(e,t,s={}){let i;try{i=JSON.parse(r.decode(t))}catch{}if(!C(i))throw new S("JWT Claims Set must be a top-level JSON object");const{typ:n}=s;if(n&&("string"!=typeof e.typ||ee(e.typ)!==ee(n)))throw new f('unexpected "typ" JWT header value',i,"typ","check_failed");const{requiredClaims:o=[],issuer:a,subject:c,audience:d,maxTokenAge:l}=s,h=[...o];void 0!==l&&h.push("iat"),void 0!==d&&h.push("aud"),void 0!==c&&h.push("sub"),void 0!==a&&h.push("iss");for(const e of new Set(h.reverse()))if(!(e in i))throw new f(`missing required "${e}" claim`,i,e,"missing");if(a&&!(Array.isArray(a)?a:[a]).includes(i.iss))throw new f('unexpected "iss" claim value',i,"iss","check_failed");if(c&&i.sub!==c)throw new f('unexpected "sub" claim value',i,"sub","check_failed");if(d&&(u=i.aud,p="string"==typeof d?[d]:d,!("string"==typeof u?p.includes(u):Array.isArray(u)&&p.some(Set.prototype.has.bind(new Set(u))))))throw new f('unexpected "aud" claim value',i,"aud","check_failed");var u,p;let g;switch(typeof s.clockTolerance){case"string":g=Y(s.clockTolerance);break;case"number":g=s.clockTolerance;break;case"undefined":g=0;break;default:throw new TypeError("Invalid clockTolerance option type")}const{currentDate:w}=s,_=G(w||new Date);if((void 0!==i.iat||l)&&"number"!=typeof i.iat)throw new f('"iat" claim must be a number',i,"iat","invalid");if(void 0!==i.nbf){if("number"!=typeof i.nbf)throw new f('"nbf" claim must be a number',i,"nbf","invalid");if(i.nbf>_+g)throw new f('"nbf" claim timestamp check failed',i,"nbf","check_failed")}if(void 0!==i.exp){if("number"!=typeof i.exp)throw new f('"exp" claim must be a number',i,"exp","invalid");if(i.exp<=_-g)throw new y('"exp" claim timestamp check failed',i,"exp","check_failed")}if(l){const e=_-i.iat;if(e-g>("number"==typeof l?l:Y(l)))throw new y('"iat" claim timestamp check failed (too far in the past)',i,"iat","check_failed");if(e<0-g)throw new f('"iat" claim timestamp check failed (it should be in the past)',i,"iat","check_failed")}return i}class re{#e;constructor(e){if(!C(e))throw new TypeError("JWT Claims Set MUST be an object");this.#e=structuredClone(e)}data(){return t.encode(JSON.stringify(this.#e))}get iss(){return this.#e.iss}set iss(e){this.#e.iss=e}get sub(){return this.#e.sub}set sub(e){this.#e.sub=e}get aud(){return this.#e.aud}set aud(e){this.#e.aud=e}set jti(e){this.#e.jti=e}set nbf(e){"number"==typeof e?this.#e.nbf=Z("setNotBefore",e):e instanceof Date?this.#e.nbf=Z("setNotBefore",G(e)):this.#e.nbf=G(new Date)+Y(e)}set exp(e){"number"==typeof e?this.#e.exp=Z("setExpirationTime",e):e instanceof Date?this.#e.exp=Z("setExpirationTime",G(e)):this.#e.exp=G(new Date)+Y(e)}set iat(e){void 0===e?this.#e.iat=G(new Date):e instanceof Date?this.#e.iat=Z("setIssuedAt",G(e)):this.#e.iat=Z("setIssuedAt","string"==typeof e?G(new Date)+Y(e):e)}}async function se(e,t,s){const i=await async function(e,t,s){if(e instanceof Uint8Array&&(e=r.decode(e)),"string"!=typeof e)throw new m("Compact JWS must be a string or Uint8Array");const{0:i,1:n,2:o,length:a}=e.split(".");if(3!==a)throw new m("Invalid Compact JWS");const c=await z({payload:n,protected:i,signature:o},t,s),d={payload:c.payload,protectedHeader:c.protectedHeader};return"function"==typeof t?{...d,key:c.key}:d}(e,t,s);if(i.protectedHeader.crit?.includes("b64")&&!1===i.protectedHeader.b64)throw new S("JWTs MUST NOT use unencoded payload");const n={payload:te(i.protectedHeader,i.payload,s),protectedHeader:i.protectedHeader};return"function"==typeof t?{...n,key:i.key}:n}class ie{#e;#t;#r;constructor(e){if(!(e instanceof Uint8Array))throw new TypeError("payload must be an instance of Uint8Array");this.#e=e}setProtectedHeader(e){return U(this.#t,"setProtectedHeader"),this.#t=e,this}setUnprotectedHeader(e){return U(this.#r,"setUnprotectedHeader"),this.#r=e,this}async sign(e,t){if(!this.#t&&!this.#r)throw new m("either setProtectedHeader or setUnprotectedHeader must be called before #sign()");if(!x(this.#t,this.#r))throw new m("JWS Protected and JWS Unprotected Header Parameter names must be disjoint");const r={...this.#t,...this.#r};let n=!0;if(F(m,new Map([["b64",!0]]),t?.crit,this.#t,r).has("b64")&&(n=this.#t.b64,"boolean"!=typeof n))throw new m('The "b64" (base64url-encode payload) Header Parameter must be a boolean');const{alg:a}=r;if("string"!=typeof a||!a)throw new m('JWS "alg" (Algorithm) Header Parameter missing or invalid');let c,d,l,h;V(a,e,"sign"),n?(c=o(this.#e),d=i(c)):(d=this.#e,c=""),this.#t?(l=o(JSON.stringify(this.#t)),h=i(l)):(l="",h=new Uint8Array);const u=s(h,i("."),d),p=await W(e,a),g=await async function(e,t,r){const s=await D(e,t,"sign");K(e,s);const i=await crypto.subtle.sign(H(e,s.algorithm),s,r);return new Uint8Array(i)}(a,p,u),f={signature:o(g),payload:c};return this.#r&&(f.header=this.#r),this.#t&&(f.protected=l),f}}class ne{#s;constructor(e){this.#s=new ie(e)}setProtectedHeader(e){return this.#s.setProtectedHeader(e),this}async sign(e,t){const r=await this.#s.sign(e,t);if(void 0===r.payload)throw new TypeError("use the flattened module for creating JWS with b64: false");return`${r.protected}.${r.payload}.${r.signature}`}}class oe{#t;#i;constructor(e={}){this.#i=new re(e)}setIssuer(e){return this.#i.iss=e,this}setSubject(e){return this.#i.sub=e,this}setAudience(e){return this.#i.aud=e,this}setJti(e){return this.#i.jti=e,this}setNotBefore(e){return this.#i.nbf=e,this}setExpirationTime(e){return this.#i.exp=e,this}setIssuedAt(e){return this.#i.iat=e,this}setProtectedHeader(e){return this.#t=e,this}async sign(e,t){const r=new ne(this.#i.data());if(r.setProtectedHeader(this.#t),Array.isArray(this.#t?.crit)&&this.#t.crit.includes("b64")&&!1===this.#t.b64)throw new S("JWTs MUST NOT use unencoded payload");return r.sign(e,t)}}function ae(e){return C(e)}class ce{#n;#o=new WeakMap;constructor(e){if(!function(e){return e&&"object"==typeof e&&Array.isArray(e.keys)&&e.keys.every(ae)}(e))throw new v("JSON Web Key Set malformed");this.#n=structuredClone(e)}jwks(){return this.#n}async getKey(e,t){const{alg:r,kid:s}={...e,...t?.header},i=function(e){switch("string"==typeof e&&e.slice(0,2)){case"RS":case"PS":return"RSA";case"ES":return"EC";case"Ed":return"OKP";case"ML":return"AKP";default:throw new _('Unsupported "alg" value for a JSON Web Key Set')}}(r),n=this.#n.keys.filter((e=>{let t=i===e.kty;if(t&&"string"==typeof s&&(t=s===e.kid),!t||"string"!=typeof e.alg&&"AKP"!==i||(t=r===e.alg),t&&"string"==typeof e.use&&(t="sig"===e.use),t&&Array.isArray(e.key_ops)&&(t=e.key_ops.includes("verify")),t)switch(r){case"ES256":t="P-256"===e.crv;break;case"ES384":t="P-384"===e.crv;break;case"ES512":t="P-521"===e.crv;break;case"Ed25519":case"EdDSA":t="Ed25519"===e.crv}return t})),{0:o,length:a}=n;if(0===a)throw new b;if(1!==a){const e=new E,t=this.#o;throw e[Symbol.asyncIterator]=async function*(){for(const e of n)try{yield await de(t,e,r)}catch{}},e}return de(this.#o,o,r)}}async function de(e,t,r){const s=e.get(t)||e.set(t,{}).get(t);if(void 0===s[r]){const e=await async function(e,t){if(!C(e))throw new TypeError("JWK must be an object");let r;switch(t??=e.alg,r??=e.ext,e.kty){case"oct":if("string"!=typeof e.k||!e.k)throw new TypeError('missing "k" (Key Value) Parameter value');return n(e.k);case"RSA":if("oth"in e&&void 0!==e.oth)throw new _('RSA JWK "oth" (Other Primes Info) Parameter value is not supported');return L({...e,alg:t,ext:r});case"AKP":if("string"!=typeof e.alg||!e.alg)throw new TypeError('missing "alg" (Algorithm) Parameter value');if(void 0!==t&&t!==e.alg)throw new TypeError("JWK alg and alg option value mismatch");return L({...e,ext:r});case"EC":case"OKP":return L({...e,alg:t,ext:r});default:throw new _('Unsupported "kty" (Key Type) Parameter value')}}({...t,ext:!0},r);if(e instanceof Uint8Array||"public"!==e.type)throw new v("JSON Web Key Set members must be public keys");s[r]=e}return s[r]}function le(e){const t=new ce(e),r=async(e,r)=>t.getKey(e,r);return Object.defineProperties(r,{jwks:{value:()=>structuredClone(t.jwks()),enumerable:!1,configurable:!1,writable:!1}}),r}let he;if("undefined"==typeof navigator||!navigator.userAgent?.startsWith?.("Mozilla/5.0 ")){he=`${"jose"}/${"v6.2.3"}`}const ue=Symbol();const pe=Symbol();class ge{#a;#c;#d;#l;#h;#u;#p;#g;#f;#y;constructor(e,t){if(!(e instanceof URL))throw new TypeError("url must be an instance of URL");var r,s;this.#a=new URL(e.href),this.#c="number"==typeof t?.timeoutDuration?t?.timeoutDuration:5e3,this.#d="number"==typeof t?.cooldownDuration?t?.cooldownDuration:3e4,this.#l="number"==typeof t?.cacheMaxAge?t?.cacheMaxAge:6e5,this.#p=new Headers(t?.headers),he&&!this.#p.has("User-Agent")&&this.#p.set("User-Agent",he),this.#p.has("accept")||(this.#p.set("accept","application/json"),this.#p.append("accept","application/jwk-set+json")),this.#g=t?.[ue],void 0!==t?.[pe]&&(this.#y=t?.[pe],r=t?.[pe],s=this.#l,"object"==typeof r&&null!==r&&"uat"in r&&"number"==typeof r.uat&&!(Date.now()-r.uat>=s)&&"jwks"in r&&C(r.jwks)&&Array.isArray(r.jwks.keys)&&Array.prototype.every.call(r.jwks.keys,C)&&(this.#h=this.#y.uat,this.#f=le(this.#y.jwks)))}pendingFetch(){return!!this.#u}coolingDown(){return"number"==typeof this.#h&&Date.now()<this.#h+this.#d}fresh(){return"number"==typeof this.#h&&Date.now()<this.#h+this.#l}jwks(){return this.#f?.jwks()}async getKey(e,t){this.#f&&this.fresh()||await this.reload();try{return await this.#f(e,t)}catch(r){if(r instanceof b&&!1===this.coolingDown())return await this.reload(),this.#f(e,t);throw r}}async reload(){this.#u&&("undefined"!=typeof WebSocketPair||"undefined"!=typeof navigator&&"Cloudflare-Workers"===navigator.userAgent||"undefined"!=typeof EdgeRuntime&&"vercel"===EdgeRuntime)&&(this.#u=void 0),this.#u||=async function(e,t,r,s=fetch){const i=await s(e,{method:"GET",signal:r,redirect:"manual",headers:t}).catch((e=>{if("TimeoutError"===e.name)throw new k;throw e}));if(200!==i.status)throw new g("Expected 200 OK from the JSON Web Key Set HTTP response");try{return await i.json()}catch{throw new g("Failed to parse the JSON Web Key Set HTTP response as JSON")}}(this.#a.href,this.#p,AbortSignal.timeout(this.#c),this.#g).then((e=>{this.#f=le(e),this.#y&&(this.#y.uat=Date.now(),this.#y.jwks=e),this.#h=Date.now(),this.#u=void 0})).catch((e=>{throw this.#u=void 0,e})),await this.#u}}function fe(e){const t=e?.modulusLength??2048;if("number"!=typeof t||t<2048)throw new _("Invalid or unsupported modulusLength option provided, 2048 bits or larger keys must be used");return t}const ye=[];for(let e=0;e<256;++e)ye.push((e+256).toString(16).slice(1));const we=new Uint8Array(16);function _e(e,t,r){return crypto.randomUUID?crypto.randomUUID():function(e){e=e||{};const t=e.random??e.rng?.()??crypto.getRandomValues(we);if(t.length<16)throw new Error("Random bytes length must be >= 16");return t[6]=15&t[6]|64,t[8]=63&t[8]|128,function(e,t=0){return(ye[e[t+0]]+ye[e[t+1]]+ye[e[t+2]]+ye[e[t+3]]+"-"+ye[e[t+4]]+ye[e[t+5]]+"-"+ye[e[t+6]]+ye[e[t+7]]+"-"+ye[e[t+8]]+ye[e[t+9]]+"-"+ye[e[t+10]]+ye[e[t+11]]+ye[e[t+12]]+ye[e[t+13]]+ye[e[t+14]]+ye[e[t+15]]).toLowerCase()}(t)}(e)}const me="solidClientAuthn:",Se=["ES256","RS256"],ve={ERROR:"error",LOGIN:"login",LOGOUT:"logout",NEW_REFRESH_TOKEN:"newRefreshToken",NEW_TOKENS:"newTokens",AUTHORIZATION_REQUEST:"authorizationRequest",SESSION_EXPIRED:"sessionExpired",SESSION_EXTENDED:"sessionExtended",SESSION_RESTORED:"sessionRestore",TIMEOUT_SET:"timeoutSet"},be=["openid","offline_access","webid"];class Ee{handleables;constructor(e){this.handleables=e,this.handleables=e}async getProperHandler(e){const t=await Promise.all(this.handleables.map((t=>t.canHandle(...e))));for(let e=0;e<t.length;e+=1)if(t[e])return this.handleables[e];return null}async canHandle(...e){return null!==await this.getProperHandler(e)}async handle(...e){const t=await this.getProperHandler(e);if(t)return t.handle(...e);throw new Error(`[${this.constructor.name}] cannot find a suitable handler for: ${e.map((e=>{try{return JSON.stringify(e)}catch(t){return e.toString()}})).join(", ")}`)}}async function ke(e,t,r,s){let i,n;try{const{payload:n}=await se(e,function(e,t){const r=new ge(e,t),s=async(e,t)=>r.getKey(e,t);return Object.defineProperties(s,{coolingDown:{get:()=>r.coolingDown(),enumerable:!0,configurable:!1},fresh:{get:()=>r.fresh(),enumerable:!0,configurable:!1},reload:{value:()=>r.reload(),enumerable:!0,configurable:!1,writable:!1},reloading:{get:()=>r.pendingFetch(),enumerable:!0,configurable:!1},jwks:{value:()=>r.jwks(),enumerable:!0,configurable:!1,writable:!1}}),s}(new URL(t)),{issuer:r,audience:s});i=n}catch(e){throw new Error(`Token verification failed: ${e.stack}`)}if("string"==typeof i.azp&&(n=i.azp),"string"==typeof i.webid)return{webId:i.webid,clientId:n};if("string"!=typeof i.sub)throw new Error(`The token ${JSON.stringify(i)} is invalid: it has no 'webid' claim and no 'sub' claim.`);try{return new URL(i.sub),{webId:i.sub,clientId:n}}catch(e){throw new Error(`The token has no 'webid' claim, and its 'sub' claim of [${i.sub}] is invalid as a URL - error [${e}].`)}}function Ae(e){try{const t=new URL(e),r=!t.searchParams.has("code")&&!t.searchParams.has("state"),s=""===t.hash;return r&&s}catch(e){return!1}}function Ie(e){const t=new URL(e);return t.searchParams.delete("state"),t.searchParams.delete("code"),t.searchParams.delete("error"),t.searchParams.delete("error_description"),t.searchParams.delete("iss"),t}class Te{storageUtility;redirector;constructor(e,t){this.storageUtility=e,this.redirector=t,this.storageUtility=e,this.redirector=t}parametersGuard=e=>void 0!==e.issuerConfiguration.grantTypesSupported&&e.issuerConfiguration.grantTypesSupported.indexOf("authorization_code")>-1&&void 0!==e.redirectUrl;async canHandle(e){return this.parametersGuard(e)}async setupRedirectHandler({oidcLoginOptions:e,state:t,codeVerifier:r,targetUrl:s}){if(!this.parametersGuard(e))throw new Error("The authorization code grant requires a redirectUrl.");var i,n;await Promise.all([this.storageUtility.setForUser(t,{sessionId:e.sessionId}),this.storageUtility.setForUser(e.sessionId,{codeVerifier:r,issuer:e.issuer.toString(),redirectUrl:e.redirectUrl,dpop:Boolean(e.dpop).toString(),keepAlive:(i=e.keepAlive,n=!0,"boolean"==typeof i?Boolean(i):Boolean(n)).toString()})]),this.redirector.redirect(s,{handleRedirect:e.handleRedirect})}}class Re{sessionInfoManager;constructor(e){this.sessionInfoManager=e,this.sessionInfoManager=e}async canHandle(){return!0}async handle(e){await this.sessionInfoManager.clear(e)}}class Ue{redirector;constructor(e){this.redirector=e,this.redirector=e}async canHandle(e,t){return"idp"===t?.logoutType}async handle(e,t){if("idp"!==t?.logoutType)throw new Error("Attempting to call idp logout handler to perform app logout");if(void 0===t.toLogoutUrl)throw new Error("Cannot perform IDP logout. Did you log in using the OIDC authentication flow?");this.redirector.redirect(t.toLogoutUrl(t),{handleRedirect:t.handleRedirect})}}class Pe{handlers;constructor(e,t){this.handlers=[new Re(e),new Ue(t)]}async canHandle(){return!0}async handle(e,t){for(const r of this.handlers)await r.canHandle(e,t)&&await r.handle(e,t)}}function Ce(){return{isLoggedIn:!1,sessionId:_e(),fetch:(...e)=>fetch(...e)}}async function xe(e,t){await Promise.all([t.deleteAllUserData(e,{secure:!1}),t.deleteAllUserData(e,{secure:!0})])}class Oe{storageUtility;constructor(e){this.storageUtility=e,this.storageUtility=e}update(e,t){throw new Error("Not Implemented")}set(e,t){throw new Error("Not Implemented")}get(e){throw new Error("Not implemented")}async getAll(){throw new Error("Not implemented")}async clear(e){return xe(e,this.storageUtility)}async register(e){throw new Error("Not implemented")}async getRegisteredSessionIdAll(){throw new Error("Not implemented")}async clearAll(){throw new Error("Not implemented")}async setOidcContext(e,t){throw new Error("Not implemented")}}function Ke({endSessionEndpoint:e,idTokenHint:t}){if(void 0!==e)return function({state:r,postLogoutUrl:s}){return function({endSessionEndpoint:e,idTokenHint:t,postLogoutRedirectUri:r,state:s}){const i=new URL(e);return void 0!==t&&i.searchParams.append("id_token_hint",t),void 0!==r&&(i.searchParams.append("post_logout_redirect_uri",r),void 0!==s&&i.searchParams.append("state",s)),i.toString()}({endSessionEndpoint:e,idTokenHint:t,state:r,postLogoutRedirectUri:s})}}function He(e){try{return new URL(e),!0}catch{return!1}}async function De(e,t,r,s){let i;if(function(e,t){return t.scopesSupported.includes("webid")&&void 0!==e.clientId&&He(e.clientId)}(e,t))i={clientId:e.clientId,clientName:e.clientName,clientType:"solid-oidc"};else{if(!function(e){return void 0!==e.clientId&&!He(e.clientId)}(e))return s.getClient({sessionId:e.sessionId,clientName:e.clientName,redirectUrl:e.redirectUrl},t);i={clientId:e.clientId,clientSecret:e.clientSecret,clientName:e.clientName,clientType:"static"}}const n={clientId:i.clientId,clientType:i.clientType};return"static"===i.clientType&&(n.clientSecret=i.clientSecret),i.clientName&&(n.clientName=i.clientName),await r.setForUser(e.sessionId,n),i}const je=(e,t)=>fetch(e,t);let Le=class{loginHandler;redirectHandler;logoutHandler;sessionInfoManager;issuerConfigFetcher;boundLogout;constructor(e,t,r,s,i){this.loginHandler=e,this.redirectHandler=t,this.logoutHandler=r,this.sessionInfoManager=s,this.issuerConfigFetcher=i,this.loginHandler=e,this.redirectHandler=t,this.logoutHandler=r,this.sessionInfoManager=s,this.issuerConfigFetcher=i}fetch=je;logout=async(e,t)=>{await this.logoutHandler.handle(e,"idp"===t?.logoutType?{...t,toLogoutUrl:this.boundLogout}:t),this.fetch=je,delete this.boundLogout};getSessionInfo=async e=>this.sessionInfoManager.get(e);getAllSessionInfo=async()=>this.sessionInfoManager.getAll()};async function $e(e,t,r){try{const[s,i,n,o,a]=await Promise.all([t.getForUser(e,"issuer",{errorIfNull:!0}),t.getForUser(e,"codeVerifier"),t.getForUser(e,"redirectUrl"),t.getForUser(e,"dpop",{errorIfNull:!0}),t.getForUser(e,"keepAlive")]);await t.deleteForUser(e,"codeVerifier");return{codeVerifier:i,redirectUrl:n,issuerConfig:await r.fetchConfig(s),dpop:"true"===o,keepAlive:"string"!=typeof a||"true"===a}}catch(t){throw new Error(`Failed to retrieve OIDC context from storage associated with session [${e}]: ${t}`)}}class Ne{secureStorage;insecureStorage;constructor(e,t){this.secureStorage=e,this.insecureStorage=t,this.secureStorage=e,this.insecureStorage=t}getKey(e){return`solidClientAuthenticationUser:${e}`}async getUserData(e,t){const r=await(t?this.secureStorage:this.insecureStorage).get(this.getKey(e));if(void 0===r)return{};try{return JSON.parse(r)}catch(s){throw new Error(`Data for user [${e}] in [${t?"secure":"unsecure"}] storage is corrupted - expected valid JSON, but got: ${r}`)}}async setUserData(e,t,r){await(r?this.secureStorage:this.insecureStorage).set(this.getKey(e),JSON.stringify(t))}async get(e,t){const r=await(t?.secure?this.secureStorage:this.insecureStorage).get(e);if(void 0===r&&t?.errorIfNull)throw new Error(`[${e}] is not stored`);return r}async set(e,t,r){return(r?.secure?this.secureStorage:this.insecureStorage).set(e,t)}async delete(e,t){return(t?.secure?this.secureStorage:this.insecureStorage).delete(e)}async getForUser(e,t,r){const s=await this.getUserData(e,r?.secure);let i;if(s&&s[t]||(i=void 0),i=s[t],void 0===i&&r?.errorIfNull)throw new Error(`Field [${t}] for user [${e}] is not stored`);return i||void 0}async setForUser(e,t,r){let s;try{s=await this.getUserData(e,r?.secure)}catch{s={}}await this.setUserData(e,{...s,...t},r?.secure)}async deleteForUser(e,t,r){const s=await this.getUserData(e,r?.secure);delete s[t],await this.setUserData(e,s,r?.secure)}async deleteAllUserData(e,t){await(t?.secure?this.secureStorage:this.insecureStorage).delete(this.getKey(e))}}class Je{map={};async get(e){return this.map[e]||void 0}async set(e,t){this.map[e]=t}async delete(e){delete this.map[e]}}class We extends Error{constructor(e){super(e)}}class Me extends Error{constructor(e){super(`[${e}] is not implemented`)}}class Fe extends Error{missingFields;constructor(e){super(`Invalid response from OIDC provider: missing fields ${e}`),this.missingFields=e}}class qe extends Error{error;errorDescription;constructor(e,t,r){super(e),this.error=t,this.errorDescription=r}}function Be(e){const t=new URL(e);return new URL(t.pathname,t.origin).toString()}async function Ve(e,t,r){return new oe({htu:Be(e),htm:t.toUpperCase(),jti:_e()}).setProtectedHeader({alg:Se[0],jwk:r.publicKey,typ:"dpop+jwt"}).setIssuedAt().sign(r.privateKey,{})}async function ze(){const{privateKey:e,publicKey:t}=await async function(e,t){let r,s;switch(e){case"PS256":case"PS384":case"PS512":r={name:"RSA-PSS",hash:`SHA-${e.slice(-3)}`,publicExponent:Uint8Array.of(1,0,1),modulusLength:fe(t)},s=["sign","verify"];break;case"RS256":case"RS384":case"RS512":r={name:"RSASSA-PKCS1-v1_5",hash:`SHA-${e.slice(-3)}`,publicExponent:Uint8Array.of(1,0,1),modulusLength:fe(t)},s=["sign","verify"];break;case"RSA-OAEP":case"RSA-OAEP-256":case"RSA-OAEP-384":case"RSA-OAEP-512":r={name:"RSA-OAEP",hash:`SHA-${parseInt(e.slice(-3),10)||1}`,publicExponent:Uint8Array.of(1,0,1),modulusLength:fe(t)},s=["decrypt","unwrapKey","encrypt","wrapKey"];break;case"ES256":r={name:"ECDSA",namedCurve:"P-256"},s=["sign","verify"];break;case"ES384":r={name:"ECDSA",namedCurve:"P-384"},s=["sign","verify"];break;case"ES512":r={name:"ECDSA",namedCurve:"P-521"},s=["sign","verify"];break;case"Ed25519":case"EdDSA":s=["sign","verify"],r={name:"Ed25519"};break;case"ML-DSA-44":case"ML-DSA-65":case"ML-DSA-87":s=["sign","verify"],r={name:e};break;case"ECDH-ES":case"ECDH-ES+A128KW":case"ECDH-ES+A192KW":case"ECDH-ES+A256KW":{s=["deriveBits"];const e=t?.crv??"P-256";switch(e){case"P-256":case"P-384":case"P-521":r={name:"ECDH",namedCurve:e};break;case"X25519":r={name:"X25519"};break;default:throw new _("Invalid or unsupported crv option provided, supported values are P-256, P-384, P-521, and X25519")}break}default:throw new _('Invalid or unsupported JWK "alg" (Algorithm) Parameter value')}return crypto.subtle.generateKey(r,t?.extractable,s)}(Se[0],{extractable:!0}),r={privateKey:e,publicKey:await M(t)};return[r.publicKey.alg]=Se,r}async function Ge(e,t,r,s){if(void 0!==r)return async function(e,t,r,s){const i=new Headers(s?.headers);return i.set("Authorization",`DPoP ${t}`),i.set("DPoP",await Ve(e,s?.method??"get",r)),{...s,headers:i}}(e,t,r,s);const i=new Headers(s?.headers);return i.set("Authorization",`Bearer ${t}`),{...s,headers:i}}async function Xe(e,t,r,s,i=fetch){return i(t,await Ge(t.toString(),e,s,r))}const Qe=e=>void 0!==e?e-5>0?e-5:e:600;function Ye(e,t){let r,s=e;const i=t?.refreshOptions,n=t?.eventEmitter;if(void 0!==t&&void 0!==i){const e=async()=>{try{const{accessToken:o,refreshToken:a,expiresIn:c}=await async function(e,t,r){const s=await e.tokenRefresher.refresh(e.sessionId,e.refreshToken,t);return r?.emit(ve.SESSION_EXTENDED,s.expiresIn??600),{accessToken:s.accessToken,refreshToken:s.refreshToken,expiresIn:s.expiresIn}}(i,t.dpopKey,n);s=o,void 0!==a&&(i.refreshToken=a),clearTimeout(r),r=setTimeout(e,1e3*Qe(c)),t.eventEmitter?.emit(ve.TIMEOUT_SET,r)}catch(e){e instanceof qe&&(n?.emit(ve.ERROR,e.error,e.errorDescription),n?.emit(ve.SESSION_EXPIRED)),e instanceof Fe&&e.missingFields.includes("access_token")&&n?.emit(ve.SESSION_EXPIRED)}};r=setTimeout(e,1e3*Qe(t.expiresIn)),n?.emit(ve.TIMEOUT_SET,r)}else if(void 0!==n){const e=setTimeout((()=>{n.emit(ve.SESSION_EXPIRED)}),1e3*Qe(t?.expiresIn));n.emit(ve.TIMEOUT_SET,e)}return async(e,r)=>{let i=await Xe(s,e,r,t?.dpopKey,t?.fetch);const n=!i.ok&&(o=i.status,![401,403].includes(o));var o;if(i.ok||n)return i;return i.url!==e&&void 0!==t?.dpopKey&&(i=await Xe(s,i.url,r,t.dpopKey,t.fetch)),i}}const Ze=[];for(let e=0;e<256;++e)Ze.push((e+256).toString(16).slice(1));const et=new Uint8Array(16);function tt(e,t,r){return crypto.randomUUID?crypto.randomUUID():function(e){e=e||{};const t=e.random??e.rng?.()??crypto.getRandomValues(et);if(t.length<16)throw new Error("Random bytes length must be >= 16");return t[6]=15&t[6]|64,t[8]=63&t[8]|128,function(e,t=0){return(Ze[e[t+0]]+Ze[e[t+1]]+Ze[e[t+2]]+Ze[e[t+3]]+"-"+Ze[e[t+4]]+Ze[e[t+5]]+"-"+Ze[e[t+6]]+Ze[e[t+7]]+"-"+Ze[e[t+8]]+Ze[e[t+9]]+"-"+Ze[e[t+10]]+Ze[e[t+11]]+Ze[e[t+12]]+Ze[e[t+13]]+Ze[e[t+14]]+Ze[e[t+15]]).toLowerCase()}(t)}(e)}function rt(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}var st,it={exports:{}};var nt=function(){if(st)return it.exports;st=1;var e,t="object"==typeof Reflect?Reflect:null,r=t&&"function"==typeof t.apply?t.apply:function(e,t,r){return Function.prototype.apply.call(e,t,r)};e=t&&"function"==typeof t.ownKeys?t.ownKeys:Object.getOwnPropertySymbols?function(e){return Object.getOwnPropertyNames(e).concat(Object.getOwnPropertySymbols(e))}:function(e){return Object.getOwnPropertyNames(e)};var s=Number.isNaN||function(e){return e!=e};function i(){i.init.call(this)}it.exports=i,it.exports.once=function(e,t){return new Promise((function(r,s){function i(r){e.removeListener(t,n),s(r)}function n(){"function"==typeof e.removeListener&&e.removeListener("error",i),r([].slice.call(arguments))}g(e,t,n,{once:!0}),"error"!==t&&function(e,t,r){"function"==typeof e.on&&g(e,"error",t,r)}(e,i,{once:!0})}))},i.EventEmitter=i,i.prototype._events=void 0,i.prototype._eventsCount=0,i.prototype._maxListeners=void 0;var n=10;function o(e){if("function"!=typeof e)throw new TypeError('The "listener" argument must be of type Function. Received type '+typeof e)}function a(e){return void 0===e._maxListeners?i.defaultMaxListeners:e._maxListeners}function c(e,t,r,s){var i,n,c,d;if(o(r),void 0===(n=e._events)?(n=e._events=Object.create(null),e._eventsCount=0):(void 0!==n.newListener&&(e.emit("newListener",t,r.listener?r.listener:r),n=e._events),c=n[t]),void 0===c)c=n[t]=r,++e._eventsCount;else if("function"==typeof c?c=n[t]=s?[r,c]:[c,r]:s?c.unshift(r):c.push(r),(i=a(e))>0&&c.length>i&&!c.warned){c.warned=!0;var l=new Error("Possible EventEmitter memory leak detected. "+c.length+" "+String(t)+" listeners added. Use emitter.setMaxListeners() to increase limit");l.name="MaxListenersExceededWarning",l.emitter=e,l.type=t,l.count=c.length,d=l,console&&console.warn&&console.warn(d)}return e}function d(){if(!this.fired)return this.target.removeListener(this.type,this.wrapFn),this.fired=!0,0===arguments.length?this.listener.call(this.target):this.listener.apply(this.target,arguments)}function l(e,t,r){var s={fired:!1,wrapFn:void 0,target:e,type:t,listener:r},i=d.bind(s);return i.listener=r,s.wrapFn=i,i}function h(e,t,r){var s=e._events;if(void 0===s)return[];var i=s[t];return void 0===i?[]:"function"==typeof i?r?[i.listener||i]:[i]:r?function(e){for(var t=new Array(e.length),r=0;r<t.length;++r)t[r]=e[r].listener||e[r];return t}(i):p(i,i.length)}function u(e){var t=this._events;if(void 0!==t){var r=t[e];if("function"==typeof r)return 1;if(void 0!==r)return r.length}return 0}function p(e,t){for(var r=new Array(t),s=0;s<t;++s)r[s]=e[s];return r}function g(e,t,r,s){if("function"==typeof e.on)s.once?e.once(t,r):e.on(t,r);else{if("function"!=typeof e.addEventListener)throw new TypeError('The "emitter" argument must be of type EventEmitter. Received type '+typeof e);e.addEventListener(t,(function i(n){s.once&&e.removeEventListener(t,i),r(n)}))}}return Object.defineProperty(i,"defaultMaxListeners",{enumerable:!0,get:function(){return n},set:function(e){if("number"!=typeof e||e<0||s(e))throw new RangeError('The value of "defaultMaxListeners" is out of range. It must be a non-negative number. Received '+e+".");n=e}}),i.init=function(){void 0!==this._events&&this._events!==Object.getPrototypeOf(this)._events||(this._events=Object.create(null),this._eventsCount=0),this._maxListeners=this._maxListeners||void 0},i.prototype.setMaxListeners=function(e){if("number"!=typeof e||e<0||s(e))throw new RangeError('The value of "n" is out of range. It must be a non-negative number. Received '+e+".");return this._maxListeners=e,this},i.prototype.getMaxListeners=function(){return a(this)},i.prototype.emit=function(e){for(var t=[],s=1;s<arguments.length;s++)t.push(arguments[s]);var i="error"===e,n=this._events;if(void 0!==n)i=i&&void 0===n.error;else if(!i)return!1;if(i){var o;if(t.length>0&&(o=t[0]),o instanceof Error)throw o;var a=new Error("Unhandled error."+(o?" ("+o.message+")":""));throw a.context=o,a}var c=n[e];if(void 0===c)return!1;if("function"==typeof c)r(c,this,t);else{var d=c.length,l=p(c,d);for(s=0;s<d;++s)r(l[s],this,t)}return!0},i.prototype.addListener=function(e,t){return c(this,e,t,!1)},i.prototype.on=i.prototype.addListener,i.prototype.prependListener=function(e,t){return c(this,e,t,!0)},i.prototype.once=function(e,t){return o(t),this.on(e,l(this,e,t)),this},i.prototype.prependOnceListener=function(e,t){return o(t),this.prependListener(e,l(this,e,t)),this},i.prototype.removeListener=function(e,t){var r,s,i,n,a;if(o(t),void 0===(s=this._events))return this;if(void 0===(r=s[e]))return this;if(r===t||r.listener===t)0==--this._eventsCount?this._events=Object.create(null):(delete s[e],s.removeListener&&this.emit("removeListener",e,r.listener||t));else if("function"!=typeof r){for(i=-1,n=r.length-1;n>=0;n--)if(r[n]===t||r[n].listener===t){a=r[n].listener,i=n;break}if(i<0)return this;0===i?r.shift():function(e,t){for(;t+1<e.length;t++)e[t]=e[t+1];e.pop()}(r,i),1===r.length&&(s[e]=r[0]),void 0!==s.removeListener&&this.emit("removeListener",e,a||t)}return this},i.prototype.off=i.prototype.removeListener,i.prototype.removeAllListeners=function(e){var t,r,s;if(void 0===(r=this._events))return this;if(void 0===r.removeListener)return 0===arguments.length?(this._events=Object.create(null),this._eventsCount=0):void 0!==r[e]&&(0==--this._eventsCount?this._events=Object.create(null):delete r[e]),this;if(0===arguments.length){var i,n=Object.keys(r);for(s=0;s<n.length;++s)"removeListener"!==(i=n[s])&&this.removeAllListeners(i);return this.removeAllListeners("removeListener"),this._events=Object.create(null),this._eventsCount=0,this}if("function"==typeof(t=r[e]))this.removeListener(e,t);else if(void 0!==t)for(s=t.length-1;s>=0;s--)this.removeListener(e,t[s]);return this},i.prototype.listeners=function(e){return h(this,e,!0)},i.prototype.rawListeners=function(e){return h(this,e,!1)},i.listenerCount=function(e,t){return"function"==typeof e.listenerCount?e.listenerCount(t):u.call(e,t)},i.prototype.listenerCount=u,i.prototype.eventNames=function(){return this._eventsCount>0?e(this._events):[]},it.exports}(),ot=rt(nt);class at extends Ne{constructor(e,t){super(e,t)}}class ct extends Error{}function dt(e){let t=e.replace(/-/g,"+").replace(/_/g,"/");switch(t.length%4){case 0:break;case 2:t+="==";break;case 3:t+="=";break;default:throw new Error("base64 string is not of the correct length")}try{return function(e){return decodeURIComponent(atob(e).replace(/(.)/g,((e,t)=>{let r=t.charCodeAt(0).toString(16).toUpperCase();return r.length<2&&(r="0"+r),"%"+r})))}(t)}catch(e){return atob(t)}}ct.prototype.name="InvalidTokenError";var lt,ht,ut,pt={debug:()=>{},info:()=>{},warn:()=>{},error:()=>{}},gt=(e=>(e[e.NONE=0]="NONE",e[e.ERROR=1]="ERROR",e[e.WARN=2]="WARN",e[e.INFO=3]="INFO",e[e.DEBUG=4]="DEBUG",e))(gt||{});(ut=gt||(gt={})).reset=function(){lt=3,ht=pt},ut.setLevel=function(e){if(!(0<=e&&e<=4))throw new Error("Invalid log level");lt=e},ut.setLogger=function(e){ht=e};var ft=class e{constructor(e){this._name=e}debug(...t){lt>=4&&ht.debug(e._format(this._name,this._method),...t)}info(...t){lt>=3&&ht.info(e._format(this._name,this._method),...t)}warn(...t){lt>=2&&ht.warn(e._format(this._name,this._method),...t)}error(...t){lt>=1&&ht.error(e._format(this._name,this._method),...t)}throw(e){throw this.error(e),e}create(e){const t=Object.create(this);return t._method=e,t.debug("begin"),t}static createStatic(t,r){const s=new e(`${t}.${r}`);return s.debug("begin"),s}static _format(e,t){const r=`[${e}]`;return t?`${r} ${t}:`:r}static debug(t,...r){lt>=4&&ht.debug(e._format(t),...r)}static info(t,...r){lt>=3&&ht.info(e._format(t),...r)}static warn(t,...r){lt>=2&&ht.warn(e._format(t),...r)}static error(t,...r){lt>=1&&ht.error(e._format(t),...r)}};gt.reset();var yt=class{static decode(e){try{return function(e,t){if("string"!=typeof e)throw new ct("Invalid token specified: must be a string");t||(t={});const r=!0===t.header?0:1,s=e.split(".")[r];if("string"!=typeof s)throw new ct(`Invalid token specified: missing part #${r+1}`);let i;try{i=dt(s)}catch(e){throw new ct(`Invalid token specified: invalid base64 for part #${r+1} (${e.message})`)}try{return JSON.parse(i)}catch(e){throw new ct(`Invalid token specified: invalid json for part #${r+1} (${e.message})`)}}(e)}catch(e){throw ft.error("JwtUtils.decode",e),e}}static async generateSignedJwt(e,t,r){const s=`${mt.encodeBase64Url((new TextEncoder).encode(JSON.stringify(e)))}.${mt.encodeBase64Url((new TextEncoder).encode(JSON.stringify(t)))}`,i=await window.crypto.subtle.sign({name:"ECDSA",hash:{name:"SHA-256"}},r,(new TextEncoder).encode(s));return`${s}.${mt.encodeBase64Url(new Uint8Array(i))}`}static async generateSignedJwtWithHmac(e,t,r){const s=`${mt.encodeBase64Url((new TextEncoder).encode(JSON.stringify(e)))}.${mt.encodeBase64Url((new TextEncoder).encode(JSON.stringify(t)))}`,i=await window.crypto.subtle.sign("HMAC",r,(new TextEncoder).encode(s));return`${s}.${mt.encodeBase64Url(new Uint8Array(i))}`}},wt=e=>btoa([...new Uint8Array(e)].map((e=>String.fromCharCode(e))).join("")),_t=class e{static _randomWord(){const e=new Uint32Array(1);return crypto.getRandomValues(e),e[0]}static generateUUIDv4(){return"10000000-1000-4000-8000-100000000000".replace(/[018]/g,(t=>(+t^e._randomWord()&15>>+t/4).toString(16))).replace(/-/g,"")}static generateCodeVerifier(){return e.generateUUIDv4()+e.generateUUIDv4()+e.generateUUIDv4()}static async generateCodeChallenge(e){if(!crypto.subtle)throw new Error("Crypto.subtle is available only in secure contexts (HTTPS).");try{const t=(new TextEncoder).encode(e),r=await crypto.subtle.digest("SHA-256",t);return wt(r).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}catch(e){throw ft.error("CryptoUtils.generateCodeChallenge",e),e}}static generateBasicAuth(e,t){const r=(new TextEncoder).encode([e,t].join(":"));return wt(r)}static async hash(e,t){const r=(new TextEncoder).encode(t),s=await crypto.subtle.digest(e,r);return new Uint8Array(s)}static async customCalculateJwkThumbprint(t){let r;switch(t.kty){case"RSA":r={e:t.e,kty:t.kty,n:t.n};break;case"EC":r={crv:t.crv,kty:t.kty,x:t.x,y:t.y};break;case"OKP":r={crv:t.crv,kty:t.kty,x:t.x};break;case"oct":r={crv:t.k,kty:t.kty};break;default:throw new Error("Unknown jwk type")}const s=await e.hash("SHA-256",JSON.stringify(r));return e.encodeBase64Url(s)}static async generateDPoPProof({url:t,accessToken:r,httpMethod:s,keyPair:i,nonce:n}){let o,a;const c={jti:window.crypto.randomUUID(),htm:null!=s?s:"GET",htu:t,iat:Math.floor(Date.now()/1e3)};r&&(o=await e.hash("SHA-256",r),a=e.encodeBase64Url(o),c.ath=a),n&&(c.nonce=n);try{const e=await crypto.subtle.exportKey("jwk",i.publicKey),t={alg:"ES256",typ:"dpop+jwt",jwk:{crv:e.crv,kty:e.kty,x:e.x,y:e.y}};return await yt.generateSignedJwt(t,c,i.privateKey)}catch(e){throw e instanceof TypeError?new Error(`Error exporting dpop public key: ${e.message}`):e}}static async generateDPoPJkt(t){try{const r=await crypto.subtle.exportKey("jwk",t.publicKey);return await e.customCalculateJwkThumbprint(r)}catch(e){throw e instanceof TypeError?new Error(`Could not retrieve dpop keys from storage: ${e.message}`):e}}static async generateDPoPKeys(){return await window.crypto.subtle.generateKey({name:"ECDSA",namedCurve:"P-256"},!1,["sign","verify"])}static async generateClientAssertionJwt(t,r,s,i="HS256"){const n=Math.floor(Date.now()/1e3),o={alg:i,typ:"JWT"},a={iss:t,sub:t,aud:s,jti:e.generateUUIDv4(),exp:n+300,iat:n},c={HS256:"SHA-256",HS384:"SHA-384",HS512:"SHA-512"}[i];if(!c)throw new Error(`Unsupported algorithm: ${i}. Supported algorithms are: HS256, HS384, HS512`);const d=new TextEncoder,l=await crypto.subtle.importKey("raw",d.encode(r),{name:"HMAC",hash:c},!1,["sign"]);return await yt.generateSignedJwtWithHmac(o,a,l)}};_t.encodeBase64Url=e=>wt(e).replace(/=/g,"").replace(/\+/g,"-").replace(/\//g,"_");var mt=_t,St=class{constructor(e){this._name=e,this._callbacks=[],this._logger=new ft(`Event('${this._name}')`)}addHandler(e){return this._callbacks.push(e),()=>this.removeHandler(e)}removeHandler(e){const t=this._callbacks.lastIndexOf(e);t>=0&&this._callbacks.splice(t,1)}async raise(...e){this._logger.debug("raise:",...e);for(const t of this._callbacks)await t(...e)}},vt=class e extends St{constructor(){super(...arguments),this._logger=new ft(`Timer('${this._name}')`),this._timerHandle=null,this._expiration=0,this._callback=()=>{const t=this._expiration-e.getEpochTime();this._logger.debug("timer completes in",t),this._expiration<=e.getEpochTime()&&(this.cancel(),super.raise())}}static getEpochTime(){return Math.floor(Date.now()/1e3)}init(t){const r=this._logger.create("init");t=Math.max(Math.floor(t),1);const s=e.getEpochTime()+t;if(this.expiration===s&&this._timerHandle)return void r.debug("skipping since already initialized for expiration at",this.expiration);this.cancel(),r.debug("using duration",t),this._expiration=s;const i=Math.min(t,5);this._timerHandle=setInterval(this._callback,1e3*i)}get expiration(){return this._expiration}cancel(){this._logger.create("cancel"),this._timerHandle&&(clearInterval(this._timerHandle),this._timerHandle=null)}},bt=class{static readParams(e,t="query"){if(!e)throw new TypeError("Invalid URL");const r=new URL(e,"http://127.0.0.1")["fragment"===t?"hash":"search"];return new URLSearchParams(r.slice(1))}},Et=";",kt=class extends Error{constructor(e,t){var r,s,i;if(super(e.error_description||e.error||""),this.form=t,this.name="ErrorResponse",!e.error)throw ft.error("ErrorResponse","No error passed"),new Error("No error passed");this.error=e.error,this.error_description=null!=(r=e.error_description)?r:null,this.error_uri=null!=(s=e.error_uri)?s:null,this.state=e.userState,this.session_state=null!=(i=e.session_state)?i:null,this.url_state=e.url_state}},At=class extends Error{constructor(e){super(e),this.name="ErrorTimeout"}},It=class{constructor(){this._logger=new ft("InMemoryWebStorage"),this._data={}}clear(){this._logger.create("clear"),this._data={}}getItem(e){return this._logger.create(`getItem('${e}')`),this._data[e]}setItem(e,t){this._logger.create(`setItem('${e}')`),this._data[e]=t}removeItem(e){this._logger.create(`removeItem('${e}')`),delete this._data[e]}get length(){return Object.getOwnPropertyNames(this._data).length}key(e){return Object.getOwnPropertyNames(this._data)[e]}},Tt=class extends Error{constructor(e,t){super(t),this.name="ErrorDPoPNonce",this.nonce=e}},Rt=class{constructor(e=[],t=null,r={}){this._jwtHandler=t,this._extraHeaders=r,this._logger=new ft("JsonService"),this._contentTypes=[],this._contentTypes.push(...e,"application/json"),t&&this._contentTypes.push("application/jwt")}async fetchWithTimeout(e,t={}){const{timeoutInSeconds:r,...s}=t;if(!r)return await fetch(e,s);const i=new AbortController,n=setTimeout((()=>i.abort()),1e3*r);try{return await fetch(e,{...t,signal:i.signal})}catch(e){if(e instanceof DOMException&&"AbortError"===e.name)throw new At("Network timed out");throw e}finally{clearTimeout(n)}}async getJson(e,{token:t,credentials:r,timeoutInSeconds:s}={}){const i=this._logger.create("getJson"),n={Accept:this._contentTypes.join(", ")};let o;t&&(i.debug("token passed, setting Authorization header"),n.Authorization="Bearer "+t),this._appendExtraHeaders(n);try{i.debug("url:",e),o=await this.fetchWithTimeout(e,{method:"GET",headers:n,timeoutInSeconds:s,credentials:r})}catch(e){throw i.error("Network Error"),e}i.debug("HTTP response received, status",o.status);const a=o.headers.get("Content-Type");if(a&&!this._contentTypes.find((e=>a.startsWith(e)))&&i.throw(new Error(`Invalid response Content-Type: ${null!=a?a:"undefined"}, from URL: ${e}`)),o.ok&&this._jwtHandler&&(null==a?void 0:a.startsWith("application/jwt")))return await this._jwtHandler(await o.text());let c;try{c=await o.json()}catch(e){if(i.error("Error parsing JSON response",e),o.ok)throw e;throw new Error(`${o.statusText} (${o.status})`)}if(!o.ok){if(i.error("Error from server:",c),c.error)throw new kt(c);throw new Error(`${o.statusText} (${o.status}): ${JSON.stringify(c)}`)}return c}async postForm(e,{body:t,basicAuth:r,timeoutInSeconds:s,initCredentials:i,extraHeaders:n}){const o=this._logger.create("postForm"),a={Accept:this._contentTypes.join(", "),"Content-Type":"application/x-www-form-urlencoded",...n};let c;void 0!==r&&(a.Authorization="Basic "+r),this._appendExtraHeaders(a);try{o.debug("url:",e),c=await this.fetchWithTimeout(e,{method:"POST",headers:a,body:t,timeoutInSeconds:s,credentials:i})}catch(e){throw o.error("Network error"),e}o.debug("HTTP response received, status",c.status);const d=c.headers.get("Content-Type");if(d&&!this._contentTypes.find((e=>d.startsWith(e))))throw new Error(`Invalid response Content-Type: ${null!=d?d:"undefined"}, from URL: ${e}`);const l=await c.text();let h={};if(l)try{h=JSON.parse(l)}catch(e){if(o.error("Error parsing JSON response",e),c.ok)throw e;throw new Error(`${c.statusText} (${c.status})`)}if(!c.ok){if(o.error("Error from server:",h),c.headers.has("dpop-nonce")){const e=c.headers.get("dpop-nonce");throw new Tt(e,`${JSON.stringify(h)}`)}if(h.error)throw new kt(h,t);throw new Error(`${c.statusText} (${c.status}): ${JSON.stringify(h)}`)}return h}_appendExtraHeaders(e){const t=this._logger.create("appendExtraHeaders"),r=Object.keys(this._extraHeaders),s=["accept","content-type"],i=["authorization"];0!==r.length&&r.forEach((r=>{if(s.includes(r.toLocaleLowerCase()))return void t.warn("Protected header could not be set",r,s);if(i.includes(r.toLocaleLowerCase())&&Object.keys(e).includes(r))return void t.warn("Header could not be overridden",r,i);const n="function"==typeof this._extraHeaders[r]?this._extraHeaders[r]():this._extraHeaders[r];n&&""!==n&&(e[r]=n)}))}},Ut=class{constructor(e){this._settings=e,this._logger=new ft("MetadataService"),this._signingKeys=null,this._metadata=null,this._metadataUrl=this._settings.metadataUrl,this._jsonService=new Rt(["application/jwk-set+json"],null,this._settings.extraHeaders),this._settings.signingKeys&&(this._logger.debug("using signingKeys from settings"),this._signingKeys=this._settings.signingKeys),this._settings.metadata&&(this._logger.debug("using metadata from settings"),this._metadata=this._settings.metadata),this._settings.fetchRequestCredentials&&(this._logger.debug("using fetchRequestCredentials from settings"),this._fetchRequestCredentials=this._settings.fetchRequestCredentials)}resetSigningKeys(){this._signingKeys=null}async getMetadata(){const e=this._logger.create("getMetadata");if(this._metadata)return e.debug("using cached values"),this._metadata;if(!this._metadataUrl)throw e.throw(new Error("No authority or metadataUrl configured on settings")),null;e.debug("getting metadata from",this._metadataUrl);const t=await this._jsonService.getJson(this._metadataUrl,{credentials:this._fetchRequestCredentials,timeoutInSeconds:this._settings.requestTimeoutInSeconds});return e.debug("merging remote JSON with seed metadata"),this._metadata=Object.assign({},t,this._settings.metadataSeed),this._metadata}getIssuer(){return this._getMetadataProperty("issuer")}getAuthorizationEndpoint(){return this._getMetadataProperty("authorization_endpoint")}getUserInfoEndpoint(){return this._getMetadataProperty("userinfo_endpoint")}getTokenEndpoint(e=!0){return this._getMetadataProperty("token_endpoint",e)}getCheckSessionIframe(){return this._getMetadataProperty("check_session_iframe",!0)}getEndSessionEndpoint(){return this._getMetadataProperty("end_session_endpoint",!0)}getRevocationEndpoint(e=!0){return this._getMetadataProperty("revocation_endpoint",e)}getKeysEndpoint(e=!0){return this._getMetadataProperty("jwks_uri",e)}async _getMetadataProperty(e,t=!1){const r=this._logger.create(`_getMetadataProperty('${e}')`),s=await this.getMetadata();if(r.debug("resolved"),void 0===s[e]){if(!0===t)return void r.warn("Metadata does not contain optional property");r.throw(new Error("Metadata does not contain property "+e))}return s[e]}async getSigningKeys(){const e=this._logger.create("getSigningKeys");if(this._signingKeys)return e.debug("returning signingKeys from cache"),this._signingKeys;const t=await this.getKeysEndpoint(!1);e.debug("got jwks_uri",t);const r=await this._jsonService.getJson(t,{timeoutInSeconds:this._settings.requestTimeoutInSeconds});if(e.debug("got key set",r),!Array.isArray(r.keys))throw e.throw(new Error("Missing keys on keyset")),null;return this._signingKeys=r.keys,this._signingKeys}},Pt=class{constructor({prefix:e="oidc.",store:t=localStorage}={}){this._logger=new ft("WebStorageStateStore"),this._store=t,this._prefix=e}async set(e,t){this._logger.create(`set('${e}')`),e=this._prefix+e,await this._store.setItem(e,t)}async get(e){this._logger.create(`get('${e}')`),e=this._prefix+e;return await this._store.getItem(e)}async remove(e){this._logger.create(`remove('${e}')`),e=this._prefix+e;const t=await this._store.getItem(e);return await this._store.removeItem(e),t}async getAllKeys(){this._logger.create("getAllKeys");const e=await this._store.length,t=[];for(let r=0;r<e;r++){const e=await this._store.key(r);e&&0===e.indexOf(this._prefix)&&t.push(e.substr(this._prefix.length))}return t}},Ct=class{constructor({authority:e,metadataUrl:t,metadata:r,signingKeys:s,metadataSeed:i,client_id:n,client_secret:o,response_type:a="code",scope:c="openid",redirect_uri:d,post_logout_redirect_uri:l,client_authentication:h="client_secret_post",token_endpoint_auth_signing_alg:u="HS256",prompt:p,display:g,max_age:f,ui_locales:y,acr_values:w,resource:_,response_mode:m,filterProtocolClaims:S=!0,loadUserInfo:v=!1,requestTimeoutInSeconds:b,staleStateAgeInSeconds:E=900,mergeClaimsStrategy:k={array:"replace"},disablePKCE:A=!1,stateStore:I,revokeTokenAdditionalContentTypes:T,fetchRequestCredentials:R,refreshTokenAllowedScope:U,extraQueryParams:P={},extraTokenParams:C={},extraHeaders:x={},dpop:O,omitScopeWhenRequesting:K=!1}){var H;if(this.authority=e,t?this.metadataUrl=t:(this.metadataUrl=e,e&&(this.metadataUrl.endsWith("/")||(this.metadataUrl+="/"),this.metadataUrl+=".well-known/openid-configuration")),this.metadata=r,this.metadataSeed=i,this.signingKeys=s,this.client_id=n,this.client_secret=o,this.response_type=a,this.scope=c,this.redirect_uri=d,this.post_logout_redirect_uri=l,this.client_authentication=h,this.token_endpoint_auth_signing_alg=u,this.prompt=p,this.display=g,this.max_age=f,this.ui_locales=y,this.acr_values=w,this.resource=_,this.response_mode=m,this.filterProtocolClaims=null==S||S,this.loadUserInfo=!!v,this.staleStateAgeInSeconds=E,this.mergeClaimsStrategy=k,this.omitScopeWhenRequesting=K,this.disablePKCE=!!A,this.revokeTokenAdditionalContentTypes=T,this.fetchRequestCredentials=R||"same-origin",this.requestTimeoutInSeconds=b,I)this.stateStore=I;else{const e="undefined"!=typeof window?window.localStorage:new It;this.stateStore=new Pt({store:e})}if(this.refreshTokenAllowedScope=U,this.extraQueryParams=P,this.extraTokenParams=C,this.extraHeaders=x,this.dpop=O,this.dpop&&!(null==(H=this.dpop)?void 0:H.store))throw new Error("A DPoPStore is required when dpop is enabled")}},xt=class{constructor(e,t){this._settings=e,this._metadataService=t,this._logger=new ft("UserInfoService"),this._getClaimsFromJwt=async e=>{const t=this._logger.create("_getClaimsFromJwt");try{const r=yt.decode(e);return t.debug("JWT decoding successful"),r}catch(e){throw t.error("Error parsing JWT response"),e}},this._jsonService=new Rt(void 0,this._getClaimsFromJwt,this._settings.extraHeaders)}async getClaims(e){const t=this._logger.create("getClaims");e||this._logger.throw(new Error("No token passed"));const r=await this._metadataService.getUserInfoEndpoint();t.debug("got userinfo url",r);const s=await this._jsonService.getJson(r,{token:e,credentials:this._settings.fetchRequestCredentials,timeoutInSeconds:this._settings.requestTimeoutInSeconds});return t.debug("got claims",s),s}},Ot=class{constructor(e,t){this._settings=e,this._metadataService=t,this._logger=new ft("TokenClient"),this._jsonService=new Rt(this._settings.revokeTokenAdditionalContentTypes,null,this._settings.extraHeaders)}async exchangeCode({grant_type:e="authorization_code",redirect_uri:t=this._settings.redirect_uri,client_id:r=this._settings.client_id,client_secret:s=this._settings.client_secret,extraHeaders:i,...n}){const o=this._logger.create("exchangeCode");r||o.throw(new Error("A client_id is required")),t||o.throw(new Error("A redirect_uri is required")),n.code||o.throw(new Error("A code is required"));const a=new URLSearchParams({grant_type:e,redirect_uri:t});for(const[e,t]of Object.entries(n))null!=t&&a.set(e,t);if(("client_secret_basic"===this._settings.client_authentication||"client_secret_jwt"===this._settings.client_authentication)&&null==s)throw o.throw(new Error("A client_secret is required")),null;let c;const d=await this._metadataService.getTokenEndpoint(!1);switch(this._settings.client_authentication){case"client_secret_basic":c=mt.generateBasicAuth(r,s);break;case"client_secret_post":a.append("client_id",r),s&&a.append("client_secret",s);break;case"client_secret_jwt":{const e=await mt.generateClientAssertionJwt(r,s,d,this._settings.token_endpoint_auth_signing_alg);a.append("client_id",r),a.append("client_assertion_type","urn:ietf:params:oauth:client-assertion-type:jwt-bearer"),a.append("client_assertion",e);break}}o.debug("got token endpoint");const l=await this._jsonService.postForm(d,{body:a,basicAuth:c,timeoutInSeconds:this._settings.requestTimeoutInSeconds,initCredentials:this._settings.fetchRequestCredentials,extraHeaders:i});return o.debug("got response"),l}async exchangeCredentials({grant_type:e="password",client_id:t=this._settings.client_id,client_secret:r=this._settings.client_secret,scope:s=this._settings.scope,...i}){const n=this._logger.create("exchangeCredentials");t||n.throw(new Error("A client_id is required"));const o=new URLSearchParams({grant_type:e});this._settings.omitScopeWhenRequesting||o.set("scope",s);for(const[e,t]of Object.entries(i))null!=t&&o.set(e,t);if(("client_secret_basic"===this._settings.client_authentication||"client_secret_jwt"===this._settings.client_authentication)&&null==r)throw n.throw(new Error("A client_secret is required")),null;let a;const c=await this._metadataService.getTokenEndpoint(!1);switch(this._settings.client_authentication){case"client_secret_basic":a=mt.generateBasicAuth(t,r);break;case"client_secret_post":o.append("client_id",t),r&&o.append("client_secret",r);break;case"client_secret_jwt":{const e=await mt.generateClientAssertionJwt(t,r,c,this._settings.token_endpoint_auth_signing_alg);o.append("client_id",t),o.append("client_assertion_type","urn:ietf:params:oauth:client-assertion-type:jwt-bearer"),o.append("client_assertion",e);break}}n.debug("got token endpoint");const d=await this._jsonService.postForm(c,{body:o,basicAuth:a,timeoutInSeconds:this._settings.requestTimeoutInSeconds,initCredentials:this._settings.fetchRequestCredentials});return n.debug("got response"),d}async exchangeRefreshToken({grant_type:e="refresh_token",client_id:t=this._settings.client_id,client_secret:r=this._settings.client_secret,timeoutInSeconds:s,extraHeaders:i,...n}){const o=this._logger.create("exchangeRefreshToken");t||o.throw(new Error("A client_id is required")),n.refresh_token||o.throw(new Error("A refresh_token is required"));const a=new URLSearchParams({grant_type:e});for(const[e,t]of Object.entries(n))Array.isArray(t)?t.forEach((t=>a.append(e,t))):null!=t&&a.set(e,t);if(("client_secret_basic"===this._settings.client_authentication||"client_secret_jwt"===this._settings.client_authentication)&&null==r)throw o.throw(new Error("A client_secret is required")),null;let c;const d=await this._metadataService.getTokenEndpoint(!1);switch(this._settings.client_authentication){case"client_secret_basic":c=mt.generateBasicAuth(t,r);break;case"client_secret_post":a.append("client_id",t),r&&a.append("client_secret",r);break;case"client_secret_jwt":{const e=await mt.generateClientAssertionJwt(t,r,d,this._settings.token_endpoint_auth_signing_alg);a.append("client_id",t),a.append("client_assertion_type","urn:ietf:params:oauth:client-assertion-type:jwt-bearer"),a.append("client_assertion",e);break}}o.debug("got token endpoint");const l=await this._jsonService.postForm(d,{body:a,basicAuth:c,timeoutInSeconds:s,initCredentials:this._settings.fetchRequestCredentials,extraHeaders:i});return o.debug("got response"),l}async revoke(e){var t;const r=this._logger.create("revoke");e.token||r.throw(new Error("A token is required"));const s=await this._metadataService.getRevocationEndpoint(!1);r.debug(`got revocation endpoint, revoking ${null!=(t=e.token_type_hint)?t:"default token type"}`);const i=new URLSearchParams;for(const[t,r]of Object.entries(e))null!=r&&i.set(t,r);i.set("client_id",this._settings.client_id),this._settings.client_secret&&i.set("client_secret",this._settings.client_secret),await this._jsonService.postForm(s,{body:i,timeoutInSeconds:this._settings.requestTimeoutInSeconds}),r.debug("got response")}},Kt=class{constructor(e,t,r){this._settings=e,this._metadataService=t,this._claimsService=r,this._logger=new ft("ResponseValidator"),this._userInfoService=new xt(this._settings,this._metadataService),this._tokenClient=new Ot(this._settings,this._metadataService)}async validateSigninResponse(e,t,r){const s=this._logger.create("validateSigninResponse");this._processSigninState(e,t),s.debug("state processed"),await this._processCode(e,t,r),s.debug("code processed"),e.isOpenId&&this._validateIdTokenAttributes(e,"",t.nonce),s.debug("tokens validated"),await this._processClaims(e,null==t?void 0:t.skipUserInfo,e.isOpenId),s.debug("claims processed")}async validateCredentialsResponse(e,t){const r=this._logger.create("validateCredentialsResponse"),s=e.isOpenId&&!!e.id_token;s&&this._validateIdTokenAttributes(e),r.debug("tokens validated"),await this._processClaims(e,t,s),r.debug("claims processed")}async validateRefreshResponse(e,t){const r=this._logger.create("validateRefreshResponse");e.userState=t.data,null!=e.session_state||(e.session_state=t.session_state),null!=e.scope||(e.scope=t.scope),e.isOpenId&&e.id_token&&(this._validateIdTokenAttributes(e,t.id_token),r.debug("ID Token validated")),e.id_token||(e.id_token=t.id_token,e.profile=t.profile);const s=e.isOpenId&&!!e.id_token;await this._processClaims(e,!1,s),r.debug("claims processed")}validateSignoutResponse(e,t){const r=this._logger.create("validateSignoutResponse");if(t.id!==e.state&&r.throw(new Error("State does not match")),r.debug("state validated"),e.userState=t.data,e.error)throw r.warn("Response was error",e.error),new kt(e)}_processSigninState(e,t){const r=this._logger.create("_processSigninState");if(t.id!==e.state&&r.throw(new Error("State does not match")),t.client_id||r.throw(new Error("No client_id on state")),t.authority||r.throw(new Error("No authority on state")),this._settings.authority!==t.authority&&r.throw(new Error("authority mismatch on settings vs. signin state")),this._settings.client_id&&this._settings.client_id!==t.client_id&&r.throw(new Error("client_id mismatch on settings vs. signin state")),r.debug("state validated"),e.userState=t.data,e.url_state=t.url_state,null!=e.scope||(e.scope=t.scope),e.error)throw r.warn("Response was error",e.error),new kt(e);t.code_verifier&&!e.code&&r.throw(new Error("Expected code in response"))}async _processClaims(e,t=!1,r=!0){const s=this._logger.create("_processClaims");if(e.profile=this._claimsService.filterProtocolClaims(e.profile),t||!this._settings.loadUserInfo||!e.access_token)return void s.debug("not loading user info");s.debug("loading user info");const i=await this._userInfoService.getClaims(e.access_token);s.debug("user info claims received from user info endpoint"),r&&i.sub!==e.profile.sub&&s.throw(new Error("subject from UserInfo response does not match subject in ID Token")),e.profile=this._claimsService.mergeClaims(e.profile,this._claimsService.filterProtocolClaims(i)),s.debug("user info claims received, updated profile:",e.profile)}async _processCode(e,t,r){const s=this._logger.create("_processCode");if(e.code){s.debug("Validating code");const i=await this._tokenClient.exchangeCode({client_id:t.client_id,client_secret:t.client_secret,code:e.code,redirect_uri:t.redirect_uri,code_verifier:t.code_verifier,extraHeaders:r,...t.extraTokenParams});Object.assign(e,i)}else s.debug("No code to process")}_validateIdTokenAttributes(e,t,r){var s;const i=this._logger.create("_validateIdTokenAttributes");i.debug("decoding ID Token JWT");const n=yt.decode(null!=(s=e.id_token)?s:"");if(n.sub||i.throw(new Error("ID Token is missing a subject claim")),r&&n.nonce!==r&&i.throw(new Error("nonce in id_token does not match nonce in client storage")),t){const e=yt.decode(t);n.sub!==e.sub&&i.throw(new Error("sub in id_token does not match current sub")),n.auth_time&&n.auth_time!==e.auth_time&&i.throw(new Error("auth_time in id_token does not match original auth_time")),n.azp&&n.azp!==e.azp&&i.throw(new Error("azp in id_token does not match original azp")),!n.azp&&e.azp&&i.throw(new Error("azp not in id_token, but present in original id_token"))}e.profile=n}},Ht=class e{constructor(e){this.id=e.id||mt.generateUUIDv4(),this.data=e.data,e.created&&e.created>0?this.created=e.created:this.created=vt.getEpochTime(),this.request_type=e.request_type,this.url_state=e.url_state}toStorageString(){return new ft("State").create("toStorageString"),JSON.stringify({id:this.id,data:this.data,created:this.created,request_type:this.request_type,url_state:this.url_state})}static fromStorageString(t){return ft.createStatic("State","fromStorageString"),Promise.resolve(new e(JSON.parse(t)))}static async clearStaleState(t,r){const s=ft.createStatic("State","clearStaleState"),i=vt.getEpochTime()-r,n=await t.getAllKeys();s.debug("got keys",n);for(let r=0;r<n.length;r++){const o=n[r],a=await t.get(o);let c=!1;if(a)try{const t=await e.fromStorageString(a);s.debug("got item from key:",o,t.created),t.created<=i&&(c=!0)}catch(e){s.error("Error parsing state for key:",o,e),c=!0}else s.debug("no item in storage for key:",o),c=!0;c&&(s.debug("removed item for key:",o),t.remove(o))}}},Dt=class e extends Ht{constructor(e){super(e),this.code_verifier=e.code_verifier,this.code_challenge=e.code_challenge,this.authority=e.authority,this.client_id=e.client_id,this.redirect_uri=e.redirect_uri,this.scope=e.scope,this.client_secret=e.client_secret,this.extraTokenParams=e.extraTokenParams,this.response_mode=e.response_mode,this.skipUserInfo=e.skipUserInfo,this.nonce=e.nonce}static async create(t){const r=!0===t.code_verifier?mt.generateCodeVerifier():t.code_verifier||void 0,s=r?await mt.generateCodeChallenge(r):void 0;return new e({...t,code_verifier:r,code_challenge:s})}toStorageString(){return new ft("SigninState").create("toStorageString"),JSON.stringify({id:this.id,data:this.data,created:this.created,request_type:this.request_type,url_state:this.url_state,code_verifier:this.code_verifier,authority:this.authority,client_id:this.client_id,redirect_uri:this.redirect_uri,scope:this.scope,client_secret:this.client_secret,extraTokenParams:this.extraTokenParams,response_mode:this.response_mode,skipUserInfo:this.skipUserInfo,nonce:this.nonce})}static fromStorageString(t){ft.createStatic("SigninState","fromStorageString");const r=JSON.parse(t);return e.create(r)}},jt=class e{constructor(e){this.url=e.url,this.state=e.state}static async create({url:t,authority:r,client_id:s,redirect_uri:i,response_type:n,scope:o,state_data:a,response_mode:c,request_type:d,client_secret:l,nonce:h,url_state:u,resource:p,skipUserInfo:g,extraQueryParams:f,extraTokenParams:y,disablePKCE:w,dpopJkt:_,omitScopeWhenRequesting:m,...S}){if(!t)throw this._logger.error("create: No url passed"),new Error("url");if(!s)throw this._logger.error("create: No client_id passed"),new Error("client_id");if(!i)throw this._logger.error("create: No redirect_uri passed"),new Error("redirect_uri");if(!n)throw this._logger.error("create: No response_type passed"),new Error("response_type");if(!o)throw this._logger.error("create: No scope passed"),new Error("scope");if(!r)throw this._logger.error("create: No authority passed"),new Error("authority");const v=await Dt.create({data:a,request_type:d,url_state:u,code_verifier:!w,client_id:s,authority:r,redirect_uri:i,response_mode:c,client_secret:l,scope:o,extraTokenParams:y,skipUserInfo:g,nonce:h}),b=new URL(t);b.searchParams.append("client_id",s),b.searchParams.append("redirect_uri",i),b.searchParams.append("response_type",n),m||b.searchParams.append("scope",o),h&&b.searchParams.append("nonce",h),_&&b.searchParams.append("dpop_jkt",_);let E=v.id;if(u&&(E=`${E}${Et}${u}`),b.searchParams.append("state",E),v.code_challenge&&(b.searchParams.append("code_challenge",v.code_challenge),b.searchParams.append("code_challenge_method","S256")),p){(Array.isArray(p)?p:[p]).forEach((e=>b.searchParams.append("resource",e)))}for(const[e,t]of Object.entries({response_mode:c,...S,...f}))null!=t&&b.searchParams.append(e,t.toString());return new e({url:b.href,state:v})}};jt._logger=new ft("SigninRequest");var Lt=jt,$t=class{constructor(e){if(this.access_token="",this.token_type="",this.profile={},this.state=e.get("state"),this.session_state=e.get("session_state"),this.state){const e=decodeURIComponent(this.state).split(Et);this.state=e[0],e.length>1&&(this.url_state=e.slice(1).join(Et))}this.error=e.get("error"),this.error_description=e.get("error_description"),this.error_uri=e.get("error_uri"),this.code=e.get("code")}get expires_in(){if(void 0!==this.expires_at)return this.expires_at-vt.getEpochTime()}set expires_in(e){"string"==typeof e&&(e=Number(e)),void 0!==e&&e>=0&&(this.expires_at=Math.floor(e)+vt.getEpochTime())}get isOpenId(){var e;return(null==(e=this.scope)?void 0:e.split(" ").includes("openid"))||!!this.id_token}},Nt=class{constructor({url:e,state_data:t,id_token_hint:r,post_logout_redirect_uri:s,extraQueryParams:i,request_type:n,client_id:o,url_state:a}){if(this._logger=new ft("SignoutRequest"),!e)throw this._logger.error("ctor: No url passed"),new Error("url");const c=new URL(e);if(r&&c.searchParams.append("id_token_hint",r),o&&c.searchParams.append("client_id",o),s&&(c.searchParams.append("post_logout_redirect_uri",s),t||a)){this.state=new Ht({data:t,request_type:n,url_state:a});let e=this.state.id;a&&(e=`${e}${Et}${a}`),c.searchParams.append("state",e)}for(const[e,t]of Object.entries({...i}))null!=t&&c.searchParams.append(e,t.toString());this.url=c.href}},Jt=class{constructor(e){if(this.state=e.get("state"),this.state){const e=decodeURIComponent(this.state).split(Et);this.state=e[0],e.length>1&&(this.url_state=e.slice(1).join(Et))}this.error=e.get("error"),this.error_description=e.get("error_description"),this.error_uri=e.get("error_uri")}},Wt=["nbf","jti","auth_time","nonce","acr","amr","azp","at_hash"],Mt=["sub","iss","aud","exp","iat"],Ft=class{constructor(e){this._settings=e,this._logger=new ft("ClaimsService")}filterProtocolClaims(e){const t={...e};if(this._settings.filterProtocolClaims){let e;e=Array.isArray(this._settings.filterProtocolClaims)?this._settings.filterProtocolClaims:Wt;for(const r of e)Mt.includes(r)||delete t[r]}return t}mergeClaims(e,t){const r={...e};for(const[e,s]of Object.entries(t))if(r[e]!==s)if(Array.isArray(r[e])||Array.isArray(s))if("replace"==this._settings.mergeClaimsStrategy.array)r[e]=s;else{const t=Array.isArray(r[e])?r[e]:[r[e]];for(const e of Array.isArray(s)?s:[s])t.includes(e)||t.push(e);r[e]=t}else"object"==typeof r[e]&&"object"==typeof s?r[e]=this.mergeClaims(r[e],s):r[e]=s;return r}},qt=class{constructor(e,t){this.keys=e,this.nonce=t}},Bt=class{constructor(e,t){this._logger=new ft("OidcClient"),this.settings=e instanceof Ct?e:new Ct(e),this.metadataService=null!=t?t:new Ut(this.settings),this._claimsService=new Ft(this.settings),this._validator=new Kt(this.settings,this.metadataService,this._claimsService),this._tokenClient=new Ot(this.settings,this.metadataService)}async createSigninRequest({state:e,request:t,request_uri:r,request_type:s,id_token_hint:i,login_hint:n,skipUserInfo:o,nonce:a,url_state:c,response_type:d=this.settings.response_type,scope:l=this.settings.scope,redirect_uri:h=this.settings.redirect_uri,prompt:u=this.settings.prompt,display:p=this.settings.display,max_age:g=this.settings.max_age,ui_locales:f=this.settings.ui_locales,acr_values:y=this.settings.acr_values,resource:w=this.settings.resource,response_mode:_=this.settings.response_mode,extraQueryParams:m=this.settings.extraQueryParams,extraTokenParams:S=this.settings.extraTokenParams,dpopJkt:v,omitScopeWhenRequesting:b=this.settings.omitScopeWhenRequesting}){const E=this._logger.create("createSigninRequest");if("code"!==d)throw new Error("Only the Authorization Code flow (with PKCE) is supported");const k=await this.metadataService.getAuthorizationEndpoint();E.debug("Received authorization endpoint",k);const A=await Lt.create({url:k,authority:this.settings.authority,client_id:this.settings.client_id,redirect_uri:h,response_type:d,scope:l,state_data:e,url_state:c,prompt:u,display:p,max_age:g,ui_locales:f,id_token_hint:i,login_hint:n,acr_values:y,dpopJkt:v,resource:w,request:t,request_uri:r,extraQueryParams:m,extraTokenParams:S,request_type:s,response_mode:_,client_secret:this.settings.client_secret,skipUserInfo:o,nonce:a,disablePKCE:this.settings.disablePKCE,omitScopeWhenRequesting:b});await this.clearStaleState();const I=A.state;return await this.settings.stateStore.set(I.id,I.toStorageString()),A}async readSigninResponseState(e,t=!1){const r=this._logger.create("readSigninResponseState"),s=new $t(bt.readParams(e,this.settings.response_mode));if(!s.state)throw r.throw(new Error("No state in response")),null;const i=await this.settings.stateStore[t?"remove":"get"](s.state);if(!i)throw r.throw(new Error("No matching state found in storage")),null;return{state:await Dt.fromStorageString(i),response:s}}async processSigninResponse(e,t,r=!0){const s=this._logger.create("processSigninResponse"),{state:i,response:n}=await this.readSigninResponseState(e,r);if(s.debug("received state from storage; validating response"),this.settings.dpop&&this.settings.dpop.store){const e=await this.getDpopProof(this.settings.dpop.store);t={...t,DPoP:e}}try{await this._validator.validateSigninResponse(n,i,t)}catch(e){if(!(e instanceof Tt&&this.settings.dpop))throw e;{const r=await this.getDpopProof(this.settings.dpop.store,e.nonce);t.DPoP=r,await this._validator.validateSigninResponse(n,i,t)}}return n}async getDpopProof(e,t){let r,s;return(await e.getAllKeys()).includes(this.settings.client_id)?(s=await e.get(this.settings.client_id),s.nonce!==t&&t&&(s.nonce=t,await e.set(this.settings.client_id,s))):(r=await mt.generateDPoPKeys(),s=new qt(r,t),await e.set(this.settings.client_id,s)),await mt.generateDPoPProof({url:await this.metadataService.getTokenEndpoint(!1),httpMethod:"POST",keyPair:s.keys,nonce:s.nonce})}async processResourceOwnerPasswordCredentials({username:e,password:t,skipUserInfo:r=!1,extraTokenParams:s={}}){const i=await this._tokenClient.exchangeCredentials({username:e,password:t,...s}),n=new $t(new URLSearchParams);return Object.assign(n,i),await this._validator.validateCredentialsResponse(n,r),n}async useRefreshToken({state:e,redirect_uri:t,resource:r,timeoutInSeconds:s,extraHeaders:i,extraTokenParams:n}){var o;const a=this._logger.create("useRefreshToken");let c,d;if(void 0===this.settings.refreshTokenAllowedScope)c=e.scope;else{const t=this.settings.refreshTokenAllowedScope.split(" ");c=((null==(o=e.scope)?void 0:o.split(" "))||[]).filter((e=>t.includes(e))).join(" ")}if(this.settings.dpop&&this.settings.dpop.store){const e=await this.getDpopProof(this.settings.dpop.store);i={...i,DPoP:e}}try{d=await this._tokenClient.exchangeRefreshToken({refresh_token:e.refresh_token,scope:c,redirect_uri:t,resource:r,timeoutInSeconds:s,extraHeaders:i,...n})}catch(o){if(!(o instanceof Tt&&this.settings.dpop))throw o;i.DPoP=await this.getDpopProof(this.settings.dpop.store,o.nonce),d=await this._tokenClient.exchangeRefreshToken({refresh_token:e.refresh_token,scope:c,redirect_uri:t,resource:r,timeoutInSeconds:s,extraHeaders:i,...n})}const l=new $t(new URLSearchParams);return Object.assign(l,d),a.debug("validating response",l),await this._validator.validateRefreshResponse(l,{...e,scope:c}),l}async createSignoutRequest({state:e,id_token_hint:t,client_id:r,request_type:s,url_state:i,post_logout_redirect_uri:n=this.settings.post_logout_redirect_uri,extraQueryParams:o=this.settings.extraQueryParams}={}){const a=this._logger.create("createSignoutRequest"),c=await this.metadataService.getEndSessionEndpoint();if(!c)throw a.throw(new Error("No end session endpoint")),null;a.debug("Received end session endpoint",c),r||!n||t||(r=this.settings.client_id);const d=new Nt({url:c,id_token_hint:t,client_id:r,post_logout_redirect_uri:n,state_data:e,extraQueryParams:o,request_type:s,url_state:i});await this.clearStaleState();const l=d.state;return l&&(a.debug("Signout request has state to persist"),await this.settings.stateStore.set(l.id,l.toStorageString())),d}async readSignoutResponseState(e,t=!1){const r=this._logger.create("readSignoutResponseState"),s=new Jt(bt.readParams(e,this.settings.response_mode));if(!s.state){if(r.debug("No state in response"),s.error)throw r.warn("Response was error:",s.error),new kt(s);return{state:void 0,response:s}}const i=await this.settings.stateStore[t?"remove":"get"](s.state);if(!i)throw r.throw(new Error("No matching state found in storage")),null;return{state:await Ht.fromStorageString(i),response:s}}async processSignoutResponse(e){const t=this._logger.create("processSignoutResponse"),{state:r,response:s}=await this.readSignoutResponseState(e,!0);return r?(t.debug("Received state from storage; validating response"),this._validator.validateSignoutResponse(s,r)):t.debug("No state from storage; skipping response validation"),s}clearStaleState(){return this._logger.create("clearStaleState"),Ht.clearStaleState(this.settings.stateStore,this.settings.staleStateAgeInSeconds)}async revokeToken(e,t){return this._logger.create("revokeToken"),await this._tokenClient.revoke({token:e,token_type_hint:t})}};function Vt(e,t){if("string"!=typeof e.client_id)throw new Error(`Dynamic client registration failed: no client_id has been found on ${JSON.stringify(e)}`);if(t.redirectUrl&&function(e){return Array.isArray(e.redirect_uris)&&e.redirect_uris.every((e=>"string"==typeof e))}(e)&&e.redirect_uris[0]!==t.redirectUrl.toString())throw new Error(`Dynamic client registration failed: the returned redirect URIs ${JSON.stringify(e.redirect_uris)} don't match the provided ${JSON.stringify([t.redirectUrl.toString()])}`);return!0}async function zt(e,t){if(!t.registrationEndpoint)throw new Error("Dynamic Registration could not be completed because the issuer has no registration endpoint.");if(!Array.isArray(t.idTokenSigningAlgValuesSupported))throw new Error("The OIDC issuer discovery profile is missing the 'id_token_signing_alg_values_supported' value, which is mandatory.");const r=(s=t.idTokenSigningAlgValuesSupported,Se.find((e=>s.includes(e)))??null);var s;const i={client_name:e.clientName,application_type:"web",redirect_uris:[e.redirectUrl?.toString()],subject_type:"public",token_endpoint_auth_method:"client_secret_basic",id_token_signed_response_alg:r,grant_types:["authorization_code","refresh_token"]},n=await fetch(t.registrationEndpoint.toString(),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(i)});if(n.ok){const t=await n.json();return Vt(t,e),{clientId:t.client_id,clientSecret:t.client_secret,expiresAt:t.client_secret_expires_at,idTokenSignedResponseAlg:t.id_token_signed_response_alg,clientType:"dynamic"}}throw 400===n.status&&function(e,t){if("invalid_redirect_uri"===e.error)throw new Error(`Dynamic client registration failed: the provided redirect uri [${t.redirectUrl?.toString()}] is invalid - ${e.error_description??""}`);if("invalid_client_metadata"===e.error)throw new Error(`Dynamic client registration failed: the provided client metadata ${JSON.stringify(t)} is invalid - ${e.error_description??""}`);throw new Error(`Dynamic client registration failed: ${e.error} - ${e.error_description??""}`)}(await n.json(),e),new Error(`Dynamic client registration failed: the server returned ${n.status} ${n.statusText} - ${await n.text()}`)}function Gt(e){return void 0!==e.error_description&&"string"==typeof e.error_description}function Xt(e,t){if(void 0!==(r=e).error&&"string"==typeof r.error)throw new qe(`Token endpoint returned error [${e.error}]${Gt(e)?`: ${e.error_description}`:""}${function(e){return void 0!==e.error_uri&&"string"==typeof e.error_uri}(e)?` (see ${e.error_uri})`:""}`,e.error,Gt(e)?e.error_description:void 0);var r;if(!function(e){return void 0!==e.access_token&&"string"==typeof e.access_token}(e))throw new Fe(["access_token"]);if(!function(e){return void 0!==e.id_token&&"string"==typeof e.id_token}(e))throw new Fe(["id_token"]);if(!function(e){return void 0!==e.token_type&&"string"==typeof e.token_type}(e))throw new Fe(["token_type"]);if(!function(e){return void 0===e.expires_in||"number"==typeof e.expires_in}(e))throw new Fe(["expires_in"]);if(!t&&"bearer"!==e.token_type.toLowerCase())throw new Error(`Invalid token endpoint response: requested a [Bearer] token, but got a 'token_type' value of [${e.token_type}].`);return e}async function Qt(e,t,r,s){!function(e,t){if(t.grantType&&(!e.grantTypesSupported||!e.grantTypesSupported.includes(t.grantType)))throw new Error(`The issuer [${e.issuer}] does not support the [${t.grantType}] grant`);if(!e.tokenEndpoint)throw new Error(`This issuer [${e.issuer}] does not have a token endpoint`)}(e,r);const i={"content-type":"application/x-www-form-urlencoded"};let n;s&&(n=await ze(),i.DPoP=await Ve(e.tokenEndpoint,"POST",n)),t.clientSecret&&(i.Authorization=`Basic ${btoa(`${t.clientId}:${t.clientSecret}`)}`);const o={grant_type:r.grantType,redirect_uri:r.redirectUrl,code:r.code,code_verifier:r.codeVerifier,client_id:t.clientId},a={method:"POST",headers:i,body:new URLSearchParams(o).toString()},c=await fetch(e.tokenEndpoint,a),d=Xt(await c.json(),s),{webId:l,clientId:h}=await ke(d.id_token,e.jwksUri,e.issuer,t.clientId);return{accessToken:d.access_token,idToken:d.id_token,refreshToken:(u=d,void 0!==u.refresh_token&&"string"==typeof u.refresh_token?d.refresh_token:void 0),webId:l,clientId:h,dpopKey:n,expiresIn:d.expires_in};var u}async function Yt(e,t,r,s){if(void 0===r.clientId)throw new Error("No client ID available when trying to refresh the access token.");const i={grant_type:"refresh_token",refresh_token:e};let n={};void 0!==s&&(n={DPoP:await Ve(t.tokenEndpoint,"POST",s)});let o={};void 0!==r.clientSecret?o={Authorization:`Basic ${btoa(`${r.clientId}:${r.clientSecret}`)}`}:(e=>{try{return new URL(e),!0}catch{return!1}})(r.clientId)&&(i.client_id=r.clientId);const a=await fetch(t.tokenEndpoint,{method:"POST",body:new URLSearchParams(i).toString(),headers:{...n,...o,"Content-Type":"application/x-www-form-urlencoded"}});let c;try{c=await a.json()}catch(e){throw new Error(`The token endpoint of issuer ${t.issuer} returned a malformed response.`)}const d=Xt(c,void 0!==s),{webId:l}=await ke(d.id_token,t.jwksUri,t.issuer,r.clientId);return{accessToken:d.access_token,idToken:d.id_token,refreshToken:"string"==typeof d.refresh_token?d.refresh_token:void 0,webId:l,dpopKey:s,expiresIn:d.expires_in}}class Zt extends Le{login=async(e,t)=>{"none"!==e.prompt&&await this.sessionInfoManager.clear(e.sessionId);const r=e.redirectUrl??function(e){const t=Ie(e);return t.hash="",e.includes(`${t.origin}/`)?t.href:`${t.origin}${t.href.substring(t.origin.length+1)}`}(window.location.href);if(!Ae(r))throw new Error(`${r} is not a valid redirect URL, it is either a malformed IRI, includes a hash fragment, or reserved query parameters ('code' or 'state').`);await this.loginHandler.handle({...e,redirectUrl:r,clientName:e.clientName??e.clientId,eventEmitter:t})};validateCurrentSession=async e=>{const t=await this.sessionInfoManager.get(e);return void 0===t||void 0===t.clientAppId||void 0===t.issuer||function(e){return void 0!==e.clientExpiresAt&&0!==e.clientExpiresAt&&e.clientExpiresAt<Math.floor(Date.now()/1e3)}(t)?null:t};handleIncomingRedirect=async(e,t)=>{try{const r=await this.redirectHandler.handle(e,t,void 0);return this.fetch=r.fetch.bind(window),this.boundLogout=r.getLogoutUrl,await this.cleanUrlAfterRedirect(e),{isLoggedIn:r.isLoggedIn,webId:r.webId,sessionId:r.sessionId,expirationDate:r.expirationDate,clientAppId:r.clientAppId}}catch(r){return await this.cleanUrlAfterRedirect(e),void t.emit(ve.ERROR,"redirect",r)}};async cleanUrlAfterRedirect(e){const t=Ie(e).href;for(window.history.replaceState(null,"",t);window.location.href!==t;)await new Promise((e=>{setTimeout((()=>e()),1)}))}}function er(e){return"string"==typeof e.oidcIssuer}function tr(e){return"string"==typeof e.redirectUrl}class rr{storageUtility;oidcHandler;issuerConfigFetcher;clientRegistrar;constructor(e,t,r,s){this.storageUtility=e,this.oidcHandler=t,this.issuerConfigFetcher=r,this.clientRegistrar=s,this.storageUtility=e,this.oidcHandler=t,this.issuerConfigFetcher=r,this.clientRegistrar=s}async canHandle(e){return er(e)&&tr(e)}async handle(e){if(!er(e))throw new We(`OidcLoginHandler requires an OIDC issuer: missing property 'oidcIssuer' in ${JSON.stringify(e)}`);if(!tr(e))throw new We(`OidcLoginHandler requires a redirect URL: missing property 'redirectUrl' in ${JSON.stringify(e)}`);const t=await this.issuerConfigFetcher.fetchConfig(e.oidcIssuer),r=await De(e,t,this.storageUtility,this.clientRegistrar),s={issuer:t.issuer,dpop:"dpop"===e.tokenType.toLowerCase(),...e,issuerConfiguration:t,client:r,scopes:(i=e.customScopes,Array.isArray(i)?Array.from(new Set([...be,...i.filter((e=>"string"==typeof e&&!e.includes(" ")))])):be)};var i;return this.oidcHandler.handle(s)}}class sr extends Te{async handle(e){const t=e.redirectUrl??"",r={authority:e.issuer.toString(),client_id:e.client.clientId,client_secret:e.client.clientSecret,redirect_uri:t,response_type:"code",scope:e.scopes.join(" "),filterProtocolClaims:!0,loadUserInfo:!1,prompt:e.prompt??"consent"},s=new Bt(r);try{const t=await s.createSigninRequest({});return await this.setupRedirectHandler({oidcLoginOptions:e,state:t.state.id,codeVerifier:t.state.code_verifier??"",targetUrl:t.url.toString()})}catch(e){console.error(e)}}}const ir={issuer:{toKey:"issuer",convertToUrl:!0},authorization_endpoint:{toKey:"authorizationEndpoint",convertToUrl:!0},token_endpoint:{toKey:"tokenEndpoint",convertToUrl:!0},userinfo_endpoint:{toKey:"userinfoEndpoint",convertToUrl:!0},jwks_uri:{toKey:"jwksUri",convertToUrl:!0},registration_endpoint:{toKey:"registrationEndpoint",convertToUrl:!0},end_session_endpoint:{toKey:"endSessionEndpoint",convertToUrl:!0},scopes_supported:{toKey:"scopesSupported"},response_types_supported:{toKey:"responseTypesSupported"},response_modes_supported:{toKey:"responseModesSupported"},grant_types_supported:{toKey:"grantTypesSupported"},acr_values_supported:{toKey:"acrValuesSupported"},subject_types_supported:{toKey:"subjectTypesSupported"},id_token_signing_alg_values_supported:{toKey:"idTokenSigningAlgValuesSupported"},id_token_encryption_alg_values_supported:{toKey:"idTokenEncryptionAlgValuesSupported"},id_token_encryption_enc_values_supported:{toKey:"idTokenEncryptionEncValuesSupported"},userinfo_signing_alg_values_supported:{toKey:"userinfoSigningAlgValuesSupported"},userinfo_encryption_alg_values_supported:{toKey:"userinfoEncryptionAlgValuesSupported"},userinfo_encryption_enc_values_supported:{toKey:"userinfoEncryptionEncValuesSupported"},request_object_signing_alg_values_supported:{toKey:"requestObjectSigningAlgValuesSupported"},request_object_encryption_alg_values_supported:{toKey:"requestObjectEncryptionAlgValuesSupported"},request_object_encryption_enc_values_supported:{toKey:"requestObjectEncryptionEncValuesSupported"},token_endpoint_auth_methods_supported:{toKey:"tokenEndpointAuthMethodsSupported"},token_endpoint_auth_signing_alg_values_supported:{toKey:"tokenEndpointAuthSigningAlgValuesSupported"},display_values_supported:{toKey:"displayValuesSupported"},claim_types_supported:{toKey:"claimTypesSupported"},claims_supported:{toKey:"claimsSupported"},service_documentation:{toKey:"serviceDocumentation"},claims_locales_supported:{toKey:"claimsLocalesSupported"},ui_locales_supported:{toKey:"uiLocalesSupported"},claims_parameter_supported:{toKey:"claimsParameterSupported"},request_parameter_supported:{toKey:"requestParameterSupported"},request_uri_parameter_supported:{toKey:"requestUriParameterSupported"},require_request_uri_registration:{toKey:"requireRequestUriRegistration"},op_policy_uri:{toKey:"opPolicyUri",convertToUrl:!0},op_tos_uri:{toKey:"opTosUri",convertToUrl:!0}};class nr{storageUtility;constructor(e){this.storageUtility=e,this.storageUtility=e}static getLocalStorageKey(e){return`issuerConfig:${e}`}async fetchConfig(e){let t;const r=new URL(".well-known/openid-configuration",e.endsWith("/")?e:`${e}/`).href,s=await fetch(r);try{t=function(e){const t={};return Object.keys(e).forEach((r=>{ir[r]&&(t[ir[r].toKey]=e[r])})),Array.isArray(t.scopesSupported)||(t.scopesSupported=["openid"]),t}(await s.json())}catch(t){throw new We(`[${e.toString()}] has an invalid configuration: ${t.message}`)}return await this.storageUtility.set(nr.getLocalStorageKey(e),JSON.stringify(t)),t}}async function or(e,t){await xe(e,t),await async function(){const e=new Pt({});await Ht.clearStaleState(e,900);const t=window.localStorage,r=[];for(let e=0;e<=t.length;e+=1){const s=t.key(e);s&&(s.match(/^oidc\..+$/)||s.match(/^solidClientAuthenticationUser:.+$/))&&r.push(s)}r.forEach((e=>t.removeItem(e)))}()}class ar extends Oe{async get(e){const[t,r,s,i,n,o,a,c,d]=await Promise.all([this.storageUtility.getForUser(e,"isLoggedIn",{secure:!0}),this.storageUtility.getForUser(e,"webId",{secure:!0}),this.storageUtility.getForUser(e,"clientId",{secure:!1}),this.storageUtility.getForUser(e,"clientSecret",{secure:!1}),this.storageUtility.getForUser(e,"redirectUrl",{secure:!1}),this.storageUtility.getForUser(e,"refreshToken",{secure:!0}),this.storageUtility.getForUser(e,"issuer",{secure:!1}),this.storageUtility.getForUser(e,"tokenType",{secure:!1}),this.storageUtility.getForUser(e,"expiresAt",{secure:!1})]);if("string"!=typeof n||Ae(n)){if(void 0!==c&&("string"!=typeof(l=c)||!["DPoP","Bearer"].includes(l)))throw new Error(`Tokens of type [${c}] are not supported.`);var l;if(void 0!==s||void 0!==t||void 0!==r||void 0!==o)return{sessionId:e,webId:r,isLoggedIn:"true"===t,redirectUrl:n,refreshToken:o,issuer:a,clientAppId:s,clientAppSecret:i,tokenType:c??"DPoP",clientExpiresAt:void 0!==d?Number.parseInt(d,10):void 0}}else await Promise.all([this.storageUtility.deleteAllUserData(e,{secure:!1}),this.storageUtility.deleteAllUserData(e,{secure:!0})])}async clear(e){return or(e,this.storageUtility)}}class cr{async canHandle(e){try{return new URL(e),!0}catch(t){throw new Error(`[${e}] is not a valid URL, and cannot be used as a redirect URL: ${t}`)}}async handle(e){return Ce()}}class dr{storageUtility;sessionInfoManager;issuerConfigFetcher;clientRegistrar;tokerRefresher;constructor(e,t,r,s,i){this.storageUtility=e,this.sessionInfoManager=t,this.issuerConfigFetcher=r,this.clientRegistrar=s,this.tokerRefresher=i,this.storageUtility=e,this.sessionInfoManager=t,this.issuerConfigFetcher=r,this.clientRegistrar=s,this.tokerRefresher=i}async canHandle(e){try{const t=new URL(e);return null!==t.searchParams.get("code")&&null!==t.searchParams.get("state")}catch(t){throw new Error(`[${e}] is not a valid URL, and cannot be used as a redirect URL: ${t}`)}}async handle(e,t){if(!await this.canHandle(e))throw new Error(`AuthCodeRedirectHandler cannot handle [${e}]: it is missing one of [code, state].`);const r=new URL(e),s=r.searchParams.get("state"),i=await this.storageUtility.getForUser(s,"sessionId",{errorIfNull:!0}),{issuerConfig:n,codeVerifier:o,redirectUrl:a,dpop:c}=await $e(i,this.storageUtility,this.issuerConfigFetcher),d=r.searchParams.get("iss");if("string"==typeof d&&d!==n.issuer)throw new Error(`The value of the iss parameter (${d}) does not match the issuer identifier of the authorization server (${n.issuer}). See [rfc9207](https://www.rfc-editor.org/rfc/rfc9207.html#section-2.3-3.1.1)`);if(void 0===o)throw new Error(`The code verifier for session ${i} is missing from storage.`);if(void 0===a)throw new Error(`The redirect URL for session ${i} is missing from storage.`);const l=await this.clientRegistrar.getClient({sessionId:i},n),h=Date.now(),u=await Qt(n,l,{grantType:"authorization_code",code:r.searchParams.get("code"),codeVerifier:o,redirectUrl:a},c);let p;window.localStorage.removeItem(`oidc.${s}`),void 0!==u.refreshToken&&(p={sessionId:i,refreshToken:u.refreshToken,tokenRefresher:this.tokerRefresher});const g=Ye(u.accessToken,{dpopKey:u.dpopKey,refreshOptions:p,eventEmitter:t,expiresIn:u.expiresIn});await async function(e,t,r,s,i,n,o){void 0!==r&&await e.setForUser(t,{webId:r},{secure:o}),void 0!==s&&await e.setForUser(t,{clientId:s},{secure:o}),await e.setForUser(t,{isLoggedIn:i},{secure:o})}(this.storageUtility,i,u.webId,u.clientId,"true",0,!0);const f=await this.sessionInfoManager.get(i);if(!f)throw new Error(`Could not retrieve session: [${i}].`);return Object.assign(f,{fetch:g,getLogoutUrl:Ke({idTokenHint:u.idToken,endSessionEndpoint:n.endSessionEndpoint}),expirationDate:"number"==typeof u.expiresIn?h+1e3*u.expiresIn:void 0})}}class lr extends Ee{constructor(e){super(e)}}class hr{get storage(){return window.localStorage}async get(e){return this.storage.getItem(e)||void 0}async set(e,t){this.storage.setItem(e,t)}async delete(e){this.storage.removeItem(e)}}class ur{redirect(e,t){t&&t.handleRedirect?t.handleRedirect(e):t&&t.redirectByReplacingState?window.history.replaceState({},"",e):window.location.href=e}}class pr{storageUtility;constructor(e){this.storageUtility=e,this.storageUtility=e}async getClient(e,t){const[r,s,i,n,o]=await Promise.all([this.storageUtility.getForUser(e.sessionId,"clientId",{secure:!1}),this.storageUtility.getForUser(e.sessionId,"clientSecret",{secure:!1}),this.storageUtility.getForUser(e.sessionId,"expiresAt",{secure:!1}),this.storageUtility.getForUser(e.sessionId,"clientName",{secure:!1}),this.storageUtility.getForUser(e.sessionId,"clientType",{secure:!1})]),a=void 0!==i?Number.parseInt(i,10):-1,c=void 0!==s&&0!==a&&Math.floor(Date.now()/1e3)>a;if(r&&("string"==typeof(d=o)&&["dynamic","static","solid-oidc"].includes(d))&&!c)return void 0!==s?{clientId:r,clientSecret:s,clientName:n,clientType:"dynamic",expiresAt:a}:{clientId:r,clientName:n,clientType:o};var d;try{const r=await zt(e,t),s={clientId:r.clientId,clientType:"dynamic"};return void 0!==r.clientSecret&&(s.clientSecret=r.clientSecret,s.expiresAt=String(r.expiresAt)),r.idTokenSignedResponseAlg&&(s.idTokenSignedResponseAlg=r.idTokenSignedResponseAlg),await this.storageUtility.setForUser(e.sessionId,s,{secure:!1}),r}catch(e){throw new Error("Client registration failed.",{cause:e})}}}class gr{async canHandle(e){try{return new URL(e).searchParams.has("error")}catch(t){throw new Error(`[${e}] is not a valid URL, and cannot be used as a redirect URL: ${t}`)}}async handle(e,t){if(void 0!==t){const r=new URL(e),s=r.searchParams.get("error"),i=r.searchParams.get("error_description");t.emit(ve.ERROR,s,i)}return Ce()}}class fr{storageUtility;issuerConfigFetcher;clientRegistrar;constructor(e,t,r){this.storageUtility=e,this.issuerConfigFetcher=t,this.clientRegistrar=r,this.storageUtility=e,this.issuerConfigFetcher=t,this.clientRegistrar=r}async refresh(e,t,r,s){const i=await $e(e,this.storageUtility,this.issuerConfigFetcher),n=await this.clientRegistrar.getClient({sessionId:e},i.issuerConfig);if(void 0===t)throw new Error(`Session [${e}] has no refresh token to allow it to refresh its access token.`);if(i.dpop&&void 0===r)throw new Error(`For session [${e}], the key bound to the DPoP access token must be provided to refresh said access token.`);const o=await Yt(t,i.issuerConfig,n,r);return void 0!==o.refreshToken&&s?.emit(ve.NEW_REFRESH_TOKEN,o.refreshToken),o}}function yr(e){const t=new Je,r=e.secureStorage||t,s=e.insecureStorage||new hr,i=new at(r,s),n=new nr(i),o=new pr(i),a=new ar(i),c=new fr(i,n,o),d=new ur,l=new rr(i,new sr(i,d),n,o),h=new lr([new gr,new dr(i,a,n,o,c),new cr]);return new Zt(l,h,new Pe(a,d),a,n)}const wr=`${me}currentSession`,_r=`${me}currentUrl`;class mr{info;events;clientAuthentication;tokenRequestInProgress=!1;constructor(e={},t=void 0){this.events=new ot,e.clientAuthentication?this.clientAuthentication=e.clientAuthentication:e.secureStorage&&e.insecureStorage?this.clientAuthentication=yr({secureStorage:e.secureStorage,insecureStorage:e.insecureStorage}):this.clientAuthentication=yr({}),e.sessionInfo?this.info={sessionId:e.sessionInfo.sessionId,isLoggedIn:!1,webId:e.sessionInfo.webId,clientAppId:e.sessionInfo.clientAppId}:this.info={sessionId:t??tt(),isLoggedIn:!1},this.events.on(ve.LOGIN,(()=>window.localStorage.setItem(wr,this.info.sessionId))),this.events.on(ve.SESSION_EXPIRED,(()=>this.internalLogout(!1))),this.events.on(ve.ERROR,(()=>this.internalLogout(!1)))}login=async e=>(await this.clientAuthentication.login({sessionId:this.info.sessionId,...e,tokenType:e.tokenType??"DPoP"},this.events),new Promise((()=>{})));fetch=(e,t)=>this.clientAuthentication.fetch(e,t);internalLogout=async(e,t)=>{window.localStorage.removeItem(wr),await this.clientAuthentication.logout(this.info.sessionId,t),this.info.isLoggedIn=!1,e&&this.events.emit(ve.LOGOUT)};logout=async e=>this.internalLogout(!0,e);handleIncomingRedirect=async(e={})=>{if(this.info.isLoggedIn)return this.info;if(this.tokenRequestInProgress)return;const t="string"==typeof e?{url:e}:e,r=t.url??window.location.href;this.tokenRequestInProgress=!0;const s=await this.clientAuthentication.handleIncomingRedirect(r,this.events);if(function(e){return!!e?.isLoggedIn}(s)){this.setSessionInfo(s);const e=window.localStorage.getItem(_r);null===e?this.events.emit(ve.LOGIN):(window.localStorage.removeItem(_r),this.events.emit(ve.SESSION_RESTORED,e))}else if(!0===t.restorePreviousSession){const e=window.localStorage.getItem(wr);if(null!==e){if(await async function(e,t,r){const s=await t.validateCurrentSession(e);return null!==s&&(window.localStorage.setItem(_r,window.location.href),await t.login({sessionId:e,prompt:"none",oidcIssuer:s.issuer,redirectUrl:s.redirectUrl,clientId:s.clientAppId,clientSecret:s.clientAppSecret,tokenType:s.tokenType??"DPoP"},r.events),!0)}(e,this.clientAuthentication,this))return new Promise((()=>{}))}}return this.tokenRequestInProgress=!1,s};setSessionInfo(e){this.info.isLoggedIn=e.isLoggedIn,this.info.webId=e.webId,this.info.sessionId=e.sessionId,this.info.clientAppId=e.clientAppId,this.info.expirationDate=e.expirationDate,this.events.on(ve.SESSION_EXTENDED,(e=>{this.info.expirationDate=Date.now()+1e3*e}))}}let Sr;function vr(){return void 0===Sr&&(Sr=new mr),Sr}return e.ConfigurationError=We,e.EVENTS=ve,e.InMemoryStorage=Je,e.NotImplementedError=Me,e.Session=mr,e.events=function(){return vr().events},e.fetch=function(...e){return vr().fetch(...e)},e.getDefaultSession=vr,e.handleIncomingRedirect=function(...e){return vr().handleIncomingRedirect(...e)},e.login=function(...e){return vr().login(...e)},e.logout=function(...e){return vr().logout(...e)},e}({});
|
|
2
|
-
//# sourceMappingURL=solid-client-authn.bundle.js.map
|