@rivm-loculus-tools/common-frontend 0.8.0 → 0.10.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/dist/_chunks/LoculusService.js +1049 -0
- package/dist/_chunks/LoculusService.js.map +1 -0
- package/dist/_chunks/decorateParam.js +7 -0
- package/dist/_chunks/keycloak-dpop.js +241 -0
- package/dist/_chunks/keycloak-dpop.js.map +1 -0
- package/dist/classes/services/EmotionCacheService/index.js +27 -1
- package/dist/classes/services/EmotionCacheService/index.js.map +1 -0
- package/dist/classes/services/LoculusService/LoculusService.d.ts +26 -0
- package/dist/classes/services/LoculusService/index.d.ts +1 -0
- package/dist/classes/services/LoculusService/index.js +1 -0
- package/dist/classes/services/UrlService/UrlService.d.ts +18 -0
- package/dist/classes/services/UrlService/index.d.ts +1 -0
- package/dist/classes/services/UrlService/index.js +45 -0
- package/dist/classes/services/UrlService/index.js.map +1 -0
- package/dist/components/app/App/App.d.ts +4 -2
- package/dist/components/app/App/index.js +35 -34
- package/dist/components/app/App/index.js.map +1 -1
- package/dist/components/authentication/AuthenticationLoader/AuthenticationLoader.d.ts +3 -0
- package/dist/components/authentication/AuthenticationLoader/index.d.ts +1 -0
- package/dist/components/authentication/AuthenticationLoader/index.js +31 -0
- package/dist/components/authentication/AuthenticationLoader/index.js.map +1 -0
- package/dist/components/fasta/FastaDataImportDialog/FastaDataImportDialog.d.ts +1 -0
- package/dist/components/fasta/FastaDataImportDialog/index.js +80 -80
- package/dist/components/fasta/FastaDataImportDialog/index.js.map +1 -1
- package/dist/components/fasta/FastaDataImportDialogLoader/FastaDataImportDialogLoader.d.ts +2 -1
- package/dist/components/fasta/FastaDataImportDialogLoader/index.js +61 -52
- package/dist/components/fasta/FastaDataImportDialogLoader/index.js.map +1 -1
- package/dist/components/fasta/FastaPageHeaderResetButton/index.js +27 -24
- package/dist/components/fasta/FastaPageHeaderResetButton/index.js.map +1 -1
- package/dist/components/loculus/UploadToLoculusModal/UploadToLoculusModal.d.ts +11 -0
- package/dist/components/loculus/UploadToLoculusModal/index.d.ts +1 -0
- package/dist/components/loculus/UploadToLoculusModal/index.js +159 -0
- package/dist/components/loculus/UploadToLoculusModal/index.js.map +1 -0
- package/dist/components/ui/ButtonWithMenu/ButtonWithMenu.d.ts +1 -0
- package/dist/components/ui/ButtonWithMenu/index.js +5 -4
- package/dist/components/ui/ButtonWithMenu/index.js.map +1 -1
- package/dist/components/ui/PageCommon/index.js +2 -2
- package/dist/components/ui/PageCommon/index.js.map +1 -1
- package/dist/components/ui/PageCommonDrawer/index.js +1 -1
- package/dist/components/ui/SearchBar/index.js +1 -0
- package/dist/components/ui/SearchBar/index.js.map +1 -1
- package/dist/constants/page.constants.d.ts +1 -1
- package/dist/constants/page.constants.js +1 -1
- package/dist/constants/page.constants.js.map +1 -1
- package/dist/models/url.models.d.ts +9 -0
- package/dist/models/url.models.js +0 -0
- package/dist/utils/MetaDataUtil/MetaDataUtil.d.ts +2 -1
- package/dist/utils/MetaDataUtil/index.js +5 -2
- package/dist/utils/MetaDataUtil/index.js.map +1 -1
- package/dist/utils/UrlUtil/UrlUtil.d.ts +2 -1
- package/dist/utils/UrlUtil/index.js +8 -5
- package/dist/utils/UrlUtil/index.js.map +1 -1
- package/package.json +23 -2
- package/dist/_chunks/EmotionCacheService.js +0 -32
- package/dist/_chunks/EmotionCacheService.js.map +0 -1
|
@@ -0,0 +1,1049 @@
|
|
|
1
|
+
import { MetaDataUtil as e } from "../utils/MetaDataUtil/index.js";
|
|
2
|
+
import { FastaUtil as t } from "../utils/FastaUtil/index.js";
|
|
3
|
+
import { t as n } from "./decorate.js";
|
|
4
|
+
import { singleton as r } from "tsyringe";
|
|
5
|
+
import { SubscribableAbstract as i } from "@gen-epix/ui-core/classes/abstracts/SubscribableAbstract";
|
|
6
|
+
import { Subject as a } from "@gen-epix/ui-core/classes/Subject";
|
|
7
|
+
// ../../../node_modules/.pnpm/keycloak-js@26.2.5/node_modules/keycloak-js/lib/deprecations.js
|
|
8
|
+
var o = /* @__PURE__ */ new Set();
|
|
9
|
+
function s(e, t) {
|
|
10
|
+
o.has(e) || (o.add(e), console.warn(`[KEYCLOAK] ${e}: ${t}`));
|
|
11
|
+
}
|
|
12
|
+
function c(e, t, n) {
|
|
13
|
+
for (let r of n) {
|
|
14
|
+
let n = e[r];
|
|
15
|
+
e[r] = function(...i) {
|
|
16
|
+
return this !== e && s(`${t}:${r}`, `Calling '${r}()' without a bound 'this' is deprecated and will stop working in a future major version.`), n.apply(e, i);
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
// ../../../node_modules/.pnpm/keycloak-js@26.2.5/node_modules/keycloak-js/lib/keycloak.js
|
|
21
|
+
var l = "application/json", u = class {
|
|
22
|
+
#e = [];
|
|
23
|
+
#t;
|
|
24
|
+
#n = !0;
|
|
25
|
+
#r;
|
|
26
|
+
#i = this.#F(console.info);
|
|
27
|
+
#a = this.#F(console.warn);
|
|
28
|
+
#o = {
|
|
29
|
+
enable: !0,
|
|
30
|
+
callbackList: [],
|
|
31
|
+
interval: 5
|
|
32
|
+
};
|
|
33
|
+
#s;
|
|
34
|
+
#c;
|
|
35
|
+
didInitialize = !1;
|
|
36
|
+
authenticated = !1;
|
|
37
|
+
loginRequired = !1;
|
|
38
|
+
responseMode = "fragment";
|
|
39
|
+
responseType = "code";
|
|
40
|
+
flow = "standard";
|
|
41
|
+
timeSkew = null;
|
|
42
|
+
redirectUri;
|
|
43
|
+
silentCheckSsoRedirectUri;
|
|
44
|
+
silentCheckSsoFallback = !0;
|
|
45
|
+
pkceMethod = "S256";
|
|
46
|
+
enableLogging = !1;
|
|
47
|
+
logoutMethod = "GET";
|
|
48
|
+
scope;
|
|
49
|
+
messageReceiveTimeout = 1e4;
|
|
50
|
+
idToken;
|
|
51
|
+
idTokenParsed;
|
|
52
|
+
token;
|
|
53
|
+
tokenParsed;
|
|
54
|
+
refreshToken;
|
|
55
|
+
refreshTokenParsed;
|
|
56
|
+
clientId;
|
|
57
|
+
sessionId;
|
|
58
|
+
subject;
|
|
59
|
+
authServerUrl;
|
|
60
|
+
realm;
|
|
61
|
+
realmAccess;
|
|
62
|
+
resourceAccess;
|
|
63
|
+
profile;
|
|
64
|
+
userInfo;
|
|
65
|
+
endpoints;
|
|
66
|
+
tokenTimeoutHandle;
|
|
67
|
+
onAuthSuccess;
|
|
68
|
+
onAuthError;
|
|
69
|
+
onAuthRefreshSuccess;
|
|
70
|
+
onAuthRefreshError;
|
|
71
|
+
onTokenExpired;
|
|
72
|
+
onAuthLogout;
|
|
73
|
+
onReady;
|
|
74
|
+
onActionUpdate;
|
|
75
|
+
constructor(e) {
|
|
76
|
+
if (typeof e != "string" && !D(e)) throw Error("The 'Keycloak' constructor must be provided with a configuration object, or a URL to a JSON configuration file.");
|
|
77
|
+
if (D(e)) {
|
|
78
|
+
let t = "oidcProvider" in e ? ["clientId"] : [
|
|
79
|
+
"url",
|
|
80
|
+
"realm",
|
|
81
|
+
"clientId"
|
|
82
|
+
];
|
|
83
|
+
for (let n of t) if (!(n in e)) throw Error(`The configuration object is missing the required '${n}' property.`);
|
|
84
|
+
}
|
|
85
|
+
globalThis.isSecureContext || this.#a("[KEYCLOAK] Keycloak JS must be used in a 'secure context' to function properly as it relies on browser APIs that are otherwise not available.\nContinuing to run your application insecurely will lead to unexpected behavior and breakage.\n\nFor more information see: https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts"), this.#c = e, c(this, "KC-DEP-003", [
|
|
86
|
+
"init",
|
|
87
|
+
"login",
|
|
88
|
+
"createLoginUrl",
|
|
89
|
+
"logout",
|
|
90
|
+
"createLogoutUrl",
|
|
91
|
+
"register",
|
|
92
|
+
"createRegisterUrl",
|
|
93
|
+
"createAccountUrl",
|
|
94
|
+
"accountManagement",
|
|
95
|
+
"hasRealmRole",
|
|
96
|
+
"hasResourceRole",
|
|
97
|
+
"loadUserProfile",
|
|
98
|
+
"loadUserInfo",
|
|
99
|
+
"isTokenExpired",
|
|
100
|
+
"updateToken",
|
|
101
|
+
"clearToken"
|
|
102
|
+
]);
|
|
103
|
+
}
|
|
104
|
+
async init(e = {}) {
|
|
105
|
+
if (this.didInitialize) throw Error("A 'Keycloak' instance can only be initialized once.");
|
|
106
|
+
if (this.didInitialize = !0, this.#r = v(), this.#t = typeof e.adapter == "string" && [
|
|
107
|
+
"default",
|
|
108
|
+
"cordova",
|
|
109
|
+
"cordova-native"
|
|
110
|
+
].includes(e.adapter) ? this.#l(e.adapter) : typeof e.adapter == "object" ? e.adapter : "Cordova" in window || "cordova" in window ? this.#l("cordova") : this.#l("default"), e.useNonce !== void 0 && (this.#n = e.useNonce), e.checkLoginIframe !== void 0 && (this.#o.enable = e.checkLoginIframe), e.checkLoginIframeInterval && (this.#o.interval = e.checkLoginIframeInterval), e.onLoad === "login-required" && (this.loginRequired = !0), e.responseMode) {
|
|
111
|
+
if (e.responseMode === "query" || e.responseMode === "fragment") this.responseMode = e.responseMode;
|
|
112
|
+
else throw Error("Invalid value for responseMode");
|
|
113
|
+
}
|
|
114
|
+
if (e.flow) {
|
|
115
|
+
switch (e.flow) {
|
|
116
|
+
case "standard":
|
|
117
|
+
this.responseType = "code";
|
|
118
|
+
break;
|
|
119
|
+
case "implicit":
|
|
120
|
+
this.responseType = "id_token token";
|
|
121
|
+
break;
|
|
122
|
+
case "hybrid":
|
|
123
|
+
this.responseType = "code id_token token";
|
|
124
|
+
break;
|
|
125
|
+
default: throw Error("Invalid value for flow");
|
|
126
|
+
}
|
|
127
|
+
this.flow = e.flow;
|
|
128
|
+
}
|
|
129
|
+
if (typeof e.timeSkew == "number" && (this.timeSkew = e.timeSkew), e.redirectUri && (this.redirectUri = e.redirectUri), e.silentCheckSsoRedirectUri && (this.silentCheckSsoRedirectUri = e.silentCheckSsoRedirectUri), typeof e.silentCheckSsoFallback == "boolean" && (this.silentCheckSsoFallback = e.silentCheckSsoFallback), e.pkceMethod !== void 0) {
|
|
130
|
+
if (e.pkceMethod !== "S256" && e.pkceMethod !== !1) throw TypeError(`Invalid value for pkceMethod', expected 'S256' or false but got ${e.pkceMethod}.`);
|
|
131
|
+
this.pkceMethod = e.pkceMethod;
|
|
132
|
+
}
|
|
133
|
+
if (typeof e.enableLogging == "boolean" && (this.enableLogging = e.enableLogging), e.logoutMethod === "POST" && (this.logoutMethod = "POST"), typeof e.scope == "string" && (this.scope = e.scope), typeof e.messageReceiveTimeout == "number" && e.messageReceiveTimeout > 0 && (this.messageReceiveTimeout = e.messageReceiveTimeout), e.dpopConfig && (this.dpopConfig = e.dpopConfig), await this.#p(), this.dpopConfig?.mode === "strict" && (!this.dpopSigningAlgValuesSupported || this.dpopSigningAlgValuesSupported.length === 0)) throw Error("DPoP is set to strict mode but the server does not advertise DPoP support (dpop_signing_alg_values_supported is missing or empty).");
|
|
134
|
+
if (this.dpopConfig && this.dpopSigningAlgValuesSupported?.length) {
|
|
135
|
+
let e = this.#P();
|
|
136
|
+
if (!e) throw Error("Cannot initialize DPoP: issuer URL is not available. Ensure authServerUrl and realm are configured, or use OIDC provider mode with a valid issuer.");
|
|
137
|
+
if (!this.clientId) throw Error("Cannot initialize DPoP: clientId is not configured.");
|
|
138
|
+
let { DPoPSignatureProvider: t, BrowserSignatureAlgs: n } = await import("./keycloak-dpop.js");
|
|
139
|
+
this.#s = new t({
|
|
140
|
+
issuerUrl: new URL(e),
|
|
141
|
+
clientId: this.clientId,
|
|
142
|
+
serverSupportedAlgorithms: this.dpopSigningAlgValuesSupported,
|
|
143
|
+
requestedAlgorithm: this.dpopConfig.alg ? n[this.dpopConfig.alg] : n.ES256,
|
|
144
|
+
strictStorage: this.dpopConfig.strictStorage ?? !1,
|
|
145
|
+
getTimeSkew: () => this.timeSkew ?? 0
|
|
146
|
+
}), await this.#s.init(), this.#i("[KEYCLOAK] DPoP initialized");
|
|
147
|
+
}
|
|
148
|
+
return await this.#_(), await this.#v(e), this.onReady?.(this.authenticated), this.authenticated;
|
|
149
|
+
}
|
|
150
|
+
#l(e) {
|
|
151
|
+
if (e === "default") return this.#u();
|
|
152
|
+
if (e === "cordova") return s("KC-DEP-001", "The 'cordova' adapter is deprecated and will be removed in a future version. Use a custom adapter instead."), this.#o.enable = !1, this.#d();
|
|
153
|
+
if (e === "cordova-native") return s("KC-DEP-002", "The 'cordova-native' adapter is deprecated and will be removed in a future version. Use a custom adapter instead."), this.#o.enable = !1, this.#f();
|
|
154
|
+
throw Error("invalid adapter type: " + e);
|
|
155
|
+
}
|
|
156
|
+
#u() {
|
|
157
|
+
let e = (e) => e?.redirectUri || this.redirectUri || globalThis.location.href;
|
|
158
|
+
return {
|
|
159
|
+
login: async (e) => (window.location.assign(await this.createLoginUrl(e)), await new Promise(() => {})),
|
|
160
|
+
logout: async (t) => {
|
|
161
|
+
if ((t?.logoutMethod ?? this.logoutMethod) === "GET") {
|
|
162
|
+
window.location.replace(this.createLogoutUrl(t));
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
let n = document.createElement("form");
|
|
166
|
+
n.setAttribute("method", "POST"), n.setAttribute("action", this.createLogoutUrl(t)), n.style.display = "none";
|
|
167
|
+
let r = {
|
|
168
|
+
id_token_hint: this.idToken,
|
|
169
|
+
client_id: this.clientId,
|
|
170
|
+
post_logout_redirect_uri: e(t)
|
|
171
|
+
};
|
|
172
|
+
for (let [e, t] of Object.entries(r)) {
|
|
173
|
+
let r = document.createElement("input");
|
|
174
|
+
r.setAttribute("type", "hidden"), r.setAttribute("name", e), r.setAttribute("value", t), n.appendChild(r);
|
|
175
|
+
}
|
|
176
|
+
document.body.appendChild(n), n.submit();
|
|
177
|
+
},
|
|
178
|
+
register: async (e) => (window.location.assign(await this.createRegisterUrl(e)), await new Promise(() => {})),
|
|
179
|
+
accountManagement: async () => {
|
|
180
|
+
let e = this.createAccountUrl();
|
|
181
|
+
if (e !== void 0) window.location.href = e;
|
|
182
|
+
else throw Error("Not supported by the OIDC server");
|
|
183
|
+
return await new Promise(() => {});
|
|
184
|
+
},
|
|
185
|
+
redirectUri: e
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
#d() {
|
|
189
|
+
let e = (e, t, n) => window.cordova && window.cordova.InAppBrowser ? window.cordova.InAppBrowser.open(e, t, n) : window.open(e, t, n), t = (e) => e && e.cordovaOptions ? Object.keys(e.cordovaOptions).reduce((t, n) => (t[n] = e.cordovaOptions[n], t), {}) : {}, n = (e) => Object.keys(e).reduce((t, n) => (t.push(n + "=" + e[n]), t), []).join(","), r = (e) => {
|
|
190
|
+
let r = t(e);
|
|
191
|
+
return r.location = "no", e && e.prompt === "none" && (r.hidden = "yes"), n(r);
|
|
192
|
+
}, i = () => this.redirectUri || "http://localhost";
|
|
193
|
+
return {
|
|
194
|
+
login: async (t) => {
|
|
195
|
+
let n = r(t), a = await this.createLoginUrl(t), o = e(a, "_blank", n), s = !1, c = !1;
|
|
196
|
+
function l() {
|
|
197
|
+
c = !0, o.close();
|
|
198
|
+
}
|
|
199
|
+
return await new Promise((e, t) => {
|
|
200
|
+
o.addEventListener("loadstart", async (n) => {
|
|
201
|
+
if (n.url.indexOf(i()) === 0) {
|
|
202
|
+
let r = this.#S(n.url);
|
|
203
|
+
if (s = !0, l(), !r || !r.valid) {
|
|
204
|
+
t(/* @__PURE__ */ Error("Invalid OAuth state in login callback."));
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
try {
|
|
208
|
+
await this.#T(r), e();
|
|
209
|
+
} catch (e) {
|
|
210
|
+
t(e);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}), o.addEventListener("loaderror", async (n) => {
|
|
214
|
+
if (!s) {
|
|
215
|
+
if (n.url.indexOf(i()) === 0) {
|
|
216
|
+
let r = this.#S(n.url);
|
|
217
|
+
if (s = !0, l(), !r || !r.valid) {
|
|
218
|
+
t(/* @__PURE__ */ Error("Invalid OAuth state in login callback."));
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
try {
|
|
222
|
+
await this.#T(r), e();
|
|
223
|
+
} catch (e) {
|
|
224
|
+
t(e);
|
|
225
|
+
}
|
|
226
|
+
} else t(/* @__PURE__ */ Error("Unable to process login.")), l();
|
|
227
|
+
}
|
|
228
|
+
}), o.addEventListener("exit", function(e) {
|
|
229
|
+
c || t(/* @__PURE__ */ Error("User closed the login window."));
|
|
230
|
+
});
|
|
231
|
+
});
|
|
232
|
+
},
|
|
233
|
+
logout: async (t) => {
|
|
234
|
+
let n = this.createLogoutUrl(t), r = e(n, "_blank", "location=no,hidden=yes,clearcache=yes"), a = !1;
|
|
235
|
+
r.addEventListener("loadstart", (e) => {
|
|
236
|
+
e.url.indexOf(i()) === 0 && r.close();
|
|
237
|
+
}), r.addEventListener("loaderror", (e) => {
|
|
238
|
+
e.url.indexOf(i()) === 0 || (a = !0), r.close();
|
|
239
|
+
}), await new Promise((e, t) => {
|
|
240
|
+
r.addEventListener("exit", () => {
|
|
241
|
+
a ? t(/* @__PURE__ */ Error("User closed the login window.")) : (this.clearToken(), e());
|
|
242
|
+
});
|
|
243
|
+
});
|
|
244
|
+
},
|
|
245
|
+
register: async (t) => {
|
|
246
|
+
let n = await this.createRegisterUrl(), a = r(t), o = e(n, "_blank", a);
|
|
247
|
+
await new Promise((e, t) => {
|
|
248
|
+
o.addEventListener("loadstart", async (n) => {
|
|
249
|
+
if (n.url.indexOf(i()) === 0) {
|
|
250
|
+
o.close();
|
|
251
|
+
let r = this.#S(n.url);
|
|
252
|
+
if (!r || !r.valid) {
|
|
253
|
+
t(/* @__PURE__ */ Error("Invalid OAuth state in register callback."));
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
try {
|
|
257
|
+
await this.#T(r), e();
|
|
258
|
+
} catch (e) {
|
|
259
|
+
t(e);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
});
|
|
264
|
+
},
|
|
265
|
+
accountManagement: async () => {
|
|
266
|
+
let t = this.createAccountUrl();
|
|
267
|
+
if (t !== void 0) {
|
|
268
|
+
let n = e(t, "_blank", "location=no");
|
|
269
|
+
n.addEventListener("loadstart", function(e) {
|
|
270
|
+
e.url.indexOf(i()) === 0 && n.close();
|
|
271
|
+
});
|
|
272
|
+
} else throw Error("Not supported by the OIDC server");
|
|
273
|
+
},
|
|
274
|
+
redirectUri: () => i()
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
#f() {
|
|
278
|
+
return {
|
|
279
|
+
login: async (e) => {
|
|
280
|
+
let t = await this.createLoginUrl(e);
|
|
281
|
+
await new Promise((e, n) => {
|
|
282
|
+
universalLinks.subscribe("keycloak", async (t) => {
|
|
283
|
+
universalLinks.unsubscribe("keycloak"), window.cordova.plugins.browsertab.close();
|
|
284
|
+
let r = this.#S(t.url);
|
|
285
|
+
if (!r || !r.valid) n(/* @__PURE__ */ Error("Invalid OAuth state in login callback."));
|
|
286
|
+
else try {
|
|
287
|
+
await this.#T(r), e();
|
|
288
|
+
} catch (e) {
|
|
289
|
+
n(e);
|
|
290
|
+
}
|
|
291
|
+
}), window.cordova.plugins.browsertab.openUrl(t);
|
|
292
|
+
});
|
|
293
|
+
},
|
|
294
|
+
logout: async (e) => {
|
|
295
|
+
let t = this.createLogoutUrl(e);
|
|
296
|
+
await new Promise((e) => {
|
|
297
|
+
universalLinks.subscribe("keycloak", () => {
|
|
298
|
+
universalLinks.unsubscribe("keycloak"), window.cordova.plugins.browsertab.close(), this.clearToken(), e();
|
|
299
|
+
}), window.cordova.plugins.browsertab.openUrl(t);
|
|
300
|
+
});
|
|
301
|
+
},
|
|
302
|
+
register: async (e) => {
|
|
303
|
+
let t = await this.createRegisterUrl(e);
|
|
304
|
+
await new Promise((e, n) => {
|
|
305
|
+
universalLinks.subscribe("keycloak", async (t) => {
|
|
306
|
+
universalLinks.unsubscribe("keycloak"), window.cordova.plugins.browsertab.close();
|
|
307
|
+
let r = this.#S(t.url);
|
|
308
|
+
if (!r || !r.valid) n(/* @__PURE__ */ Error("Invalid OAuth state in register callback."));
|
|
309
|
+
else try {
|
|
310
|
+
await this.#T(r), e();
|
|
311
|
+
} catch (e) {
|
|
312
|
+
n(e);
|
|
313
|
+
}
|
|
314
|
+
}), window.cordova.plugins.browsertab.openUrl(t);
|
|
315
|
+
});
|
|
316
|
+
},
|
|
317
|
+
accountManagement: async () => {
|
|
318
|
+
let e = this.createAccountUrl();
|
|
319
|
+
if (e !== void 0) window.cordova.plugins.browsertab.openUrl(e);
|
|
320
|
+
else throw Error("Not supported by the OIDC server");
|
|
321
|
+
},
|
|
322
|
+
redirectUri: (e) => e && e.redirectUri ? e.redirectUri : this.redirectUri ? this.redirectUri : "http://localhost"
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
async #p() {
|
|
326
|
+
if (typeof this.#c == "string") {
|
|
327
|
+
let e = await O(this.#c);
|
|
328
|
+
this.authServerUrl = e["auth-server-url"], this.realm = e.realm, this.clientId = e.resource, await this.#m();
|
|
329
|
+
} else this.clientId = this.#c.clientId, "oidcProvider" in this.#c ? await this.#h(this.#c.oidcProvider) : (this.authServerUrl = this.#c.url, this.realm = this.#c.realm, await this.#m());
|
|
330
|
+
}
|
|
331
|
+
async #m() {
|
|
332
|
+
if (this.dpopConfig) {
|
|
333
|
+
let e = this.#P();
|
|
334
|
+
if (!e) this.#a("[KEYCLOAK] Cannot fetch OIDC metadata: issuer URL is not available");
|
|
335
|
+
else {
|
|
336
|
+
let t = `${N(e)}/.well-known/openid-configuration`;
|
|
337
|
+
try {
|
|
338
|
+
let e = await k(t);
|
|
339
|
+
this.dpopSigningAlgValuesSupported = e.dpop_signing_alg_values_supported ? e.dpop_signing_alg_values_supported : void 0;
|
|
340
|
+
} catch (e) {
|
|
341
|
+
this.#a("[KEYCLOAK] Failed to fetch OIDC metadata for DPoP configuration: " + (e instanceof Error ? e.message : e));
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
this.endpoints = {
|
|
346
|
+
authorize: () => this.#N() + "/protocol/openid-connect/auth",
|
|
347
|
+
token: () => this.#N() + "/protocol/openid-connect/token",
|
|
348
|
+
logout: () => this.#N() + "/protocol/openid-connect/logout",
|
|
349
|
+
checkSessionIframe: () => this.#N() + "/protocol/openid-connect/login-status-iframe.html",
|
|
350
|
+
thirdPartyCookiesIframe: () => this.#N() + "/protocol/openid-connect/3p-cookies/step1.html",
|
|
351
|
+
register: () => this.#N() + "/protocol/openid-connect/registrations",
|
|
352
|
+
userinfo: () => this.#N() + "/protocol/openid-connect/userinfo"
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
async #h(e) {
|
|
356
|
+
if (typeof e == "string") {
|
|
357
|
+
let t = await k(`${N(e)}/.well-known/openid-configuration`);
|
|
358
|
+
this.#g(t);
|
|
359
|
+
} else this.#g(e);
|
|
360
|
+
}
|
|
361
|
+
#g(e) {
|
|
362
|
+
this.dpopSigningAlgValuesSupported = e.dpop_signing_alg_values_supported, this.issuer = e.issuer, this.endpoints = {
|
|
363
|
+
authorize() {
|
|
364
|
+
return e.authorization_endpoint;
|
|
365
|
+
},
|
|
366
|
+
token() {
|
|
367
|
+
return e.token_endpoint;
|
|
368
|
+
},
|
|
369
|
+
logout() {
|
|
370
|
+
if (!e.end_session_endpoint) throw Error("Not supported by the OIDC server");
|
|
371
|
+
return e.end_session_endpoint;
|
|
372
|
+
},
|
|
373
|
+
checkSessionIframe() {
|
|
374
|
+
if (!e.check_session_iframe) throw Error("Not supported by the OIDC server");
|
|
375
|
+
return e.check_session_iframe;
|
|
376
|
+
},
|
|
377
|
+
register() {
|
|
378
|
+
throw Error("Redirection to \"Register user\" page not supported in standard OIDC mode");
|
|
379
|
+
},
|
|
380
|
+
userinfo() {
|
|
381
|
+
if (!e.userinfo_endpoint) throw Error("Not supported by the OIDC server");
|
|
382
|
+
return e.userinfo_endpoint;
|
|
383
|
+
}
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
async #_() {
|
|
387
|
+
if (!this.#o.enable && !this.silentCheckSsoRedirectUri || typeof this.endpoints.thirdPartyCookiesIframe != "function") return;
|
|
388
|
+
let e = document.createElement("iframe");
|
|
389
|
+
return e.setAttribute("src", this.endpoints.thirdPartyCookiesIframe()), e.setAttribute("sandbox", "allow-storage-access-by-user-activation allow-scripts allow-same-origin"), e.setAttribute("title", "keycloak-3p-check-iframe"), e.style.display = "none", document.body.appendChild(e), await _(new Promise((t) => {
|
|
390
|
+
let n = (r) => {
|
|
391
|
+
e.contentWindow === r.source && (r.data === "supported" || r.data === "unsupported") && (r.data === "unsupported" && (this.#a("[KEYCLOAK] Your browser is blocking access to 3rd-party cookies, this means:\n\n - It is not possible to retrieve tokens without redirecting to the Keycloak server (a.k.a. no support for silent authentication).\n - It is not possible to automatically detect changes to the session status (such as the user logging out in another tab).\n\nFor more information see: https://www.keycloak.org/securing-apps/javascript-adapter#_modern_browsers"), this.#o.enable = !1, this.silentCheckSsoFallback && (this.silentCheckSsoRedirectUri = void 0)), document.body.removeChild(e), window.removeEventListener("message", n), t());
|
|
392
|
+
};
|
|
393
|
+
window.addEventListener("message", n, !1);
|
|
394
|
+
}), this.messageReceiveTimeout, "Timeout when waiting for 3rd party check iframe message.");
|
|
395
|
+
}
|
|
396
|
+
async #v(e) {
|
|
397
|
+
let t = this.#S(window.location.href);
|
|
398
|
+
if (t?.newUrl && window.history.replaceState(window.history.state, "", t.newUrl), t && t.valid) {
|
|
399
|
+
await this.#y(), await this.#T(t);
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
let n = async (t) => {
|
|
403
|
+
let n = {};
|
|
404
|
+
t || (n.prompt = "none"), e.locale && (n.locale = e.locale), await this.login(n);
|
|
405
|
+
}, r = async () => {
|
|
406
|
+
switch (e.onLoad) {
|
|
407
|
+
case "check-sso":
|
|
408
|
+
this.#o.enable ? (await this.#y(), await this.#b() || (this.silentCheckSsoRedirectUri ? await this.#x() : await n(!1))) : this.silentCheckSsoRedirectUri ? await this.#x() : await n(!1);
|
|
409
|
+
break;
|
|
410
|
+
case "login-required":
|
|
411
|
+
await n(!0);
|
|
412
|
+
break;
|
|
413
|
+
default: throw Error("Invalid value for onLoad");
|
|
414
|
+
}
|
|
415
|
+
};
|
|
416
|
+
if (e.token && e.refreshToken) {
|
|
417
|
+
if (this.#M(e.token, e.refreshToken, e.idToken), this.#o.enable) await this.#y(), await this.#b() && (this.onAuthSuccess?.(), this.#E());
|
|
418
|
+
else try {
|
|
419
|
+
await this.updateToken(-1), this.onAuthSuccess?.();
|
|
420
|
+
} catch (t) {
|
|
421
|
+
if (this.onAuthError?.(), e.onLoad) await r();
|
|
422
|
+
else throw t;
|
|
423
|
+
}
|
|
424
|
+
} else e.onLoad && await r();
|
|
425
|
+
}
|
|
426
|
+
async #y() {
|
|
427
|
+
if (!this.#o.enable || this.#o.iframe) return;
|
|
428
|
+
let e = document.createElement("iframe");
|
|
429
|
+
this.#o.iframe = e, e.setAttribute("src", this.endpoints.checkSessionIframe()), e.setAttribute("sandbox", "allow-storage-access-by-user-activation allow-scripts allow-same-origin"), e.setAttribute("title", "keycloak-session-iframe"), e.style.display = "none", document.body.appendChild(e), window.addEventListener("message", (e) => {
|
|
430
|
+
if (e.origin !== this.#o.iframeOrigin || this.#o.iframe?.contentWindow !== e.source || e.data !== "unchanged" && e.data !== "changed" && e.data !== "error") return;
|
|
431
|
+
e.data !== "unchanged" && this.clearToken();
|
|
432
|
+
let t = this.#o.callbackList;
|
|
433
|
+
this.#o.callbackList = [];
|
|
434
|
+
for (let n of t.reverse()) e.data === "error" ? n(/* @__PURE__ */ Error("Error while checking login iframe")) : n(null, e.data === "unchanged");
|
|
435
|
+
}, !1), await new Promise((t) => {
|
|
436
|
+
e.addEventListener("load", () => {
|
|
437
|
+
let e = this.endpoints.authorize();
|
|
438
|
+
e.startsWith("/") ? this.#o.iframeOrigin = globalThis.location.origin : this.#o.iframeOrigin = new URL(e).origin, t();
|
|
439
|
+
});
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
async #b() {
|
|
443
|
+
if (!this.#o.iframe || !this.#o.iframeOrigin) return;
|
|
444
|
+
let e = `${this.clientId} ${this.sessionId ? this.sessionId : ""}`, t = this.#o.iframeOrigin;
|
|
445
|
+
return await new Promise((n, r) => {
|
|
446
|
+
this.#o.callbackList.push((e, t) => e ? r(e) : n(t)), this.#o.callbackList.length === 1 && this.#o.iframe?.contentWindow?.postMessage(e, t);
|
|
447
|
+
});
|
|
448
|
+
}
|
|
449
|
+
async #x() {
|
|
450
|
+
let e = document.createElement("iframe"), t = await this.createLoginUrl({
|
|
451
|
+
prompt: "none",
|
|
452
|
+
redirectUri: this.silentCheckSsoRedirectUri
|
|
453
|
+
});
|
|
454
|
+
return e.setAttribute("src", t), e.setAttribute("sandbox", "allow-storage-access-by-user-activation allow-scripts allow-same-origin"), e.setAttribute("title", "keycloak-silent-check-sso"), e.style.display = "none", document.body.appendChild(e), await new Promise((t, n) => {
|
|
455
|
+
let r = async (i) => {
|
|
456
|
+
if (i.origin !== window.location.origin || e.contentWindow !== i.source) return;
|
|
457
|
+
let a = this.#S(i.data);
|
|
458
|
+
try {
|
|
459
|
+
await this.#T(a), t();
|
|
460
|
+
} catch (e) {
|
|
461
|
+
n(e);
|
|
462
|
+
}
|
|
463
|
+
document.body.removeChild(e), window.removeEventListener("message", r);
|
|
464
|
+
};
|
|
465
|
+
window.addEventListener("message", r);
|
|
466
|
+
});
|
|
467
|
+
}
|
|
468
|
+
#S(e) {
|
|
469
|
+
let t = this.#C(e);
|
|
470
|
+
if (!t) return;
|
|
471
|
+
let n = this.#r.get(t.state);
|
|
472
|
+
if (n && (t.valid = !0, t.redirectUri = n.redirectUri, t.storedNonce = n.nonce, t.prompt = n.prompt, t.pkceCodeVerifier = n.pkceCodeVerifier, t.loginOptions = n.loginOptions, n.appHash)) {
|
|
473
|
+
let r = new URL(t.newUrl || e);
|
|
474
|
+
r.hash = n.appHash, t.newUrl = r.href;
|
|
475
|
+
}
|
|
476
|
+
return t;
|
|
477
|
+
}
|
|
478
|
+
#C(e) {
|
|
479
|
+
let t = [];
|
|
480
|
+
switch (this.flow) {
|
|
481
|
+
case "standard":
|
|
482
|
+
t = [
|
|
483
|
+
"code",
|
|
484
|
+
"state",
|
|
485
|
+
"session_state",
|
|
486
|
+
"kc_action_status",
|
|
487
|
+
"kc_action",
|
|
488
|
+
"iss"
|
|
489
|
+
];
|
|
490
|
+
break;
|
|
491
|
+
case "implicit":
|
|
492
|
+
t = [
|
|
493
|
+
"access_token",
|
|
494
|
+
"token_type",
|
|
495
|
+
"id_token",
|
|
496
|
+
"state",
|
|
497
|
+
"session_state",
|
|
498
|
+
"expires_in",
|
|
499
|
+
"kc_action_status",
|
|
500
|
+
"kc_action",
|
|
501
|
+
"iss"
|
|
502
|
+
];
|
|
503
|
+
break;
|
|
504
|
+
case "hybrid": t = [
|
|
505
|
+
"access_token",
|
|
506
|
+
"token_type",
|
|
507
|
+
"id_token",
|
|
508
|
+
"code",
|
|
509
|
+
"state",
|
|
510
|
+
"session_state",
|
|
511
|
+
"expires_in",
|
|
512
|
+
"kc_action_status",
|
|
513
|
+
"kc_action",
|
|
514
|
+
"iss"
|
|
515
|
+
];
|
|
516
|
+
}
|
|
517
|
+
t.push("error"), t.push("error_description"), t.push("error_uri");
|
|
518
|
+
let n = new URL(e), r = "", i;
|
|
519
|
+
if (this.responseMode === "query" && n.searchParams.size > 0 ? (i = this.#w(n.search, t), n.search = i.paramsString, r = n.toString()) : this.responseMode === "fragment" && n.hash.length > 0 && (i = this.#w(n.hash.substring(1), t), n.hash = i.paramsString, r = n.toString()), i?.oauthParams) {
|
|
520
|
+
if (this.flow === "standard" || this.flow === "hybrid") {
|
|
521
|
+
if ((i.oauthParams.code || i.oauthParams.error) && i.oauthParams.state) return i.oauthParams.newUrl = r, i.oauthParams;
|
|
522
|
+
} else if (this.flow === "implicit" && (i.oauthParams.access_token || i.oauthParams.error) && i.oauthParams.state) return i.oauthParams.newUrl = r, i.oauthParams;
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
#w(e, t) {
|
|
526
|
+
let n = e.split("&"), r = {}, i = "";
|
|
527
|
+
for (let e of n.reverse()) {
|
|
528
|
+
let n = new URLSearchParams(e).entries().next().value;
|
|
529
|
+
if (!n) {
|
|
530
|
+
i = "&" + i;
|
|
531
|
+
continue;
|
|
532
|
+
}
|
|
533
|
+
let [a, o] = n;
|
|
534
|
+
t.includes(a) && !(a in r) ? r[a] = o : i = i.length === 0 ? e : e + "&" + i;
|
|
535
|
+
}
|
|
536
|
+
return {
|
|
537
|
+
paramsString: i,
|
|
538
|
+
oauthParams: r
|
|
539
|
+
};
|
|
540
|
+
}
|
|
541
|
+
async #T(e) {
|
|
542
|
+
let { code: t, error: n, prompt: r } = e, i = (/* @__PURE__ */ new Date()).getTime(), a = (t, n, r) => {
|
|
543
|
+
if (i = (i + (/* @__PURE__ */ new Date()).getTime()) / 2, this.#M(t, n, r, i), this.#n && this.idTokenParsed && this.idTokenParsed.nonce !== e.storedNonce) throw this.#i("[KEYCLOAK] Invalid nonce, clearing token"), this.clearToken(), Error("Invalid nonce.");
|
|
544
|
+
};
|
|
545
|
+
if (e.kc_action_status && this.onActionUpdate && this.onActionUpdate(e.kc_action_status, e.kc_action), n) {
|
|
546
|
+
if (r !== "none") {
|
|
547
|
+
if (e.error_description && e.error_description === "authentication_expired") await this.login(e.loginOptions);
|
|
548
|
+
else {
|
|
549
|
+
let t = {
|
|
550
|
+
error: n,
|
|
551
|
+
error_description: e.error_description
|
|
552
|
+
};
|
|
553
|
+
throw this.onAuthError?.(t), t;
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
} else if (this.flow !== "standard" && (e.access_token || e.id_token) && (a(e.access_token, void 0, e.id_token), this.onAuthSuccess?.()), this.flow !== "implicit" && t) try {
|
|
557
|
+
let n = await this.#D(this.endpoints.token(), t, this.clientId, e.redirectUri, e.pkceCodeVerifier);
|
|
558
|
+
a(n.access_token, n.refresh_token, n.id_token), this.flow === "standard" && this.onAuthSuccess?.(), this.#E();
|
|
559
|
+
} catch (e) {
|
|
560
|
+
throw this.onAuthError?.(), e;
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
async #E() {
|
|
564
|
+
this.#o.enable && this.token && (await F(this.#o.interval * 1e3), await this.#b() && await this.#E());
|
|
565
|
+
}
|
|
566
|
+
login(e) {
|
|
567
|
+
return this.#t.login(e);
|
|
568
|
+
}
|
|
569
|
+
async createLoginUrl(e) {
|
|
570
|
+
let t = d(), n = d(), r = new URL(this.#t.redirectUri(e), globalThis.location?.href), i = r.hash;
|
|
571
|
+
r.hash = "";
|
|
572
|
+
let a = r.href, o = {
|
|
573
|
+
state: t,
|
|
574
|
+
nonce: n,
|
|
575
|
+
redirectUri: a,
|
|
576
|
+
appHash: i,
|
|
577
|
+
loginOptions: e
|
|
578
|
+
};
|
|
579
|
+
e?.prompt && (o.prompt = e.prompt);
|
|
580
|
+
let s = e?.action === "register" ? this.endpoints.register() : this.endpoints.authorize(), c = e?.scope || this.scope, l = c ? c.split(" ") : [];
|
|
581
|
+
l.includes("openid") || l.unshift("openid"), c = l.join(" ");
|
|
582
|
+
let u = new URLSearchParams([
|
|
583
|
+
["client_id", this.clientId],
|
|
584
|
+
["redirect_uri", a],
|
|
585
|
+
["state", t],
|
|
586
|
+
["response_mode", this.responseMode],
|
|
587
|
+
["response_type", this.responseType],
|
|
588
|
+
["scope", c]
|
|
589
|
+
]);
|
|
590
|
+
if (this.#n && u.append("nonce", n), e?.prompt && u.append("prompt", e.prompt), typeof e?.maxAge == "number" && u.append("max_age", e.maxAge.toString()), e?.loginHint && u.append("login_hint", e.loginHint), e?.idpHint && u.append("kc_idp_hint", e.idpHint), e?.action && e.action !== "register" && u.append("kc_action", e.action), e?.locale && u.append("ui_locales", e.locale), e?.acr && u.append("claims", f(e.acr)), e?.acrValues && u.append("acr_values", e.acrValues), this.pkceMethod) try {
|
|
591
|
+
let e = p(96), t = await m(this.pkceMethod, e);
|
|
592
|
+
o.pkceCodeVerifier = e, u.append("code_challenge", t), u.append("code_challenge_method", this.pkceMethod);
|
|
593
|
+
} catch (e) {
|
|
594
|
+
throw Error("Failed to generate PKCE challenge.", { cause: e });
|
|
595
|
+
}
|
|
596
|
+
return this.#r.add(o), `${s}?${u.toString()}`;
|
|
597
|
+
}
|
|
598
|
+
async logout(e) {
|
|
599
|
+
return await this.#s?.flush(), this.#t.logout(e);
|
|
600
|
+
}
|
|
601
|
+
createLogoutUrl(e) {
|
|
602
|
+
let t = e?.logoutMethod ?? this.logoutMethod, n = this.endpoints.logout();
|
|
603
|
+
if (t === "POST") return n;
|
|
604
|
+
let r = new URLSearchParams([["client_id", this.clientId], ["post_logout_redirect_uri", this.#t.redirectUri(e)]]);
|
|
605
|
+
return this.idToken && r.append("id_token_hint", this.idToken), `${n}?${r.toString()}`;
|
|
606
|
+
}
|
|
607
|
+
register(e) {
|
|
608
|
+
return this.#t.register(e);
|
|
609
|
+
}
|
|
610
|
+
createRegisterUrl(e) {
|
|
611
|
+
return this.createLoginUrl({
|
|
612
|
+
...e,
|
|
613
|
+
action: "register"
|
|
614
|
+
});
|
|
615
|
+
}
|
|
616
|
+
createAccountUrl(e) {
|
|
617
|
+
let t = this.#N();
|
|
618
|
+
if (!t) throw Error("Unable to create account URL, make sure the adapter is not configured using a generic OIDC provider.");
|
|
619
|
+
return `${t}/account?${new URLSearchParams([["referrer", this.clientId], ["referrer_uri", this.#t.redirectUri(e)]]).toString()}`;
|
|
620
|
+
}
|
|
621
|
+
accountManagement() {
|
|
622
|
+
return this.#t.accountManagement();
|
|
623
|
+
}
|
|
624
|
+
hasRealmRole(e) {
|
|
625
|
+
let t = this.realmAccess;
|
|
626
|
+
return !!t && t.roles.indexOf(e) >= 0;
|
|
627
|
+
}
|
|
628
|
+
hasResourceRole(e, t) {
|
|
629
|
+
if (!this.resourceAccess) return !1;
|
|
630
|
+
let n = this.resourceAccess[t || this.clientId];
|
|
631
|
+
return !!n && n.roles.indexOf(e) >= 0;
|
|
632
|
+
}
|
|
633
|
+
async loadUserProfile() {
|
|
634
|
+
let e = this.#N();
|
|
635
|
+
if (!e) throw Error("Unable to load user profile, make sure the adapter is not configured using a generic OIDC provider.");
|
|
636
|
+
let t = await A(`${e}/account`, { headers: [M(this.token)] });
|
|
637
|
+
return this.profile = t;
|
|
638
|
+
}
|
|
639
|
+
async loadUserInfo() {
|
|
640
|
+
let e = await A(this.endpoints.userinfo(), { headers: [M(this.token)] });
|
|
641
|
+
return this.userInfo = e;
|
|
642
|
+
}
|
|
643
|
+
isTokenExpired(e) {
|
|
644
|
+
if (!this.tokenParsed || !this.refreshToken && this.flow !== "implicit") throw Error("Not authenticated");
|
|
645
|
+
if (this.timeSkew == null) return this.#i("[KEYCLOAK] Unable to determine if token is expired as timeskew is not set"), !0;
|
|
646
|
+
if (typeof this.tokenParsed.exp != "number") return !1;
|
|
647
|
+
let t = this.tokenParsed.exp - Math.ceil((/* @__PURE__ */ new Date()).getTime() / 1e3) + this.timeSkew;
|
|
648
|
+
if (e) {
|
|
649
|
+
if (isNaN(e)) throw Error("Invalid minValidity");
|
|
650
|
+
t -= e;
|
|
651
|
+
}
|
|
652
|
+
return t < 0;
|
|
653
|
+
}
|
|
654
|
+
async updateToken(e) {
|
|
655
|
+
if (!this.refreshToken) throw Error("Unable to update token, no refresh token available.");
|
|
656
|
+
e ||= 5, this.#o.enable && await this.#b();
|
|
657
|
+
let t = !1;
|
|
658
|
+
if (e === -1 ? (t = !0, this.#i("[KEYCLOAK] Refreshing token: forced refresh")) : (!this.tokenParsed || this.isTokenExpired(e)) && (t = !0, this.#i("[KEYCLOAK] Refreshing token: token expired")), !t) return !1;
|
|
659
|
+
let { promise: n, resolve: r, reject: i } = Promise.withResolvers();
|
|
660
|
+
if (this.#e.push({
|
|
661
|
+
resolve: r,
|
|
662
|
+
reject: i
|
|
663
|
+
}), this.#e.length === 1) {
|
|
664
|
+
let e = this.endpoints.token(), t = (/* @__PURE__ */ new Date()).getTime();
|
|
665
|
+
try {
|
|
666
|
+
let n = await this.#j(e, this.refreshToken, this.clientId);
|
|
667
|
+
this.#i("[KEYCLOAK] Token refreshed"), t = (t + (/* @__PURE__ */ new Date()).getTime()) / 2, this.#M(n.access_token, n.refresh_token, n.id_token, t), this.onAuthRefreshSuccess?.();
|
|
668
|
+
for (let e = this.#e.pop(); e != null; e = this.#e.pop()) e.resolve(!0);
|
|
669
|
+
} catch (e) {
|
|
670
|
+
this.#a("[KEYCLOAK] Failed to refresh token"), e instanceof P && e.response.status === 400 && this.clearToken(), this.onAuthRefreshError?.();
|
|
671
|
+
for (let t = this.#e.pop(); t != null; t = this.#e.pop()) t.reject(e);
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
return await n;
|
|
675
|
+
}
|
|
676
|
+
async clearToken() {
|
|
677
|
+
this.token && (await this.#s?.flush(), await this.#s?.init(), this.#M(), this.onAuthLogout?.(), this.loginRequired && await this.login());
|
|
678
|
+
}
|
|
679
|
+
fetch = async (e, t = {}) => {
|
|
680
|
+
let n = this.#s;
|
|
681
|
+
if (n && this.authenticated && this.token) {
|
|
682
|
+
if (new Headers(t.headers).get("Authorization") !== `Bearer ${this.token}`) return fetch(e, t);
|
|
683
|
+
let r = new URL(String(e), document.baseURI), i = r.href, a = r.origin, o = t.method ?? "GET", s = n.getResourceServerNonce(a), c = await this.#k(e, t, i, o, s), l = c.headers.get("DPoP-Nonce");
|
|
684
|
+
if (l && n.updateResourceServerNonce(a, l), c.status === 401 && l) {
|
|
685
|
+
let r = c.headers.get("WWW-Authenticate") ?? "";
|
|
686
|
+
if (r.includes("DPoP") && r.includes("error=\"use_dpop_nonce\"")) {
|
|
687
|
+
let r = await this.#k(e, t, i, o, l), s = r.headers.get("DPoP-Nonce");
|
|
688
|
+
return s && n.updateResourceServerNonce(a, s), r;
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
return c;
|
|
692
|
+
}
|
|
693
|
+
return fetch(e, t);
|
|
694
|
+
};
|
|
695
|
+
async #D(e, t, n, r, i) {
|
|
696
|
+
let a = new URLSearchParams([
|
|
697
|
+
["code", t],
|
|
698
|
+
["grant_type", "authorization_code"],
|
|
699
|
+
["client_id", n],
|
|
700
|
+
["redirect_uri", r]
|
|
701
|
+
]);
|
|
702
|
+
return i && a.append("code_verifier", i), this.#s ? await this.#A(e, a) : await A(e, {
|
|
703
|
+
method: "POST",
|
|
704
|
+
credentials: "include",
|
|
705
|
+
body: a
|
|
706
|
+
});
|
|
707
|
+
}
|
|
708
|
+
#O(e) {
|
|
709
|
+
let t = e.token_type?.toLowerCase();
|
|
710
|
+
if (this.#s && t !== "dpop") {
|
|
711
|
+
if (this.dpopConfig?.mode === "strict") throw Error("DPoP strict mode enabled but server returned non-DPoP token (token_type: " + e.token_type + ")");
|
|
712
|
+
this.#a("[KEYCLOAK] Server returned token_type \"" + e.token_type + "\" instead of \"DPoP\". Disabling DPoP for this session."), this.#s = void 0;
|
|
713
|
+
}
|
|
714
|
+
return e;
|
|
715
|
+
}
|
|
716
|
+
async #k(e, t, n, r, i) {
|
|
717
|
+
let a = this.#s;
|
|
718
|
+
if (!a) throw Error("DPoP provider not initialized");
|
|
719
|
+
let o = await a.generateDPoPProof(n, r, this.token, i), s = new Headers(t.headers);
|
|
720
|
+
return s.set("Authorization", `DPoP ${this.token}`), s.set("DPoP", o), fetch(e, {
|
|
721
|
+
...t,
|
|
722
|
+
headers: s
|
|
723
|
+
});
|
|
724
|
+
}
|
|
725
|
+
async #A(e, t) {
|
|
726
|
+
let n = this.#s;
|
|
727
|
+
if (!n) throw Error("DPoP provider not initialized");
|
|
728
|
+
let r = async (r) => {
|
|
729
|
+
let i = await n.generateDPoPProof(e, "POST", void 0, r), a = new Headers();
|
|
730
|
+
a.set("DPoP", i), a.set("Accept", l);
|
|
731
|
+
let o = await j(e, {
|
|
732
|
+
method: "POST",
|
|
733
|
+
credentials: "include",
|
|
734
|
+
body: t,
|
|
735
|
+
headers: a
|
|
736
|
+
}), s = o.headers.get("DPoP-Nonce");
|
|
737
|
+
s && await n.updateAuthServerNonce(s);
|
|
738
|
+
let c = await o.json();
|
|
739
|
+
return this.#O(c);
|
|
740
|
+
};
|
|
741
|
+
try {
|
|
742
|
+
return await r(await n.getAuthServerNonce());
|
|
743
|
+
} catch (e) {
|
|
744
|
+
if (e instanceof P && e.response.status === 400) {
|
|
745
|
+
let t = e.response.headers.get("DPoP-Nonce");
|
|
746
|
+
if (t && (await e.response.json().catch(() => ({}))).error === "use_dpop_nonce") return await n.updateAuthServerNonce(t), await r(t);
|
|
747
|
+
}
|
|
748
|
+
throw e;
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
async #j(e, t, n) {
|
|
752
|
+
let r = new URLSearchParams([
|
|
753
|
+
["grant_type", "refresh_token"],
|
|
754
|
+
["refresh_token", t],
|
|
755
|
+
["client_id", n]
|
|
756
|
+
]);
|
|
757
|
+
return this.#s ? await this.#A(e, r) : await A(e, {
|
|
758
|
+
method: "POST",
|
|
759
|
+
credentials: "include",
|
|
760
|
+
body: r
|
|
761
|
+
});
|
|
762
|
+
}
|
|
763
|
+
#M(e, t, n, r) {
|
|
764
|
+
if (this.tokenTimeoutHandle &&= (clearTimeout(this.tokenTimeoutHandle), void 0), t ? (this.refreshToken = t, this.refreshTokenParsed = w(t)) : (delete this.refreshToken, delete this.refreshTokenParsed), n ? (this.idToken = n, this.idTokenParsed = w(n)) : (delete this.idToken, delete this.idTokenParsed), e) {
|
|
765
|
+
if (this.token = e, this.tokenParsed = w(e), this.sessionId = this.tokenParsed.sid, this.authenticated = !0, this.subject = this.tokenParsed.sub, this.realmAccess = this.tokenParsed.realm_access, this.resourceAccess = this.tokenParsed.resource_access, r && (this.timeSkew = Math.floor(r / 1e3) - this.tokenParsed.iat), this.timeSkew !== null && (this.#i("[KEYCLOAK] Estimated time difference between browser and server is " + this.timeSkew + " seconds"), this.onTokenExpired)) {
|
|
766
|
+
let e = (this.tokenParsed.exp - (/* @__PURE__ */ new Date()).getTime() / 1e3 + this.timeSkew) * 1e3;
|
|
767
|
+
this.#i("[KEYCLOAK] Token expires in " + Math.round(e / 1e3) + " s"), e <= 0 ? this.onTokenExpired() : this.tokenTimeoutHandle = window.setTimeout(this.onTokenExpired, e);
|
|
768
|
+
}
|
|
769
|
+
} else delete this.token, delete this.tokenParsed, delete this.subject, delete this.realmAccess, delete this.resourceAccess, this.authenticated = !1;
|
|
770
|
+
}
|
|
771
|
+
#N() {
|
|
772
|
+
if (this.authServerUrl !== void 0) return `${N(this.authServerUrl)}/realms/${encodeURIComponent(this.realm)}`;
|
|
773
|
+
}
|
|
774
|
+
#P() {
|
|
775
|
+
return this.issuer ? this.issuer : this.#N();
|
|
776
|
+
}
|
|
777
|
+
#F(e) {
|
|
778
|
+
return (t) => {
|
|
779
|
+
this.enableLogging && e.call(console, t);
|
|
780
|
+
};
|
|
781
|
+
}
|
|
782
|
+
};
|
|
783
|
+
function d() {
|
|
784
|
+
if (typeof crypto > "u" || crypto.randomUUID === void 0) throw Error("Web Crypto API is not available.");
|
|
785
|
+
return crypto.randomUUID();
|
|
786
|
+
}
|
|
787
|
+
function f(e) {
|
|
788
|
+
return JSON.stringify({ id_token: { acr: e } });
|
|
789
|
+
}
|
|
790
|
+
function p(e) {
|
|
791
|
+
return h(e, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");
|
|
792
|
+
}
|
|
793
|
+
async function m(e, t) {
|
|
794
|
+
if (e !== "S256") throw TypeError(`Invalid value for 'pkceMethod', expected 'S256' but got '${e}'.`);
|
|
795
|
+
return S(new Uint8Array(await C(t))).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
|
|
796
|
+
}
|
|
797
|
+
function h(e, t) {
|
|
798
|
+
let n = g(e), r = Array(e);
|
|
799
|
+
for (let i = 0; i < e; i++) r[i] = t.charCodeAt(n[i] % t.length);
|
|
800
|
+
return String.fromCharCode.apply(null, r);
|
|
801
|
+
}
|
|
802
|
+
function g(e) {
|
|
803
|
+
if (typeof crypto > "u" || crypto.getRandomValues === void 0) throw Error("Web Crypto API is not available.");
|
|
804
|
+
return crypto.getRandomValues(new Uint8Array(e));
|
|
805
|
+
}
|
|
806
|
+
function _(e, t, n) {
|
|
807
|
+
let r, i = new Promise(function(e, i) {
|
|
808
|
+
r = window.setTimeout(function() {
|
|
809
|
+
i(Error(n || "Promise is not settled within timeout of " + t + "ms"));
|
|
810
|
+
}, t);
|
|
811
|
+
});
|
|
812
|
+
return Promise.race([e, i]).finally(function() {
|
|
813
|
+
clearTimeout(r);
|
|
814
|
+
});
|
|
815
|
+
}
|
|
816
|
+
function v() {
|
|
817
|
+
try {
|
|
818
|
+
return new b();
|
|
819
|
+
} catch {
|
|
820
|
+
return new x();
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
var y = "kc-callback-", b = class {
|
|
824
|
+
constructor() {
|
|
825
|
+
globalThis.localStorage.setItem("kc-test", "test"), globalThis.localStorage.removeItem("kc-test");
|
|
826
|
+
}
|
|
827
|
+
get(e) {
|
|
828
|
+
if (!e) return null;
|
|
829
|
+
this.#e();
|
|
830
|
+
let t = y + e, n = globalThis.localStorage.getItem(t);
|
|
831
|
+
return n ? (globalThis.localStorage.removeItem(t), JSON.parse(n)) : null;
|
|
832
|
+
}
|
|
833
|
+
add(e) {
|
|
834
|
+
this.#e();
|
|
835
|
+
let t = y + e.state, n = JSON.stringify({
|
|
836
|
+
...e,
|
|
837
|
+
expires: Date.now() + 36e5
|
|
838
|
+
});
|
|
839
|
+
try {
|
|
840
|
+
globalThis.localStorage.setItem(t, n);
|
|
841
|
+
} catch {
|
|
842
|
+
this.#t(), globalThis.localStorage.setItem(t, n);
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
#e() {
|
|
846
|
+
let e = Date.now();
|
|
847
|
+
for (let [t, n] of this.#n()) {
|
|
848
|
+
let r = this.#r(n);
|
|
849
|
+
(r === null || r < e) && globalThis.localStorage.removeItem(t);
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
#t() {
|
|
853
|
+
for (let [e] of this.#n()) globalThis.localStorage.removeItem(e);
|
|
854
|
+
}
|
|
855
|
+
#n() {
|
|
856
|
+
return Object.entries(globalThis.localStorage).filter(([e]) => e.startsWith(y));
|
|
857
|
+
}
|
|
858
|
+
#r(e) {
|
|
859
|
+
let t;
|
|
860
|
+
try {
|
|
861
|
+
t = JSON.parse(e);
|
|
862
|
+
} catch {
|
|
863
|
+
return null;
|
|
864
|
+
}
|
|
865
|
+
return D(t) && "expires" in t && typeof t.expires == "number" ? t.expires : null;
|
|
866
|
+
}
|
|
867
|
+
}, x = class {
|
|
868
|
+
get(e) {
|
|
869
|
+
if (!e) return null;
|
|
870
|
+
let t = this.#e(y + e);
|
|
871
|
+
return this.#t(y + e, "", this.#n(-100)), t ? JSON.parse(t) : null;
|
|
872
|
+
}
|
|
873
|
+
add(e) {
|
|
874
|
+
this.#t(y + e.state, JSON.stringify(e), this.#n(60));
|
|
875
|
+
}
|
|
876
|
+
#e(e) {
|
|
877
|
+
let t = e + "=", n = document.cookie.split(";");
|
|
878
|
+
for (let e = 0; e < n.length; e++) {
|
|
879
|
+
let r = n[e];
|
|
880
|
+
for (; r.charAt(0) === " ";) r = r.substring(1);
|
|
881
|
+
if (r.indexOf(t) === 0) return r.substring(t.length, r.length);
|
|
882
|
+
}
|
|
883
|
+
return "";
|
|
884
|
+
}
|
|
885
|
+
#t(e, t, n) {
|
|
886
|
+
let r = e + "=" + t + "; expires=" + n.toUTCString() + "; ";
|
|
887
|
+
document.cookie = r;
|
|
888
|
+
}
|
|
889
|
+
#n(e) {
|
|
890
|
+
let t = /* @__PURE__ */ new Date();
|
|
891
|
+
return t.setTime(t.getTime() + e * 60 * 1e3), t;
|
|
892
|
+
}
|
|
893
|
+
};
|
|
894
|
+
function S(e) {
|
|
895
|
+
let t = String.fromCodePoint(...e);
|
|
896
|
+
return btoa(t);
|
|
897
|
+
}
|
|
898
|
+
async function C(e) {
|
|
899
|
+
let t = new TextEncoder().encode(e);
|
|
900
|
+
if (typeof crypto > "u" || crypto.subtle === void 0) throw Error("Web Crypto API is not available.");
|
|
901
|
+
return await crypto.subtle.digest("SHA-256", t);
|
|
902
|
+
}
|
|
903
|
+
function w(e) {
|
|
904
|
+
let [, t] = e.split(".");
|
|
905
|
+
if (typeof t != "string") throw Error("Unable to decode token, payload not found.");
|
|
906
|
+
let n;
|
|
907
|
+
try {
|
|
908
|
+
n = T(t);
|
|
909
|
+
} catch (e) {
|
|
910
|
+
throw Error("Unable to decode token, payload is not a valid Base64URL value.", { cause: e });
|
|
911
|
+
}
|
|
912
|
+
try {
|
|
913
|
+
return JSON.parse(n);
|
|
914
|
+
} catch (e) {
|
|
915
|
+
throw Error("Unable to decode token, payload is not a valid JSON value.", { cause: e });
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
function T(e) {
|
|
919
|
+
let t = e.replaceAll("-", "+").replaceAll("_", "/");
|
|
920
|
+
switch (t.length % 4) {
|
|
921
|
+
case 0: break;
|
|
922
|
+
case 2:
|
|
923
|
+
t += "==";
|
|
924
|
+
break;
|
|
925
|
+
case 3:
|
|
926
|
+
t += "=";
|
|
927
|
+
break;
|
|
928
|
+
default: throw Error("Input is not of the correct length.");
|
|
929
|
+
}
|
|
930
|
+
try {
|
|
931
|
+
return E(t);
|
|
932
|
+
} catch {
|
|
933
|
+
return atob(t);
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
function E(e) {
|
|
937
|
+
return decodeURIComponent(atob(e).replace(/(.)/g, (e, t) => {
|
|
938
|
+
let n = t.charCodeAt(0).toString(16).toUpperCase();
|
|
939
|
+
return n.length < 2 && (n = "0" + n), "%" + n;
|
|
940
|
+
}));
|
|
941
|
+
}
|
|
942
|
+
function D(e) {
|
|
943
|
+
return typeof e == "object" && !!e;
|
|
944
|
+
}
|
|
945
|
+
async function O(e) {
|
|
946
|
+
return await A(e);
|
|
947
|
+
}
|
|
948
|
+
async function k(e) {
|
|
949
|
+
return await A(e);
|
|
950
|
+
}
|
|
951
|
+
async function A(e, t = {}) {
|
|
952
|
+
let n = new Headers(t.headers);
|
|
953
|
+
return n.set("Accept", l), await (await j(e, {
|
|
954
|
+
...t,
|
|
955
|
+
headers: n
|
|
956
|
+
})).json();
|
|
957
|
+
}
|
|
958
|
+
async function j(e, t) {
|
|
959
|
+
let n = await fetch(e, t);
|
|
960
|
+
if (!n.ok) throw new P("Server responded with an invalid status.", { response: n });
|
|
961
|
+
return n;
|
|
962
|
+
}
|
|
963
|
+
function M(e) {
|
|
964
|
+
if (!e) throw Error("Unable to build authorization header, token is not set, make sure the user is authenticated.");
|
|
965
|
+
return ["Authorization", `bearer ${e}`];
|
|
966
|
+
}
|
|
967
|
+
function N(e) {
|
|
968
|
+
return e.endsWith("/") ? e.slice(0, -1) : e;
|
|
969
|
+
}
|
|
970
|
+
var P = class extends Error {
|
|
971
|
+
response;
|
|
972
|
+
constructor(e, t) {
|
|
973
|
+
super(e, t), this.response = t.response;
|
|
974
|
+
}
|
|
975
|
+
}, F = (e) => new Promise((t) => setTimeout(t, e)), I, L = I = class extends i {
|
|
976
|
+
get code() {
|
|
977
|
+
return this.keycloak?.idToken ?? null;
|
|
978
|
+
}
|
|
979
|
+
keycloak = null;
|
|
980
|
+
constructor() {
|
|
981
|
+
super(new a({ authenticated: !1 }));
|
|
982
|
+
}
|
|
983
|
+
static createRevisedSequences(n, r, i = n) {
|
|
984
|
+
let a = t.parseFasta(n);
|
|
985
|
+
if (a.error) throw Error(a.error);
|
|
986
|
+
let o = t.parseFasta(i);
|
|
987
|
+
if (o.error) throw Error(o.error);
|
|
988
|
+
let s = (e) => Object.values(r.recordsByColumn).some((t) => t[e]), c = new Set(a.sequences.map((e) => e.id)), l = new Set(o.sequences.map((e) => e.id)), u = a.sequences.filter((e) => s(e.id)), d = new Set(u.map((e) => e.id)), f = r.records.filter((e) => Object.values(e).some((e) => d.has(e)));
|
|
989
|
+
return {
|
|
990
|
+
addedSequencesWithoutMetadata: a.sequences.filter((e) => !l.has(e.id) && !s(e.id)).map((e) => e.id),
|
|
991
|
+
fasta: u.map((e) => `>${e.header}\n${e.sequence}`).join("\n"),
|
|
992
|
+
groupId: e.getGroupId(r),
|
|
993
|
+
metadata: [r.columns.join(" "), ...f.map((e) => r.columns.map((t) => e[t] ?? "").join(" "))].join("\n"),
|
|
994
|
+
removedSequences: o.sequences.filter((e) => !c.has(e.id)).map((e) => e.id),
|
|
995
|
+
sequenceCount: u.length
|
|
996
|
+
};
|
|
997
|
+
}
|
|
998
|
+
clear() {
|
|
999
|
+
this.keycloak = null, this.next({ authenticated: !1 });
|
|
1000
|
+
}
|
|
1001
|
+
async init(e) {
|
|
1002
|
+
let { authClientId: t, authEndpoint: n, authRealm: r } = e, i = !1, a = null;
|
|
1003
|
+
try {
|
|
1004
|
+
let e = new u({
|
|
1005
|
+
clientId: t,
|
|
1006
|
+
realm: r,
|
|
1007
|
+
url: n
|
|
1008
|
+
});
|
|
1009
|
+
a = e, this.keycloak = e, e.onAuthLogout = () => {
|
|
1010
|
+
this.next({ authenticated: !1 });
|
|
1011
|
+
}, e.onTokenExpired = () => {
|
|
1012
|
+
this.keycloak === e && e.updateToken(-1).catch((t) => {
|
|
1013
|
+
console.error("Failed to refresh Keycloak token:", t), e.clearToken();
|
|
1014
|
+
});
|
|
1015
|
+
}, i = await e.init({
|
|
1016
|
+
enableLogging: !0,
|
|
1017
|
+
onLoad: "check-sso",
|
|
1018
|
+
pkceMethod: "S256",
|
|
1019
|
+
silentCheckSsoFallback: !1,
|
|
1020
|
+
silentCheckSsoRedirectUri: `${window.location.origin}/silent-check-sso.html`,
|
|
1021
|
+
useNonce: !0
|
|
1022
|
+
});
|
|
1023
|
+
} catch (e) {
|
|
1024
|
+
console.error("Failed to initialize Keycloak:", e);
|
|
1025
|
+
}
|
|
1026
|
+
a && this.keycloak !== a || this.next({ authenticated: i });
|
|
1027
|
+
}
|
|
1028
|
+
async reviseSequences(e, t, n, r, i) {
|
|
1029
|
+
let a = I.createRevisedSequences(e, t), o = new FormData();
|
|
1030
|
+
o.append("sequenceFile", new File([a.fasta], "sequences.fasta", { type: "text/plain" })), o.append("metadataFile", new File([a.metadata], "metadata.tsv", { type: "text/tab-separated-values" }));
|
|
1031
|
+
let s = `${n.replace(/\/$/, "")}/${encodeURIComponent(r)}/revise?${new URLSearchParams({
|
|
1032
|
+
dataUseTermsType: "OPEN",
|
|
1033
|
+
groupId: a.groupId
|
|
1034
|
+
})}`, c = await this.keycloak?.fetch(s, {
|
|
1035
|
+
body: o,
|
|
1036
|
+
method: "POST",
|
|
1037
|
+
signal: i.signal
|
|
1038
|
+
});
|
|
1039
|
+
if (!c?.ok) {
|
|
1040
|
+
if (c?.status === 401 || c?.status === 403) throw Error("Your Loculus session is no longer valid. Please log in to Loculus again in a separate tab, then try uploading again.");
|
|
1041
|
+
let e = await c?.text();
|
|
1042
|
+
throw Error(`Failed to revise sequences: ${e}`);
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
};
|
|
1046
|
+
L = I = n([r()], L);
|
|
1047
|
+
export { L as t };
|
|
1048
|
+
|
|
1049
|
+
//# sourceMappingURL=LoculusService.js.map
|