@cimplify/sdk 0.65.0 → 0.67.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/advanced.d.mts +1 -1
- package/dist/advanced.d.ts +1 -1
- package/dist/advanced.js +20 -20
- package/dist/advanced.mjs +1 -1
- package/dist/{chunk-AVQL3TSJ.js → chunk-3QSW33RG.js} +389 -78
- package/dist/chunk-E6T5VJMG.mjs +156 -0
- package/dist/{chunk-WDTFIIY6.mjs → chunk-FNK6UZOE.mjs} +2 -2
- package/dist/{chunk-3G4QCENX.mjs → chunk-FVGVJ4TC.mjs} +12 -50
- package/dist/chunk-IQLJJW2H.js +168 -0
- package/dist/{chunk-PKYZBKZD.mjs → chunk-JAPCAZN4.mjs} +10 -29
- package/dist/{chunk-R6363CC7.mjs → chunk-QQMDEGAK.mjs} +382 -79
- package/dist/{chunk-VKID3QUE.js → chunk-SH6HJW4A.js} +4 -4
- package/dist/{chunk-VAMZPBA4.js → chunk-SSLFZPAI.js} +44 -63
- package/dist/{chunk-HCZTBWU4.js → chunk-SXGFJEME.js} +12 -50
- package/dist/{client-BS5lNWWH.d.mts → client-ByI_3jfL.d.mts} +33 -20
- package/dist/{client-p1Iofyc9.d.ts → client-CQlEWH5j.d.ts} +1 -1
- package/dist/{client-QTrsv6Gu.d.ts → client-DVFQoDtI.d.ts} +33 -20
- package/dist/{client-XoswXzib.d.mts → client-Dd82KQ_G.d.mts} +1 -1
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +80 -81
- package/dist/index.mjs +4 -5
- package/dist/mock/cli.mjs +12 -50
- package/dist/mock/library.js +12 -50
- package/dist/mock/library.mjs +12 -50
- package/dist/mock/msw.js +12 -50
- package/dist/mock/msw.mjs +12 -50
- package/dist/react.d.mts +4 -7
- package/dist/react.d.ts +4 -7
- package/dist/react.js +63 -71
- package/dist/react.mjs +5 -13
- package/dist/server.d.mts +18 -3
- package/dist/server.d.ts +18 -3
- package/dist/server.js +175 -29
- package/dist/server.mjs +167 -25
- package/dist/{sign-in-dr7m09nm.d.ts → sign-in-RFuWZOgF.d.mts} +6 -1
- package/dist/{sign-in-dr7m09nm.d.mts → sign-in-RFuWZOgF.d.ts} +6 -1
- package/dist/styles.css +1 -1
- package/dist/testing/msw.js +2 -2
- package/dist/testing/msw.mjs +1 -1
- package/dist/testing/suite.d.mts +2 -2
- package/dist/testing/suite.d.ts +2 -2
- package/dist/testing/suite.js +23 -23
- package/dist/testing/suite.mjs +4 -4
- package/dist/testing.d.mts +2 -2
- package/dist/testing.d.ts +2 -2
- package/dist/testing.js +79 -79
- package/dist/testing.mjs +5 -5
- package/package.json +1 -1
- package/dist/chunk-2AU263DK.mjs +0 -323
- package/dist/chunk-6DMHVQ42.js +0 -338
- package/dist/chunk-DKT62WQW.js +0 -53
- package/dist/chunk-XSOYJLMZ.mjs +0 -45
package/dist/server.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
4
|
-
var
|
|
5
|
-
require('./chunk-AVQL3TSJ.js');
|
|
3
|
+
var chunkSSLFZPAI_js = require('./chunk-SSLFZPAI.js');
|
|
4
|
+
var chunk3QSW33RG_js = require('./chunk-3QSW33RG.js');
|
|
6
5
|
require('./chunk-7Y2O3E4D.js');
|
|
7
6
|
var chunkXA3ZNR75_js = require('./chunk-XA3ZNR75.js');
|
|
8
7
|
require('./chunk-OWW5GUSB.js');
|
|
@@ -27,7 +26,7 @@ function readEnv(...keys) {
|
|
|
27
26
|
var getServerClient = react.cache((opts = {}) => {
|
|
28
27
|
const baseUrl = opts.apiUrl ?? readEnv("CIMPLIFY_API_URL", "NEXT_PUBLIC_CIMPLIFY_API_URL") ?? defaultBaseUrl();
|
|
29
28
|
const publicKey = opts.secretKey ?? readEnv("CIMPLIFY_SECRET_KEY", "NEXT_PUBLIC_CIMPLIFY_PUBLIC_KEY") ?? "mock-dev";
|
|
30
|
-
const client =
|
|
29
|
+
const client = chunkSSLFZPAI_js.createCimplifyClient({
|
|
31
30
|
baseUrl,
|
|
32
31
|
publicKey,
|
|
33
32
|
suppressPublicKeyWarning: true
|
|
@@ -240,6 +239,9 @@ async function refreshPage() {
|
|
|
240
239
|
}
|
|
241
240
|
var SESSION_COOKIE = "cimplify_session";
|
|
242
241
|
var ACCESS_TOKEN_COOKIE = "cimplify_access";
|
|
242
|
+
var REFRESH_TOKEN_COOKIE = "cimplify_refresh";
|
|
243
|
+
var REFRESH_COOKIE_MAX_AGE_SECONDS = 60 * 60 * 24 * 30;
|
|
244
|
+
var ACCESS_REFRESH_SKEW_SECONDS = 30;
|
|
243
245
|
var HEADER_LOCATION = "Location";
|
|
244
246
|
var PARAM_CODE = "code";
|
|
245
247
|
var PARAM_STATE = "state";
|
|
@@ -248,7 +250,7 @@ var QUERY_AUTH_ERROR = "cimplify_auth_error";
|
|
|
248
250
|
var discoveryCache = /* @__PURE__ */ new Map();
|
|
249
251
|
var jwksCache = /* @__PURE__ */ new Map();
|
|
250
252
|
function issuerBase(cfg) {
|
|
251
|
-
return (cfg.issuer ?? cfg.authUrl ??
|
|
253
|
+
return (cfg.issuer ?? cfg.authUrl ?? chunk3QSW33RG_js.DEFAULT_ISSUER).replace(/\/$/, "");
|
|
252
254
|
}
|
|
253
255
|
function cookieName(cfg) {
|
|
254
256
|
return cfg.cookieName ?? SESSION_COOKIE;
|
|
@@ -256,6 +258,9 @@ function cookieName(cfg) {
|
|
|
256
258
|
function accessTokenCookieName(cfg) {
|
|
257
259
|
return cfg.accessTokenCookieName ?? ACCESS_TOKEN_COOKIE;
|
|
258
260
|
}
|
|
261
|
+
function refreshTokenCookieName(cfg) {
|
|
262
|
+
return cfg.refreshTokenCookieName ?? REFRESH_TOKEN_COOKIE;
|
|
263
|
+
}
|
|
259
264
|
async function discovery(cfg) {
|
|
260
265
|
const base = issuerBase(cfg);
|
|
261
266
|
let p = discoveryCache.get(base);
|
|
@@ -356,28 +361,38 @@ function buildAccessTokenCookie(cfg, accessToken, maxAgeSeconds) {
|
|
|
356
361
|
if (cfg.cookieDomain) parts.push(`Domain=${cfg.cookieDomain}`);
|
|
357
362
|
return parts.join("; ");
|
|
358
363
|
}
|
|
359
|
-
function
|
|
360
|
-
const
|
|
361
|
-
`${
|
|
364
|
+
function buildRefreshTokenCookie(cfg, refreshToken, maxAgeSeconds) {
|
|
365
|
+
const parts = [
|
|
366
|
+
`${refreshTokenCookieName(cfg)}=${refreshToken}`,
|
|
362
367
|
"Path=/",
|
|
363
368
|
"HttpOnly",
|
|
364
369
|
"Secure",
|
|
365
370
|
"SameSite=Lax",
|
|
366
|
-
|
|
371
|
+
`Max-Age=${maxAgeSeconds}`
|
|
367
372
|
];
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
"SameSite=Lax",
|
|
374
|
-
|
|
373
|
+
if (cfg.cookieDomain) parts.push(`Domain=${cfg.cookieDomain}`);
|
|
374
|
+
return parts.join("; ");
|
|
375
|
+
}
|
|
376
|
+
function buildSignoutCookies(cfg) {
|
|
377
|
+
const clear = (name) => {
|
|
378
|
+
const parts = [`${name}=`, "Path=/", "HttpOnly", "Secure", "SameSite=Lax", "Max-Age=0"];
|
|
379
|
+
if (cfg.cookieDomain) parts.push(`Domain=${cfg.cookieDomain}`);
|
|
380
|
+
return parts.join("; ");
|
|
381
|
+
};
|
|
382
|
+
return [
|
|
383
|
+
clear(cookieName(cfg)),
|
|
384
|
+
clear(accessTokenCookieName(cfg)),
|
|
385
|
+
clear(refreshTokenCookieName(cfg))
|
|
375
386
|
];
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
}
|
|
380
|
-
|
|
387
|
+
}
|
|
388
|
+
function readCookie(name, cookieHeader) {
|
|
389
|
+
if (!cookieHeader) return null;
|
|
390
|
+
const match = cookieHeader.split(/;\s*/).find((p) => p.startsWith(`${name}=`));
|
|
391
|
+
if (!match) return null;
|
|
392
|
+
return match.slice(name.length + 1) || null;
|
|
393
|
+
}
|
|
394
|
+
function getRefreshTokenFromCookieHeader(cfg, cookieHeader) {
|
|
395
|
+
return readCookie(refreshTokenCookieName(cfg), cookieHeader);
|
|
381
396
|
}
|
|
382
397
|
function getAccessTokenFromCookieHeader(cfg, cookieHeader) {
|
|
383
398
|
if (!cookieHeader) return null;
|
|
@@ -387,7 +402,21 @@ function getAccessTokenFromCookieHeader(cfg, cookieHeader) {
|
|
|
387
402
|
const token = match.slice(name.length + 1);
|
|
388
403
|
return token || null;
|
|
389
404
|
}
|
|
405
|
+
function isSameOriginCallback(req, redirectUri) {
|
|
406
|
+
const site = req.headers.get("sec-fetch-site");
|
|
407
|
+
if (site) return site === "same-origin";
|
|
408
|
+
const origin = req.headers.get("origin");
|
|
409
|
+
if (!origin) return false;
|
|
410
|
+
try {
|
|
411
|
+
return origin === new URL(redirectUri).origin;
|
|
412
|
+
} catch {
|
|
413
|
+
return false;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
390
416
|
async function handleOidcCallback(req, opts) {
|
|
417
|
+
if (!isSameOriginCallback(req, opts.redirectUri)) {
|
|
418
|
+
return Response.json({ error: "forbidden_origin" }, { status: 403 });
|
|
419
|
+
}
|
|
391
420
|
let body;
|
|
392
421
|
try {
|
|
393
422
|
body = await req.json();
|
|
@@ -404,30 +433,44 @@ async function handleOidcCallback(req, opts) {
|
|
|
404
433
|
code: body.code,
|
|
405
434
|
codeVerifier: body.codeVerifier
|
|
406
435
|
});
|
|
407
|
-
} catch
|
|
408
|
-
return Response.json({ error: "exchange_failed"
|
|
436
|
+
} catch {
|
|
437
|
+
return Response.json({ error: "exchange_failed" }, { status: 400 });
|
|
409
438
|
}
|
|
410
439
|
if (!tokens.idToken) {
|
|
411
440
|
return Response.json({ error: "missing_id_token" }, { status: 400 });
|
|
412
441
|
}
|
|
413
442
|
try {
|
|
414
443
|
await verifyIdToken(opts, tokens.idToken);
|
|
415
|
-
} catch
|
|
416
|
-
return Response.json({ error: "id_token_invalid"
|
|
444
|
+
} catch {
|
|
445
|
+
return Response.json({ error: "id_token_invalid" }, { status: 401 });
|
|
417
446
|
}
|
|
418
447
|
const headers = new Headers({ "Content-Type": "application/json" });
|
|
419
448
|
headers.append("Set-Cookie", buildSessionCookie(opts, tokens.idToken, tokens.expiresIn));
|
|
420
449
|
headers.append("Set-Cookie", buildAccessTokenCookie(opts, tokens.accessToken, tokens.expiresIn));
|
|
421
|
-
|
|
450
|
+
if (tokens.refreshToken) {
|
|
451
|
+
headers.append(
|
|
452
|
+
"Set-Cookie",
|
|
453
|
+
buildRefreshTokenCookie(opts, tokens.refreshToken, REFRESH_COOKIE_MAX_AGE_SECONDS)
|
|
454
|
+
);
|
|
455
|
+
}
|
|
456
|
+
return new Response(
|
|
457
|
+
JSON.stringify({
|
|
458
|
+
ok: true,
|
|
459
|
+
accessToken: tokens.accessToken,
|
|
460
|
+
expiresIn: tokens.expiresIn,
|
|
461
|
+
scope: tokens.scope
|
|
462
|
+
}),
|
|
463
|
+
{ status: 200, headers }
|
|
464
|
+
);
|
|
422
465
|
}
|
|
423
466
|
function readPkceCookie(cookieHeader) {
|
|
424
467
|
if (!cookieHeader) return null;
|
|
425
|
-
const match = cookieHeader.split(/;\s*/).find((p) => p.startsWith(`${
|
|
468
|
+
const match = cookieHeader.split(/;\s*/).find((p) => p.startsWith(`${chunk3QSW33RG_js.PKCE_COOKIE}=`));
|
|
426
469
|
if (!match) return null;
|
|
427
|
-
return
|
|
470
|
+
return chunk3QSW33RG_js.decodePkceState(match.slice(chunk3QSW33RG_js.PKCE_COOKIE.length + 1));
|
|
428
471
|
}
|
|
429
472
|
function buildPkceClearCookie() {
|
|
430
|
-
return `${
|
|
473
|
+
return `${chunk3QSW33RG_js.PKCE_COOKIE}=; Path=${chunk3QSW33RG_js.PKCE_COOKIE_PATH}; Max-Age=0; Secure; SameSite=Lax`;
|
|
431
474
|
}
|
|
432
475
|
function safeReturnPath(returnTo, fallback) {
|
|
433
476
|
if (returnTo && returnTo.startsWith("/") && !returnTo.startsWith("//")) return returnTo;
|
|
@@ -467,6 +510,12 @@ async function handleRedirectCallback(req, opts) {
|
|
|
467
510
|
const headers = new Headers({ [HEADER_LOCATION]: returnTo });
|
|
468
511
|
headers.append("Set-Cookie", buildSessionCookie(opts, tokens.idToken, tokens.expiresIn));
|
|
469
512
|
headers.append("Set-Cookie", buildAccessTokenCookie(opts, tokens.accessToken, tokens.expiresIn));
|
|
513
|
+
if (tokens.refreshToken) {
|
|
514
|
+
headers.append(
|
|
515
|
+
"Set-Cookie",
|
|
516
|
+
buildRefreshTokenCookie(opts, tokens.refreshToken, REFRESH_COOKIE_MAX_AGE_SECONDS)
|
|
517
|
+
);
|
|
518
|
+
}
|
|
470
519
|
headers.append("Set-Cookie", buildPkceClearCookie());
|
|
471
520
|
return new Response(null, { status: 302, headers });
|
|
472
521
|
}
|
|
@@ -497,6 +546,99 @@ async function getSessionFromCookieHeader(cfg, cookieHeader) {
|
|
|
497
546
|
return null;
|
|
498
547
|
}
|
|
499
548
|
}
|
|
549
|
+
async function refreshTokens(cfg, refreshToken) {
|
|
550
|
+
const doc = await discovery(cfg);
|
|
551
|
+
const body = new URLSearchParams({
|
|
552
|
+
grant_type: "refresh_token",
|
|
553
|
+
refresh_token: refreshToken,
|
|
554
|
+
client_id: cfg.clientId
|
|
555
|
+
});
|
|
556
|
+
const res = await fetch(doc.token_endpoint, {
|
|
557
|
+
method: "POST",
|
|
558
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
559
|
+
body: body.toString()
|
|
560
|
+
});
|
|
561
|
+
if (!res.ok) {
|
|
562
|
+
const text = await res.text().catch(() => "");
|
|
563
|
+
throw new Error(`token refresh failed (${res.status}): ${text}`);
|
|
564
|
+
}
|
|
565
|
+
const payload = await res.json();
|
|
566
|
+
return {
|
|
567
|
+
accessToken: payload.access_token,
|
|
568
|
+
idToken: payload.id_token,
|
|
569
|
+
refreshToken: payload.refresh_token,
|
|
570
|
+
expiresIn: payload.expires_in,
|
|
571
|
+
scope: payload.scope
|
|
572
|
+
};
|
|
573
|
+
}
|
|
574
|
+
function decodeBase64Url(segment) {
|
|
575
|
+
const b64 = segment.replace(/-/g, "+").replace(/_/g, "/");
|
|
576
|
+
const pad = b64.length % 4 ? "=".repeat(4 - b64.length % 4) : "";
|
|
577
|
+
return atob(b64 + pad);
|
|
578
|
+
}
|
|
579
|
+
function accessTokenExpiry(token) {
|
|
580
|
+
if (!token) return null;
|
|
581
|
+
const parts = token.split(".");
|
|
582
|
+
if (parts.length < 2) return null;
|
|
583
|
+
try {
|
|
584
|
+
const payload = JSON.parse(decodeBase64Url(parts[1]));
|
|
585
|
+
return typeof payload.exp === "number" ? payload.exp : null;
|
|
586
|
+
} catch {
|
|
587
|
+
return null;
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
var refreshInflight = /* @__PURE__ */ new Map();
|
|
591
|
+
function dedupedRefresh(cfg, refreshToken) {
|
|
592
|
+
const existing = refreshInflight.get(refreshToken);
|
|
593
|
+
if (existing) return existing;
|
|
594
|
+
const p = refreshTokens(cfg, refreshToken).finally(() => refreshInflight.delete(refreshToken));
|
|
595
|
+
refreshInflight.set(refreshToken, p);
|
|
596
|
+
return p;
|
|
597
|
+
}
|
|
598
|
+
function clearedResult(cfg) {
|
|
599
|
+
return {
|
|
600
|
+
outcome: "cleared",
|
|
601
|
+
setCookies: buildSignoutCookies(cfg),
|
|
602
|
+
cookies: [
|
|
603
|
+
{ name: cookieName(cfg), value: "" },
|
|
604
|
+
{ name: accessTokenCookieName(cfg), value: "" },
|
|
605
|
+
{ name: refreshTokenCookieName(cfg), value: "" }
|
|
606
|
+
]
|
|
607
|
+
};
|
|
608
|
+
}
|
|
609
|
+
async function handleTokenRefresh(req, cfg) {
|
|
610
|
+
const cookieHeader = req.headers.get("cookie");
|
|
611
|
+
const refreshToken = getRefreshTokenFromCookieHeader(cfg, cookieHeader);
|
|
612
|
+
if (!refreshToken) return { outcome: "noop", setCookies: [], cookies: [] };
|
|
613
|
+
const access = getAccessTokenFromCookieHeader(cfg, cookieHeader);
|
|
614
|
+
const exp = accessTokenExpiry(access);
|
|
615
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
616
|
+
if (access && exp !== null && exp - now > ACCESS_REFRESH_SKEW_SECONDS) {
|
|
617
|
+
return { outcome: "noop", setCookies: [], cookies: [] };
|
|
618
|
+
}
|
|
619
|
+
let tokens;
|
|
620
|
+
try {
|
|
621
|
+
tokens = await dedupedRefresh(cfg, refreshToken);
|
|
622
|
+
} catch {
|
|
623
|
+
return clearedResult(cfg);
|
|
624
|
+
}
|
|
625
|
+
if (!tokens.idToken) return clearedResult(cfg);
|
|
626
|
+
const setCookies = [
|
|
627
|
+
buildSessionCookie(cfg, tokens.idToken, tokens.expiresIn),
|
|
628
|
+
buildAccessTokenCookie(cfg, tokens.accessToken, tokens.expiresIn)
|
|
629
|
+
];
|
|
630
|
+
const cookies = [
|
|
631
|
+
{ name: cookieName(cfg), value: tokens.idToken },
|
|
632
|
+
{ name: accessTokenCookieName(cfg), value: tokens.accessToken }
|
|
633
|
+
];
|
|
634
|
+
if (tokens.refreshToken) {
|
|
635
|
+
setCookies.push(
|
|
636
|
+
buildRefreshTokenCookie(cfg, tokens.refreshToken, REFRESH_COOKIE_MAX_AGE_SECONDS)
|
|
637
|
+
);
|
|
638
|
+
cookies.push({ name: refreshTokenCookieName(cfg), value: tokens.refreshToken });
|
|
639
|
+
}
|
|
640
|
+
return { outcome: "refreshed", setCookies, cookies };
|
|
641
|
+
}
|
|
500
642
|
|
|
501
643
|
Object.defineProperty(exports, "CimplifyError", {
|
|
502
644
|
enumerable: true,
|
|
@@ -505,17 +647,21 @@ Object.defineProperty(exports, "CimplifyError", {
|
|
|
505
647
|
exports.CACHE_LIFE_DEFAULT = CACHE_LIFE_DEFAULT;
|
|
506
648
|
exports.CACHE_LIFE_PROBE = CACHE_LIFE_PROBE;
|
|
507
649
|
exports.buildAccessTokenCookie = buildAccessTokenCookie;
|
|
650
|
+
exports.buildRefreshTokenCookie = buildRefreshTokenCookie;
|
|
508
651
|
exports.buildSessionCookie = buildSessionCookie;
|
|
509
652
|
exports.buildSignoutCookie = buildSignoutCookie;
|
|
510
653
|
exports.buildSignoutCookies = buildSignoutCookies;
|
|
511
654
|
exports.exchangeCode = exchangeCode;
|
|
512
655
|
exports.getAccessTokenFromCookieHeader = getAccessTokenFromCookieHeader;
|
|
656
|
+
exports.getRefreshTokenFromCookieHeader = getRefreshTokenFromCookieHeader;
|
|
513
657
|
exports.getServerClient = getServerClient;
|
|
514
658
|
exports.getSessionFromCookieHeader = getSessionFromCookieHeader;
|
|
515
659
|
exports.handleOidcCallback = handleOidcCallback;
|
|
516
660
|
exports.handleRedirectCallback = handleRedirectCallback;
|
|
517
661
|
exports.handleSessionRequest = handleSessionRequest;
|
|
662
|
+
exports.handleTokenRefresh = handleTokenRefresh;
|
|
518
663
|
exports.refreshPage = refreshPage;
|
|
664
|
+
exports.refreshTokens = refreshTokens;
|
|
519
665
|
exports.revalidateAddOn = revalidateAddOn;
|
|
520
666
|
exports.revalidateAddOns = revalidateAddOns;
|
|
521
667
|
exports.revalidateBrand = revalidateBrand;
|
package/dist/server.mjs
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import './chunk-R6363CC7.mjs';
|
|
1
|
+
import { createCimplifyClient } from './chunk-JAPCAZN4.mjs';
|
|
2
|
+
import { PKCE_COOKIE, decodePkceState, PKCE_COOKIE_PATH, DEFAULT_ISSUER } from './chunk-QQMDEGAK.mjs';
|
|
4
3
|
import './chunk-TD3AY34U.mjs';
|
|
5
4
|
export { CimplifyError } from './chunk-AMZXALF6.mjs';
|
|
6
5
|
import './chunk-3G6RQLXK.mjs';
|
|
@@ -238,6 +237,9 @@ async function refreshPage() {
|
|
|
238
237
|
}
|
|
239
238
|
var SESSION_COOKIE = "cimplify_session";
|
|
240
239
|
var ACCESS_TOKEN_COOKIE = "cimplify_access";
|
|
240
|
+
var REFRESH_TOKEN_COOKIE = "cimplify_refresh";
|
|
241
|
+
var REFRESH_COOKIE_MAX_AGE_SECONDS = 60 * 60 * 24 * 30;
|
|
242
|
+
var ACCESS_REFRESH_SKEW_SECONDS = 30;
|
|
241
243
|
var HEADER_LOCATION = "Location";
|
|
242
244
|
var PARAM_CODE = "code";
|
|
243
245
|
var PARAM_STATE = "state";
|
|
@@ -254,6 +256,9 @@ function cookieName(cfg) {
|
|
|
254
256
|
function accessTokenCookieName(cfg) {
|
|
255
257
|
return cfg.accessTokenCookieName ?? ACCESS_TOKEN_COOKIE;
|
|
256
258
|
}
|
|
259
|
+
function refreshTokenCookieName(cfg) {
|
|
260
|
+
return cfg.refreshTokenCookieName ?? REFRESH_TOKEN_COOKIE;
|
|
261
|
+
}
|
|
257
262
|
async function discovery(cfg) {
|
|
258
263
|
const base = issuerBase(cfg);
|
|
259
264
|
let p = discoveryCache.get(base);
|
|
@@ -354,28 +359,38 @@ function buildAccessTokenCookie(cfg, accessToken, maxAgeSeconds) {
|
|
|
354
359
|
if (cfg.cookieDomain) parts.push(`Domain=${cfg.cookieDomain}`);
|
|
355
360
|
return parts.join("; ");
|
|
356
361
|
}
|
|
357
|
-
function
|
|
358
|
-
const
|
|
359
|
-
`${
|
|
362
|
+
function buildRefreshTokenCookie(cfg, refreshToken, maxAgeSeconds) {
|
|
363
|
+
const parts = [
|
|
364
|
+
`${refreshTokenCookieName(cfg)}=${refreshToken}`,
|
|
360
365
|
"Path=/",
|
|
361
366
|
"HttpOnly",
|
|
362
367
|
"Secure",
|
|
363
368
|
"SameSite=Lax",
|
|
364
|
-
|
|
369
|
+
`Max-Age=${maxAgeSeconds}`
|
|
365
370
|
];
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
"SameSite=Lax",
|
|
372
|
-
|
|
371
|
+
if (cfg.cookieDomain) parts.push(`Domain=${cfg.cookieDomain}`);
|
|
372
|
+
return parts.join("; ");
|
|
373
|
+
}
|
|
374
|
+
function buildSignoutCookies(cfg) {
|
|
375
|
+
const clear = (name) => {
|
|
376
|
+
const parts = [`${name}=`, "Path=/", "HttpOnly", "Secure", "SameSite=Lax", "Max-Age=0"];
|
|
377
|
+
if (cfg.cookieDomain) parts.push(`Domain=${cfg.cookieDomain}`);
|
|
378
|
+
return parts.join("; ");
|
|
379
|
+
};
|
|
380
|
+
return [
|
|
381
|
+
clear(cookieName(cfg)),
|
|
382
|
+
clear(accessTokenCookieName(cfg)),
|
|
383
|
+
clear(refreshTokenCookieName(cfg))
|
|
373
384
|
];
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
}
|
|
378
|
-
|
|
385
|
+
}
|
|
386
|
+
function readCookie(name, cookieHeader) {
|
|
387
|
+
if (!cookieHeader) return null;
|
|
388
|
+
const match = cookieHeader.split(/;\s*/).find((p) => p.startsWith(`${name}=`));
|
|
389
|
+
if (!match) return null;
|
|
390
|
+
return match.slice(name.length + 1) || null;
|
|
391
|
+
}
|
|
392
|
+
function getRefreshTokenFromCookieHeader(cfg, cookieHeader) {
|
|
393
|
+
return readCookie(refreshTokenCookieName(cfg), cookieHeader);
|
|
379
394
|
}
|
|
380
395
|
function getAccessTokenFromCookieHeader(cfg, cookieHeader) {
|
|
381
396
|
if (!cookieHeader) return null;
|
|
@@ -385,7 +400,21 @@ function getAccessTokenFromCookieHeader(cfg, cookieHeader) {
|
|
|
385
400
|
const token = match.slice(name.length + 1);
|
|
386
401
|
return token || null;
|
|
387
402
|
}
|
|
403
|
+
function isSameOriginCallback(req, redirectUri) {
|
|
404
|
+
const site = req.headers.get("sec-fetch-site");
|
|
405
|
+
if (site) return site === "same-origin";
|
|
406
|
+
const origin = req.headers.get("origin");
|
|
407
|
+
if (!origin) return false;
|
|
408
|
+
try {
|
|
409
|
+
return origin === new URL(redirectUri).origin;
|
|
410
|
+
} catch {
|
|
411
|
+
return false;
|
|
412
|
+
}
|
|
413
|
+
}
|
|
388
414
|
async function handleOidcCallback(req, opts) {
|
|
415
|
+
if (!isSameOriginCallback(req, opts.redirectUri)) {
|
|
416
|
+
return Response.json({ error: "forbidden_origin" }, { status: 403 });
|
|
417
|
+
}
|
|
389
418
|
let body;
|
|
390
419
|
try {
|
|
391
420
|
body = await req.json();
|
|
@@ -402,21 +431,35 @@ async function handleOidcCallback(req, opts) {
|
|
|
402
431
|
code: body.code,
|
|
403
432
|
codeVerifier: body.codeVerifier
|
|
404
433
|
});
|
|
405
|
-
} catch
|
|
406
|
-
return Response.json({ error: "exchange_failed"
|
|
434
|
+
} catch {
|
|
435
|
+
return Response.json({ error: "exchange_failed" }, { status: 400 });
|
|
407
436
|
}
|
|
408
437
|
if (!tokens.idToken) {
|
|
409
438
|
return Response.json({ error: "missing_id_token" }, { status: 400 });
|
|
410
439
|
}
|
|
411
440
|
try {
|
|
412
441
|
await verifyIdToken(opts, tokens.idToken);
|
|
413
|
-
} catch
|
|
414
|
-
return Response.json({ error: "id_token_invalid"
|
|
442
|
+
} catch {
|
|
443
|
+
return Response.json({ error: "id_token_invalid" }, { status: 401 });
|
|
415
444
|
}
|
|
416
445
|
const headers = new Headers({ "Content-Type": "application/json" });
|
|
417
446
|
headers.append("Set-Cookie", buildSessionCookie(opts, tokens.idToken, tokens.expiresIn));
|
|
418
447
|
headers.append("Set-Cookie", buildAccessTokenCookie(opts, tokens.accessToken, tokens.expiresIn));
|
|
419
|
-
|
|
448
|
+
if (tokens.refreshToken) {
|
|
449
|
+
headers.append(
|
|
450
|
+
"Set-Cookie",
|
|
451
|
+
buildRefreshTokenCookie(opts, tokens.refreshToken, REFRESH_COOKIE_MAX_AGE_SECONDS)
|
|
452
|
+
);
|
|
453
|
+
}
|
|
454
|
+
return new Response(
|
|
455
|
+
JSON.stringify({
|
|
456
|
+
ok: true,
|
|
457
|
+
accessToken: tokens.accessToken,
|
|
458
|
+
expiresIn: tokens.expiresIn,
|
|
459
|
+
scope: tokens.scope
|
|
460
|
+
}),
|
|
461
|
+
{ status: 200, headers }
|
|
462
|
+
);
|
|
420
463
|
}
|
|
421
464
|
function readPkceCookie(cookieHeader) {
|
|
422
465
|
if (!cookieHeader) return null;
|
|
@@ -465,6 +508,12 @@ async function handleRedirectCallback(req, opts) {
|
|
|
465
508
|
const headers = new Headers({ [HEADER_LOCATION]: returnTo });
|
|
466
509
|
headers.append("Set-Cookie", buildSessionCookie(opts, tokens.idToken, tokens.expiresIn));
|
|
467
510
|
headers.append("Set-Cookie", buildAccessTokenCookie(opts, tokens.accessToken, tokens.expiresIn));
|
|
511
|
+
if (tokens.refreshToken) {
|
|
512
|
+
headers.append(
|
|
513
|
+
"Set-Cookie",
|
|
514
|
+
buildRefreshTokenCookie(opts, tokens.refreshToken, REFRESH_COOKIE_MAX_AGE_SECONDS)
|
|
515
|
+
);
|
|
516
|
+
}
|
|
468
517
|
headers.append("Set-Cookie", buildPkceClearCookie());
|
|
469
518
|
return new Response(null, { status: 302, headers });
|
|
470
519
|
}
|
|
@@ -495,5 +544,98 @@ async function getSessionFromCookieHeader(cfg, cookieHeader) {
|
|
|
495
544
|
return null;
|
|
496
545
|
}
|
|
497
546
|
}
|
|
547
|
+
async function refreshTokens(cfg, refreshToken) {
|
|
548
|
+
const doc = await discovery(cfg);
|
|
549
|
+
const body = new URLSearchParams({
|
|
550
|
+
grant_type: "refresh_token",
|
|
551
|
+
refresh_token: refreshToken,
|
|
552
|
+
client_id: cfg.clientId
|
|
553
|
+
});
|
|
554
|
+
const res = await fetch(doc.token_endpoint, {
|
|
555
|
+
method: "POST",
|
|
556
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
557
|
+
body: body.toString()
|
|
558
|
+
});
|
|
559
|
+
if (!res.ok) {
|
|
560
|
+
const text = await res.text().catch(() => "");
|
|
561
|
+
throw new Error(`token refresh failed (${res.status}): ${text}`);
|
|
562
|
+
}
|
|
563
|
+
const payload = await res.json();
|
|
564
|
+
return {
|
|
565
|
+
accessToken: payload.access_token,
|
|
566
|
+
idToken: payload.id_token,
|
|
567
|
+
refreshToken: payload.refresh_token,
|
|
568
|
+
expiresIn: payload.expires_in,
|
|
569
|
+
scope: payload.scope
|
|
570
|
+
};
|
|
571
|
+
}
|
|
572
|
+
function decodeBase64Url(segment) {
|
|
573
|
+
const b64 = segment.replace(/-/g, "+").replace(/_/g, "/");
|
|
574
|
+
const pad = b64.length % 4 ? "=".repeat(4 - b64.length % 4) : "";
|
|
575
|
+
return atob(b64 + pad);
|
|
576
|
+
}
|
|
577
|
+
function accessTokenExpiry(token) {
|
|
578
|
+
if (!token) return null;
|
|
579
|
+
const parts = token.split(".");
|
|
580
|
+
if (parts.length < 2) return null;
|
|
581
|
+
try {
|
|
582
|
+
const payload = JSON.parse(decodeBase64Url(parts[1]));
|
|
583
|
+
return typeof payload.exp === "number" ? payload.exp : null;
|
|
584
|
+
} catch {
|
|
585
|
+
return null;
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
var refreshInflight = /* @__PURE__ */ new Map();
|
|
589
|
+
function dedupedRefresh(cfg, refreshToken) {
|
|
590
|
+
const existing = refreshInflight.get(refreshToken);
|
|
591
|
+
if (existing) return existing;
|
|
592
|
+
const p = refreshTokens(cfg, refreshToken).finally(() => refreshInflight.delete(refreshToken));
|
|
593
|
+
refreshInflight.set(refreshToken, p);
|
|
594
|
+
return p;
|
|
595
|
+
}
|
|
596
|
+
function clearedResult(cfg) {
|
|
597
|
+
return {
|
|
598
|
+
outcome: "cleared",
|
|
599
|
+
setCookies: buildSignoutCookies(cfg),
|
|
600
|
+
cookies: [
|
|
601
|
+
{ name: cookieName(cfg), value: "" },
|
|
602
|
+
{ name: accessTokenCookieName(cfg), value: "" },
|
|
603
|
+
{ name: refreshTokenCookieName(cfg), value: "" }
|
|
604
|
+
]
|
|
605
|
+
};
|
|
606
|
+
}
|
|
607
|
+
async function handleTokenRefresh(req, cfg) {
|
|
608
|
+
const cookieHeader = req.headers.get("cookie");
|
|
609
|
+
const refreshToken = getRefreshTokenFromCookieHeader(cfg, cookieHeader);
|
|
610
|
+
if (!refreshToken) return { outcome: "noop", setCookies: [], cookies: [] };
|
|
611
|
+
const access = getAccessTokenFromCookieHeader(cfg, cookieHeader);
|
|
612
|
+
const exp = accessTokenExpiry(access);
|
|
613
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
614
|
+
if (access && exp !== null && exp - now > ACCESS_REFRESH_SKEW_SECONDS) {
|
|
615
|
+
return { outcome: "noop", setCookies: [], cookies: [] };
|
|
616
|
+
}
|
|
617
|
+
let tokens;
|
|
618
|
+
try {
|
|
619
|
+
tokens = await dedupedRefresh(cfg, refreshToken);
|
|
620
|
+
} catch {
|
|
621
|
+
return clearedResult(cfg);
|
|
622
|
+
}
|
|
623
|
+
if (!tokens.idToken) return clearedResult(cfg);
|
|
624
|
+
const setCookies = [
|
|
625
|
+
buildSessionCookie(cfg, tokens.idToken, tokens.expiresIn),
|
|
626
|
+
buildAccessTokenCookie(cfg, tokens.accessToken, tokens.expiresIn)
|
|
627
|
+
];
|
|
628
|
+
const cookies = [
|
|
629
|
+
{ name: cookieName(cfg), value: tokens.idToken },
|
|
630
|
+
{ name: accessTokenCookieName(cfg), value: tokens.accessToken }
|
|
631
|
+
];
|
|
632
|
+
if (tokens.refreshToken) {
|
|
633
|
+
setCookies.push(
|
|
634
|
+
buildRefreshTokenCookie(cfg, tokens.refreshToken, REFRESH_COOKIE_MAX_AGE_SECONDS)
|
|
635
|
+
);
|
|
636
|
+
cookies.push({ name: refreshTokenCookieName(cfg), value: tokens.refreshToken });
|
|
637
|
+
}
|
|
638
|
+
return { outcome: "refreshed", setCookies, cookies };
|
|
639
|
+
}
|
|
498
640
|
|
|
499
|
-
export { CACHE_LIFE_DEFAULT, CACHE_LIFE_PROBE, buildAccessTokenCookie, buildSessionCookie, buildSignoutCookie, buildSignoutCookies, exchangeCode, getAccessTokenFromCookieHeader, getServerClient, getSessionFromCookieHeader, handleOidcCallback, handleRedirectCallback, handleSessionRequest, refreshPage, revalidateAddOn, revalidateAddOns, revalidateBrand, revalidateBusiness, revalidateByTag, revalidateCategories, revalidateCategory, revalidateCollection, revalidateCollections, revalidateLocation, revalidateLocations, revalidatePricing, revalidateProduct, revalidateProducts, revalidateStock, revalidateSubscription, revalidateSubscriptions, tags, updateAddOn, updateAddOns, updateBrand, updateBusiness, updateByTag, updateCategories, updateCategory, updateCollection, updateCollections, updateLocation, updateLocations, updatePricing, updateProduct, updateProducts, updateStock, updateSubscription, updateSubscriptions, verifyIdToken };
|
|
641
|
+
export { CACHE_LIFE_DEFAULT, CACHE_LIFE_PROBE, buildAccessTokenCookie, buildRefreshTokenCookie, buildSessionCookie, buildSignoutCookie, buildSignoutCookies, exchangeCode, getAccessTokenFromCookieHeader, getRefreshTokenFromCookieHeader, getServerClient, getSessionFromCookieHeader, handleOidcCallback, handleRedirectCallback, handleSessionRequest, handleTokenRefresh, refreshPage, refreshTokens, revalidateAddOn, revalidateAddOns, revalidateBrand, revalidateBusiness, revalidateByTag, revalidateCategories, revalidateCategory, revalidateCollection, revalidateCollections, revalidateLocation, revalidateLocations, revalidatePricing, revalidateProduct, revalidateProducts, revalidateStock, revalidateSubscription, revalidateSubscriptions, tags, updateAddOn, updateAddOns, updateBrand, updateBusiness, updateByTag, updateCategories, updateCategory, updateCollection, updateCollections, updateLocation, updateLocations, updatePricing, updateProduct, updateProducts, updateStock, updateSubscription, updateSubscriptions, verifyIdToken };
|
|
@@ -50,7 +50,12 @@ interface SignInOptions {
|
|
|
50
50
|
error: string;
|
|
51
51
|
description?: string;
|
|
52
52
|
}) => void;
|
|
53
|
-
onSuccess?: () => void;
|
|
53
|
+
onSuccess?: (result: SignInSuccess) => void;
|
|
54
|
+
}
|
|
55
|
+
interface SignInSuccess {
|
|
56
|
+
accessToken?: string;
|
|
57
|
+
expiresIn?: number;
|
|
58
|
+
scope?: string;
|
|
54
59
|
}
|
|
55
60
|
declare function startSignIn(opts: SignInOptions): Promise<void>;
|
|
56
61
|
|
|
@@ -50,7 +50,12 @@ interface SignInOptions {
|
|
|
50
50
|
error: string;
|
|
51
51
|
description?: string;
|
|
52
52
|
}) => void;
|
|
53
|
-
onSuccess?: () => void;
|
|
53
|
+
onSuccess?: (result: SignInSuccess) => void;
|
|
54
|
+
}
|
|
55
|
+
interface SignInSuccess {
|
|
56
|
+
accessToken?: string;
|
|
57
|
+
expiresIn?: number;
|
|
58
|
+
scope?: string;
|
|
54
59
|
}
|
|
55
60
|
declare function startSignIn(opts: SignInOptions): Promise<void>;
|
|
56
61
|
|