@enterprisestandard/react 0.0.5 → 0.0.6

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/index.js CHANGED
@@ -1,1366 +1 @@
1
- // src/iam.ts
2
- async function iam(config) {
3
- return {};
4
- }
5
-
6
- // src/oidc-schema.ts
7
- function oidcCallbackSchema(vendor) {
8
- return {
9
- "~standard": {
10
- version: 1,
11
- vendor,
12
- validate: (value) => {
13
- if (typeof value !== "object" || value === null) {
14
- return {
15
- issues: [
16
- {
17
- message: "Expected an object"
18
- }
19
- ]
20
- };
21
- }
22
- const params = value;
23
- const issues = [];
24
- const result = {};
25
- if ("code" in params) {
26
- if (typeof params.code === "string") {
27
- result.code = params.code;
28
- } else {
29
- issues.push({
30
- message: "code must be a string",
31
- path: ["code"]
32
- });
33
- }
34
- } else if (!("error" in params)) {
35
- issues.push({
36
- message: "code is required",
37
- path: ["code"]
38
- });
39
- }
40
- if ("state" in params) {
41
- if (typeof params.state === "string" || params.state === undefined) {
42
- result.state = params.state;
43
- } else {
44
- issues.push({
45
- message: "state must be a string",
46
- path: ["state"]
47
- });
48
- }
49
- }
50
- if ("session_state" in params) {
51
- if (typeof params.session_state === "string" || params.session_state === undefined) {
52
- result.session_state = params.session_state;
53
- } else {
54
- issues.push({
55
- message: "session_state must be a string",
56
- path: ["session_state"]
57
- });
58
- }
59
- }
60
- if ("error" in params) {
61
- if (typeof params.error === "string") {
62
- result.error = params.error;
63
- } else {
64
- issues.push({
65
- message: "error must be a string",
66
- path: ["error"]
67
- });
68
- }
69
- if ("error_description" in params) {
70
- if (typeof params.error_description === "string" || params.error_description === undefined) {
71
- result.error_description = params.error_description;
72
- } else {
73
- issues.push({
74
- message: "error_description must be a string",
75
- path: ["error_description"]
76
- });
77
- }
78
- }
79
- if ("error_uri" in params) {
80
- if (typeof params.error_uri === "string" || params.error_uri === undefined) {
81
- result.error_uri = params.error_uri;
82
- } else {
83
- issues.push({
84
- message: "error_uri must be a string",
85
- path: ["error_uri"]
86
- });
87
- }
88
- }
89
- }
90
- if ("iss" in params) {
91
- if (typeof params.iss === "string" || params.iss === undefined) {
92
- result.iss = params.iss;
93
- } else {
94
- issues.push({
95
- message: "iss must be a string",
96
- path: ["iss"]
97
- });
98
- }
99
- }
100
- if (issues.length > 0) {
101
- return { issues };
102
- }
103
- return { value: result };
104
- }
105
- }
106
- };
107
- }
108
- function tokenResponseSchema(vendor) {
109
- return {
110
- "~standard": {
111
- version: 1,
112
- vendor,
113
- validate: (value) => {
114
- if (typeof value !== "object" || value === null) {
115
- return {
116
- issues: [
117
- {
118
- message: "Expected an object"
119
- }
120
- ]
121
- };
122
- }
123
- const response = value;
124
- const issues = [];
125
- const result = {};
126
- if ("access_token" in response) {
127
- if (typeof response.access_token === "string") {
128
- result.access_token = response.access_token;
129
- } else {
130
- issues.push({
131
- message: "access_token must be a string",
132
- path: ["access_token"]
133
- });
134
- }
135
- } else {
136
- issues.push({
137
- message: "access_token is required",
138
- path: ["access_token"]
139
- });
140
- }
141
- if ("id_token" in response) {
142
- if (typeof response.id_token === "string") {
143
- result.id_token = response.id_token;
144
- } else {
145
- issues.push({
146
- message: "id_token must be a string",
147
- path: ["id_token"]
148
- });
149
- }
150
- } else {
151
- issues.push({
152
- message: "id_token is required",
153
- path: ["id_token"]
154
- });
155
- }
156
- if ("token_type" in response) {
157
- if (typeof response.token_type === "string") {
158
- result.token_type = response.token_type;
159
- } else {
160
- issues.push({
161
- message: "token_type must be a string",
162
- path: ["token_type"]
163
- });
164
- }
165
- } else {
166
- issues.push({
167
- message: "token_type is required",
168
- path: ["token_type"]
169
- });
170
- }
171
- if ("refresh_token" in response) {
172
- if (typeof response.refresh_token === "string" || response.refresh_token === undefined) {
173
- result.refresh_token = response.refresh_token;
174
- } else {
175
- issues.push({
176
- message: "refresh_token must be a string",
177
- path: ["refresh_token"]
178
- });
179
- }
180
- }
181
- if ("scope" in response) {
182
- if (typeof response.scope === "string" || response.scope === undefined) {
183
- result.scope = response.scope;
184
- } else {
185
- issues.push({
186
- message: "scope must be a string",
187
- path: ["scope"]
188
- });
189
- }
190
- }
191
- if ("session_state" in response) {
192
- if (typeof response.session_state === "string" || response.session_state === undefined) {
193
- result.session_state = response.session_state;
194
- } else {
195
- issues.push({
196
- message: "session_state must be a string",
197
- path: ["session_state"]
198
- });
199
- }
200
- }
201
- if ("expires" in response) {
202
- if (typeof response.expires === "string" || response.expires === undefined) {
203
- result.expires = response.expires;
204
- } else {
205
- issues.push({
206
- message: "expires must be a string",
207
- path: ["expires"]
208
- });
209
- }
210
- }
211
- if ("expires_in" in response) {
212
- if (typeof response.expires_in === "number" || response.expires_in === undefined) {
213
- result.expires_in = response.expires_in;
214
- } else {
215
- issues.push({
216
- message: "expires_in must be a number",
217
- path: ["expires_in"]
218
- });
219
- }
220
- }
221
- if ("refresh_expires_in" in response) {
222
- if (typeof response.refresh_expires_in === "number" || response.refresh_expires_in === undefined) {
223
- result.refresh_expires_in = response.refresh_expires_in;
224
- } else {
225
- issues.push({
226
- message: "refresh_expires_in must be a number",
227
- path: ["refresh_expires_in"]
228
- });
229
- }
230
- }
231
- if (issues.length > 0) {
232
- return { issues };
233
- }
234
- return { value: result };
235
- }
236
- }
237
- };
238
- }
239
- function idTokenClaimsSchema(vendor) {
240
- return {
241
- "~standard": {
242
- version: 1,
243
- vendor,
244
- validate: (value) => {
245
- if (typeof value !== "object" || value === null) {
246
- return {
247
- issues: [
248
- {
249
- message: "Expected an object"
250
- }
251
- ]
252
- };
253
- }
254
- const claims = value;
255
- const issues = [];
256
- const result = { ...claims };
257
- const stringFields = ["iss", "aud", "sub", "sid", "name", "email", "preferred_username", "picture"];
258
- for (const field of stringFields) {
259
- if (field in claims && claims[field] !== undefined) {
260
- if (typeof claims[field] !== "string") {
261
- issues.push({
262
- message: `${field} must be a string`,
263
- path: [field]
264
- });
265
- }
266
- }
267
- }
268
- const numberFields = ["exp", "iat"];
269
- for (const field of numberFields) {
270
- if (field in claims && claims[field] !== undefined) {
271
- if (typeof claims[field] !== "number") {
272
- issues.push({
273
- message: `${field} must be a number`,
274
- path: [field]
275
- });
276
- }
277
- }
278
- }
279
- if (issues.length > 0) {
280
- return { issues };
281
- }
282
- return { value: result };
283
- }
284
- }
285
- };
286
- }
287
-
288
- // src/utils.ts
289
- var defaultInstance;
290
- function must(value, message = "Assertion failed. Required value is null or undefined.") {
291
- if (value === undefined || value === null) {
292
- throw new Error(message);
293
- }
294
- return value;
295
- }
296
- function setDefaultInstance(es) {
297
- defaultInstance = es;
298
- }
299
- function getDefaultInstance() {
300
- return defaultInstance;
301
- }
302
- function getES(es) {
303
- if (es)
304
- return es;
305
- if (defaultInstance)
306
- return defaultInstance;
307
- throw new Error(`TODO standardize the error message when there isn't a default EntepriseStandard`);
308
- }
309
-
310
- // src/sso.ts
311
- var jwksCache = new Map;
312
- function sso(config) {
313
- const configWithDefaults = {
314
- ...config,
315
- authority: must(config.authority, "Missing 'authority' from SSO Config"),
316
- token_url: must(config.token_url, "Missing 'token_url' from SSO Config"),
317
- authorization_url: must(config.authorization_url, "Missing 'authorization_url' from SSO Config"),
318
- client_id: must(config.client_id, "Missing 'client_id' from SSO Config"),
319
- redirect_uri: must(config.redirect_uri, "Missing 'redirect_uri' from SSO Config"),
320
- scope: must(config.scope, "Missing 'scope' from SSO Config"),
321
- response_type: config.response_type ?? "code",
322
- cookies_secure: config.cookies_secure !== undefined ? config.cookies_secure : true,
323
- cookies_same_site: config.cookies_same_site !== undefined ? config.cookies_same_site : "Strict",
324
- cookies_prefix: config.cookies_prefix ?? `es.sso.${config.client_id}`,
325
- cookies_path: config.cookies_path ?? "/"
326
- };
327
- async function getUser(request) {
328
- if (!configWithDefaults) {
329
- console.error("SSO Manager not initialized");
330
- return;
331
- }
332
- try {
333
- const { tokens } = await getTokenFromCookies(request);
334
- if (!tokens)
335
- return;
336
- return await parseUser(tokens);
337
- } catch (error) {
338
- console.error("Error parsing user from cookies:", error);
339
- return;
340
- }
341
- }
342
- async function getRequiredUser(request) {
343
- const user = await getUser(request);
344
- if (user)
345
- return user;
346
- throw new Response("Unauthorized", {
347
- status: 401,
348
- statusText: "Unauthorized"
349
- });
350
- }
351
- async function initiateLogin({ landingUrl, errorUrl }) {
352
- if (!configWithDefaults) {
353
- console.error("SSO Manager not initialized");
354
- return Promise.resolve(new Response("SSO Manager not initialized", { status: 503 }));
355
- }
356
- const state = generateRandomString();
357
- const codeVerifier = generateRandomString(64);
358
- const url = new URL(configWithDefaults.authorization_url);
359
- url.searchParams.append("client_id", configWithDefaults.client_id);
360
- url.searchParams.append("redirect_uri", configWithDefaults.redirect_uri);
361
- url.searchParams.append("response_type", "code");
362
- url.searchParams.append("scope", configWithDefaults.scope);
363
- url.searchParams.append("state", state);
364
- const codeChallenge = await pkceChallengeFromVerifier(codeVerifier);
365
- url.searchParams.append("code_challenge", codeChallenge);
366
- url.searchParams.append("code_challenge_method", "S256");
367
- const val = {
368
- state,
369
- codeVerifier,
370
- landingUrl,
371
- errorUrl
372
- };
373
- return new Response("Redirecting to SSO Provider", {
374
- status: 302,
375
- headers: {
376
- Location: url.toString(),
377
- "Set-Cookie": createCookie("state", val, 86400)
378
- }
379
- });
380
- }
381
- async function logout(request, _config) {
382
- try {
383
- const refreshToken2 = getCookie("refresh", request);
384
- if (refreshToken2) {
385
- await revokeToken(refreshToken2);
386
- }
387
- } catch (error) {
388
- console.warn("Failed to revoke token:", error);
389
- }
390
- if (config.session_store) {
391
- try {
392
- const user = await getUser(request);
393
- if (user?.sso?.profile.sid) {
394
- const sid = user.sso.profile.sid;
395
- await config.session_store.delete(sid);
396
- console.log(`Session ${sid} deleted from store`);
397
- }
398
- } catch (error) {
399
- console.warn("Failed to delete session:", error);
400
- }
401
- }
402
- const clearHeaders = [
403
- ["Set-Cookie", clearCookie("access")],
404
- ["Set-Cookie", clearCookie("id")],
405
- ["Set-Cookie", clearCookie("refresh")],
406
- ["Set-Cookie", clearCookie("control")],
407
- ["Set-Cookie", clearCookie("state")]
408
- ];
409
- const url = new URL(request.url);
410
- const redirectTo = url.searchParams.get("redirect");
411
- if (redirectTo) {
412
- return new Response("Logged out", {
413
- status: 302,
414
- headers: [["Location", redirectTo], ...clearHeaders]
415
- });
416
- }
417
- const accept = request.headers.get("accept");
418
- const isAjax = accept?.includes("application/json") || accept?.includes("text/javascript");
419
- if (isAjax) {
420
- return new Response(JSON.stringify({ success: true, message: "Logged out" }), {
421
- status: 200,
422
- headers: [["Content-Type", "application/json"], ...clearHeaders]
423
- });
424
- } else {
425
- return new Response(`
426
- <!DOCTYPE html><html lang="en"><body>
427
- <h1>Logout Complete</h1>
428
- <div style="display: none">
429
- It is not recommended to show the default logout page. Include '?redirect=/someHomePage' or logout asynchronously.
430
- Check the <a href="https://EnterpriseStandard.com/sso#logout">Enterprise Standard Packages</a> for more information.
431
- </div>
432
- </body></html>
433
- `, {
434
- status: 200,
435
- headers: [["Content-Type", "text/html"], ...clearHeaders]
436
- });
437
- }
438
- }
439
- async function logoutBackChannel(request) {
440
- if (!configWithDefaults.session_store) {
441
- return new Response("Back-Channel Logout requires session_store configuration", {
442
- status: 400,
443
- statusText: "Bad Request"
444
- });
445
- }
446
- try {
447
- const contentType = request.headers.get("content-type");
448
- if (!contentType || !contentType.includes("application/x-www-form-urlencoded")) {
449
- return new Response("Invalid Content-Type, expected application/x-www-form-urlencoded", {
450
- status: 400
451
- });
452
- }
453
- const body = await request.text();
454
- const params = new URLSearchParams(body);
455
- const logoutToken = params.get("logout_token");
456
- if (!logoutToken) {
457
- return new Response("Missing logout_token parameter", { status: 400 });
458
- }
459
- const claims = await parseJwt(logoutToken);
460
- const sid = claims.sid;
461
- if (!sid) {
462
- console.warn("Back-Channel Logout: logout_token missing sid claim");
463
- return new Response("Invalid logout_token: missing sid claim", { status: 400 });
464
- }
465
- await configWithDefaults.session_store.delete(sid);
466
- console.log(`Back-Channel Logout: successfully deleted session ${sid}`);
467
- return new Response("OK", { status: 200 });
468
- } catch (error) {
469
- console.error("Error during back-channel logout:", error);
470
- return new Response("Internal Server Error", { status: 500 });
471
- }
472
- }
473
- async function callbackHandler(request, validation) {
474
- if (!configWithDefaults) {
475
- console.error("SSO Manager not initialized");
476
- return Promise.resolve(new Response("SSO Manager not initialized", { status: 503 }));
477
- }
478
- const url = new URL(request.url);
479
- const params = new URLSearchParams(url.search);
480
- const callbackParamsValidator = validation?.callbackParams ?? oidcCallbackSchema("builtin");
481
- const paramsObject = Object.fromEntries(params.entries());
482
- const paramsResult = await callbackParamsValidator["~standard"].validate(paramsObject);
483
- if ("issues" in paramsResult) {
484
- return new Response(JSON.stringify({
485
- error: "validation_failed",
486
- message: "OIDC callback parameters validation failed",
487
- issues: paramsResult.issues?.map((i) => ({
488
- path: i.path?.join("."),
489
- message: i.message
490
- }))
491
- }), {
492
- status: 400,
493
- headers: { "Content-Type": "application/json" }
494
- });
495
- }
496
- const { code: codeFromUrl, state: stateFromUrl } = paramsResult.value;
497
- try {
498
- const cookie = getCookie("state", request, true);
499
- const { codeVerifier, state, landingUrl } = cookie ?? {};
500
- must(codeVerifier, 'OIDC "codeVerifier" was not present in cookies, ensure that the SSO login was initiated correctly');
501
- must(state, 'OIDC "stateVerifier" was not present in cookies, ensure that the SSO login was initiated correctly');
502
- must(landingUrl, 'OIDC "landingUrl" was not present in cookies');
503
- if (stateFromUrl !== state) {
504
- throw new Error('SSO State Verifier failed, the "state" request parameter does not equal the "state" in the SSO cookie');
505
- }
506
- const tokenResponse = await exchangeCodeForToken(codeFromUrl, codeVerifier, validation);
507
- const user = await parseUser(tokenResponse, validation);
508
- if (config.session_store) {
509
- try {
510
- const sid = user.sso.profile.sid;
511
- const sub = user.id;
512
- if (sid && sub) {
513
- const session = {
514
- sid,
515
- sub,
516
- createdAt: new Date,
517
- lastActivityAt: new Date
518
- };
519
- await config.session_store.create(session);
520
- } else {
521
- console.warn("Session creation skipped: missing sid or sub in ID token claims");
522
- }
523
- } catch (error) {
524
- console.warn("Failed to create session:", error);
525
- }
526
- }
527
- return new Response("Authentication successful, redirecting", {
528
- status: 302,
529
- headers: [
530
- ["Location", landingUrl],
531
- ["Set-Cookie", clearCookie("state")],
532
- ...createJwtCookies(tokenResponse, user.sso.expires)
533
- ]
534
- });
535
- } catch (error) {
536
- console.error("Error during sign-in callback:", error);
537
- try {
538
- const cookie = getCookie("state", request, true);
539
- const { errorUrl } = cookie ?? {};
540
- if (errorUrl) {
541
- return new Response("Redirecting to error url", {
542
- status: 302,
543
- headers: [["Location", errorUrl]]
544
- });
545
- }
546
- } catch (_err) {
547
- console.warn("Error parsing the errorUrl from the OIDC cookie");
548
- }
549
- console.warn("No error page was found in the cookies. The user will be shown a default error page.");
550
- return new Response("An error occurred during authentication, please return to the application homepage and try again.", {
551
- status: 500
552
- });
553
- }
554
- }
555
- async function parseUser(token, validation) {
556
- if (!configWithDefaults)
557
- throw new Error("SSO Manager not initialized");
558
- const idToken = await parseJwt(token.id_token, validation);
559
- const expiresIn = Number(token.refresh_expires_in ?? token.expires_in ?? 3600);
560
- const expires = token.expires ? new Date(token.expires) : new Date(Date.now() + expiresIn * 1000);
561
- return {
562
- id: idToken.sub,
563
- userName: idToken.preferred_username || "",
564
- name: idToken.name || "",
565
- email: idToken.email || "",
566
- emails: [
567
- {
568
- value: idToken.email || "",
569
- primary: true
570
- }
571
- ],
572
- avatarUrl: idToken.picture,
573
- sso: {
574
- profile: {
575
- ...idToken,
576
- iss: idToken.iss || configWithDefaults.authority,
577
- aud: idToken.aud || configWithDefaults.client_id
578
- },
579
- tenant: {
580
- id: idToken.idp || idToken.iss || configWithDefaults.authority,
581
- name: idToken.iss || configWithDefaults.authority
582
- },
583
- scope: token.scope,
584
- tokenType: token.token_type,
585
- sessionState: token.session_state,
586
- expires
587
- }
588
- };
589
- }
590
- async function exchangeCodeForToken(code, codeVerifier, validation) {
591
- if (!configWithDefaults)
592
- throw new Error("SSO Manager not initialized");
593
- const tokenUrl = configWithDefaults.token_url;
594
- const body = new URLSearchParams;
595
- body.append("grant_type", "authorization_code");
596
- body.append("code", code);
597
- body.append("redirect_uri", configWithDefaults.redirect_uri);
598
- body.append("client_id", configWithDefaults.client_id);
599
- body.append("code_verifier", codeVerifier);
600
- try {
601
- const response = await fetch(tokenUrl, {
602
- method: "POST",
603
- headers: {
604
- "Content-Type": "application/x-www-form-urlencoded",
605
- Accept: "application/json"
606
- },
607
- body: body.toString()
608
- });
609
- const data = await response.json();
610
- if (!response.ok) {
611
- console.error("Token exchange error:", data);
612
- throw new Error(`Token exchange failed: ${data.error || response.statusText} - ${data.error_description || ""}`.trim());
613
- }
614
- const tokenResponseValidator = validation?.tokenResponse ?? tokenResponseSchema("builtin");
615
- const tokenResult = await tokenResponseValidator["~standard"].validate(data);
616
- if ("issues" in tokenResult) {
617
- console.error("Token response validation failed:", tokenResult.issues);
618
- throw new Error(`Token response validation failed: ${tokenResult.issues?.map((i) => i.message).join("; ")}`);
619
- }
620
- return tokenResult.value;
621
- } catch (error) {
622
- console.error("Error during token exchange:", error);
623
- throw error;
624
- }
625
- }
626
- async function refreshToken(refreshToken2) {
627
- return retryWithBackoff(async () => {
628
- if (!configWithDefaults)
629
- throw new Error("SSO Manager not initialized");
630
- const tokenUrl = configWithDefaults.token_url;
631
- const body = new URLSearchParams;
632
- body.append("grant_type", "refresh_token");
633
- body.append("refresh_token", refreshToken2);
634
- body.append("client_id", configWithDefaults.client_id);
635
- const response = await fetch(tokenUrl, {
636
- method: "POST",
637
- headers: {
638
- "Content-Type": "application/x-www-form-urlencoded",
639
- Accept: "application/json"
640
- },
641
- body: body.toString()
642
- });
643
- const data = await response.json();
644
- if (!response.ok) {
645
- console.error("Token refresh error:", data);
646
- throw new Error(`Token refresh failed: ${data.error || response.statusText} - ${data.error_description || ""}`.trim());
647
- }
648
- return data;
649
- });
650
- }
651
- async function revokeToken(token) {
652
- try {
653
- if (!configWithDefaults)
654
- throw new Error("SSO Manager not initialized");
655
- if (!configWithDefaults.revocation_endpoint) {
656
- return;
657
- }
658
- const body = new URLSearchParams;
659
- body.append("token", token);
660
- body.append("token_type_hint", "refresh_token");
661
- body.append("client_id", configWithDefaults.client_id);
662
- const response = await fetch(configWithDefaults.revocation_endpoint, {
663
- method: "POST",
664
- headers: {
665
- "Content-Type": "application/x-www-form-urlencoded"
666
- },
667
- body: body.toString()
668
- });
669
- if (!response.ok) {
670
- console.warn("Token revocation failed:", response.status, response.statusText);
671
- } else {
672
- console.log("Token revoked successfully");
673
- }
674
- } catch (error) {
675
- console.warn("Error revoking token:", error);
676
- }
677
- }
678
- async function fetchJwks() {
679
- const url = configWithDefaults.jwks_uri || `${configWithDefaults.authority}/protocol/openid-connect/certs`;
680
- const cached = jwksCache.get(url);
681
- if (cached)
682
- return cached;
683
- return retryWithBackoff(async () => {
684
- if (!configWithDefaults)
685
- throw new Error("SSO Manager not initialized");
686
- const response = await fetch(url);
687
- if (!response.ok)
688
- throw new Error("Failed to fetch JWKS");
689
- const jwks = await response.json();
690
- jwksCache.set(url, jwks);
691
- return jwks;
692
- });
693
- }
694
- async function retryWithBackoff(operation, maxRetries = 3, baseDelay = 1000, maxDelay = 30000) {
695
- let lastError = new Error("Placeholder Error");
696
- for (let attempt = 0;attempt <= maxRetries; attempt++) {
697
- try {
698
- return await operation();
699
- } catch (error) {
700
- lastError = error instanceof Error ? error : new Error(String(error));
701
- if (error instanceof Error && error.message.includes("400")) {
702
- throw error;
703
- }
704
- if (attempt === maxRetries) {
705
- throw lastError;
706
- }
707
- const delay = Math.min(baseDelay * 2 ** attempt, maxDelay);
708
- const jitter = Math.random() * 0.1 * delay;
709
- await new Promise((resolve) => setTimeout(resolve, delay + jitter));
710
- console.warn(`Retry attempt ${attempt + 1} after ${delay + jitter}ms delay`);
711
- }
712
- }
713
- throw lastError;
714
- }
715
- async function parseJwt(token, validation) {
716
- try {
717
- const parts = token.split(".");
718
- if (parts.length !== 3)
719
- throw new Error("Invalid JWT");
720
- const header = JSON.parse(atob(parts[0].replace(/-/g, "+").replace(/_/g, "/")));
721
- const payload = JSON.parse(atob(parts[1].replace(/-/g, "+").replace(/_/g, "/")));
722
- const signature = parts[2].replace(/-/g, "+").replace(/_/g, "/");
723
- const publicKey = await getPublicKey(header.kid);
724
- const encoder = new TextEncoder;
725
- const data = encoder.encode(`${parts[0]}.${parts[1]}`);
726
- const isValid = await crypto.subtle.verify("RSASSA-PKCS1-v1_5", publicKey, Uint8Array.from(atob(signature), (c) => c.charCodeAt(0)), data);
727
- if (!isValid)
728
- throw new Error("Invalid JWT signature");
729
- const idTokenClaimsValidator = validation?.idTokenClaims ?? idTokenClaimsSchema("builtin");
730
- const claimsResult = await idTokenClaimsValidator["~standard"].validate(payload);
731
- if ("issues" in claimsResult) {
732
- console.error("ID token claims validation failed:", claimsResult.issues);
733
- throw new Error(`ID token claims validation failed: ${claimsResult.issues?.map((i) => i.message).join("; ")}`);
734
- }
735
- return claimsResult.value;
736
- } catch (e) {
737
- console.error("Error verifying JWT:", e);
738
- throw e;
739
- }
740
- }
741
- function generateRandomString(length = 32) {
742
- const array = new Uint8Array(length);
743
- crypto.getRandomValues(array);
744
- return Array.from(array, (byte) => byte.toString(16).padStart(2, "0")).join("").substring(0, length);
745
- }
746
- async function pkceChallengeFromVerifier(verifier) {
747
- const encoder = new TextEncoder;
748
- const data = encoder.encode(verifier);
749
- const hashBuffer = await crypto.subtle.digest("SHA-256", data);
750
- const hashArray = Array.from(new Uint8Array(hashBuffer));
751
- const hashBase64 = btoa(String.fromCharCode(...hashArray)).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
752
- return hashBase64;
753
- }
754
- async function getPublicKey(kid) {
755
- const jwks = await fetchJwks();
756
- const key = jwks.keys.find((k) => k.kid === kid);
757
- if (!key)
758
- throw new Error("Public key not found");
759
- const publicKey = await crypto.subtle.importKey("jwk", {
760
- kty: key.kty,
761
- n: key.n,
762
- e: key.e
763
- }, { name: "RSASSA-PKCS1-v1_5", hash: "SHA-256" }, false, ["verify"]);
764
- return publicKey;
765
- }
766
- function createJwtCookies(token, expires) {
767
- const control = {
768
- expires_in: token.expires_in,
769
- refresh_expires_in: token.refresh_expires_in,
770
- scope: token.scope,
771
- session_state: token.session_state,
772
- token_type: token.token_type,
773
- expires: expires.toISOString()
774
- };
775
- return [
776
- ["Set-Cookie", createCookie("access", token.access_token, expires)],
777
- ["Set-Cookie", createCookie("id", token.id_token, expires)],
778
- ["Set-Cookie", createCookie("refresh", token.refresh_token ?? "", expires)],
779
- ["Set-Cookie", createCookie("control", control, expires)]
780
- ];
781
- }
782
- async function getTokenFromCookies(req) {
783
- const access_token = getCookie("access", req);
784
- const id_token = getCookie("id", req);
785
- const refresh_token = getCookie("refresh", req);
786
- const control = getCookie("control", req, true);
787
- if (!access_token || !id_token || !refresh_token || !control) {
788
- return { tokens: undefined, refreshHeaders: [] };
789
- }
790
- let tokenResponse = {
791
- access_token,
792
- id_token,
793
- refresh_token,
794
- ...control
795
- };
796
- if (control.expires && refresh_token && Date.now() > new Date(control.expires).getTime()) {
797
- tokenResponse = await refreshToken(refresh_token);
798
- const user = await parseUser(tokenResponse);
799
- const refreshHeaders = createJwtCookies(tokenResponse, user.sso.expires);
800
- return { tokens: tokenResponse, refreshHeaders };
801
- }
802
- return { tokens: tokenResponse, refreshHeaders: [] };
803
- }
804
- async function getJwt(request) {
805
- const { tokens } = await getTokenFromCookies(request);
806
- if (!tokens)
807
- return;
808
- return tokens.access_token;
809
- }
810
- function createCookie(name, value, expires) {
811
- name = `${configWithDefaults.cookies_prefix}.${name}`;
812
- if (typeof value !== "string") {
813
- value = btoa(JSON.stringify(value));
814
- }
815
- let exp;
816
- if (expires instanceof Date) {
817
- exp = `Expires=${expires.toUTCString()}`;
818
- } else if (typeof expires === "number") {
819
- exp = `Max-Age=${expires}`;
820
- } else {
821
- throw new Error("Invalid expires type", expires);
822
- }
823
- if (value.length > 4000) {
824
- throw new Error(`Error setting cookie: ${name}. Cookie length is: ${value.length}`);
825
- }
826
- return `${name}=${value}; ${exp}; Path=${configWithDefaults.cookies_path}; HttpOnly;${configWithDefaults.cookies_secure ? " Secure;" : ""} SameSite=${configWithDefaults.cookies_same_site};`;
827
- }
828
- function clearCookie(name) {
829
- return `${configWithDefaults.cookies_prefix}.${name}=; Max-Age=0; Path=${configWithDefaults.cookies_path}; HttpOnly;${configWithDefaults.cookies_secure ? " Secure;" : ""} SameSite=${configWithDefaults.cookies_same_site};`;
830
- }
831
- function getCookie(name, req, parse = false) {
832
- const header = req.headers.get("cookie");
833
- if (!header)
834
- return null;
835
- const cookie = header.split(";").find((row) => row.trim().startsWith(`${configWithDefaults.cookies_prefix}.${name}=`));
836
- if (!cookie)
837
- return null;
838
- const val = cookie.split("=")[1].trim();
839
- if (!parse)
840
- return val;
841
- const str = atob(val);
842
- return JSON.parse(str);
843
- }
844
- async function handler(request, handlerConfig) {
845
- const { loginUrl, userUrl, errorUrl, landingUrl, tokenUrl, refreshUrl, logoutUrl, logoutBackChannelUrl, jwksUrl, validation } = handlerConfig ?? {};
846
- if (!loginUrl) {
847
- console.error("loginUrl is required");
848
- }
849
- const path = new URL(request.url).pathname;
850
- if (new URL(configWithDefaults.redirect_uri).pathname === path) {
851
- return callbackHandler(request, validation);
852
- }
853
- if (loginUrl === path) {
854
- return initiateLogin({
855
- landingUrl: landingUrl || "/",
856
- errorUrl
857
- });
858
- }
859
- if (userUrl === path) {
860
- const { tokens, refreshHeaders } = await getTokenFromCookies(request);
861
- if (!tokens) {
862
- return new Response("User not logged in", { status: 401 });
863
- }
864
- const user = await parseUser(tokens);
865
- return new Response(JSON.stringify(user), {
866
- headers: [["Content-Type", "application/json"], ...refreshHeaders]
867
- });
868
- }
869
- if (tokenUrl === path) {
870
- const { tokens, refreshHeaders } = await getTokenFromCookies(request);
871
- if (!tokens) {
872
- return new Response("User not logged in", { status: 401 });
873
- }
874
- return new Response(JSON.stringify({
875
- token: tokens.access_token,
876
- expires: tokens.expires
877
- }), {
878
- headers: [["Content-Type", "application/json"], ...refreshHeaders]
879
- });
880
- }
881
- if (refreshUrl === path) {
882
- const refresh_token = getCookie("refresh", request);
883
- if (!refresh_token) {
884
- return new Response("User not logged in", { status: 401 });
885
- }
886
- const newTokenResponse = await refreshToken(refresh_token);
887
- const user = await parseUser(newTokenResponse);
888
- const refreshHeaders = createJwtCookies(newTokenResponse, user.sso.expires);
889
- return new Response("Refresh Complete", {
890
- status: 200,
891
- headers: refreshHeaders
892
- });
893
- }
894
- if (logoutUrl === path) {
895
- return logout(request, { landingUrl: landingUrl || "/" });
896
- }
897
- if (logoutBackChannelUrl === path) {
898
- return logoutBackChannel(request);
899
- }
900
- if (jwksUrl === path) {
901
- const jwks = await fetchJwks();
902
- return new Response(JSON.stringify(jwks), {
903
- headers: [["Content-Type", "application/json"]]
904
- });
905
- }
906
- return new Response("Not Found", { status: 404 });
907
- }
908
- return {
909
- getUser,
910
- getRequiredUser,
911
- getJwt,
912
- initiateLogin,
913
- logout,
914
- callbackHandler,
915
- handler
916
- };
917
- }
918
-
919
- // src/vault.ts
920
- function vault(url) {
921
- async function getFullSecret(path, token) {
922
- const resp = await fetch(`${url}/${path}`, { headers: { "X-Vault-Token": token } });
923
- if (resp.status !== 200) {
924
- throw new Error(`Vault returned invalid status, ${resp.status}: '${resp.statusText}' from URL: ${url}`);
925
- }
926
- try {
927
- const secret = await resp.json();
928
- return secret.data;
929
- } catch (cause) {
930
- throw new Error("Error retrieving secret", { cause });
931
- }
932
- }
933
- return {
934
- url,
935
- getFullSecret,
936
- getSecret: async (path, token) => {
937
- return (await getFullSecret(path, token)).data;
938
- }
939
- };
940
- }
941
- // src/server.ts
942
- function getSSO(config) {
943
- const es = getES(config?.es);
944
- if (!es.sso) {
945
- console.error("TODO tell them how to connect SSO");
946
- return;
947
- }
948
- return es.sso;
949
- }
950
- function unavailable() {
951
- new Response(JSON.stringify({ error: "SSO Unavailable" }), {
952
- status: 503,
953
- statusText: "SSO Unavailable",
954
- headers: { "Content-Type": "application/json" }
955
- });
956
- }
957
- async function getUser(request, config) {
958
- return getSSO(config)?.getUser(request);
959
- }
960
- async function getRequiredUser(request, config) {
961
- const sso2 = getSSO(config);
962
- if (!sso2)
963
- throw unavailable();
964
- return sso2.getRequiredUser(request);
965
- }
966
- async function initiateLogin(config) {
967
- const sso2 = getSSO(config);
968
- if (!sso2)
969
- throw unavailable();
970
- return sso2.initiateLogin(config);
971
- }
972
- async function callback(request, config) {
973
- const sso2 = getSSO(config);
974
- if (!sso2)
975
- throw unavailable();
976
- return sso2.callbackHandler(request);
977
- }
978
- async function handler(request, config) {
979
- const sso2 = getSSO(config);
980
- if (!sso2)
981
- throw unavailable();
982
- return sso2.handler(request, config);
983
- }
984
- // src/session-store.ts
985
- class InMemorySessionStore {
986
- sessions = new Map;
987
- async create(session) {
988
- if (this.sessions.has(session.sid)) {
989
- throw new Error(`Session with sid ${session.sid} already exists`);
990
- }
991
- this.sessions.set(session.sid, session);
992
- }
993
- async get(sid) {
994
- return this.sessions.get(sid) ?? null;
995
- }
996
- async update(sid, data) {
997
- const session = this.sessions.get(sid);
998
- if (!session) {
999
- throw new Error(`Session with sid ${sid} not found`);
1000
- }
1001
- const updated = { ...session, ...data };
1002
- this.sessions.set(sid, updated);
1003
- }
1004
- async delete(sid) {
1005
- this.sessions.delete(sid);
1006
- }
1007
- }
1008
- // src/ui/sign-in-loading.tsx
1009
- import { jsxDEV, Fragment } from "react/jsx-dev-runtime";
1010
- function SignInLoading({ complete = false, children }) {
1011
- const { isLoading } = useUser();
1012
- if (isLoading && !complete)
1013
- return /* @__PURE__ */ jsxDEV(Fragment, {
1014
- children
1015
- }, undefined, false, undefined, this);
1016
- return null;
1017
- }
1018
- // src/ui/signed-in.tsx
1019
- import { jsxDEV as jsxDEV2, Fragment as Fragment2 } from "react/jsx-dev-runtime";
1020
- function SignedIn({ children }) {
1021
- const { user } = useUser();
1022
- if (user)
1023
- return /* @__PURE__ */ jsxDEV2(Fragment2, {
1024
- children
1025
- }, undefined, false, undefined, this);
1026
- return null;
1027
- }
1028
- // src/ui/signed-out.tsx
1029
- import { jsxDEV as jsxDEV3, Fragment as Fragment3 } from "react/jsx-dev-runtime";
1030
- function SignedOut({ children }) {
1031
- const { user, isLoading } = useUser();
1032
- if (user || isLoading)
1033
- return null;
1034
- return /* @__PURE__ */ jsxDEV3(Fragment3, {
1035
- children
1036
- }, undefined, false, undefined, this);
1037
- }
1038
- // src/ui/sso-provider.tsx
1039
- import { createContext, useCallback, useContext, useEffect, useState } from "react";
1040
- import { jsxDEV as jsxDEV4 } from "react/jsx-dev-runtime";
1041
- var CTX = createContext(undefined);
1042
- var generateStorageKey = (tenantId) => {
1043
- return `es-sso-user-${tenantId.replace(/[^a-zA-Z0-9]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "")}`;
1044
- };
1045
- function SSOProvider({
1046
- tenantId,
1047
- storage = "memory",
1048
- storageKey,
1049
- userUrl,
1050
- tokenUrl,
1051
- refreshUrl,
1052
- disableListener = false,
1053
- children
1054
- }) {
1055
- const [user, setUserState] = useState(null);
1056
- const [isLoading, setIsLoading] = useState(!!userUrl);
1057
- const actualStorageKey = storageKey || (tenantId ? generateStorageKey(tenantId) : "es-sso-user");
1058
- const isValidUser = useCallback((user2) => {
1059
- if (!user2 || !tenantId)
1060
- return true;
1061
- return user2.sso?.tenant?.id === tenantId;
1062
- }, [tenantId]);
1063
- const loadUserFromStorage = useCallback(() => {
1064
- if (storage === "memory" || typeof window === "undefined")
1065
- return null;
1066
- try {
1067
- const storageObject = storage === "local" ? localStorage : sessionStorage;
1068
- const userData = storageObject.getItem(actualStorageKey);
1069
- if (!userData)
1070
- return null;
1071
- const parsedUser = JSON.parse(userData);
1072
- if (parsedUser.sso?.expires) {
1073
- parsedUser.sso.expires = new Date(parsedUser.sso.expires);
1074
- }
1075
- return isValidUser(parsedUser) ? parsedUser : null;
1076
- } catch (error) {
1077
- console.error("Error loading user from storage:", error);
1078
- return null;
1079
- }
1080
- }, [storage, actualStorageKey, isValidUser]);
1081
- const saveUserToStorage = useCallback((user2) => {
1082
- if (storage === "memory" || typeof window === "undefined")
1083
- return;
1084
- try {
1085
- const storageObject = storage === "local" ? localStorage : sessionStorage;
1086
- if (user2 === null) {
1087
- storageObject.removeItem(actualStorageKey);
1088
- } else {
1089
- storageObject.setItem(actualStorageKey, JSON.stringify(user2));
1090
- }
1091
- } catch (error) {
1092
- console.error("Error saving user to storage:", error);
1093
- }
1094
- }, [storage, actualStorageKey]);
1095
- const setUser = useCallback((newUser) => {
1096
- if (newUser && !isValidUser(newUser))
1097
- return;
1098
- setUserState(newUser);
1099
- saveUserToStorage(newUser);
1100
- }, [isValidUser, saveUserToStorage]);
1101
- const fetchUserFromUrl = useCallback(async () => {
1102
- if (!userUrl)
1103
- return;
1104
- setIsLoading(true);
1105
- try {
1106
- const response = await fetch(userUrl);
1107
- if (response.status === 401) {
1108
- setUserState(null);
1109
- saveUserToStorage(null);
1110
- setIsLoading(false);
1111
- return;
1112
- }
1113
- if (!response.ok) {
1114
- throw new Error(`Failed to fetch user: ${response.status} ${response.statusText}`);
1115
- }
1116
- const userData = await response.json();
1117
- if (userData.sso?.expires && typeof userData.sso.expires === "string") {
1118
- userData.sso.expires = new Date(userData.sso.expires);
1119
- }
1120
- if (isValidUser(userData)) {
1121
- setUserState(userData);
1122
- saveUserToStorage(userData);
1123
- }
1124
- } catch (error) {
1125
- console.error("Error fetching user from URL:", error);
1126
- } finally {
1127
- setIsLoading(false);
1128
- }
1129
- }, [userUrl, isValidUser, saveUserToStorage]);
1130
- useEffect(() => {
1131
- const storedUser = loadUserFromStorage();
1132
- if (storedUser) {
1133
- setUserState(storedUser);
1134
- }
1135
- if (userUrl) {
1136
- fetchUserFromUrl();
1137
- } else {
1138
- setIsLoading(false);
1139
- }
1140
- }, [loadUserFromStorage, userUrl, fetchUserFromUrl]);
1141
- useEffect(() => {
1142
- if (disableListener || storage === "memory")
1143
- return;
1144
- const handleStorageChange = (event) => {
1145
- if (event.key !== actualStorageKey)
1146
- return;
1147
- if (event.newValue === null) {
1148
- setUserState(null);
1149
- } else {
1150
- try {
1151
- const parsedUser = JSON.parse(event.newValue);
1152
- if (parsedUser.sso?.expires) {
1153
- parsedUser.sso.expires = new Date(parsedUser.sso.expires);
1154
- }
1155
- if (isValidUser(parsedUser)) {
1156
- setUserState(parsedUser);
1157
- }
1158
- } catch (error) {
1159
- console.error("Error parsing user from storage event:", error);
1160
- }
1161
- }
1162
- };
1163
- const handleLogout = () => {
1164
- setUserState(null);
1165
- };
1166
- window.addEventListener("storage", handleStorageChange);
1167
- window.addEventListener("es-sso-logout", handleLogout);
1168
- return () => {
1169
- window.removeEventListener("storage", handleStorageChange);
1170
- window.removeEventListener("es-sso-logout", handleLogout);
1171
- };
1172
- }, [disableListener, storage, actualStorageKey, isValidUser]);
1173
- const contextValue = {
1174
- user,
1175
- setUser,
1176
- isLoading,
1177
- tokenUrl,
1178
- refreshUrl
1179
- };
1180
- return /* @__PURE__ */ jsxDEV4(CTX.Provider, {
1181
- value: contextValue,
1182
- children
1183
- }, undefined, false, undefined, this);
1184
- }
1185
- function useUser() {
1186
- const context = useContext(CTX);
1187
- if (context === undefined) {
1188
- throw new Error("useUser must be used within a SSOProvider");
1189
- }
1190
- return context;
1191
- }
1192
- function useToken() {
1193
- const context = useContext(CTX);
1194
- if (context === undefined) {
1195
- throw new Error("useToken must be used within a SSOProvider");
1196
- }
1197
- const { tokenUrl, refreshUrl } = context;
1198
- if (!tokenUrl || !refreshUrl) {
1199
- throw new Error('useToken requires that a "tokenUrl" and "refreshUrl" be set in the SSOProvider');
1200
- }
1201
- const [token, setToken] = useState(null);
1202
- const [expires, setExpires] = useState(null);
1203
- const [isLoading, setIsLoading] = useState(!!tokenUrl);
1204
- const [error, setError] = useState(null);
1205
- const fetchJwt = useCallback(async (url) => {
1206
- setIsLoading(true);
1207
- setError(null);
1208
- try {
1209
- const response = await fetch(url);
1210
- if (response.status === 401) {
1211
- context.setUser(null);
1212
- setToken(null);
1213
- setExpires(null);
1214
- setIsLoading(false);
1215
- return;
1216
- }
1217
- if (!response.ok) {
1218
- throw new Error(`Failed to fetch JWT: ${response.status} ${response.statusText}`);
1219
- }
1220
- const data = await response.json();
1221
- setToken(data.token);
1222
- setExpires(new Date(data.expires));
1223
- } catch (err) {
1224
- const error2 = err instanceof Error ? err : new Error(String(err));
1225
- setError(error2);
1226
- setToken(null);
1227
- setExpires(null);
1228
- console.error("Error fetching JWT:", error2);
1229
- } finally {
1230
- setIsLoading(false);
1231
- }
1232
- }, [context]);
1233
- const refresh = useCallback(async () => {
1234
- const url = refreshUrl || tokenUrl;
1235
- if (!url) {
1236
- console.warn("No tokenUrl or refreshUrl provided");
1237
- return;
1238
- }
1239
- await fetchJwt(url);
1240
- }, [refreshUrl, tokenUrl, fetchJwt]);
1241
- useEffect(() => {
1242
- if (!tokenUrl) {
1243
- setIsLoading(false);
1244
- return;
1245
- }
1246
- fetchJwt(tokenUrl);
1247
- }, [tokenUrl, fetchJwt]);
1248
- useEffect(() => {
1249
- if (!expires || !refreshUrl)
1250
- return;
1251
- const checkExpiration = () => {
1252
- const now = new Date;
1253
- const timeUntilExpiry = expires.getTime() - now.getTime();
1254
- if (timeUntilExpiry <= 60000 && timeUntilExpiry > 0) {
1255
- refresh();
1256
- }
1257
- };
1258
- checkExpiration();
1259
- const interval = setInterval(checkExpiration, 30000);
1260
- return () => clearInterval(interval);
1261
- }, [expires, refreshUrl, refresh]);
1262
- return {
1263
- token,
1264
- isLoading,
1265
- error,
1266
- refresh
1267
- };
1268
- }
1269
- async function logout(logoutUrl) {
1270
- try {
1271
- const response = await fetch(logoutUrl, {
1272
- headers: { Accept: "application/json" }
1273
- });
1274
- if (!response.ok) {
1275
- return { success: false, error: `HTTP ${response.status}` };
1276
- }
1277
- const data = await response.json();
1278
- if (!data.success) {
1279
- return { success: false, error: data.message || "Logout failed" };
1280
- }
1281
- if (typeof window !== "undefined") {
1282
- for (let i = localStorage.length - 1;i >= 0; i--) {
1283
- const key = localStorage.key(i);
1284
- if (key?.startsWith("es-sso-user")) {
1285
- localStorage.removeItem(key);
1286
- }
1287
- }
1288
- for (let i = sessionStorage.length - 1;i >= 0; i--) {
1289
- const key = sessionStorage.key(i);
1290
- if (key?.startsWith("es-sso-user")) {
1291
- sessionStorage.removeItem(key);
1292
- }
1293
- }
1294
- window.dispatchEvent(new CustomEvent("es-sso-logout"));
1295
- }
1296
- return { success: true };
1297
- } catch (error) {
1298
- return {
1299
- success: false,
1300
- error: error instanceof Error ? error.message : "Network error"
1301
- };
1302
- }
1303
- }
1304
-
1305
- // src/index.ts
1306
- async function enterpriseStandard(appKey, initConfig) {
1307
- let vaultUrl;
1308
- let vaultToken;
1309
- let secrets;
1310
- const ioniteUrl = initConfig?.ioniteUrl ?? "https://ionite.com";
1311
- if (appKey?.startsWith("IONITE_PUBLIC_DEMO_")) {
1312
- vaultUrl = "https://vault-ionite.ionite.dev/v1/secret/data";
1313
- const port = appKey.slice("IONITE_PUBLIC_DEMO_".length);
1314
- secrets = {
1315
- sso: {
1316
- path: `public/IONITE_PUBLIC_DEMO_SSO_${port}`,
1317
- token: "hvs.VGhD2hmXDH9PmZjTacZx0G5K"
1318
- }
1319
- };
1320
- } else if (appKey) {
1321
- if (!vaultUrl || !vaultToken) {
1322
- throw new Error("TODO something is wrong with the ionite config, handle this error");
1323
- }
1324
- secrets = {};
1325
- } else {
1326
- throw new Error("TODO tell them how to connect to ionite");
1327
- }
1328
- const defaultInstance2 = getDefaultInstance();
1329
- const vaultClient = vault(vaultUrl);
1330
- const result = {
1331
- ioniteUrl,
1332
- defaultInstance: initConfig?.defaultInstance || initConfig?.defaultInstance !== false && !defaultInstance2,
1333
- vault: vaultClient,
1334
- sso: secrets.sso ? sso({
1335
- ...await vaultClient.getSecret(secrets.sso.path, secrets.sso.token),
1336
- ...initConfig
1337
- }) : undefined,
1338
- iam: secrets.iam ? await iam(await vaultClient.getSecret(secrets.iam.path, secrets.iam.token)) : undefined
1339
- };
1340
- if (result.defaultInstance) {
1341
- if (defaultInstance2) {
1342
- defaultInstance2.defaultInstance = false;
1343
- }
1344
- setDefaultInstance(result);
1345
- }
1346
- return result;
1347
- }
1348
- export {
1349
- useUser,
1350
- useToken,
1351
- tokenResponseSchema,
1352
- oidcCallbackSchema,
1353
- logout,
1354
- initiateLogin,
1355
- idTokenClaimsSchema,
1356
- handler,
1357
- getUser,
1358
- getRequiredUser,
1359
- enterpriseStandard,
1360
- callback,
1361
- SignedOut,
1362
- SignedIn,
1363
- SignInLoading,
1364
- SSOProvider,
1365
- InMemorySessionStore
1366
- };
1
+ import{createContext as p,useCallback as $,useContext as w,useEffect as T,useState as m}from"react";import{jsxDEV as c}from"react/jsx-dev-runtime";var b=p(void 0),E=(R)=>{return`es-sso-user-${R.replace(/[^a-zA-Z0-9]/g,"-").replace(/-+/g,"-").replace(/^-|-$/g,"")}`};function k({tenantId:R,storage:P="memory",storageKey:A,userUrl:q,tokenUrl:H,refreshUrl:C,disableListener:X=!1,children:f}){let[J,Q]=m(null),[F,Y]=m(!!q),M=A||(R?E(R):"es-sso-user"),z=$((W)=>{if(!W||!R)return!0;return W.sso?.tenant?.id===R},[R]),B=$(()=>{if(P==="memory"||typeof window>"u")return null;try{let N=(P==="local"?localStorage:sessionStorage).getItem(M);if(!N)return null;let Z=JSON.parse(N);if(Z.sso?.expires)Z.sso.expires=new Date(Z.sso.expires);return z(Z)?Z:null}catch(W){return console.error("Error loading user from storage:",W),null}},[P,M,z]),G=$((W)=>{if(P==="memory"||typeof window>"u")return;try{let N=P==="local"?localStorage:sessionStorage;if(W===null)N.removeItem(M);else N.setItem(M,JSON.stringify(W))}catch(N){console.error("Error saving user to storage:",N)}},[P,M]),K=$((W)=>{if(W&&!z(W))return;Q(W),G(W)},[z,G]),j=$(async()=>{if(!q)return;Y(!0);try{let W=await fetch(q);if(W.status===401){Q(null),G(null),Y(!1);return}if(!W.ok)throw Error(`Failed to fetch user: ${W.status} ${W.statusText}`);let N=await W.json();if(N.sso?.expires&&typeof N.sso.expires==="string")N.sso.expires=new Date(N.sso.expires);if(z(N))Q(N),G(N)}catch(W){console.error("Error fetching user from URL:",W)}finally{Y(!1)}},[q,z,G]);T(()=>{let W=B();if(W)Q(W);if(q)j();else Y(!1)},[B,q,j]),T(()=>{if(X||P==="memory")return;let W=(Z)=>{if(Z.key!==M)return;if(Z.newValue===null)Q(null);else try{let _=JSON.parse(Z.newValue);if(_.sso?.expires)_.sso.expires=new Date(_.sso.expires);if(z(_))Q(_)}catch(_){console.error("Error parsing user from storage event:",_)}},N=()=>{Q(null)};return window.addEventListener("storage",W),window.addEventListener("es-sso-logout",N),()=>{window.removeEventListener("storage",W),window.removeEventListener("es-sso-logout",N)}},[X,P,M,z]);let I={user:J,setUser:K,isLoading:F,tokenUrl:H,refreshUrl:C};return c(b.Provider,{value:I,children:f},void 0,!1,void 0,this)}function O(){let R=w(b);if(R===void 0)throw Error("useUser must be used within a SSOProvider");return R}function S(){let R=w(b);if(R===void 0)throw Error("useToken must be used within a SSOProvider");let{tokenUrl:P,refreshUrl:A}=R;if(!P||!A)throw Error('useToken requires that a "tokenUrl" and "refreshUrl" be set in the SSOProvider');let[q,H]=m(null),[C,X]=m(null),[f,J]=m(!!P),[Q,F]=m(null),Y=$(async(z)=>{J(!0),F(null);try{let B=await fetch(z);if(B.status===401){R.setUser(null),H(null),X(null),J(!1);return}if(!B.ok)throw Error(`Failed to fetch JWT: ${B.status} ${B.statusText}`);let G=await B.json();H(G.token),X(new Date(G.expires))}catch(B){let G=B instanceof Error?B:Error(String(B));F(G),H(null),X(null),console.error("Error fetching JWT:",G)}finally{J(!1)}},[R]),M=$(async()=>{let z=A||P;if(!z){console.warn("No tokenUrl or refreshUrl provided");return}await Y(z)},[A,P,Y]);return T(()=>{if(!P){J(!1);return}Y(P)},[P,Y]),T(()=>{if(!C||!A)return;let z=()=>{let G=new Date,K=C.getTime()-G.getTime();if(K<=60000&&K>0)M()};z();let B=setInterval(z,30000);return()=>clearInterval(B)},[C,A,M]),{token:q,isLoading:f,error:Q,refresh:M}}async function x(R){try{let P=await fetch(R,{headers:{Accept:"application/json"}});if(!P.ok)return{success:!1,error:`HTTP ${P.status}`};let A=await P.json();if(!A.success)return{success:!1,error:A.message||"Logout failed"};if(typeof window<"u"){for(let q=localStorage.length-1;q>=0;q--){let H=localStorage.key(q);if(H?.startsWith("es-sso-user"))localStorage.removeItem(H)}for(let q=sessionStorage.length-1;q>=0;q--){let H=sessionStorage.key(q);if(H?.startsWith("es-sso-user"))sessionStorage.removeItem(H)}window.dispatchEvent(new CustomEvent("es-sso-logout"))}return{success:!0}}catch(P){return{success:!1,error:P instanceof Error?P.message:"Network error"}}}import{jsxDEV as L,Fragment as y}from"react/jsx-dev-runtime";function i({complete:R=!1,children:P}){let{isLoading:A}=O();if(A&&!R)return L(y,{children:P},void 0,!1,void 0,this);return L(y,{},void 0,!1,void 0,this)}import{jsxDEV as V,Fragment as h}from"react/jsx-dev-runtime";function d({children:R}){let{user:P}=O();if(P)return V(h,{children:R},void 0,!1,void 0,this);return V(h,{},void 0,!1,void 0,this)}import{jsxDEV as v,Fragment as D}from"react/jsx-dev-runtime";function g({children:R}){let{user:P,isLoading:A}=O();if(P||A)return v(D,{},void 0,!1,void 0,this);return v(D,{children:R},void 0,!1,void 0,this)}export{O as useUser,S as useToken,x as logout,g as SignedOut,d as SignedIn,i as SignInLoading,k as SSOProvider};