fedipod 0.14.0 → 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 +20 -4
- package/web/admin/setup/setup.js +50 -10
- package/web/front/admin.html +21 -17
- package/web/front/new-account.html +2 -2
- package/web/front/run.html +19 -16
- package/web/front/solid-oidc-client.js +6 -0
- package/web/front/solid-client-authn.bundle.js +0 -2
package/web/admin/setup/setup.js
CHANGED
|
@@ -60,20 +60,34 @@ function paneForm() {
|
|
|
60
60
|
if (fr.kind) $('form').elements.kind.value = fr.kind;
|
|
61
61
|
if (fr.handle) $('handle').value = fr.handle;
|
|
62
62
|
if (fr.pod) { $('form').elements.mode.value = 'existing'; $('pod').value = fr.pod; }
|
|
63
|
-
|
|
63
|
+
// A gateway-arranged signup names its own provider; give the dropdown that
|
|
64
|
+
// choice too, so the carried-over answer survives the narrower control.
|
|
65
|
+
if (fr.issuer) {
|
|
66
|
+
$('issuer').value = fr.issuer;
|
|
67
|
+
const sel = $('issuer-new');
|
|
68
|
+
if (![...sel.options].some(o => o.value === fr.issuer)) {
|
|
69
|
+
const o = document.createElement('option');
|
|
70
|
+
o.value = fr.issuer;
|
|
71
|
+
o.textContent = host(fr.issuer);
|
|
72
|
+
sel.appendChild(o);
|
|
73
|
+
}
|
|
74
|
+
sel.value = fr.issuer;
|
|
75
|
+
}
|
|
64
76
|
if (fr.gatewayHost) strap(`finishing your ${fr.gatewayHost} signup — enter your Solid account details`);
|
|
65
77
|
}
|
|
66
78
|
// Resuming: the account exists and the credential is minted. Asking for a
|
|
67
79
|
// password again would mint a second one and orphan the first, which cannot
|
|
68
80
|
// be recovered.
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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
|
|
77
91
|
show('pane-form');
|
|
78
92
|
// The markup's autofocus was set while this pane was still hidden, so it
|
|
79
93
|
// never fired — move focus to the first field now that the pane is showing.
|
|
@@ -94,7 +108,7 @@ function answers() {
|
|
|
94
108
|
kind,
|
|
95
109
|
mode,
|
|
96
110
|
handle: f.handle.value.trim(),
|
|
97
|
-
issuer: f.issuer.value.trim(),
|
|
111
|
+
issuer: (mode === 'new' ? f.issuerNew.value : f.issuer.value).trim(),
|
|
98
112
|
email: f.email.value.trim(),
|
|
99
113
|
};
|
|
100
114
|
if (mode === 'new') a.podName = f.podName.value.trim() || a.handle;
|
|
@@ -123,6 +137,8 @@ function onEdit() {
|
|
|
123
137
|
const mode = state.resumable ? 'existing' : $('form').elements.mode.value;
|
|
124
138
|
$('row-podname').hidden = mode !== 'new';
|
|
125
139
|
$('row-pod').hidden = mode === 'new';
|
|
140
|
+
$('row-issuer-new').hidden = mode !== 'new';
|
|
141
|
+
$('row-issuer-existing').hidden = mode === 'new';
|
|
126
142
|
clearTimeout(editTimer);
|
|
127
143
|
editTimer = setTimeout(preview, 150);
|
|
128
144
|
}
|
|
@@ -194,6 +210,30 @@ async function watchRun() {
|
|
|
194
210
|
state = json || state;
|
|
195
211
|
paneForm();
|
|
196
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();
|
|
197
237
|
}
|
|
198
238
|
|
|
199
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>
|
|
@@ -19,8 +19,8 @@
|
|
|
19
19
|
</style>
|
|
20
20
|
</head>
|
|
21
21
|
<body>
|
|
22
|
-
<h1>FediPod - join the
|
|
23
|
-
<p class="lede">Welcome to <b>FediPod</b>! Here, you can get a free pod and
|
|
22
|
+
<h1>FediPod - join the Fediverse from a Solid pod.</h1>
|
|
23
|
+
<p class="lede">Welcome to <b>FediPod</b>! Here, you can get a free pod and Fediverse account or link to your existing pod/account.</p>
|
|
24
24
|
|
|
25
25
|
<p class="hint" id="current-version" hidden>Current FediPod: <strong id="current-version-num"></strong><br>
|
|
26
26
|
— install or update an existing install with <code id="current-version-cmd"></code></p>
|
package/web/front/run.html
CHANGED
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
</head>
|
|
26
26
|
<body>
|
|
27
27
|
<h1>Run your identity on this server</h1>
|
|
28
|
-
<p class="lede">If your pod is hosted by this server, the server can run your
|
|
28
|
+
<p class="lede">If your pod is hosted by this server, the server can run your Fediverse identity
|
|
29
29
|
for you: it accepts follows, delivers your posts and serves your Mastodon app, with nothing for
|
|
30
30
|
you to keep running. Sign in with your pod to prove it is yours.</p>
|
|
31
31
|
|
|
@@ -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
|
+
|