@feelflow/ffid-sdk 2.17.0 → 2.18.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/{chunk-OWE6EPWE.cjs → chunk-EK2W67BW.cjs} +108 -6
- package/dist/{chunk-O2YI5N6S.js → chunk-IEYXT3LA.js} +108 -6
- package/dist/components/index.cjs +8 -8
- package/dist/components/index.d.cts +1 -1
- package/dist/components/index.d.ts +1 -1
- package/dist/components/index.js +1 -1
- package/dist/{index-DbEyptzr.d.cts → index--S6rLHjr.d.cts} +26 -4
- package/dist/{index-DbEyptzr.d.ts → index--S6rLHjr.d.ts} +26 -4
- package/dist/index.cjs +28 -28
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/server/index.cjs +121 -6
- package/dist/server/index.d.cts +25 -3
- package/dist/server/index.d.ts +25 -3
- package/dist/server/index.js +121 -6
- package/package.json +1 -1
|
@@ -105,10 +105,7 @@ function createTokenStore(storageType) {
|
|
|
105
105
|
// src/client/oauth-userinfo.ts
|
|
106
106
|
var SESSION_ELIGIBLE_SUBSCRIPTION_STATUSES = [
|
|
107
107
|
"trialing",
|
|
108
|
-
"active"
|
|
109
|
-
"past_due",
|
|
110
|
-
"canceled",
|
|
111
|
-
"paused"
|
|
108
|
+
"active"
|
|
112
109
|
];
|
|
113
110
|
function isSessionEligibleSubscriptionStatus(value) {
|
|
114
111
|
return typeof value === "string" && SESSION_ELIGIBLE_SUBSCRIPTION_STATUSES.includes(value);
|
|
@@ -810,10 +807,11 @@ function createProfileMethods(deps) {
|
|
|
810
807
|
}
|
|
811
808
|
|
|
812
809
|
// src/client/version-check.ts
|
|
813
|
-
var SDK_VERSION = "2.
|
|
810
|
+
var SDK_VERSION = "2.18.0";
|
|
814
811
|
var SDK_USER_AGENT = `FFID-SDK/${SDK_VERSION} (TypeScript)`;
|
|
815
812
|
var SDK_VERSION_HEADER = "X-FFID-SDK-Version";
|
|
816
813
|
function sdkHeaders() {
|
|
814
|
+
if (typeof window !== "undefined") return {};
|
|
817
815
|
return {
|
|
818
816
|
"User-Agent": SDK_USER_AGENT,
|
|
819
817
|
[SDK_VERSION_HEADER]: SDK_VERSION
|
|
@@ -1372,6 +1370,87 @@ var OAUTH_AUTHORIZE_ENDPOINT = "/api/v1/oauth/authorize";
|
|
|
1372
1370
|
var AUTH_LOGOUT_ENDPOINT = "/api/v1/auth/logout";
|
|
1373
1371
|
var STATE_RANDOM_BYTES = 16;
|
|
1374
1372
|
var HEX_BASE2 = 16;
|
|
1373
|
+
var REDIRECT_LOOP_KEY = "ffid_sdk_redirect_loop_history";
|
|
1374
|
+
var REDIRECT_LOOP_WINDOW_MS = 6e4;
|
|
1375
|
+
var REDIRECT_LOOP_THRESHOLD = 3;
|
|
1376
|
+
var storageReadFailureLogged = false;
|
|
1377
|
+
var storageWriteFailureLogged = false;
|
|
1378
|
+
function logStorageReadFailure(logger, err) {
|
|
1379
|
+
if (storageReadFailureLogged) return;
|
|
1380
|
+
storageReadFailureLogged = true;
|
|
1381
|
+
logger.warn(
|
|
1382
|
+
"[FFID SDK] sessionStorage read failed \u2014 redirect loop detection disabled on this browser (fail-open). iOS WebKit private mode / eviction \u304C\u5178\u578B\u8981\u56E0",
|
|
1383
|
+
err
|
|
1384
|
+
);
|
|
1385
|
+
}
|
|
1386
|
+
function logStorageWriteFailure(logger, err) {
|
|
1387
|
+
if (storageWriteFailureLogged) return;
|
|
1388
|
+
storageWriteFailureLogged = true;
|
|
1389
|
+
logger.warn(
|
|
1390
|
+
"[FFID SDK] sessionStorage write failed \u2014 redirect loop detection disabled on this browser (fail-open).",
|
|
1391
|
+
err
|
|
1392
|
+
);
|
|
1393
|
+
}
|
|
1394
|
+
function isRedirectLoopHistory(value) {
|
|
1395
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
|
|
1396
|
+
return Object.values(value).every(
|
|
1397
|
+
(arr) => Array.isArray(arr) && arr.every((t) => typeof t === "number" && Number.isFinite(t))
|
|
1398
|
+
);
|
|
1399
|
+
}
|
|
1400
|
+
function readRedirectLoopHistory(logger) {
|
|
1401
|
+
if (typeof window === "undefined") return {};
|
|
1402
|
+
try {
|
|
1403
|
+
const raw = window.sessionStorage.getItem(REDIRECT_LOOP_KEY);
|
|
1404
|
+
if (raw === null) return {};
|
|
1405
|
+
const parsed = JSON.parse(raw);
|
|
1406
|
+
return isRedirectLoopHistory(parsed) ? parsed : {};
|
|
1407
|
+
} catch (err) {
|
|
1408
|
+
logStorageReadFailure(logger, err);
|
|
1409
|
+
return {};
|
|
1410
|
+
}
|
|
1411
|
+
}
|
|
1412
|
+
function writeRedirectLoopHistory(history, logger) {
|
|
1413
|
+
if (typeof window === "undefined") return;
|
|
1414
|
+
try {
|
|
1415
|
+
window.sessionStorage.setItem(REDIRECT_LOOP_KEY, JSON.stringify(history));
|
|
1416
|
+
} catch (err) {
|
|
1417
|
+
logStorageWriteFailure(logger, err);
|
|
1418
|
+
}
|
|
1419
|
+
}
|
|
1420
|
+
function pruneRedirectLoopHistory(history, now) {
|
|
1421
|
+
const cutoff = now - REDIRECT_LOOP_WINDOW_MS;
|
|
1422
|
+
const pruned = {};
|
|
1423
|
+
for (const [key, timestamps] of Object.entries(history)) {
|
|
1424
|
+
const fresh = timestamps.filter((t) => t > cutoff);
|
|
1425
|
+
if (fresh.length > 0) pruned[key] = fresh;
|
|
1426
|
+
}
|
|
1427
|
+
return pruned;
|
|
1428
|
+
}
|
|
1429
|
+
function getRecentRedirectCount(authorizeKey, now, logger) {
|
|
1430
|
+
const pruned = pruneRedirectLoopHistory(readRedirectLoopHistory(logger), now);
|
|
1431
|
+
writeRedirectLoopHistory(pruned, logger);
|
|
1432
|
+
return pruned[authorizeKey]?.length ?? 0;
|
|
1433
|
+
}
|
|
1434
|
+
function recordRedirectAttempt(authorizeKey, now, logger) {
|
|
1435
|
+
const history = readRedirectLoopHistory(logger);
|
|
1436
|
+
const existing = history[authorizeKey] ?? [];
|
|
1437
|
+
history[authorizeKey] = [...existing, now];
|
|
1438
|
+
writeRedirectLoopHistory(history, logger);
|
|
1439
|
+
}
|
|
1440
|
+
function rollbackLastRedirectAttempt(authorizeKey, logger) {
|
|
1441
|
+
const history = readRedirectLoopHistory(logger);
|
|
1442
|
+
const existing = history[authorizeKey];
|
|
1443
|
+
if (!Array.isArray(existing) || existing.length === 0) return;
|
|
1444
|
+
history[authorizeKey] = existing.slice(0, -1);
|
|
1445
|
+
if (history[authorizeKey].length === 0) delete history[authorizeKey];
|
|
1446
|
+
writeRedirectLoopHistory(history, logger);
|
|
1447
|
+
}
|
|
1448
|
+
function buildAuthorizeKey(baseUrl, clientId, organizationId) {
|
|
1449
|
+
const params = new URLSearchParams({ client_id: clientId });
|
|
1450
|
+
const trimmedOrg = organizationId?.trim();
|
|
1451
|
+
if (trimmedOrg) params.set("organization_id", trimmedOrg);
|
|
1452
|
+
return `${baseUrl}${OAUTH_AUTHORIZE_ENDPOINT}?${params.toString()}`;
|
|
1453
|
+
}
|
|
1375
1454
|
function generateRandomState() {
|
|
1376
1455
|
const array = new Uint8Array(STATE_RANDOM_BYTES);
|
|
1377
1456
|
crypto.getRandomValues(array);
|
|
@@ -1391,6 +1470,23 @@ function createRedirectMethods(deps) {
|
|
|
1391
1470
|
logger.warn("SSR \u74B0\u5883\u3067\u306F\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3067\u304D\u307E\u305B\u3093");
|
|
1392
1471
|
return { success: false, error: "SSR \u74B0\u5883\u3067\u306F\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3067\u304D\u307E\u305B\u3093" };
|
|
1393
1472
|
}
|
|
1473
|
+
const authorizeKey = buildAuthorizeKey(baseUrl, clientId, options?.organizationId);
|
|
1474
|
+
const now = Date.now();
|
|
1475
|
+
const recentCount = getRecentRedirectCount(authorizeKey, now, logger);
|
|
1476
|
+
if (recentCount >= REDIRECT_LOOP_THRESHOLD) {
|
|
1477
|
+
cleanupVerifierStorage(logger);
|
|
1478
|
+
logger.warn("[FFID SDK] redirect loop detected \u2014 \u81EA\u52D5\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3092\u505C\u6B62\u3057\u307E\u3057\u305F", {
|
|
1479
|
+
authorizeKey,
|
|
1480
|
+
recentCount,
|
|
1481
|
+
windowMs: REDIRECT_LOOP_WINDOW_MS,
|
|
1482
|
+
threshold: REDIRECT_LOOP_THRESHOLD
|
|
1483
|
+
});
|
|
1484
|
+
return {
|
|
1485
|
+
success: false,
|
|
1486
|
+
code: "redirect_loop_detected",
|
|
1487
|
+
error: "\u77ED\u6642\u9593\u306B\u540C\u3058\u8A8D\u53EF\u30A8\u30F3\u30C9\u30DD\u30A4\u30F3\u30C8\u3078\u306E\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u304C\u7E70\u308A\u8FD4\u3055\u308C\u305F\u305F\u3081\u3001\u30EB\u30FC\u30D7\u691C\u51FA\u306B\u3088\u308A\u81EA\u52D5\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3092\u505C\u6B62\u3057\u307E\u3057\u305F"
|
|
1488
|
+
};
|
|
1489
|
+
}
|
|
1394
1490
|
const verifier = generateCodeVerifier();
|
|
1395
1491
|
storeCodeVerifier(verifier, logger);
|
|
1396
1492
|
let challenge;
|
|
@@ -1421,7 +1517,13 @@ function createRedirectMethods(deps) {
|
|
|
1421
1517
|
}
|
|
1422
1518
|
const authorizeUrl = `${baseUrl}${OAUTH_AUTHORIZE_ENDPOINT}?${params.toString()}`;
|
|
1423
1519
|
logger.debug("Redirecting to authorize:", authorizeUrl);
|
|
1424
|
-
|
|
1520
|
+
recordRedirectAttempt(authorizeKey, now, logger);
|
|
1521
|
+
try {
|
|
1522
|
+
window.location.href = authorizeUrl;
|
|
1523
|
+
} catch (err) {
|
|
1524
|
+
rollbackLastRedirectAttempt(authorizeKey, logger);
|
|
1525
|
+
throw err;
|
|
1526
|
+
}
|
|
1425
1527
|
return { success: true };
|
|
1426
1528
|
}
|
|
1427
1529
|
async function redirectToLogin() {
|
|
@@ -103,10 +103,7 @@ function createTokenStore(storageType) {
|
|
|
103
103
|
// src/client/oauth-userinfo.ts
|
|
104
104
|
var SESSION_ELIGIBLE_SUBSCRIPTION_STATUSES = [
|
|
105
105
|
"trialing",
|
|
106
|
-
"active"
|
|
107
|
-
"past_due",
|
|
108
|
-
"canceled",
|
|
109
|
-
"paused"
|
|
106
|
+
"active"
|
|
110
107
|
];
|
|
111
108
|
function isSessionEligibleSubscriptionStatus(value) {
|
|
112
109
|
return typeof value === "string" && SESSION_ELIGIBLE_SUBSCRIPTION_STATUSES.includes(value);
|
|
@@ -808,10 +805,11 @@ function createProfileMethods(deps) {
|
|
|
808
805
|
}
|
|
809
806
|
|
|
810
807
|
// src/client/version-check.ts
|
|
811
|
-
var SDK_VERSION = "2.
|
|
808
|
+
var SDK_VERSION = "2.18.0";
|
|
812
809
|
var SDK_USER_AGENT = `FFID-SDK/${SDK_VERSION} (TypeScript)`;
|
|
813
810
|
var SDK_VERSION_HEADER = "X-FFID-SDK-Version";
|
|
814
811
|
function sdkHeaders() {
|
|
812
|
+
if (typeof window !== "undefined") return {};
|
|
815
813
|
return {
|
|
816
814
|
"User-Agent": SDK_USER_AGENT,
|
|
817
815
|
[SDK_VERSION_HEADER]: SDK_VERSION
|
|
@@ -1370,6 +1368,87 @@ var OAUTH_AUTHORIZE_ENDPOINT = "/api/v1/oauth/authorize";
|
|
|
1370
1368
|
var AUTH_LOGOUT_ENDPOINT = "/api/v1/auth/logout";
|
|
1371
1369
|
var STATE_RANDOM_BYTES = 16;
|
|
1372
1370
|
var HEX_BASE2 = 16;
|
|
1371
|
+
var REDIRECT_LOOP_KEY = "ffid_sdk_redirect_loop_history";
|
|
1372
|
+
var REDIRECT_LOOP_WINDOW_MS = 6e4;
|
|
1373
|
+
var REDIRECT_LOOP_THRESHOLD = 3;
|
|
1374
|
+
var storageReadFailureLogged = false;
|
|
1375
|
+
var storageWriteFailureLogged = false;
|
|
1376
|
+
function logStorageReadFailure(logger, err) {
|
|
1377
|
+
if (storageReadFailureLogged) return;
|
|
1378
|
+
storageReadFailureLogged = true;
|
|
1379
|
+
logger.warn(
|
|
1380
|
+
"[FFID SDK] sessionStorage read failed \u2014 redirect loop detection disabled on this browser (fail-open). iOS WebKit private mode / eviction \u304C\u5178\u578B\u8981\u56E0",
|
|
1381
|
+
err
|
|
1382
|
+
);
|
|
1383
|
+
}
|
|
1384
|
+
function logStorageWriteFailure(logger, err) {
|
|
1385
|
+
if (storageWriteFailureLogged) return;
|
|
1386
|
+
storageWriteFailureLogged = true;
|
|
1387
|
+
logger.warn(
|
|
1388
|
+
"[FFID SDK] sessionStorage write failed \u2014 redirect loop detection disabled on this browser (fail-open).",
|
|
1389
|
+
err
|
|
1390
|
+
);
|
|
1391
|
+
}
|
|
1392
|
+
function isRedirectLoopHistory(value) {
|
|
1393
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
|
|
1394
|
+
return Object.values(value).every(
|
|
1395
|
+
(arr) => Array.isArray(arr) && arr.every((t) => typeof t === "number" && Number.isFinite(t))
|
|
1396
|
+
);
|
|
1397
|
+
}
|
|
1398
|
+
function readRedirectLoopHistory(logger) {
|
|
1399
|
+
if (typeof window === "undefined") return {};
|
|
1400
|
+
try {
|
|
1401
|
+
const raw = window.sessionStorage.getItem(REDIRECT_LOOP_KEY);
|
|
1402
|
+
if (raw === null) return {};
|
|
1403
|
+
const parsed = JSON.parse(raw);
|
|
1404
|
+
return isRedirectLoopHistory(parsed) ? parsed : {};
|
|
1405
|
+
} catch (err) {
|
|
1406
|
+
logStorageReadFailure(logger, err);
|
|
1407
|
+
return {};
|
|
1408
|
+
}
|
|
1409
|
+
}
|
|
1410
|
+
function writeRedirectLoopHistory(history, logger) {
|
|
1411
|
+
if (typeof window === "undefined") return;
|
|
1412
|
+
try {
|
|
1413
|
+
window.sessionStorage.setItem(REDIRECT_LOOP_KEY, JSON.stringify(history));
|
|
1414
|
+
} catch (err) {
|
|
1415
|
+
logStorageWriteFailure(logger, err);
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
1418
|
+
function pruneRedirectLoopHistory(history, now) {
|
|
1419
|
+
const cutoff = now - REDIRECT_LOOP_WINDOW_MS;
|
|
1420
|
+
const pruned = {};
|
|
1421
|
+
for (const [key, timestamps] of Object.entries(history)) {
|
|
1422
|
+
const fresh = timestamps.filter((t) => t > cutoff);
|
|
1423
|
+
if (fresh.length > 0) pruned[key] = fresh;
|
|
1424
|
+
}
|
|
1425
|
+
return pruned;
|
|
1426
|
+
}
|
|
1427
|
+
function getRecentRedirectCount(authorizeKey, now, logger) {
|
|
1428
|
+
const pruned = pruneRedirectLoopHistory(readRedirectLoopHistory(logger), now);
|
|
1429
|
+
writeRedirectLoopHistory(pruned, logger);
|
|
1430
|
+
return pruned[authorizeKey]?.length ?? 0;
|
|
1431
|
+
}
|
|
1432
|
+
function recordRedirectAttempt(authorizeKey, now, logger) {
|
|
1433
|
+
const history = readRedirectLoopHistory(logger);
|
|
1434
|
+
const existing = history[authorizeKey] ?? [];
|
|
1435
|
+
history[authorizeKey] = [...existing, now];
|
|
1436
|
+
writeRedirectLoopHistory(history, logger);
|
|
1437
|
+
}
|
|
1438
|
+
function rollbackLastRedirectAttempt(authorizeKey, logger) {
|
|
1439
|
+
const history = readRedirectLoopHistory(logger);
|
|
1440
|
+
const existing = history[authorizeKey];
|
|
1441
|
+
if (!Array.isArray(existing) || existing.length === 0) return;
|
|
1442
|
+
history[authorizeKey] = existing.slice(0, -1);
|
|
1443
|
+
if (history[authorizeKey].length === 0) delete history[authorizeKey];
|
|
1444
|
+
writeRedirectLoopHistory(history, logger);
|
|
1445
|
+
}
|
|
1446
|
+
function buildAuthorizeKey(baseUrl, clientId, organizationId) {
|
|
1447
|
+
const params = new URLSearchParams({ client_id: clientId });
|
|
1448
|
+
const trimmedOrg = organizationId?.trim();
|
|
1449
|
+
if (trimmedOrg) params.set("organization_id", trimmedOrg);
|
|
1450
|
+
return `${baseUrl}${OAUTH_AUTHORIZE_ENDPOINT}?${params.toString()}`;
|
|
1451
|
+
}
|
|
1373
1452
|
function generateRandomState() {
|
|
1374
1453
|
const array = new Uint8Array(STATE_RANDOM_BYTES);
|
|
1375
1454
|
crypto.getRandomValues(array);
|
|
@@ -1389,6 +1468,23 @@ function createRedirectMethods(deps) {
|
|
|
1389
1468
|
logger.warn("SSR \u74B0\u5883\u3067\u306F\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3067\u304D\u307E\u305B\u3093");
|
|
1390
1469
|
return { success: false, error: "SSR \u74B0\u5883\u3067\u306F\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3067\u304D\u307E\u305B\u3093" };
|
|
1391
1470
|
}
|
|
1471
|
+
const authorizeKey = buildAuthorizeKey(baseUrl, clientId, options?.organizationId);
|
|
1472
|
+
const now = Date.now();
|
|
1473
|
+
const recentCount = getRecentRedirectCount(authorizeKey, now, logger);
|
|
1474
|
+
if (recentCount >= REDIRECT_LOOP_THRESHOLD) {
|
|
1475
|
+
cleanupVerifierStorage(logger);
|
|
1476
|
+
logger.warn("[FFID SDK] redirect loop detected \u2014 \u81EA\u52D5\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3092\u505C\u6B62\u3057\u307E\u3057\u305F", {
|
|
1477
|
+
authorizeKey,
|
|
1478
|
+
recentCount,
|
|
1479
|
+
windowMs: REDIRECT_LOOP_WINDOW_MS,
|
|
1480
|
+
threshold: REDIRECT_LOOP_THRESHOLD
|
|
1481
|
+
});
|
|
1482
|
+
return {
|
|
1483
|
+
success: false,
|
|
1484
|
+
code: "redirect_loop_detected",
|
|
1485
|
+
error: "\u77ED\u6642\u9593\u306B\u540C\u3058\u8A8D\u53EF\u30A8\u30F3\u30C9\u30DD\u30A4\u30F3\u30C8\u3078\u306E\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u304C\u7E70\u308A\u8FD4\u3055\u308C\u305F\u305F\u3081\u3001\u30EB\u30FC\u30D7\u691C\u51FA\u306B\u3088\u308A\u81EA\u52D5\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3092\u505C\u6B62\u3057\u307E\u3057\u305F"
|
|
1486
|
+
};
|
|
1487
|
+
}
|
|
1392
1488
|
const verifier = generateCodeVerifier();
|
|
1393
1489
|
storeCodeVerifier(verifier, logger);
|
|
1394
1490
|
let challenge;
|
|
@@ -1419,7 +1515,13 @@ function createRedirectMethods(deps) {
|
|
|
1419
1515
|
}
|
|
1420
1516
|
const authorizeUrl = `${baseUrl}${OAUTH_AUTHORIZE_ENDPOINT}?${params.toString()}`;
|
|
1421
1517
|
logger.debug("Redirecting to authorize:", authorizeUrl);
|
|
1422
|
-
|
|
1518
|
+
recordRedirectAttempt(authorizeKey, now, logger);
|
|
1519
|
+
try {
|
|
1520
|
+
window.location.href = authorizeUrl;
|
|
1521
|
+
} catch (err) {
|
|
1522
|
+
rollbackLastRedirectAttempt(authorizeKey, logger);
|
|
1523
|
+
throw err;
|
|
1524
|
+
}
|
|
1423
1525
|
return { success: true };
|
|
1424
1526
|
}
|
|
1425
1527
|
async function redirectToLogin() {
|
|
@@ -1,34 +1,34 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunkEK2W67BW_cjs = require('../chunk-EK2W67BW.cjs');
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
Object.defineProperty(exports, "FFIDAnnouncementBadge", {
|
|
8
8
|
enumerable: true,
|
|
9
|
-
get: function () { return
|
|
9
|
+
get: function () { return chunkEK2W67BW_cjs.FFIDAnnouncementBadge; }
|
|
10
10
|
});
|
|
11
11
|
Object.defineProperty(exports, "FFIDAnnouncementList", {
|
|
12
12
|
enumerable: true,
|
|
13
|
-
get: function () { return
|
|
13
|
+
get: function () { return chunkEK2W67BW_cjs.FFIDAnnouncementList; }
|
|
14
14
|
});
|
|
15
15
|
Object.defineProperty(exports, "FFIDInquiryForm", {
|
|
16
16
|
enumerable: true,
|
|
17
|
-
get: function () { return
|
|
17
|
+
get: function () { return chunkEK2W67BW_cjs.FFIDInquiryForm; }
|
|
18
18
|
});
|
|
19
19
|
Object.defineProperty(exports, "FFIDLoginButton", {
|
|
20
20
|
enumerable: true,
|
|
21
|
-
get: function () { return
|
|
21
|
+
get: function () { return chunkEK2W67BW_cjs.FFIDLoginButton; }
|
|
22
22
|
});
|
|
23
23
|
Object.defineProperty(exports, "FFIDOrganizationSwitcher", {
|
|
24
24
|
enumerable: true,
|
|
25
|
-
get: function () { return
|
|
25
|
+
get: function () { return chunkEK2W67BW_cjs.FFIDOrganizationSwitcher; }
|
|
26
26
|
});
|
|
27
27
|
Object.defineProperty(exports, "FFIDSubscriptionBadge", {
|
|
28
28
|
enumerable: true,
|
|
29
|
-
get: function () { return
|
|
29
|
+
get: function () { return chunkEK2W67BW_cjs.FFIDSubscriptionBadge; }
|
|
30
30
|
});
|
|
31
31
|
Object.defineProperty(exports, "FFIDUserMenu", {
|
|
32
32
|
enumerable: true,
|
|
33
|
-
get: function () { return
|
|
33
|
+
get: function () { return chunkEK2W67BW_cjs.FFIDUserMenu; }
|
|
34
34
|
});
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { K as FFIDAnnouncementBadge,
|
|
1
|
+
export { K as FFIDAnnouncementBadge, ak as FFIDAnnouncementBadgeClassNames, al as FFIDAnnouncementBadgeProps, M as FFIDAnnouncementList, am as FFIDAnnouncementListClassNames, an as FFIDAnnouncementListProps, U as FFIDInquiryForm, V as FFIDInquiryFormCategoryItem, W as FFIDInquiryFormClassNames, X as FFIDInquiryFormOrganization, Y as FFIDInquiryFormPlaceholderContext, Z as FFIDInquiryFormPrefill, _ as FFIDInquiryFormProps, $ as FFIDInquiryFormSubmitData, a0 as FFIDInquiryFormSubmitResult, a2 as FFIDLoginButton, ao as FFIDLoginButtonProps, a8 as FFIDOrganizationSwitcher, ap as FFIDOrganizationSwitcherClassNames, aq as FFIDOrganizationSwitcherProps, ab as FFIDSubscriptionBadge, ar as FFIDSubscriptionBadgeClassNames, as as FFIDSubscriptionBadgeProps, ad as FFIDUserMenu, at as FFIDUserMenuClassNames, au as FFIDUserMenuProps } from '../index--S6rLHjr.cjs';
|
|
2
2
|
import 'react/jsx-runtime';
|
|
3
3
|
import 'react';
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { K as FFIDAnnouncementBadge,
|
|
1
|
+
export { K as FFIDAnnouncementBadge, ak as FFIDAnnouncementBadgeClassNames, al as FFIDAnnouncementBadgeProps, M as FFIDAnnouncementList, am as FFIDAnnouncementListClassNames, an as FFIDAnnouncementListProps, U as FFIDInquiryForm, V as FFIDInquiryFormCategoryItem, W as FFIDInquiryFormClassNames, X as FFIDInquiryFormOrganization, Y as FFIDInquiryFormPlaceholderContext, Z as FFIDInquiryFormPrefill, _ as FFIDInquiryFormProps, $ as FFIDInquiryFormSubmitData, a0 as FFIDInquiryFormSubmitResult, a2 as FFIDLoginButton, ao as FFIDLoginButtonProps, a8 as FFIDOrganizationSwitcher, ap as FFIDOrganizationSwitcherClassNames, aq as FFIDOrganizationSwitcherProps, ab as FFIDSubscriptionBadge, ar as FFIDSubscriptionBadgeClassNames, as as FFIDSubscriptionBadgeProps, ad as FFIDUserMenu, at as FFIDUserMenuClassNames, au as FFIDUserMenuProps } from '../index--S6rLHjr.js';
|
|
2
2
|
import 'react/jsx-runtime';
|
|
3
3
|
import 'react';
|
package/dist/components/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { FFIDAnnouncementBadge, FFIDAnnouncementList, FFIDInquiryForm, FFIDLoginButton, FFIDOrganizationSwitcher, FFIDSubscriptionBadge, FFIDUserMenu } from '../chunk-
|
|
1
|
+
export { FFIDAnnouncementBadge, FFIDAnnouncementList, FFIDInquiryForm, FFIDLoginButton, FFIDOrganizationSwitcher, FFIDSubscriptionBadge, FFIDUserMenu } from '../chunk-IEYXT3LA.js';
|
|
@@ -471,8 +471,11 @@ interface FFIDUserProfile {
|
|
|
471
471
|
* When `accessToken` is supplied with a non-empty value, it overrides the client's
|
|
472
472
|
* configured auth mode: authentication for that request uses only
|
|
473
473
|
* `Authorization: Bearer <accessToken>` (no service key, no cookie, no token-store
|
|
474
|
-
* lookup, no auto-refresh on 401). Non-auth headers such as `Content-Type`
|
|
475
|
-
* SDK metadata headers (User-Agent / X-FFID-SDK-Version) are
|
|
474
|
+
* lookup, no auto-refresh on 401). Non-auth headers such as `Content-Type` are
|
|
475
|
+
* still attached. SDK metadata headers (`User-Agent` / `X-FFID-SDK-Version`) are
|
|
476
|
+
* attached on non-browser runtimes only (Node.js / Cloudflare Workers / Edge /
|
|
477
|
+
* Deno); browsers receive an empty object to avoid iOS WebKit `fetch()` breakage
|
|
478
|
+
* (see #2417).
|
|
476
479
|
*
|
|
477
480
|
* Runtime semantics:
|
|
478
481
|
* - `accessToken` omitted (or `undefined`) → no override, configured `authMode` is used
|
|
@@ -534,17 +537,36 @@ interface FFIDUpdateUserProfileRequest {
|
|
|
534
537
|
/** Arbitrary user preferences bag (null clears the column; reads return {}) */
|
|
535
538
|
preferences?: Record<string, unknown> | null;
|
|
536
539
|
}
|
|
540
|
+
/**
|
|
541
|
+
* Discriminant for redirect failures that callers need to handle
|
|
542
|
+
* programmatically (vs. logging the human-readable `error` string).
|
|
543
|
+
*
|
|
544
|
+
* - `'redirect_loop_detected'`: `redirectToAuthorize()` detected that the
|
|
545
|
+
* same authorize URL (keyed by `baseUrl + client_id + organization_id`)
|
|
546
|
+
* has been fired **3 times within 60 seconds**. Caller should surface a
|
|
547
|
+
* manual "再度ログインする" UI rather than retry automatically (#2406 / #2411).
|
|
548
|
+
*
|
|
549
|
+
* SDK 2.18.0 only ships `'redirect_loop_detected'`. Other failure paths
|
|
550
|
+
* (SSR environment, PKCE generation failure, empty organizationId) currently
|
|
551
|
+
* return `error` without a `code`. New codes will be added in future minor
|
|
552
|
+
* versions — treat this union as forward-extensible and do NOT exhaustively
|
|
553
|
+
* `switch` over it without a `default` branch for consumer code that must
|
|
554
|
+
* stay compatible across SDK upgrades.
|
|
555
|
+
*/
|
|
556
|
+
type FFIDRedirectErrorCode = 'redirect_loop_detected';
|
|
537
557
|
/**
|
|
538
558
|
* Result of a redirect operation (redirectToLogin / redirectToAuthorize / redirectToLogout)
|
|
539
559
|
*
|
|
540
560
|
* Structured return type so callers can inspect failure reasons
|
|
541
|
-
* instead of receiving a bare `false`.
|
|
561
|
+
* instead of receiving a bare `false`. When `code` is set, branch on it
|
|
562
|
+
* for programmatic handling; otherwise log `error` for humans.
|
|
542
563
|
*/
|
|
543
564
|
type FFIDRedirectResult = {
|
|
544
565
|
success: true;
|
|
545
566
|
} | {
|
|
546
567
|
success: false;
|
|
547
568
|
error: string;
|
|
569
|
+
code?: FFIDRedirectErrorCode;
|
|
548
570
|
};
|
|
549
571
|
/**
|
|
550
572
|
* OAuth 2.0 token response from FFID token endpoint
|
|
@@ -1279,4 +1301,4 @@ interface FFIDInquiryFormPlaceholderContext {
|
|
|
1279
1301
|
}
|
|
1280
1302
|
declare function FFIDInquiryForm({ mode, prefill, organizations, preselectedOrganizationId, categories, termsVersion, privacyVersion, termsHref, privacyHref, turnstileToken, turnstileSlot, onSubmit, onChange, separateLegalCheckboxes, messagePlaceholder, requireCategorySelection, unstyled, classNames, locale, className, }: FFIDInquiryFormProps): react_jsx_runtime.JSX.Element;
|
|
1281
1303
|
|
|
1282
|
-
export { type FFIDInquiryFormSubmitData as $, type FFIDSubscription as A, type FFIDSubscriptionContextValue as B, type FFIDAnnouncementsClientConfig as C, type FFIDAnnouncementsApiResponse as D, type AnnouncementListResponse as E, type FFIDSubscriptionStatus as F, type FFIDAnnouncementsLogger as G, type Announcement as H, type AnnouncementStatus as I, type AnnouncementType as J, FFIDAnnouncementBadge as K, type ListAnnouncementsOptions as L, FFIDAnnouncementList as M, type FFIDAnnouncementsError as N, type FFIDAnnouncementsErrorCode as O, type FFIDAnnouncementsServerResponse as P, type FFIDCacheConfig as Q, type FFIDContextValue as R, type FFIDInquiryCategory as S, type FFIDInquiryCategorySite2026 as T, FFIDInquiryForm as U, type FFIDInquiryFormCategoryItem as V, type FFIDInquiryFormClassNames as W, type FFIDInquiryFormOrganization as X, type FFIDInquiryFormPlaceholderContext as Y, type FFIDInquiryFormPrefill as Z, type FFIDInquiryFormProps as _, type FFIDConfig as a, type FFIDInquiryFormSubmitResult as a0, type FFIDJwtClaims as a1, FFIDLoginButton as a2, type FFIDMemberStatus as a3, type FFIDOAuthTokenResponse as a4, type FFIDOAuthUserInfoMemberRole as a5, type FFIDOAuthUserInfoSubscription as a6, type FFIDOrganizationMember as a7, FFIDOrganizationSwitcher as a8, type
|
|
1304
|
+
export { type FFIDInquiryFormSubmitData as $, type FFIDSubscription as A, type FFIDSubscriptionContextValue as B, type FFIDAnnouncementsClientConfig as C, type FFIDAnnouncementsApiResponse as D, type AnnouncementListResponse as E, type FFIDSubscriptionStatus as F, type FFIDAnnouncementsLogger as G, type Announcement as H, type AnnouncementStatus as I, type AnnouncementType as J, FFIDAnnouncementBadge as K, type ListAnnouncementsOptions as L, FFIDAnnouncementList as M, type FFIDAnnouncementsError as N, type FFIDAnnouncementsErrorCode as O, type FFIDAnnouncementsServerResponse as P, type FFIDCacheConfig as Q, type FFIDContextValue as R, type FFIDInquiryCategory as S, type FFIDInquiryCategorySite2026 as T, FFIDInquiryForm as U, type FFIDInquiryFormCategoryItem as V, type FFIDInquiryFormClassNames as W, type FFIDInquiryFormOrganization as X, type FFIDInquiryFormPlaceholderContext as Y, type FFIDInquiryFormPrefill as Z, type FFIDInquiryFormProps as _, type FFIDConfig as a, type FFIDInquiryFormSubmitResult as a0, type FFIDJwtClaims as a1, FFIDLoginButton as a2, type FFIDMemberStatus as a3, type FFIDOAuthTokenResponse as a4, type FFIDOAuthUserInfoMemberRole as a5, type FFIDOAuthUserInfoSubscription as a6, type FFIDOrganizationMember as a7, FFIDOrganizationSwitcher as a8, type FFIDRedirectErrorCode as a9, type FFIDSeatModel as aa, FFIDSubscriptionBadge as ab, type FFIDTokenIntrospectionResponse as ac, FFIDUserMenu as ad, FFID_INQUIRY_CATEGORIES as ae, FFID_INQUIRY_CATEGORIES_SITE_2026 as af, type UseFFIDAnnouncementsOptions as ag, type UseFFIDAnnouncementsReturn as ah, isFFIDInquiryCategorySite2026 as ai, useFFIDAnnouncements as aj, type FFIDAnnouncementBadgeClassNames as ak, type FFIDAnnouncementBadgeProps as al, type FFIDAnnouncementListClassNames as am, type FFIDAnnouncementListProps as an, type FFIDLoginButtonProps as ao, type FFIDOrganizationSwitcherClassNames as ap, type FFIDOrganizationSwitcherProps as aq, type FFIDSubscriptionBadgeClassNames as ar, type FFIDSubscriptionBadgeProps as as, type FFIDUserMenuClassNames as at, type FFIDUserMenuProps as au, type FFIDApiResponse as b, type FFIDSessionResponse as c, type FFIDRedirectResult as d, type FFIDError as e, type FFIDSubscriptionCheckResponse as f, type FFIDListMembersResponse as g, type FFIDMemberRole as h, type FFIDUpdateMemberRoleResponse as i, type FFIDRemoveMemberResponse as j, type FFIDProfileCallOptions as k, type FFIDUserProfile as l, type FFIDUpdateUserProfileRequest as m, type FFIDCreateCheckoutParams as n, type FFIDCheckoutSessionResponse as o, type FFIDCreatePortalParams as p, type FFIDPortalSessionResponse as q, type FFIDVerifyAccessTokenOptions as r, type FFIDOAuthUserInfo as s, type FFIDInquiryCreateParams as t, type FFIDInquiryCreateResponse as u, type FFIDAuthMode as v, type FFIDLogger as w, type FFIDCacheAdapter as x, type FFIDUser as y, type FFIDOrganization as z };
|
|
@@ -471,8 +471,11 @@ interface FFIDUserProfile {
|
|
|
471
471
|
* When `accessToken` is supplied with a non-empty value, it overrides the client's
|
|
472
472
|
* configured auth mode: authentication for that request uses only
|
|
473
473
|
* `Authorization: Bearer <accessToken>` (no service key, no cookie, no token-store
|
|
474
|
-
* lookup, no auto-refresh on 401). Non-auth headers such as `Content-Type`
|
|
475
|
-
* SDK metadata headers (User-Agent / X-FFID-SDK-Version) are
|
|
474
|
+
* lookup, no auto-refresh on 401). Non-auth headers such as `Content-Type` are
|
|
475
|
+
* still attached. SDK metadata headers (`User-Agent` / `X-FFID-SDK-Version`) are
|
|
476
|
+
* attached on non-browser runtimes only (Node.js / Cloudflare Workers / Edge /
|
|
477
|
+
* Deno); browsers receive an empty object to avoid iOS WebKit `fetch()` breakage
|
|
478
|
+
* (see #2417).
|
|
476
479
|
*
|
|
477
480
|
* Runtime semantics:
|
|
478
481
|
* - `accessToken` omitted (or `undefined`) → no override, configured `authMode` is used
|
|
@@ -534,17 +537,36 @@ interface FFIDUpdateUserProfileRequest {
|
|
|
534
537
|
/** Arbitrary user preferences bag (null clears the column; reads return {}) */
|
|
535
538
|
preferences?: Record<string, unknown> | null;
|
|
536
539
|
}
|
|
540
|
+
/**
|
|
541
|
+
* Discriminant for redirect failures that callers need to handle
|
|
542
|
+
* programmatically (vs. logging the human-readable `error` string).
|
|
543
|
+
*
|
|
544
|
+
* - `'redirect_loop_detected'`: `redirectToAuthorize()` detected that the
|
|
545
|
+
* same authorize URL (keyed by `baseUrl + client_id + organization_id`)
|
|
546
|
+
* has been fired **3 times within 60 seconds**. Caller should surface a
|
|
547
|
+
* manual "再度ログインする" UI rather than retry automatically (#2406 / #2411).
|
|
548
|
+
*
|
|
549
|
+
* SDK 2.18.0 only ships `'redirect_loop_detected'`. Other failure paths
|
|
550
|
+
* (SSR environment, PKCE generation failure, empty organizationId) currently
|
|
551
|
+
* return `error` without a `code`. New codes will be added in future minor
|
|
552
|
+
* versions — treat this union as forward-extensible and do NOT exhaustively
|
|
553
|
+
* `switch` over it without a `default` branch for consumer code that must
|
|
554
|
+
* stay compatible across SDK upgrades.
|
|
555
|
+
*/
|
|
556
|
+
type FFIDRedirectErrorCode = 'redirect_loop_detected';
|
|
537
557
|
/**
|
|
538
558
|
* Result of a redirect operation (redirectToLogin / redirectToAuthorize / redirectToLogout)
|
|
539
559
|
*
|
|
540
560
|
* Structured return type so callers can inspect failure reasons
|
|
541
|
-
* instead of receiving a bare `false`.
|
|
561
|
+
* instead of receiving a bare `false`. When `code` is set, branch on it
|
|
562
|
+
* for programmatic handling; otherwise log `error` for humans.
|
|
542
563
|
*/
|
|
543
564
|
type FFIDRedirectResult = {
|
|
544
565
|
success: true;
|
|
545
566
|
} | {
|
|
546
567
|
success: false;
|
|
547
568
|
error: string;
|
|
569
|
+
code?: FFIDRedirectErrorCode;
|
|
548
570
|
};
|
|
549
571
|
/**
|
|
550
572
|
* OAuth 2.0 token response from FFID token endpoint
|
|
@@ -1279,4 +1301,4 @@ interface FFIDInquiryFormPlaceholderContext {
|
|
|
1279
1301
|
}
|
|
1280
1302
|
declare function FFIDInquiryForm({ mode, prefill, organizations, preselectedOrganizationId, categories, termsVersion, privacyVersion, termsHref, privacyHref, turnstileToken, turnstileSlot, onSubmit, onChange, separateLegalCheckboxes, messagePlaceholder, requireCategorySelection, unstyled, classNames, locale, className, }: FFIDInquiryFormProps): react_jsx_runtime.JSX.Element;
|
|
1281
1303
|
|
|
1282
|
-
export { type FFIDInquiryFormSubmitData as $, type FFIDSubscription as A, type FFIDSubscriptionContextValue as B, type FFIDAnnouncementsClientConfig as C, type FFIDAnnouncementsApiResponse as D, type AnnouncementListResponse as E, type FFIDSubscriptionStatus as F, type FFIDAnnouncementsLogger as G, type Announcement as H, type AnnouncementStatus as I, type AnnouncementType as J, FFIDAnnouncementBadge as K, type ListAnnouncementsOptions as L, FFIDAnnouncementList as M, type FFIDAnnouncementsError as N, type FFIDAnnouncementsErrorCode as O, type FFIDAnnouncementsServerResponse as P, type FFIDCacheConfig as Q, type FFIDContextValue as R, type FFIDInquiryCategory as S, type FFIDInquiryCategorySite2026 as T, FFIDInquiryForm as U, type FFIDInquiryFormCategoryItem as V, type FFIDInquiryFormClassNames as W, type FFIDInquiryFormOrganization as X, type FFIDInquiryFormPlaceholderContext as Y, type FFIDInquiryFormPrefill as Z, type FFIDInquiryFormProps as _, type FFIDConfig as a, type FFIDInquiryFormSubmitResult as a0, type FFIDJwtClaims as a1, FFIDLoginButton as a2, type FFIDMemberStatus as a3, type FFIDOAuthTokenResponse as a4, type FFIDOAuthUserInfoMemberRole as a5, type FFIDOAuthUserInfoSubscription as a6, type FFIDOrganizationMember as a7, FFIDOrganizationSwitcher as a8, type
|
|
1304
|
+
export { type FFIDInquiryFormSubmitData as $, type FFIDSubscription as A, type FFIDSubscriptionContextValue as B, type FFIDAnnouncementsClientConfig as C, type FFIDAnnouncementsApiResponse as D, type AnnouncementListResponse as E, type FFIDSubscriptionStatus as F, type FFIDAnnouncementsLogger as G, type Announcement as H, type AnnouncementStatus as I, type AnnouncementType as J, FFIDAnnouncementBadge as K, type ListAnnouncementsOptions as L, FFIDAnnouncementList as M, type FFIDAnnouncementsError as N, type FFIDAnnouncementsErrorCode as O, type FFIDAnnouncementsServerResponse as P, type FFIDCacheConfig as Q, type FFIDContextValue as R, type FFIDInquiryCategory as S, type FFIDInquiryCategorySite2026 as T, FFIDInquiryForm as U, type FFIDInquiryFormCategoryItem as V, type FFIDInquiryFormClassNames as W, type FFIDInquiryFormOrganization as X, type FFIDInquiryFormPlaceholderContext as Y, type FFIDInquiryFormPrefill as Z, type FFIDInquiryFormProps as _, type FFIDConfig as a, type FFIDInquiryFormSubmitResult as a0, type FFIDJwtClaims as a1, FFIDLoginButton as a2, type FFIDMemberStatus as a3, type FFIDOAuthTokenResponse as a4, type FFIDOAuthUserInfoMemberRole as a5, type FFIDOAuthUserInfoSubscription as a6, type FFIDOrganizationMember as a7, FFIDOrganizationSwitcher as a8, type FFIDRedirectErrorCode as a9, type FFIDSeatModel as aa, FFIDSubscriptionBadge as ab, type FFIDTokenIntrospectionResponse as ac, FFIDUserMenu as ad, FFID_INQUIRY_CATEGORIES as ae, FFID_INQUIRY_CATEGORIES_SITE_2026 as af, type UseFFIDAnnouncementsOptions as ag, type UseFFIDAnnouncementsReturn as ah, isFFIDInquiryCategorySite2026 as ai, useFFIDAnnouncements as aj, type FFIDAnnouncementBadgeClassNames as ak, type FFIDAnnouncementBadgeProps as al, type FFIDAnnouncementListClassNames as am, type FFIDAnnouncementListProps as an, type FFIDLoginButtonProps as ao, type FFIDOrganizationSwitcherClassNames as ap, type FFIDOrganizationSwitcherProps as aq, type FFIDSubscriptionBadgeClassNames as ar, type FFIDSubscriptionBadgeProps as as, type FFIDUserMenuClassNames as at, type FFIDUserMenuProps as au, type FFIDApiResponse as b, type FFIDSessionResponse as c, type FFIDRedirectResult as d, type FFIDError as e, type FFIDSubscriptionCheckResponse as f, type FFIDListMembersResponse as g, type FFIDMemberRole as h, type FFIDUpdateMemberRoleResponse as i, type FFIDRemoveMemberResponse as j, type FFIDProfileCallOptions as k, type FFIDUserProfile as l, type FFIDUpdateUserProfileRequest as m, type FFIDCreateCheckoutParams as n, type FFIDCheckoutSessionResponse as o, type FFIDCreatePortalParams as p, type FFIDPortalSessionResponse as q, type FFIDVerifyAccessTokenOptions as r, type FFIDOAuthUserInfo as s, type FFIDInquiryCreateParams as t, type FFIDInquiryCreateResponse as u, type FFIDAuthMode as v, type FFIDLogger as w, type FFIDCacheAdapter as x, type FFIDUser as y, type FFIDOrganization as z };
|
package/dist/index.cjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunkEK2W67BW_cjs = require('./chunk-EK2W67BW.cjs');
|
|
4
4
|
var react = require('react');
|
|
5
5
|
var jsxRuntime = require('react/jsx-runtime');
|
|
6
6
|
|
|
@@ -46,7 +46,7 @@ function createKVCacheAdapter(kv) {
|
|
|
46
46
|
}
|
|
47
47
|
function withFFIDAuth(Component, options = {}) {
|
|
48
48
|
const WrappedComponent = (props) => {
|
|
49
|
-
const { isLoading, isAuthenticated, login } =
|
|
49
|
+
const { isLoading, isAuthenticated, login } = chunkEK2W67BW_cjs.useFFIDContext();
|
|
50
50
|
const hasRedirected = react.useRef(false);
|
|
51
51
|
react.useEffect(() => {
|
|
52
52
|
if (!isLoading && !isAuthenticated && options.redirectToLogin && !hasRedirected.current) {
|
|
@@ -74,107 +74,107 @@ var FFID_NEWSLETTER_TYPES = ["inquiry_followup", "general"];
|
|
|
74
74
|
|
|
75
75
|
Object.defineProperty(exports, "DEFAULT_API_BASE_URL", {
|
|
76
76
|
enumerable: true,
|
|
77
|
-
get: function () { return
|
|
77
|
+
get: function () { return chunkEK2W67BW_cjs.DEFAULT_API_BASE_URL; }
|
|
78
78
|
});
|
|
79
79
|
Object.defineProperty(exports, "FFIDAnnouncementBadge", {
|
|
80
80
|
enumerable: true,
|
|
81
|
-
get: function () { return
|
|
81
|
+
get: function () { return chunkEK2W67BW_cjs.FFIDAnnouncementBadge; }
|
|
82
82
|
});
|
|
83
83
|
Object.defineProperty(exports, "FFIDAnnouncementList", {
|
|
84
84
|
enumerable: true,
|
|
85
|
-
get: function () { return
|
|
85
|
+
get: function () { return chunkEK2W67BW_cjs.FFIDAnnouncementList; }
|
|
86
86
|
});
|
|
87
87
|
Object.defineProperty(exports, "FFIDInquiryForm", {
|
|
88
88
|
enumerable: true,
|
|
89
|
-
get: function () { return
|
|
89
|
+
get: function () { return chunkEK2W67BW_cjs.FFIDInquiryForm; }
|
|
90
90
|
});
|
|
91
91
|
Object.defineProperty(exports, "FFIDLoginButton", {
|
|
92
92
|
enumerable: true,
|
|
93
|
-
get: function () { return
|
|
93
|
+
get: function () { return chunkEK2W67BW_cjs.FFIDLoginButton; }
|
|
94
94
|
});
|
|
95
95
|
Object.defineProperty(exports, "FFIDOrganizationSwitcher", {
|
|
96
96
|
enumerable: true,
|
|
97
|
-
get: function () { return
|
|
97
|
+
get: function () { return chunkEK2W67BW_cjs.FFIDOrganizationSwitcher; }
|
|
98
98
|
});
|
|
99
99
|
Object.defineProperty(exports, "FFIDProvider", {
|
|
100
100
|
enumerable: true,
|
|
101
|
-
get: function () { return
|
|
101
|
+
get: function () { return chunkEK2W67BW_cjs.FFIDProvider; }
|
|
102
102
|
});
|
|
103
103
|
Object.defineProperty(exports, "FFIDSDKError", {
|
|
104
104
|
enumerable: true,
|
|
105
|
-
get: function () { return
|
|
105
|
+
get: function () { return chunkEK2W67BW_cjs.FFIDSDKError; }
|
|
106
106
|
});
|
|
107
107
|
Object.defineProperty(exports, "FFIDSubscriptionBadge", {
|
|
108
108
|
enumerable: true,
|
|
109
|
-
get: function () { return
|
|
109
|
+
get: function () { return chunkEK2W67BW_cjs.FFIDSubscriptionBadge; }
|
|
110
110
|
});
|
|
111
111
|
Object.defineProperty(exports, "FFIDUserMenu", {
|
|
112
112
|
enumerable: true,
|
|
113
|
-
get: function () { return
|
|
113
|
+
get: function () { return chunkEK2W67BW_cjs.FFIDUserMenu; }
|
|
114
114
|
});
|
|
115
115
|
Object.defineProperty(exports, "FFID_ANNOUNCEMENTS_ERROR_CODES", {
|
|
116
116
|
enumerable: true,
|
|
117
|
-
get: function () { return
|
|
117
|
+
get: function () { return chunkEK2W67BW_cjs.FFID_ANNOUNCEMENTS_ERROR_CODES; }
|
|
118
118
|
});
|
|
119
119
|
Object.defineProperty(exports, "FFID_INQUIRY_CATEGORIES", {
|
|
120
120
|
enumerable: true,
|
|
121
|
-
get: function () { return
|
|
121
|
+
get: function () { return chunkEK2W67BW_cjs.FFID_INQUIRY_CATEGORIES; }
|
|
122
122
|
});
|
|
123
123
|
Object.defineProperty(exports, "FFID_INQUIRY_CATEGORIES_SITE_2026", {
|
|
124
124
|
enumerable: true,
|
|
125
|
-
get: function () { return
|
|
125
|
+
get: function () { return chunkEK2W67BW_cjs.FFID_INQUIRY_CATEGORIES_SITE_2026; }
|
|
126
126
|
});
|
|
127
127
|
Object.defineProperty(exports, "createFFIDAnnouncementsClient", {
|
|
128
128
|
enumerable: true,
|
|
129
|
-
get: function () { return
|
|
129
|
+
get: function () { return chunkEK2W67BW_cjs.createFFIDAnnouncementsClient; }
|
|
130
130
|
});
|
|
131
131
|
Object.defineProperty(exports, "createFFIDClient", {
|
|
132
132
|
enumerable: true,
|
|
133
|
-
get: function () { return
|
|
133
|
+
get: function () { return chunkEK2W67BW_cjs.createFFIDClient; }
|
|
134
134
|
});
|
|
135
135
|
Object.defineProperty(exports, "createTokenStore", {
|
|
136
136
|
enumerable: true,
|
|
137
|
-
get: function () { return
|
|
137
|
+
get: function () { return chunkEK2W67BW_cjs.createTokenStore; }
|
|
138
138
|
});
|
|
139
139
|
Object.defineProperty(exports, "generateCodeChallenge", {
|
|
140
140
|
enumerable: true,
|
|
141
|
-
get: function () { return
|
|
141
|
+
get: function () { return chunkEK2W67BW_cjs.generateCodeChallenge; }
|
|
142
142
|
});
|
|
143
143
|
Object.defineProperty(exports, "generateCodeVerifier", {
|
|
144
144
|
enumerable: true,
|
|
145
|
-
get: function () { return
|
|
145
|
+
get: function () { return chunkEK2W67BW_cjs.generateCodeVerifier; }
|
|
146
146
|
});
|
|
147
147
|
Object.defineProperty(exports, "isFFIDInquiryCategorySite2026", {
|
|
148
148
|
enumerable: true,
|
|
149
|
-
get: function () { return
|
|
149
|
+
get: function () { return chunkEK2W67BW_cjs.isFFIDInquiryCategorySite2026; }
|
|
150
150
|
});
|
|
151
151
|
Object.defineProperty(exports, "normalizeRedirectUri", {
|
|
152
152
|
enumerable: true,
|
|
153
|
-
get: function () { return
|
|
153
|
+
get: function () { return chunkEK2W67BW_cjs.normalizeRedirectUri; }
|
|
154
154
|
});
|
|
155
155
|
Object.defineProperty(exports, "retrieveCodeVerifier", {
|
|
156
156
|
enumerable: true,
|
|
157
|
-
get: function () { return
|
|
157
|
+
get: function () { return chunkEK2W67BW_cjs.retrieveCodeVerifier; }
|
|
158
158
|
});
|
|
159
159
|
Object.defineProperty(exports, "storeCodeVerifier", {
|
|
160
160
|
enumerable: true,
|
|
161
|
-
get: function () { return
|
|
161
|
+
get: function () { return chunkEK2W67BW_cjs.storeCodeVerifier; }
|
|
162
162
|
});
|
|
163
163
|
Object.defineProperty(exports, "useFFID", {
|
|
164
164
|
enumerable: true,
|
|
165
|
-
get: function () { return
|
|
165
|
+
get: function () { return chunkEK2W67BW_cjs.useFFID; }
|
|
166
166
|
});
|
|
167
167
|
Object.defineProperty(exports, "useFFIDAnnouncements", {
|
|
168
168
|
enumerable: true,
|
|
169
|
-
get: function () { return
|
|
169
|
+
get: function () { return chunkEK2W67BW_cjs.useFFIDAnnouncements; }
|
|
170
170
|
});
|
|
171
171
|
Object.defineProperty(exports, "useSubscription", {
|
|
172
172
|
enumerable: true,
|
|
173
|
-
get: function () { return
|
|
173
|
+
get: function () { return chunkEK2W67BW_cjs.useSubscription; }
|
|
174
174
|
});
|
|
175
175
|
Object.defineProperty(exports, "withSubscription", {
|
|
176
176
|
enumerable: true,
|
|
177
|
-
get: function () { return
|
|
177
|
+
get: function () { return chunkEK2W67BW_cjs.withSubscription; }
|
|
178
178
|
});
|
|
179
179
|
exports.FFID_NEWSLETTER_TYPES = FFID_NEWSLETTER_TYPES;
|
|
180
180
|
exports.createKVCacheAdapter = createKVCacheAdapter;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { F as FFIDSubscriptionStatus, a as FFIDConfig, b as FFIDApiResponse, c as FFIDSessionResponse, d as FFIDRedirectResult, e as FFIDError, f as FFIDSubscriptionCheckResponse, g as FFIDListMembersResponse, h as FFIDMemberRole, i as FFIDUpdateMemberRoleResponse, j as FFIDRemoveMemberResponse, k as FFIDProfileCallOptions, l as FFIDUserProfile, m as FFIDUpdateUserProfileRequest, n as FFIDCreateCheckoutParams, o as FFIDCheckoutSessionResponse, p as FFIDCreatePortalParams, q as FFIDPortalSessionResponse, r as FFIDVerifyAccessTokenOptions, s as FFIDOAuthUserInfo, t as FFIDInquiryCreateParams, u as FFIDInquiryCreateResponse, v as FFIDAuthMode, w as FFIDLogger, x as FFIDCacheAdapter, y as FFIDUser, z as FFIDOrganization, A as FFIDSubscription, B as FFIDSubscriptionContextValue, C as FFIDAnnouncementsClientConfig, L as ListAnnouncementsOptions, D as FFIDAnnouncementsApiResponse, E as AnnouncementListResponse, G as FFIDAnnouncementsLogger } from './index
|
|
2
|
-
export { H as Announcement, I as AnnouncementStatus, J as AnnouncementType, K as FFIDAnnouncementBadge, M as FFIDAnnouncementList, N as FFIDAnnouncementsError, O as FFIDAnnouncementsErrorCode, P as FFIDAnnouncementsServerResponse, Q as FFIDCacheConfig, R as FFIDContextValue, S as FFIDInquiryCategory, T as FFIDInquiryCategorySite2026, U as FFIDInquiryForm, V as FFIDInquiryFormCategoryItem, W as FFIDInquiryFormClassNames, X as FFIDInquiryFormOrganization, Y as FFIDInquiryFormPlaceholderContext, Z as FFIDInquiryFormPrefill, _ as FFIDInquiryFormProps, $ as FFIDInquiryFormSubmitData, a0 as FFIDInquiryFormSubmitResult, a1 as FFIDJwtClaims, a2 as FFIDLoginButton, a3 as FFIDMemberStatus, a4 as FFIDOAuthTokenResponse, a5 as FFIDOAuthUserInfoMemberRole, a6 as FFIDOAuthUserInfoSubscription, a7 as FFIDOrganizationMember, a8 as FFIDOrganizationSwitcher, a9 as
|
|
1
|
+
import { F as FFIDSubscriptionStatus, a as FFIDConfig, b as FFIDApiResponse, c as FFIDSessionResponse, d as FFIDRedirectResult, e as FFIDError, f as FFIDSubscriptionCheckResponse, g as FFIDListMembersResponse, h as FFIDMemberRole, i as FFIDUpdateMemberRoleResponse, j as FFIDRemoveMemberResponse, k as FFIDProfileCallOptions, l as FFIDUserProfile, m as FFIDUpdateUserProfileRequest, n as FFIDCreateCheckoutParams, o as FFIDCheckoutSessionResponse, p as FFIDCreatePortalParams, q as FFIDPortalSessionResponse, r as FFIDVerifyAccessTokenOptions, s as FFIDOAuthUserInfo, t as FFIDInquiryCreateParams, u as FFIDInquiryCreateResponse, v as FFIDAuthMode, w as FFIDLogger, x as FFIDCacheAdapter, y as FFIDUser, z as FFIDOrganization, A as FFIDSubscription, B as FFIDSubscriptionContextValue, C as FFIDAnnouncementsClientConfig, L as ListAnnouncementsOptions, D as FFIDAnnouncementsApiResponse, E as AnnouncementListResponse, G as FFIDAnnouncementsLogger } from './index--S6rLHjr.cjs';
|
|
2
|
+
export { H as Announcement, I as AnnouncementStatus, J as AnnouncementType, K as FFIDAnnouncementBadge, M as FFIDAnnouncementList, N as FFIDAnnouncementsError, O as FFIDAnnouncementsErrorCode, P as FFIDAnnouncementsServerResponse, Q as FFIDCacheConfig, R as FFIDContextValue, S as FFIDInquiryCategory, T as FFIDInquiryCategorySite2026, U as FFIDInquiryForm, V as FFIDInquiryFormCategoryItem, W as FFIDInquiryFormClassNames, X as FFIDInquiryFormOrganization, Y as FFIDInquiryFormPlaceholderContext, Z as FFIDInquiryFormPrefill, _ as FFIDInquiryFormProps, $ as FFIDInquiryFormSubmitData, a0 as FFIDInquiryFormSubmitResult, a1 as FFIDJwtClaims, a2 as FFIDLoginButton, a3 as FFIDMemberStatus, a4 as FFIDOAuthTokenResponse, a5 as FFIDOAuthUserInfoMemberRole, a6 as FFIDOAuthUserInfoSubscription, a7 as FFIDOrganizationMember, a8 as FFIDOrganizationSwitcher, a9 as FFIDRedirectErrorCode, aa as FFIDSeatModel, ab as FFIDSubscriptionBadge, ac as FFIDTokenIntrospectionResponse, ad as FFIDUserMenu, ae as FFID_INQUIRY_CATEGORIES, af as FFID_INQUIRY_CATEGORIES_SITE_2026, ag as UseFFIDAnnouncementsOptions, ah as UseFFIDAnnouncementsReturn, ai as isFFIDInquiryCategorySite2026, aj as useFFIDAnnouncements } from './index--S6rLHjr.cjs';
|
|
3
3
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
4
4
|
import { ReactNode, ComponentType, FC } from 'react';
|
|
5
5
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { F as FFIDSubscriptionStatus, a as FFIDConfig, b as FFIDApiResponse, c as FFIDSessionResponse, d as FFIDRedirectResult, e as FFIDError, f as FFIDSubscriptionCheckResponse, g as FFIDListMembersResponse, h as FFIDMemberRole, i as FFIDUpdateMemberRoleResponse, j as FFIDRemoveMemberResponse, k as FFIDProfileCallOptions, l as FFIDUserProfile, m as FFIDUpdateUserProfileRequest, n as FFIDCreateCheckoutParams, o as FFIDCheckoutSessionResponse, p as FFIDCreatePortalParams, q as FFIDPortalSessionResponse, r as FFIDVerifyAccessTokenOptions, s as FFIDOAuthUserInfo, t as FFIDInquiryCreateParams, u as FFIDInquiryCreateResponse, v as FFIDAuthMode, w as FFIDLogger, x as FFIDCacheAdapter, y as FFIDUser, z as FFIDOrganization, A as FFIDSubscription, B as FFIDSubscriptionContextValue, C as FFIDAnnouncementsClientConfig, L as ListAnnouncementsOptions, D as FFIDAnnouncementsApiResponse, E as AnnouncementListResponse, G as FFIDAnnouncementsLogger } from './index
|
|
2
|
-
export { H as Announcement, I as AnnouncementStatus, J as AnnouncementType, K as FFIDAnnouncementBadge, M as FFIDAnnouncementList, N as FFIDAnnouncementsError, O as FFIDAnnouncementsErrorCode, P as FFIDAnnouncementsServerResponse, Q as FFIDCacheConfig, R as FFIDContextValue, S as FFIDInquiryCategory, T as FFIDInquiryCategorySite2026, U as FFIDInquiryForm, V as FFIDInquiryFormCategoryItem, W as FFIDInquiryFormClassNames, X as FFIDInquiryFormOrganization, Y as FFIDInquiryFormPlaceholderContext, Z as FFIDInquiryFormPrefill, _ as FFIDInquiryFormProps, $ as FFIDInquiryFormSubmitData, a0 as FFIDInquiryFormSubmitResult, a1 as FFIDJwtClaims, a2 as FFIDLoginButton, a3 as FFIDMemberStatus, a4 as FFIDOAuthTokenResponse, a5 as FFIDOAuthUserInfoMemberRole, a6 as FFIDOAuthUserInfoSubscription, a7 as FFIDOrganizationMember, a8 as FFIDOrganizationSwitcher, a9 as
|
|
1
|
+
import { F as FFIDSubscriptionStatus, a as FFIDConfig, b as FFIDApiResponse, c as FFIDSessionResponse, d as FFIDRedirectResult, e as FFIDError, f as FFIDSubscriptionCheckResponse, g as FFIDListMembersResponse, h as FFIDMemberRole, i as FFIDUpdateMemberRoleResponse, j as FFIDRemoveMemberResponse, k as FFIDProfileCallOptions, l as FFIDUserProfile, m as FFIDUpdateUserProfileRequest, n as FFIDCreateCheckoutParams, o as FFIDCheckoutSessionResponse, p as FFIDCreatePortalParams, q as FFIDPortalSessionResponse, r as FFIDVerifyAccessTokenOptions, s as FFIDOAuthUserInfo, t as FFIDInquiryCreateParams, u as FFIDInquiryCreateResponse, v as FFIDAuthMode, w as FFIDLogger, x as FFIDCacheAdapter, y as FFIDUser, z as FFIDOrganization, A as FFIDSubscription, B as FFIDSubscriptionContextValue, C as FFIDAnnouncementsClientConfig, L as ListAnnouncementsOptions, D as FFIDAnnouncementsApiResponse, E as AnnouncementListResponse, G as FFIDAnnouncementsLogger } from './index--S6rLHjr.js';
|
|
2
|
+
export { H as Announcement, I as AnnouncementStatus, J as AnnouncementType, K as FFIDAnnouncementBadge, M as FFIDAnnouncementList, N as FFIDAnnouncementsError, O as FFIDAnnouncementsErrorCode, P as FFIDAnnouncementsServerResponse, Q as FFIDCacheConfig, R as FFIDContextValue, S as FFIDInquiryCategory, T as FFIDInquiryCategorySite2026, U as FFIDInquiryForm, V as FFIDInquiryFormCategoryItem, W as FFIDInquiryFormClassNames, X as FFIDInquiryFormOrganization, Y as FFIDInquiryFormPlaceholderContext, Z as FFIDInquiryFormPrefill, _ as FFIDInquiryFormProps, $ as FFIDInquiryFormSubmitData, a0 as FFIDInquiryFormSubmitResult, a1 as FFIDJwtClaims, a2 as FFIDLoginButton, a3 as FFIDMemberStatus, a4 as FFIDOAuthTokenResponse, a5 as FFIDOAuthUserInfoMemberRole, a6 as FFIDOAuthUserInfoSubscription, a7 as FFIDOrganizationMember, a8 as FFIDOrganizationSwitcher, a9 as FFIDRedirectErrorCode, aa as FFIDSeatModel, ab as FFIDSubscriptionBadge, ac as FFIDTokenIntrospectionResponse, ad as FFIDUserMenu, ae as FFID_INQUIRY_CATEGORIES, af as FFID_INQUIRY_CATEGORIES_SITE_2026, ag as UseFFIDAnnouncementsOptions, ah as UseFFIDAnnouncementsReturn, ai as isFFIDInquiryCategorySite2026, aj as useFFIDAnnouncements } from './index--S6rLHjr.js';
|
|
3
3
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
4
4
|
import { ReactNode, ComponentType, FC } from 'react';
|
|
5
5
|
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { useFFIDContext } from './chunk-
|
|
2
|
-
export { DEFAULT_API_BASE_URL, FFIDAnnouncementBadge, FFIDAnnouncementList, FFIDInquiryForm, FFIDLoginButton, FFIDOrganizationSwitcher, FFIDProvider, FFIDSDKError, FFIDSubscriptionBadge, FFIDUserMenu, FFID_ANNOUNCEMENTS_ERROR_CODES, FFID_INQUIRY_CATEGORIES, FFID_INQUIRY_CATEGORIES_SITE_2026, createFFIDAnnouncementsClient, createFFIDClient, createTokenStore, generateCodeChallenge, generateCodeVerifier, isFFIDInquiryCategorySite2026, normalizeRedirectUri, retrieveCodeVerifier, storeCodeVerifier, useFFID, useFFIDAnnouncements, useSubscription, withSubscription } from './chunk-
|
|
1
|
+
import { useFFIDContext } from './chunk-IEYXT3LA.js';
|
|
2
|
+
export { DEFAULT_API_BASE_URL, FFIDAnnouncementBadge, FFIDAnnouncementList, FFIDInquiryForm, FFIDLoginButton, FFIDOrganizationSwitcher, FFIDProvider, FFIDSDKError, FFIDSubscriptionBadge, FFIDUserMenu, FFID_ANNOUNCEMENTS_ERROR_CODES, FFID_INQUIRY_CATEGORIES, FFID_INQUIRY_CATEGORIES_SITE_2026, createFFIDAnnouncementsClient, createFFIDClient, createTokenStore, generateCodeChallenge, generateCodeVerifier, isFFIDInquiryCategorySite2026, normalizeRedirectUri, retrieveCodeVerifier, storeCodeVerifier, useFFID, useFFIDAnnouncements, useSubscription, withSubscription } from './chunk-IEYXT3LA.js';
|
|
3
3
|
import { useRef, useEffect } from 'react';
|
|
4
4
|
import { jsx, Fragment } from 'react/jsx-runtime';
|
|
5
5
|
|
package/dist/server/index.cjs
CHANGED
|
@@ -101,10 +101,7 @@ function createTokenStore(storageType) {
|
|
|
101
101
|
// src/client/oauth-userinfo.ts
|
|
102
102
|
var SESSION_ELIGIBLE_SUBSCRIPTION_STATUSES = [
|
|
103
103
|
"trialing",
|
|
104
|
-
"active"
|
|
105
|
-
"past_due",
|
|
106
|
-
"canceled",
|
|
107
|
-
"paused"
|
|
104
|
+
"active"
|
|
108
105
|
];
|
|
109
106
|
function isSessionEligibleSubscriptionStatus(value) {
|
|
110
107
|
return typeof value === "string" && SESSION_ELIGIBLE_SUBSCRIPTION_STATUSES.includes(value);
|
|
@@ -806,10 +803,11 @@ function createProfileMethods(deps) {
|
|
|
806
803
|
}
|
|
807
804
|
|
|
808
805
|
// src/client/version-check.ts
|
|
809
|
-
var SDK_VERSION = "2.
|
|
806
|
+
var SDK_VERSION = "2.18.0";
|
|
810
807
|
var SDK_USER_AGENT = `FFID-SDK/${SDK_VERSION} (TypeScript)`;
|
|
811
808
|
var SDK_VERSION_HEADER = "X-FFID-SDK-Version";
|
|
812
809
|
function sdkHeaders() {
|
|
810
|
+
if (typeof window !== "undefined") return {};
|
|
813
811
|
return {
|
|
814
812
|
"User-Agent": SDK_USER_AGENT,
|
|
815
813
|
[SDK_VERSION_HEADER]: SDK_VERSION
|
|
@@ -1302,6 +1300,19 @@ function storeCodeVerifier(verifier, logger) {
|
|
|
1302
1300
|
}
|
|
1303
1301
|
return sessionStored || localStored;
|
|
1304
1302
|
}
|
|
1303
|
+
function cleanupVerifierStorage(logger) {
|
|
1304
|
+
try {
|
|
1305
|
+
window.sessionStorage.removeItem(VERIFIER_STORAGE_KEY);
|
|
1306
|
+
} catch (error) {
|
|
1307
|
+
logger?.warn("retrieveCodeVerifier: sessionStorage \u306E\u30AF\u30EA\u30FC\u30F3\u30A2\u30C3\u30D7\u306B\u5931\u6557\u3057\u307E\u3057\u305F:", error);
|
|
1308
|
+
}
|
|
1309
|
+
try {
|
|
1310
|
+
window.localStorage.removeItem(VERIFIER_FALLBACK_STORAGE_KEY);
|
|
1311
|
+
window.localStorage.removeItem(VERIFIER_FALLBACK_TIMESTAMP_KEY);
|
|
1312
|
+
} catch (error) {
|
|
1313
|
+
logger?.warn("retrieveCodeVerifier: localStorage \u306E\u30AF\u30EA\u30FC\u30F3\u30A2\u30C3\u30D7\u306B\u5931\u6557\u3057\u307E\u3057\u305F:", error);
|
|
1314
|
+
}
|
|
1315
|
+
}
|
|
1305
1316
|
function base64UrlEncode(buffer) {
|
|
1306
1317
|
const bytes = new Uint8Array(buffer);
|
|
1307
1318
|
let binary = "";
|
|
@@ -1316,6 +1327,87 @@ var OAUTH_AUTHORIZE_ENDPOINT = "/api/v1/oauth/authorize";
|
|
|
1316
1327
|
var AUTH_LOGOUT_ENDPOINT = "/api/v1/auth/logout";
|
|
1317
1328
|
var STATE_RANDOM_BYTES = 16;
|
|
1318
1329
|
var HEX_BASE2 = 16;
|
|
1330
|
+
var REDIRECT_LOOP_KEY = "ffid_sdk_redirect_loop_history";
|
|
1331
|
+
var REDIRECT_LOOP_WINDOW_MS = 6e4;
|
|
1332
|
+
var REDIRECT_LOOP_THRESHOLD = 3;
|
|
1333
|
+
var storageReadFailureLogged = false;
|
|
1334
|
+
var storageWriteFailureLogged = false;
|
|
1335
|
+
function logStorageReadFailure(logger, err) {
|
|
1336
|
+
if (storageReadFailureLogged) return;
|
|
1337
|
+
storageReadFailureLogged = true;
|
|
1338
|
+
logger.warn(
|
|
1339
|
+
"[FFID SDK] sessionStorage read failed \u2014 redirect loop detection disabled on this browser (fail-open). iOS WebKit private mode / eviction \u304C\u5178\u578B\u8981\u56E0",
|
|
1340
|
+
err
|
|
1341
|
+
);
|
|
1342
|
+
}
|
|
1343
|
+
function logStorageWriteFailure(logger, err) {
|
|
1344
|
+
if (storageWriteFailureLogged) return;
|
|
1345
|
+
storageWriteFailureLogged = true;
|
|
1346
|
+
logger.warn(
|
|
1347
|
+
"[FFID SDK] sessionStorage write failed \u2014 redirect loop detection disabled on this browser (fail-open).",
|
|
1348
|
+
err
|
|
1349
|
+
);
|
|
1350
|
+
}
|
|
1351
|
+
function isRedirectLoopHistory(value) {
|
|
1352
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
|
|
1353
|
+
return Object.values(value).every(
|
|
1354
|
+
(arr) => Array.isArray(arr) && arr.every((t) => typeof t === "number" && Number.isFinite(t))
|
|
1355
|
+
);
|
|
1356
|
+
}
|
|
1357
|
+
function readRedirectLoopHistory(logger) {
|
|
1358
|
+
if (typeof window === "undefined") return {};
|
|
1359
|
+
try {
|
|
1360
|
+
const raw = window.sessionStorage.getItem(REDIRECT_LOOP_KEY);
|
|
1361
|
+
if (raw === null) return {};
|
|
1362
|
+
const parsed = JSON.parse(raw);
|
|
1363
|
+
return isRedirectLoopHistory(parsed) ? parsed : {};
|
|
1364
|
+
} catch (err) {
|
|
1365
|
+
logStorageReadFailure(logger, err);
|
|
1366
|
+
return {};
|
|
1367
|
+
}
|
|
1368
|
+
}
|
|
1369
|
+
function writeRedirectLoopHistory(history, logger) {
|
|
1370
|
+
if (typeof window === "undefined") return;
|
|
1371
|
+
try {
|
|
1372
|
+
window.sessionStorage.setItem(REDIRECT_LOOP_KEY, JSON.stringify(history));
|
|
1373
|
+
} catch (err) {
|
|
1374
|
+
logStorageWriteFailure(logger, err);
|
|
1375
|
+
}
|
|
1376
|
+
}
|
|
1377
|
+
function pruneRedirectLoopHistory(history, now) {
|
|
1378
|
+
const cutoff = now - REDIRECT_LOOP_WINDOW_MS;
|
|
1379
|
+
const pruned = {};
|
|
1380
|
+
for (const [key, timestamps] of Object.entries(history)) {
|
|
1381
|
+
const fresh = timestamps.filter((t) => t > cutoff);
|
|
1382
|
+
if (fresh.length > 0) pruned[key] = fresh;
|
|
1383
|
+
}
|
|
1384
|
+
return pruned;
|
|
1385
|
+
}
|
|
1386
|
+
function getRecentRedirectCount(authorizeKey, now, logger) {
|
|
1387
|
+
const pruned = pruneRedirectLoopHistory(readRedirectLoopHistory(logger), now);
|
|
1388
|
+
writeRedirectLoopHistory(pruned, logger);
|
|
1389
|
+
return pruned[authorizeKey]?.length ?? 0;
|
|
1390
|
+
}
|
|
1391
|
+
function recordRedirectAttempt(authorizeKey, now, logger) {
|
|
1392
|
+
const history = readRedirectLoopHistory(logger);
|
|
1393
|
+
const existing = history[authorizeKey] ?? [];
|
|
1394
|
+
history[authorizeKey] = [...existing, now];
|
|
1395
|
+
writeRedirectLoopHistory(history, logger);
|
|
1396
|
+
}
|
|
1397
|
+
function rollbackLastRedirectAttempt(authorizeKey, logger) {
|
|
1398
|
+
const history = readRedirectLoopHistory(logger);
|
|
1399
|
+
const existing = history[authorizeKey];
|
|
1400
|
+
if (!Array.isArray(existing) || existing.length === 0) return;
|
|
1401
|
+
history[authorizeKey] = existing.slice(0, -1);
|
|
1402
|
+
if (history[authorizeKey].length === 0) delete history[authorizeKey];
|
|
1403
|
+
writeRedirectLoopHistory(history, logger);
|
|
1404
|
+
}
|
|
1405
|
+
function buildAuthorizeKey(baseUrl, clientId, organizationId) {
|
|
1406
|
+
const params = new URLSearchParams({ client_id: clientId });
|
|
1407
|
+
const trimmedOrg = organizationId?.trim();
|
|
1408
|
+
if (trimmedOrg) params.set("organization_id", trimmedOrg);
|
|
1409
|
+
return `${baseUrl}${OAUTH_AUTHORIZE_ENDPOINT}?${params.toString()}`;
|
|
1410
|
+
}
|
|
1319
1411
|
function generateRandomState() {
|
|
1320
1412
|
const array = new Uint8Array(STATE_RANDOM_BYTES);
|
|
1321
1413
|
crypto.getRandomValues(array);
|
|
@@ -1335,6 +1427,23 @@ function createRedirectMethods(deps) {
|
|
|
1335
1427
|
logger.warn("SSR \u74B0\u5883\u3067\u306F\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3067\u304D\u307E\u305B\u3093");
|
|
1336
1428
|
return { success: false, error: "SSR \u74B0\u5883\u3067\u306F\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3067\u304D\u307E\u305B\u3093" };
|
|
1337
1429
|
}
|
|
1430
|
+
const authorizeKey = buildAuthorizeKey(baseUrl, clientId, options?.organizationId);
|
|
1431
|
+
const now = Date.now();
|
|
1432
|
+
const recentCount = getRecentRedirectCount(authorizeKey, now, logger);
|
|
1433
|
+
if (recentCount >= REDIRECT_LOOP_THRESHOLD) {
|
|
1434
|
+
cleanupVerifierStorage(logger);
|
|
1435
|
+
logger.warn("[FFID SDK] redirect loop detected \u2014 \u81EA\u52D5\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3092\u505C\u6B62\u3057\u307E\u3057\u305F", {
|
|
1436
|
+
authorizeKey,
|
|
1437
|
+
recentCount,
|
|
1438
|
+
windowMs: REDIRECT_LOOP_WINDOW_MS,
|
|
1439
|
+
threshold: REDIRECT_LOOP_THRESHOLD
|
|
1440
|
+
});
|
|
1441
|
+
return {
|
|
1442
|
+
success: false,
|
|
1443
|
+
code: "redirect_loop_detected",
|
|
1444
|
+
error: "\u77ED\u6642\u9593\u306B\u540C\u3058\u8A8D\u53EF\u30A8\u30F3\u30C9\u30DD\u30A4\u30F3\u30C8\u3078\u306E\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u304C\u7E70\u308A\u8FD4\u3055\u308C\u305F\u305F\u3081\u3001\u30EB\u30FC\u30D7\u691C\u51FA\u306B\u3088\u308A\u81EA\u52D5\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3092\u505C\u6B62\u3057\u307E\u3057\u305F"
|
|
1445
|
+
};
|
|
1446
|
+
}
|
|
1338
1447
|
const verifier = generateCodeVerifier();
|
|
1339
1448
|
storeCodeVerifier(verifier, logger);
|
|
1340
1449
|
let challenge;
|
|
@@ -1365,7 +1474,13 @@ function createRedirectMethods(deps) {
|
|
|
1365
1474
|
}
|
|
1366
1475
|
const authorizeUrl = `${baseUrl}${OAUTH_AUTHORIZE_ENDPOINT}?${params.toString()}`;
|
|
1367
1476
|
logger.debug("Redirecting to authorize:", authorizeUrl);
|
|
1368
|
-
|
|
1477
|
+
recordRedirectAttempt(authorizeKey, now, logger);
|
|
1478
|
+
try {
|
|
1479
|
+
window.location.href = authorizeUrl;
|
|
1480
|
+
} catch (err) {
|
|
1481
|
+
rollbackLastRedirectAttempt(authorizeKey, logger);
|
|
1482
|
+
throw err;
|
|
1483
|
+
}
|
|
1369
1484
|
return { success: true };
|
|
1370
1485
|
}
|
|
1371
1486
|
async function redirectToLogin() {
|
package/dist/server/index.d.cts
CHANGED
|
@@ -825,8 +825,11 @@ interface FFIDUserProfile {
|
|
|
825
825
|
* When `accessToken` is supplied with a non-empty value, it overrides the client's
|
|
826
826
|
* configured auth mode: authentication for that request uses only
|
|
827
827
|
* `Authorization: Bearer <accessToken>` (no service key, no cookie, no token-store
|
|
828
|
-
* lookup, no auto-refresh on 401). Non-auth headers such as `Content-Type`
|
|
829
|
-
* SDK metadata headers (User-Agent / X-FFID-SDK-Version) are
|
|
828
|
+
* lookup, no auto-refresh on 401). Non-auth headers such as `Content-Type` are
|
|
829
|
+
* still attached. SDK metadata headers (`User-Agent` / `X-FFID-SDK-Version`) are
|
|
830
|
+
* attached on non-browser runtimes only (Node.js / Cloudflare Workers / Edge /
|
|
831
|
+
* Deno); browsers receive an empty object to avoid iOS WebKit `fetch()` breakage
|
|
832
|
+
* (see #2417).
|
|
830
833
|
*
|
|
831
834
|
* Runtime semantics:
|
|
832
835
|
* - `accessToken` omitted (or `undefined`) → no override, configured `authMode` is used
|
|
@@ -888,17 +891,36 @@ interface FFIDUpdateUserProfileRequest {
|
|
|
888
891
|
/** Arbitrary user preferences bag (null clears the column; reads return {}) */
|
|
889
892
|
preferences?: Record<string, unknown> | null;
|
|
890
893
|
}
|
|
894
|
+
/**
|
|
895
|
+
* Discriminant for redirect failures that callers need to handle
|
|
896
|
+
* programmatically (vs. logging the human-readable `error` string).
|
|
897
|
+
*
|
|
898
|
+
* - `'redirect_loop_detected'`: `redirectToAuthorize()` detected that the
|
|
899
|
+
* same authorize URL (keyed by `baseUrl + client_id + organization_id`)
|
|
900
|
+
* has been fired **3 times within 60 seconds**. Caller should surface a
|
|
901
|
+
* manual "再度ログインする" UI rather than retry automatically (#2406 / #2411).
|
|
902
|
+
*
|
|
903
|
+
* SDK 2.18.0 only ships `'redirect_loop_detected'`. Other failure paths
|
|
904
|
+
* (SSR environment, PKCE generation failure, empty organizationId) currently
|
|
905
|
+
* return `error` without a `code`. New codes will be added in future minor
|
|
906
|
+
* versions — treat this union as forward-extensible and do NOT exhaustively
|
|
907
|
+
* `switch` over it without a `default` branch for consumer code that must
|
|
908
|
+
* stay compatible across SDK upgrades.
|
|
909
|
+
*/
|
|
910
|
+
type FFIDRedirectErrorCode = 'redirect_loop_detected';
|
|
891
911
|
/**
|
|
892
912
|
* Result of a redirect operation (redirectToLogin / redirectToAuthorize / redirectToLogout)
|
|
893
913
|
*
|
|
894
914
|
* Structured return type so callers can inspect failure reasons
|
|
895
|
-
* instead of receiving a bare `false`.
|
|
915
|
+
* instead of receiving a bare `false`. When `code` is set, branch on it
|
|
916
|
+
* for programmatic handling; otherwise log `error` for humans.
|
|
896
917
|
*/
|
|
897
918
|
type FFIDRedirectResult = {
|
|
898
919
|
success: true;
|
|
899
920
|
} | {
|
|
900
921
|
success: false;
|
|
901
922
|
error: string;
|
|
923
|
+
code?: FFIDRedirectErrorCode;
|
|
902
924
|
};
|
|
903
925
|
|
|
904
926
|
/** OTP / magic link methods - sendOtp / verifyOtp */
|
package/dist/server/index.d.ts
CHANGED
|
@@ -825,8 +825,11 @@ interface FFIDUserProfile {
|
|
|
825
825
|
* When `accessToken` is supplied with a non-empty value, it overrides the client's
|
|
826
826
|
* configured auth mode: authentication for that request uses only
|
|
827
827
|
* `Authorization: Bearer <accessToken>` (no service key, no cookie, no token-store
|
|
828
|
-
* lookup, no auto-refresh on 401). Non-auth headers such as `Content-Type`
|
|
829
|
-
* SDK metadata headers (User-Agent / X-FFID-SDK-Version) are
|
|
828
|
+
* lookup, no auto-refresh on 401). Non-auth headers such as `Content-Type` are
|
|
829
|
+
* still attached. SDK metadata headers (`User-Agent` / `X-FFID-SDK-Version`) are
|
|
830
|
+
* attached on non-browser runtimes only (Node.js / Cloudflare Workers / Edge /
|
|
831
|
+
* Deno); browsers receive an empty object to avoid iOS WebKit `fetch()` breakage
|
|
832
|
+
* (see #2417).
|
|
830
833
|
*
|
|
831
834
|
* Runtime semantics:
|
|
832
835
|
* - `accessToken` omitted (or `undefined`) → no override, configured `authMode` is used
|
|
@@ -888,17 +891,36 @@ interface FFIDUpdateUserProfileRequest {
|
|
|
888
891
|
/** Arbitrary user preferences bag (null clears the column; reads return {}) */
|
|
889
892
|
preferences?: Record<string, unknown> | null;
|
|
890
893
|
}
|
|
894
|
+
/**
|
|
895
|
+
* Discriminant for redirect failures that callers need to handle
|
|
896
|
+
* programmatically (vs. logging the human-readable `error` string).
|
|
897
|
+
*
|
|
898
|
+
* - `'redirect_loop_detected'`: `redirectToAuthorize()` detected that the
|
|
899
|
+
* same authorize URL (keyed by `baseUrl + client_id + organization_id`)
|
|
900
|
+
* has been fired **3 times within 60 seconds**. Caller should surface a
|
|
901
|
+
* manual "再度ログインする" UI rather than retry automatically (#2406 / #2411).
|
|
902
|
+
*
|
|
903
|
+
* SDK 2.18.0 only ships `'redirect_loop_detected'`. Other failure paths
|
|
904
|
+
* (SSR environment, PKCE generation failure, empty organizationId) currently
|
|
905
|
+
* return `error` without a `code`. New codes will be added in future minor
|
|
906
|
+
* versions — treat this union as forward-extensible and do NOT exhaustively
|
|
907
|
+
* `switch` over it without a `default` branch for consumer code that must
|
|
908
|
+
* stay compatible across SDK upgrades.
|
|
909
|
+
*/
|
|
910
|
+
type FFIDRedirectErrorCode = 'redirect_loop_detected';
|
|
891
911
|
/**
|
|
892
912
|
* Result of a redirect operation (redirectToLogin / redirectToAuthorize / redirectToLogout)
|
|
893
913
|
*
|
|
894
914
|
* Structured return type so callers can inspect failure reasons
|
|
895
|
-
* instead of receiving a bare `false`.
|
|
915
|
+
* instead of receiving a bare `false`. When `code` is set, branch on it
|
|
916
|
+
* for programmatic handling; otherwise log `error` for humans.
|
|
896
917
|
*/
|
|
897
918
|
type FFIDRedirectResult = {
|
|
898
919
|
success: true;
|
|
899
920
|
} | {
|
|
900
921
|
success: false;
|
|
901
922
|
error: string;
|
|
923
|
+
code?: FFIDRedirectErrorCode;
|
|
902
924
|
};
|
|
903
925
|
|
|
904
926
|
/** OTP / magic link methods - sendOtp / verifyOtp */
|
package/dist/server/index.js
CHANGED
|
@@ -100,10 +100,7 @@ function createTokenStore(storageType) {
|
|
|
100
100
|
// src/client/oauth-userinfo.ts
|
|
101
101
|
var SESSION_ELIGIBLE_SUBSCRIPTION_STATUSES = [
|
|
102
102
|
"trialing",
|
|
103
|
-
"active"
|
|
104
|
-
"past_due",
|
|
105
|
-
"canceled",
|
|
106
|
-
"paused"
|
|
103
|
+
"active"
|
|
107
104
|
];
|
|
108
105
|
function isSessionEligibleSubscriptionStatus(value) {
|
|
109
106
|
return typeof value === "string" && SESSION_ELIGIBLE_SUBSCRIPTION_STATUSES.includes(value);
|
|
@@ -805,10 +802,11 @@ function createProfileMethods(deps) {
|
|
|
805
802
|
}
|
|
806
803
|
|
|
807
804
|
// src/client/version-check.ts
|
|
808
|
-
var SDK_VERSION = "2.
|
|
805
|
+
var SDK_VERSION = "2.18.0";
|
|
809
806
|
var SDK_USER_AGENT = `FFID-SDK/${SDK_VERSION} (TypeScript)`;
|
|
810
807
|
var SDK_VERSION_HEADER = "X-FFID-SDK-Version";
|
|
811
808
|
function sdkHeaders() {
|
|
809
|
+
if (typeof window !== "undefined") return {};
|
|
812
810
|
return {
|
|
813
811
|
"User-Agent": SDK_USER_AGENT,
|
|
814
812
|
[SDK_VERSION_HEADER]: SDK_VERSION
|
|
@@ -1301,6 +1299,19 @@ function storeCodeVerifier(verifier, logger) {
|
|
|
1301
1299
|
}
|
|
1302
1300
|
return sessionStored || localStored;
|
|
1303
1301
|
}
|
|
1302
|
+
function cleanupVerifierStorage(logger) {
|
|
1303
|
+
try {
|
|
1304
|
+
window.sessionStorage.removeItem(VERIFIER_STORAGE_KEY);
|
|
1305
|
+
} catch (error) {
|
|
1306
|
+
logger?.warn("retrieveCodeVerifier: sessionStorage \u306E\u30AF\u30EA\u30FC\u30F3\u30A2\u30C3\u30D7\u306B\u5931\u6557\u3057\u307E\u3057\u305F:", error);
|
|
1307
|
+
}
|
|
1308
|
+
try {
|
|
1309
|
+
window.localStorage.removeItem(VERIFIER_FALLBACK_STORAGE_KEY);
|
|
1310
|
+
window.localStorage.removeItem(VERIFIER_FALLBACK_TIMESTAMP_KEY);
|
|
1311
|
+
} catch (error) {
|
|
1312
|
+
logger?.warn("retrieveCodeVerifier: localStorage \u306E\u30AF\u30EA\u30FC\u30F3\u30A2\u30C3\u30D7\u306B\u5931\u6557\u3057\u307E\u3057\u305F:", error);
|
|
1313
|
+
}
|
|
1314
|
+
}
|
|
1304
1315
|
function base64UrlEncode(buffer) {
|
|
1305
1316
|
const bytes = new Uint8Array(buffer);
|
|
1306
1317
|
let binary = "";
|
|
@@ -1315,6 +1326,87 @@ var OAUTH_AUTHORIZE_ENDPOINT = "/api/v1/oauth/authorize";
|
|
|
1315
1326
|
var AUTH_LOGOUT_ENDPOINT = "/api/v1/auth/logout";
|
|
1316
1327
|
var STATE_RANDOM_BYTES = 16;
|
|
1317
1328
|
var HEX_BASE2 = 16;
|
|
1329
|
+
var REDIRECT_LOOP_KEY = "ffid_sdk_redirect_loop_history";
|
|
1330
|
+
var REDIRECT_LOOP_WINDOW_MS = 6e4;
|
|
1331
|
+
var REDIRECT_LOOP_THRESHOLD = 3;
|
|
1332
|
+
var storageReadFailureLogged = false;
|
|
1333
|
+
var storageWriteFailureLogged = false;
|
|
1334
|
+
function logStorageReadFailure(logger, err) {
|
|
1335
|
+
if (storageReadFailureLogged) return;
|
|
1336
|
+
storageReadFailureLogged = true;
|
|
1337
|
+
logger.warn(
|
|
1338
|
+
"[FFID SDK] sessionStorage read failed \u2014 redirect loop detection disabled on this browser (fail-open). iOS WebKit private mode / eviction \u304C\u5178\u578B\u8981\u56E0",
|
|
1339
|
+
err
|
|
1340
|
+
);
|
|
1341
|
+
}
|
|
1342
|
+
function logStorageWriteFailure(logger, err) {
|
|
1343
|
+
if (storageWriteFailureLogged) return;
|
|
1344
|
+
storageWriteFailureLogged = true;
|
|
1345
|
+
logger.warn(
|
|
1346
|
+
"[FFID SDK] sessionStorage write failed \u2014 redirect loop detection disabled on this browser (fail-open).",
|
|
1347
|
+
err
|
|
1348
|
+
);
|
|
1349
|
+
}
|
|
1350
|
+
function isRedirectLoopHistory(value) {
|
|
1351
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return false;
|
|
1352
|
+
return Object.values(value).every(
|
|
1353
|
+
(arr) => Array.isArray(arr) && arr.every((t) => typeof t === "number" && Number.isFinite(t))
|
|
1354
|
+
);
|
|
1355
|
+
}
|
|
1356
|
+
function readRedirectLoopHistory(logger) {
|
|
1357
|
+
if (typeof window === "undefined") return {};
|
|
1358
|
+
try {
|
|
1359
|
+
const raw = window.sessionStorage.getItem(REDIRECT_LOOP_KEY);
|
|
1360
|
+
if (raw === null) return {};
|
|
1361
|
+
const parsed = JSON.parse(raw);
|
|
1362
|
+
return isRedirectLoopHistory(parsed) ? parsed : {};
|
|
1363
|
+
} catch (err) {
|
|
1364
|
+
logStorageReadFailure(logger, err);
|
|
1365
|
+
return {};
|
|
1366
|
+
}
|
|
1367
|
+
}
|
|
1368
|
+
function writeRedirectLoopHistory(history, logger) {
|
|
1369
|
+
if (typeof window === "undefined") return;
|
|
1370
|
+
try {
|
|
1371
|
+
window.sessionStorage.setItem(REDIRECT_LOOP_KEY, JSON.stringify(history));
|
|
1372
|
+
} catch (err) {
|
|
1373
|
+
logStorageWriteFailure(logger, err);
|
|
1374
|
+
}
|
|
1375
|
+
}
|
|
1376
|
+
function pruneRedirectLoopHistory(history, now) {
|
|
1377
|
+
const cutoff = now - REDIRECT_LOOP_WINDOW_MS;
|
|
1378
|
+
const pruned = {};
|
|
1379
|
+
for (const [key, timestamps] of Object.entries(history)) {
|
|
1380
|
+
const fresh = timestamps.filter((t) => t > cutoff);
|
|
1381
|
+
if (fresh.length > 0) pruned[key] = fresh;
|
|
1382
|
+
}
|
|
1383
|
+
return pruned;
|
|
1384
|
+
}
|
|
1385
|
+
function getRecentRedirectCount(authorizeKey, now, logger) {
|
|
1386
|
+
const pruned = pruneRedirectLoopHistory(readRedirectLoopHistory(logger), now);
|
|
1387
|
+
writeRedirectLoopHistory(pruned, logger);
|
|
1388
|
+
return pruned[authorizeKey]?.length ?? 0;
|
|
1389
|
+
}
|
|
1390
|
+
function recordRedirectAttempt(authorizeKey, now, logger) {
|
|
1391
|
+
const history = readRedirectLoopHistory(logger);
|
|
1392
|
+
const existing = history[authorizeKey] ?? [];
|
|
1393
|
+
history[authorizeKey] = [...existing, now];
|
|
1394
|
+
writeRedirectLoopHistory(history, logger);
|
|
1395
|
+
}
|
|
1396
|
+
function rollbackLastRedirectAttempt(authorizeKey, logger) {
|
|
1397
|
+
const history = readRedirectLoopHistory(logger);
|
|
1398
|
+
const existing = history[authorizeKey];
|
|
1399
|
+
if (!Array.isArray(existing) || existing.length === 0) return;
|
|
1400
|
+
history[authorizeKey] = existing.slice(0, -1);
|
|
1401
|
+
if (history[authorizeKey].length === 0) delete history[authorizeKey];
|
|
1402
|
+
writeRedirectLoopHistory(history, logger);
|
|
1403
|
+
}
|
|
1404
|
+
function buildAuthorizeKey(baseUrl, clientId, organizationId) {
|
|
1405
|
+
const params = new URLSearchParams({ client_id: clientId });
|
|
1406
|
+
const trimmedOrg = organizationId?.trim();
|
|
1407
|
+
if (trimmedOrg) params.set("organization_id", trimmedOrg);
|
|
1408
|
+
return `${baseUrl}${OAUTH_AUTHORIZE_ENDPOINT}?${params.toString()}`;
|
|
1409
|
+
}
|
|
1318
1410
|
function generateRandomState() {
|
|
1319
1411
|
const array = new Uint8Array(STATE_RANDOM_BYTES);
|
|
1320
1412
|
crypto.getRandomValues(array);
|
|
@@ -1334,6 +1426,23 @@ function createRedirectMethods(deps) {
|
|
|
1334
1426
|
logger.warn("SSR \u74B0\u5883\u3067\u306F\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3067\u304D\u307E\u305B\u3093");
|
|
1335
1427
|
return { success: false, error: "SSR \u74B0\u5883\u3067\u306F\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3067\u304D\u307E\u305B\u3093" };
|
|
1336
1428
|
}
|
|
1429
|
+
const authorizeKey = buildAuthorizeKey(baseUrl, clientId, options?.organizationId);
|
|
1430
|
+
const now = Date.now();
|
|
1431
|
+
const recentCount = getRecentRedirectCount(authorizeKey, now, logger);
|
|
1432
|
+
if (recentCount >= REDIRECT_LOOP_THRESHOLD) {
|
|
1433
|
+
cleanupVerifierStorage(logger);
|
|
1434
|
+
logger.warn("[FFID SDK] redirect loop detected \u2014 \u81EA\u52D5\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3092\u505C\u6B62\u3057\u307E\u3057\u305F", {
|
|
1435
|
+
authorizeKey,
|
|
1436
|
+
recentCount,
|
|
1437
|
+
windowMs: REDIRECT_LOOP_WINDOW_MS,
|
|
1438
|
+
threshold: REDIRECT_LOOP_THRESHOLD
|
|
1439
|
+
});
|
|
1440
|
+
return {
|
|
1441
|
+
success: false,
|
|
1442
|
+
code: "redirect_loop_detected",
|
|
1443
|
+
error: "\u77ED\u6642\u9593\u306B\u540C\u3058\u8A8D\u53EF\u30A8\u30F3\u30C9\u30DD\u30A4\u30F3\u30C8\u3078\u306E\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u304C\u7E70\u308A\u8FD4\u3055\u308C\u305F\u305F\u3081\u3001\u30EB\u30FC\u30D7\u691C\u51FA\u306B\u3088\u308A\u81EA\u52D5\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3092\u505C\u6B62\u3057\u307E\u3057\u305F"
|
|
1444
|
+
};
|
|
1445
|
+
}
|
|
1337
1446
|
const verifier = generateCodeVerifier();
|
|
1338
1447
|
storeCodeVerifier(verifier, logger);
|
|
1339
1448
|
let challenge;
|
|
@@ -1364,7 +1473,13 @@ function createRedirectMethods(deps) {
|
|
|
1364
1473
|
}
|
|
1365
1474
|
const authorizeUrl = `${baseUrl}${OAUTH_AUTHORIZE_ENDPOINT}?${params.toString()}`;
|
|
1366
1475
|
logger.debug("Redirecting to authorize:", authorizeUrl);
|
|
1367
|
-
|
|
1476
|
+
recordRedirectAttempt(authorizeKey, now, logger);
|
|
1477
|
+
try {
|
|
1478
|
+
window.location.href = authorizeUrl;
|
|
1479
|
+
} catch (err) {
|
|
1480
|
+
rollbackLastRedirectAttempt(authorizeKey, logger);
|
|
1481
|
+
throw err;
|
|
1482
|
+
}
|
|
1368
1483
|
return { success: true };
|
|
1369
1484
|
}
|
|
1370
1485
|
async function redirectToLogin() {
|