@feelflow/ffid-sdk 5.10.0 → 5.13.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-IZSL6DI7.cjs → chunk-JFFRO2Q5.cjs} +308 -11
- package/dist/{chunk-OJ3OJTIE.js → chunk-RATTQIKM.js} +304 -12
- 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/{ffid-client-DRjzfAaS.d.cts → ffid-client-BG0Qr86U.d.ts} +228 -71
- package/dist/{ffid-client-gboJROw0.d.ts → ffid-client-C3K3sAqq.d.cts} +228 -71
- package/dist/{index-DSvX4q8E.d.cts → index-C6dnexXf.d.cts} +24 -43
- package/dist/{index-DSvX4q8E.d.ts → index-C6dnexXf.d.ts} +24 -43
- package/dist/index.cjs +56 -36
- package/dist/index.d.cts +266 -4
- package/dist/index.d.ts +266 -4
- package/dist/index.js +2 -2
- package/dist/server/index.cjs +144 -5
- package/dist/server/index.d.cts +3 -3
- package/dist/server/index.d.ts +3 -3
- package/dist/server/index.js +144 -5
- package/dist/server/test/index.d.cts +2 -2
- package/dist/server/test/index.d.ts +2 -2
- package/dist/{types-BYdtyO_2.d.cts → types-BeVl-z12.d.cts} +10 -1
- package/dist/{types-BYdtyO_2.d.ts → types-BeVl-z12.d.ts} +10 -1
- package/dist/webhooks/index.cjs +5 -2
- package/dist/webhooks/index.d.cts +1 -1
- package/dist/webhooks/index.d.ts +1 -1
- package/dist/webhooks/index.js +5 -2
- package/package.json +1 -1
|
@@ -122,6 +122,9 @@ function normalizeUserinfo(raw) {
|
|
|
122
122
|
return {
|
|
123
123
|
sub: raw.sub,
|
|
124
124
|
email: raw.email,
|
|
125
|
+
// #3791: undefined → undefined を保つ(false に丸めない)。古い FFID は wire に
|
|
126
|
+
// 含めないため、consumer は undefined を「未確認」ではなく「不明」として扱える。
|
|
127
|
+
emailVerified: raw.email_verified,
|
|
125
128
|
name: raw.name,
|
|
126
129
|
picture: raw.picture,
|
|
127
130
|
companyName: raw.company_name ?? null,
|
|
@@ -377,6 +380,9 @@ function createVerifyAccessToken(deps) {
|
|
|
377
380
|
const base = {
|
|
378
381
|
sub: introspectResponse.sub,
|
|
379
382
|
email: introspectResponse.email ?? null,
|
|
383
|
+
// #3791: preserve undefined (do NOT coerce to false) so introspect-strategy
|
|
384
|
+
// callers can distinguish "verified" / "not verified" / "older server".
|
|
385
|
+
email_verified: introspectResponse.email_verified,
|
|
380
386
|
name: introspectResponse.name ?? null,
|
|
381
387
|
picture: introspectResponse.picture ?? null,
|
|
382
388
|
company_name: introspectResponse.company_name ?? null,
|
|
@@ -434,6 +440,8 @@ function createVerifyAccessToken(deps) {
|
|
|
434
440
|
// src/client/billing-methods.ts
|
|
435
441
|
var BILLING_CHECKOUT_ENDPOINT = "/api/v1/billing/checkout";
|
|
436
442
|
var BILLING_PORTAL_ENDPOINT = "/api/v1/billing/portal";
|
|
443
|
+
var EXT_INVOICES_ENDPOINT = "/api/v1/billing/ext/invoices";
|
|
444
|
+
var EXT_RETRY_PAYMENT_ENDPOINT = "/api/v1/billing/ext/retry-payment";
|
|
437
445
|
function createBillingMethods(deps) {
|
|
438
446
|
const { fetchWithAuth, createError } = deps;
|
|
439
447
|
async function createCheckoutSession(params) {
|
|
@@ -475,7 +483,31 @@ function createBillingMethods(deps) {
|
|
|
475
483
|
`${BILLING_PORTAL_ENDPOINT}?${query.toString()}`
|
|
476
484
|
);
|
|
477
485
|
}
|
|
478
|
-
|
|
486
|
+
async function listInvoices(params) {
|
|
487
|
+
if (!params.organizationId) {
|
|
488
|
+
return { error: createError("VALIDATION_ERROR", "organizationId \u306F\u5FC5\u9808\u3067\u3059") };
|
|
489
|
+
}
|
|
490
|
+
const query = new URLSearchParams({ organizationId: params.organizationId });
|
|
491
|
+
return fetchWithAuth(`${EXT_INVOICES_ENDPOINT}?${query.toString()}`);
|
|
492
|
+
}
|
|
493
|
+
async function getInvoice(params) {
|
|
494
|
+
if (!params.invoiceId) {
|
|
495
|
+
return { error: createError("VALIDATION_ERROR", "invoiceId \u306F\u5FC5\u9808\u3067\u3059") };
|
|
496
|
+
}
|
|
497
|
+
return fetchWithAuth(
|
|
498
|
+
`${EXT_INVOICES_ENDPOINT}/${encodeURIComponent(params.invoiceId)}`
|
|
499
|
+
);
|
|
500
|
+
}
|
|
501
|
+
async function retryPayment(params) {
|
|
502
|
+
if (!params.organizationId) {
|
|
503
|
+
return { error: createError("VALIDATION_ERROR", "organizationId \u306F\u5FC5\u9808\u3067\u3059") };
|
|
504
|
+
}
|
|
505
|
+
return fetchWithAuth(EXT_RETRY_PAYMENT_ENDPOINT, {
|
|
506
|
+
method: "POST",
|
|
507
|
+
body: JSON.stringify({ organizationId: params.organizationId })
|
|
508
|
+
});
|
|
509
|
+
}
|
|
510
|
+
return { createCheckoutSession, createPortalSession, listInvoices, getInvoice, retryPayment };
|
|
479
511
|
}
|
|
480
512
|
|
|
481
513
|
// src/client/subscription-methods.ts
|
|
@@ -817,6 +849,45 @@ function createMembersMethods(deps) {
|
|
|
817
849
|
return { listMembers, addMember, updateMemberRole, removeMember };
|
|
818
850
|
}
|
|
819
851
|
|
|
852
|
+
// src/client/seat-methods.ts
|
|
853
|
+
function seatsEndpoint(subscriptionId) {
|
|
854
|
+
return `/api/v1/subscriptions/ext/${encodeURIComponent(subscriptionId)}/seats/assignments`;
|
|
855
|
+
}
|
|
856
|
+
function createSeatMethods(deps) {
|
|
857
|
+
const { fetchWithAuth, createError } = deps;
|
|
858
|
+
async function listSeatAssignments(params) {
|
|
859
|
+
if (!params.subscriptionId) {
|
|
860
|
+
return { error: createError("VALIDATION_ERROR", "subscriptionId \u306F\u5FC5\u9808\u3067\u3059") };
|
|
861
|
+
}
|
|
862
|
+
return fetchWithAuth(seatsEndpoint(params.subscriptionId));
|
|
863
|
+
}
|
|
864
|
+
async function assignSeats(params) {
|
|
865
|
+
if (!params.subscriptionId) {
|
|
866
|
+
return { error: createError("VALIDATION_ERROR", "subscriptionId \u306F\u5FC5\u9808\u3067\u3059") };
|
|
867
|
+
}
|
|
868
|
+
if (!Array.isArray(params.userIds) || params.userIds.length === 0) {
|
|
869
|
+
return { error: createError("VALIDATION_ERROR", "userIds \u306F 1 \u4EF6\u4EE5\u4E0A\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044") };
|
|
870
|
+
}
|
|
871
|
+
return fetchWithAuth(seatsEndpoint(params.subscriptionId), {
|
|
872
|
+
method: "POST",
|
|
873
|
+
body: JSON.stringify({ userIds: params.userIds })
|
|
874
|
+
});
|
|
875
|
+
}
|
|
876
|
+
async function unassignSeat(params) {
|
|
877
|
+
if (!params.subscriptionId) {
|
|
878
|
+
return { error: createError("VALIDATION_ERROR", "subscriptionId \u306F\u5FC5\u9808\u3067\u3059") };
|
|
879
|
+
}
|
|
880
|
+
if (!params.userId) {
|
|
881
|
+
return { error: createError("VALIDATION_ERROR", "userId \u306F\u5FC5\u9808\u3067\u3059") };
|
|
882
|
+
}
|
|
883
|
+
return fetchWithAuth(
|
|
884
|
+
`${seatsEndpoint(params.subscriptionId)}/${encodeURIComponent(params.userId)}`,
|
|
885
|
+
{ method: "DELETE" }
|
|
886
|
+
);
|
|
887
|
+
}
|
|
888
|
+
return { listSeatAssignments, assignSeats, unassignSeat };
|
|
889
|
+
}
|
|
890
|
+
|
|
820
891
|
// src/client/profile-methods.ts
|
|
821
892
|
var EXT_PROFILE_ENDPOINT = "/api/v1/users/ext/me";
|
|
822
893
|
function resolveAuthOverride(options, createError) {
|
|
@@ -867,7 +938,7 @@ function createProfileMethods(deps) {
|
|
|
867
938
|
}
|
|
868
939
|
|
|
869
940
|
// src/client/version-check.ts
|
|
870
|
-
var SDK_VERSION = "5.
|
|
941
|
+
var SDK_VERSION = "5.13.0";
|
|
871
942
|
var SDK_USER_AGENT = `FFID-SDK/${SDK_VERSION} (TypeScript)`;
|
|
872
943
|
var SDK_VERSION_HEADER = "X-FFID-SDK-Version";
|
|
873
944
|
function sdkHeaders() {
|
|
@@ -939,9 +1010,20 @@ function createOAuthTokenMethods(deps) {
|
|
|
939
1010
|
logger,
|
|
940
1011
|
errorCodes
|
|
941
1012
|
} = deps;
|
|
942
|
-
async function exchangeCodeForTokens(code, codeVerifier) {
|
|
1013
|
+
async function exchangeCodeForTokens(code, codeVerifier, opts) {
|
|
943
1014
|
const url = `${baseUrl}${OAUTH_TOKEN_ENDPOINT}`;
|
|
944
1015
|
logger.debug("Exchanging code for tokens:", url);
|
|
1016
|
+
if (opts?.expectedState !== void 0 || opts?.actualState !== void 0) {
|
|
1017
|
+
if (opts?.expectedState !== opts?.actualState) {
|
|
1018
|
+
logger.error("Token exchange aborted: OAuth state mismatch or omitted (possible CSRF)");
|
|
1019
|
+
return {
|
|
1020
|
+
error: {
|
|
1021
|
+
code: errorCodes.STATE_MISMATCH_ERROR,
|
|
1022
|
+
message: "OAuth state \u304C\u4E00\u81F4\u3057\u307E\u305B\u3093\u3002CSRF \u306E\u53EF\u80FD\u6027\u304C\u3042\u308B\u305F\u3081\u8A8D\u8A3C\u3092\u4E2D\u6B62\u3057\u307E\u3057\u305F"
|
|
1023
|
+
}
|
|
1024
|
+
};
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
945
1027
|
const effectiveRedirectUri = resolvedRedirectUri ?? (typeof window !== "undefined" ? window.location.origin + window.location.pathname : null);
|
|
946
1028
|
if (!effectiveRedirectUri) {
|
|
947
1029
|
logger.error("redirectUri is required for token exchange in SSR environments. Set config.redirectUri explicitly.");
|
|
@@ -1425,6 +1507,102 @@ function base64UrlEncode(buffer) {
|
|
|
1425
1507
|
return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
1426
1508
|
}
|
|
1427
1509
|
|
|
1510
|
+
// src/auth/state.ts
|
|
1511
|
+
var STATE_STORAGE_KEY = "ffid_oauth_state";
|
|
1512
|
+
var STATE_FALLBACK_STORAGE_KEY = "ffid_oauth_state_fb";
|
|
1513
|
+
var STATE_FALLBACK_TIMESTAMP_KEY = "ffid_oauth_state_fb_ts";
|
|
1514
|
+
var OAUTH_STATE_MAX_AGE_MS = 5 * 60 * 1e3;
|
|
1515
|
+
function storeState(state, logger) {
|
|
1516
|
+
if (typeof window === "undefined") {
|
|
1517
|
+
logger?.warn("storeState: storage is not available in SSR context");
|
|
1518
|
+
return false;
|
|
1519
|
+
}
|
|
1520
|
+
let sessionStored = false;
|
|
1521
|
+
try {
|
|
1522
|
+
window.sessionStorage.setItem(STATE_STORAGE_KEY, state);
|
|
1523
|
+
sessionStored = true;
|
|
1524
|
+
} catch (error) {
|
|
1525
|
+
logger?.warn("storeState: sessionStorage \u3078\u306E\u4FDD\u5B58\u306B\u5931\u6557\u3057\u307E\u3057\u305F:", error);
|
|
1526
|
+
}
|
|
1527
|
+
let localStored = false;
|
|
1528
|
+
try {
|
|
1529
|
+
window.localStorage.setItem(STATE_FALLBACK_STORAGE_KEY, state);
|
|
1530
|
+
window.localStorage.setItem(STATE_FALLBACK_TIMESTAMP_KEY, String(Date.now()));
|
|
1531
|
+
localStored = true;
|
|
1532
|
+
} catch (error) {
|
|
1533
|
+
logger?.warn("storeState: localStorage fallback \u3078\u306E\u4FDD\u5B58\u306B\u5931\u6557\u3057\u307E\u3057\u305F:", error);
|
|
1534
|
+
}
|
|
1535
|
+
return sessionStored || localStored;
|
|
1536
|
+
}
|
|
1537
|
+
function retrieveState(logger) {
|
|
1538
|
+
if (typeof window === "undefined") return null;
|
|
1539
|
+
let sessionState = null;
|
|
1540
|
+
try {
|
|
1541
|
+
sessionState = window.sessionStorage.getItem(STATE_STORAGE_KEY);
|
|
1542
|
+
} catch (error) {
|
|
1543
|
+
logger?.warn("retrieveState: sessionStorage \u304B\u3089\u306E\u53D6\u5F97\u306B\u5931\u6557\u3057\u307E\u3057\u305F:", error);
|
|
1544
|
+
}
|
|
1545
|
+
if (sessionState) {
|
|
1546
|
+
cleanupStateStorage(logger);
|
|
1547
|
+
return sessionState;
|
|
1548
|
+
}
|
|
1549
|
+
let fallbackState = null;
|
|
1550
|
+
let fallbackTimestamp = null;
|
|
1551
|
+
try {
|
|
1552
|
+
fallbackState = window.localStorage.getItem(STATE_FALLBACK_STORAGE_KEY);
|
|
1553
|
+
fallbackTimestamp = window.localStorage.getItem(STATE_FALLBACK_TIMESTAMP_KEY);
|
|
1554
|
+
} catch (error) {
|
|
1555
|
+
logger?.warn("retrieveState: localStorage fallback \u304B\u3089\u306E\u53D6\u5F97\u306B\u5931\u6557\u3057\u307E\u3057\u305F:", error);
|
|
1556
|
+
cleanupStateStorage(logger);
|
|
1557
|
+
return null;
|
|
1558
|
+
}
|
|
1559
|
+
if (!fallbackState || !fallbackTimestamp) {
|
|
1560
|
+
cleanupStateStorage(logger);
|
|
1561
|
+
return null;
|
|
1562
|
+
}
|
|
1563
|
+
const ts = Number(fallbackTimestamp);
|
|
1564
|
+
const age = Date.now() - ts;
|
|
1565
|
+
if (!Number.isFinite(ts) || age < 0 || age > OAUTH_STATE_MAX_AGE_MS) {
|
|
1566
|
+
cleanupStateStorage(logger);
|
|
1567
|
+
return null;
|
|
1568
|
+
}
|
|
1569
|
+
logger?.warn(
|
|
1570
|
+
"retrieveState: sessionStorage was empty, recovered OAuth state from localStorage fallback"
|
|
1571
|
+
);
|
|
1572
|
+
cleanupStateStorage(logger);
|
|
1573
|
+
return fallbackState;
|
|
1574
|
+
}
|
|
1575
|
+
function isStateStorageAvailable() {
|
|
1576
|
+
if (typeof window === "undefined") return false;
|
|
1577
|
+
const probeKey = "__ffid_oauth_state_probe__";
|
|
1578
|
+
try {
|
|
1579
|
+
window.sessionStorage.setItem(probeKey, "1");
|
|
1580
|
+
window.sessionStorage.removeItem(probeKey);
|
|
1581
|
+
return true;
|
|
1582
|
+
} catch {
|
|
1583
|
+
}
|
|
1584
|
+
try {
|
|
1585
|
+
window.localStorage.setItem(probeKey, "1");
|
|
1586
|
+
window.localStorage.removeItem(probeKey);
|
|
1587
|
+
return true;
|
|
1588
|
+
} catch {
|
|
1589
|
+
return false;
|
|
1590
|
+
}
|
|
1591
|
+
}
|
|
1592
|
+
function cleanupStateStorage(logger) {
|
|
1593
|
+
try {
|
|
1594
|
+
window.sessionStorage.removeItem(STATE_STORAGE_KEY);
|
|
1595
|
+
} catch (error) {
|
|
1596
|
+
logger?.warn("retrieveState: sessionStorage \u306E\u30AF\u30EA\u30FC\u30F3\u30A2\u30C3\u30D7\u306B\u5931\u6557\u3057\u307E\u3057\u305F:", error);
|
|
1597
|
+
}
|
|
1598
|
+
try {
|
|
1599
|
+
window.localStorage.removeItem(STATE_FALLBACK_STORAGE_KEY);
|
|
1600
|
+
window.localStorage.removeItem(STATE_FALLBACK_TIMESTAMP_KEY);
|
|
1601
|
+
} catch (error) {
|
|
1602
|
+
logger?.warn("retrieveState: localStorage \u306E\u30AF\u30EA\u30FC\u30F3\u30A2\u30C3\u30D7\u306B\u5931\u6557\u3057\u307E\u3057\u305F:", error);
|
|
1603
|
+
}
|
|
1604
|
+
}
|
|
1605
|
+
|
|
1428
1606
|
// src/client/redirect.ts
|
|
1429
1607
|
var OAUTH_AUTHORIZE_ENDPOINT = "/api/v1/oauth/authorize";
|
|
1430
1608
|
var AUTH_LOGOUT_ENDPOINT = "/api/v1/auth/logout";
|
|
@@ -1556,6 +1734,7 @@ function createRedirectMethods(deps) {
|
|
|
1556
1734
|
const recentCount = getRecentRedirectCount(authorizeKey, now, logger);
|
|
1557
1735
|
if (recentCount >= REDIRECT_LOOP_THRESHOLD) {
|
|
1558
1736
|
cleanupVerifierStorage(logger);
|
|
1737
|
+
cleanupStateStorage(logger);
|
|
1559
1738
|
logger.warn("[FFID SDK] redirect loop detected \u2014 \u81EA\u52D5\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3092\u505C\u6B62\u3057\u307E\u3057\u305F", {
|
|
1560
1739
|
authorizeKey,
|
|
1561
1740
|
recentCount,
|
|
@@ -1579,6 +1758,11 @@ function createRedirectMethods(deps) {
|
|
|
1579
1758
|
return { success: false, error: errorMessage };
|
|
1580
1759
|
}
|
|
1581
1760
|
const state = generateRandomState();
|
|
1761
|
+
if (!storeState(state, logger)) {
|
|
1762
|
+
logger.error(
|
|
1763
|
+
"redirectToAuthorize: OAuth state \u3092\u4FDD\u5B58\u3067\u304D\u307E\u305B\u3093\u3067\u3057\u305F\uFF08storage \u5229\u7528\u4E0D\u53EF\uFF09\u3002\u30B3\u30FC\u30EB\u30D0\u30C3\u30AF\u3067\u306E CSRF \u691C\u8A3C\u304C\u5931\u6557\u3057\u307E\u3059"
|
|
1764
|
+
);
|
|
1765
|
+
}
|
|
1582
1766
|
const redirectUri = resolvedRedirectUri ?? window.location.origin + window.location.pathname;
|
|
1583
1767
|
const params = new URLSearchParams({
|
|
1584
1768
|
response_type: "code",
|
|
@@ -2257,6 +2441,15 @@ function createInquiryMethods(deps) {
|
|
|
2257
2441
|
}
|
|
2258
2442
|
|
|
2259
2443
|
// src/subscriptions/types.ts
|
|
2444
|
+
var EFFECTIVE_SUBSCRIPTION_STATUSES = [
|
|
2445
|
+
"active",
|
|
2446
|
+
"active_canceling",
|
|
2447
|
+
"past_due_grace",
|
|
2448
|
+
"blocked",
|
|
2449
|
+
"canceled",
|
|
2450
|
+
"trial_expired",
|
|
2451
|
+
"expired"
|
|
2452
|
+
];
|
|
2260
2453
|
var PAST_DUE_GRACE_PERIOD_DAYS = 7;
|
|
2261
2454
|
var HOURS_PER_DAY = 24;
|
|
2262
2455
|
var MINUTES_PER_HOUR = 60;
|
|
@@ -2359,7 +2552,8 @@ var FFID_ERROR_CODES = {
|
|
|
2359
2552
|
TOKEN_EXCHANGE_ERROR: "TOKEN_EXCHANGE_ERROR",
|
|
2360
2553
|
TOKEN_REFRESH_ERROR: "TOKEN_REFRESH_ERROR",
|
|
2361
2554
|
NO_TOKENS: "NO_TOKENS",
|
|
2362
|
-
TOKEN_VERIFICATION_ERROR: "TOKEN_VERIFICATION_ERROR"
|
|
2555
|
+
TOKEN_VERIFICATION_ERROR: "TOKEN_VERIFICATION_ERROR",
|
|
2556
|
+
STATE_MISMATCH_ERROR: "STATE_MISMATCH_ERROR"
|
|
2363
2557
|
};
|
|
2364
2558
|
var EXT_CHECK_ENDPOINT = "/api/v1/subscriptions/ext/check";
|
|
2365
2559
|
var DEFAULT_ALLOW_GRACE = true;
|
|
@@ -2717,7 +2911,11 @@ function createFFIDClient(config) {
|
|
|
2717
2911
|
}
|
|
2718
2912
|
return result;
|
|
2719
2913
|
}
|
|
2720
|
-
const { createCheckoutSession, createPortalSession } = createBillingMethods({
|
|
2914
|
+
const { createCheckoutSession, createPortalSession, listInvoices, getInvoice, retryPayment } = createBillingMethods({
|
|
2915
|
+
fetchWithAuth,
|
|
2916
|
+
createError
|
|
2917
|
+
});
|
|
2918
|
+
const { listSeatAssignments, assignSeats, unassignSeat } = createSeatMethods({
|
|
2721
2919
|
fetchWithAuth,
|
|
2722
2920
|
createError
|
|
2723
2921
|
});
|
|
@@ -2826,6 +3024,12 @@ function createFFIDClient(config) {
|
|
|
2826
3024
|
getAnalyticsConfig,
|
|
2827
3025
|
createCheckoutSession,
|
|
2828
3026
|
createPortalSession,
|
|
3027
|
+
listInvoices,
|
|
3028
|
+
getInvoice,
|
|
3029
|
+
retryPayment,
|
|
3030
|
+
listSeatAssignments,
|
|
3031
|
+
assignSeats,
|
|
3032
|
+
unassignSeat,
|
|
2829
3033
|
listPlans,
|
|
2830
3034
|
getSubscription,
|
|
2831
3035
|
subscribe,
|
|
@@ -2867,6 +3071,99 @@ function createFFIDClient(config) {
|
|
|
2867
3071
|
redirectUri: resolvedRedirectUri
|
|
2868
3072
|
};
|
|
2869
3073
|
}
|
|
3074
|
+
|
|
3075
|
+
// src/client/callback.ts
|
|
3076
|
+
var CALLBACK_ERROR_CODES = {
|
|
3077
|
+
/** Stored state missing or did not match the callback `state` (CSRF guard) */
|
|
3078
|
+
STATE_MISMATCH: "STATE_MISMATCH_ERROR",
|
|
3079
|
+
/**
|
|
3080
|
+
* Web storage is unavailable, so `state` could never be persisted across the
|
|
3081
|
+
* OAuth bounce. An environment issue (e.g. iOS private mode), not an attack —
|
|
3082
|
+
* surfaced distinctly so callers don't mistake it for a CSRF mismatch.
|
|
3083
|
+
*/
|
|
3084
|
+
STATE_UNAVAILABLE: "STATE_STORAGE_UNAVAILABLE",
|
|
3085
|
+
/** Callback URL had no `code` param */
|
|
3086
|
+
MISSING_CODE: "MISSING_AUTHORIZATION_CODE",
|
|
3087
|
+
/** SSR with no `options.url` — cannot resolve callback params */
|
|
3088
|
+
CALLBACK_URL_UNAVAILABLE: "CALLBACK_URL_UNAVAILABLE"
|
|
3089
|
+
};
|
|
3090
|
+
function extractSearchParams(source) {
|
|
3091
|
+
try {
|
|
3092
|
+
return new URL(source).searchParams;
|
|
3093
|
+
} catch {
|
|
3094
|
+
const queryStart = source.indexOf("?");
|
|
3095
|
+
const query = queryStart >= 0 ? source.slice(queryStart + 1) : source;
|
|
3096
|
+
return new URLSearchParams(query);
|
|
3097
|
+
}
|
|
3098
|
+
}
|
|
3099
|
+
function cleanCallbackUrlParams(logger) {
|
|
3100
|
+
try {
|
|
3101
|
+
const cleanUrl = new URL(window.location.href);
|
|
3102
|
+
cleanUrl.searchParams.delete("code");
|
|
3103
|
+
cleanUrl.searchParams.delete("state");
|
|
3104
|
+
window.history.replaceState({}, "", cleanUrl.toString());
|
|
3105
|
+
} catch (error) {
|
|
3106
|
+
logger.warn("handleRedirectCallback: URL \u306E\u30AF\u30EA\u30FC\u30F3\u30A2\u30C3\u30D7\u306B\u5931\u6557\u3057\u307E\u3057\u305F:", error);
|
|
3107
|
+
}
|
|
3108
|
+
}
|
|
3109
|
+
async function handleRedirectCallback(client, options) {
|
|
3110
|
+
const { logger } = client;
|
|
3111
|
+
const hasWindow = typeof window !== "undefined";
|
|
3112
|
+
const source = options?.url ?? (hasWindow ? window.location.search : null);
|
|
3113
|
+
if (source === null) {
|
|
3114
|
+
logger.warn("handleRedirectCallback: SSR \u74B0\u5883\u3067\u306F options.url \u304C\u5FC5\u8981\u3067\u3059");
|
|
3115
|
+
return {
|
|
3116
|
+
error: client.createError(
|
|
3117
|
+
CALLBACK_ERROR_CODES.CALLBACK_URL_UNAVAILABLE,
|
|
3118
|
+
"\u30B3\u30FC\u30EB\u30D0\u30C3\u30AF URL \u3092\u89E3\u6C7A\u3067\u304D\u307E\u305B\u3093\u3002SSR \u74B0\u5883\u3067\u306F options.url \u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044"
|
|
3119
|
+
)
|
|
3120
|
+
};
|
|
3121
|
+
}
|
|
3122
|
+
const params = extractSearchParams(source);
|
|
3123
|
+
const code = params.get("code");
|
|
3124
|
+
const urlState = params.get("state");
|
|
3125
|
+
if (!code) {
|
|
3126
|
+
logger.warn("handleRedirectCallback: \u8A8D\u53EF\u30B3\u30FC\u30C9\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093");
|
|
3127
|
+
return {
|
|
3128
|
+
error: client.createError(
|
|
3129
|
+
CALLBACK_ERROR_CODES.MISSING_CODE,
|
|
3130
|
+
"\u30B3\u30FC\u30EB\u30D0\u30C3\u30AF URL \u306B\u8A8D\u53EF\u30B3\u30FC\u30C9\u304C\u542B\u307E\u308C\u3066\u3044\u307E\u305B\u3093"
|
|
3131
|
+
)
|
|
3132
|
+
};
|
|
3133
|
+
}
|
|
3134
|
+
const storedState = retrieveState(logger);
|
|
3135
|
+
if (storedState !== null || urlState !== null) {
|
|
3136
|
+
const stateMatches = storedState !== null && storedState === urlState;
|
|
3137
|
+
if (!stateMatches) {
|
|
3138
|
+
if (storedState === null && !isStateStorageAvailable()) {
|
|
3139
|
+
logger.error(
|
|
3140
|
+
"handleRedirectCallback: OAuth state \u3092\u4FDD\u5B58\u3067\u304D\u308B storage \u304C\u5229\u7528\u3067\u304D\u306A\u3044\u305F\u3081 CSRF \u691C\u8A3C\u304C\u3067\u304D\u307E\u305B\u3093\uFF08iOS private mode \u7B49\uFF09"
|
|
3141
|
+
);
|
|
3142
|
+
return {
|
|
3143
|
+
error: client.createError(
|
|
3144
|
+
CALLBACK_ERROR_CODES.STATE_UNAVAILABLE,
|
|
3145
|
+
"OAuth state \u3092\u4FDD\u5B58\u3067\u304D\u308B storage \u304C\u5229\u7528\u3067\u304D\u306A\u3044\u305F\u3081\u8A8D\u8A3C\u3092\u5B8C\u4E86\u3067\u304D\u307E\u305B\u3093\u3002\u30D6\u30E9\u30A6\u30B6\u306E\u30D7\u30E9\u30A4\u30D9\u30FC\u30C8\u30E2\u30FC\u30C9\u3084\u30B9\u30C8\u30EC\u30FC\u30B8\u8A2D\u5B9A\u3092\u3054\u78BA\u8A8D\u304F\u3060\u3055\u3044"
|
|
3146
|
+
)
|
|
3147
|
+
};
|
|
3148
|
+
}
|
|
3149
|
+
logger.error(
|
|
3150
|
+
"handleRedirectCallback: OAuth state mismatch or omitted (possible CSRF) \u2014 \u8A8D\u8A3C\u3092\u4E2D\u6B62\u3057\u307E\u3057\u305F"
|
|
3151
|
+
);
|
|
3152
|
+
return {
|
|
3153
|
+
error: client.createError(
|
|
3154
|
+
CALLBACK_ERROR_CODES.STATE_MISMATCH,
|
|
3155
|
+
"OAuth state \u304C\u4E00\u81F4\u3057\u307E\u305B\u3093\u3002CSRF \u306E\u53EF\u80FD\u6027\u304C\u3042\u308B\u305F\u3081\u8A8D\u8A3C\u3092\u4E2D\u6B62\u3057\u307E\u3057\u305F"
|
|
3156
|
+
)
|
|
3157
|
+
};
|
|
3158
|
+
}
|
|
3159
|
+
}
|
|
3160
|
+
const verifier = retrieveCodeVerifier(logger);
|
|
3161
|
+
const result = await client.exchangeCodeForTokens(code, verifier ?? void 0);
|
|
3162
|
+
if (!result.error && hasWindow) {
|
|
3163
|
+
cleanCallbackUrlParams(logger);
|
|
3164
|
+
}
|
|
3165
|
+
return result;
|
|
3166
|
+
}
|
|
2870
3167
|
var DEFAULT_REFRESH_INTERVAL_MS = 5 * 60 * 1e3;
|
|
2871
3168
|
var TOKEN_REFRESH_RATIO = 0.8;
|
|
2872
3169
|
var FFIDContext = createContext(null);
|
|
@@ -3041,8 +3338,7 @@ function FFIDProvider({
|
|
|
3041
3338
|
const code = urlParams.get("code");
|
|
3042
3339
|
if (!code) return;
|
|
3043
3340
|
client.logger.debug("Authorization code detected, exchanging for tokens");
|
|
3044
|
-
|
|
3045
|
-
client.exchangeCodeForTokens(code, codeVerifier ?? void 0).then((result) => {
|
|
3341
|
+
handleRedirectCallback(client).then((result) => {
|
|
3046
3342
|
if (result.error) {
|
|
3047
3343
|
client.logger.error("Token exchange failed:", result.error);
|
|
3048
3344
|
setError(result.error);
|
|
@@ -3050,10 +3346,6 @@ function FFIDProvider({
|
|
|
3050
3346
|
setIsLoading(false);
|
|
3051
3347
|
return;
|
|
3052
3348
|
}
|
|
3053
|
-
const cleanUrl = new URL(window.location.href);
|
|
3054
|
-
cleanUrl.searchParams.delete("code");
|
|
3055
|
-
cleanUrl.searchParams.delete("state");
|
|
3056
|
-
window.history.replaceState({}, "", cleanUrl.toString());
|
|
3057
3349
|
client.logger.debug("Token exchange successful, refreshing session");
|
|
3058
3350
|
return refresh();
|
|
3059
3351
|
}).catch((err) => {
|
|
@@ -4854,4 +5146,4 @@ function FFIDInquiryForm({
|
|
|
4854
5146
|
);
|
|
4855
5147
|
}
|
|
4856
5148
|
|
|
4857
|
-
export { ACCESS_GRANTING_EFFECTIVE_STATUSES, BLOCKING_EFFECTIVE_STATUSES, DEFAULT_API_BASE_URL, DEFAULT_OAUTH_SCOPES, FFIDAnnouncementBadge, FFIDAnnouncementList, FFIDInquiryForm, FFIDLoginButton, FFIDOrganizationSwitcher, FFIDProvider, FFIDSDKError, FFIDSubscriptionBadge, FFIDUserMenu, FFID_ANNOUNCEMENTS_ERROR_CODES, FFID_INQUIRY_CATEGORIES, FFID_INQUIRY_CATEGORIES_SITE_2026, computeEffectiveStatusFromSession, createFFIDAnnouncementsClient, createFFIDClient, createTokenStore, generateCodeChallenge, generateCodeVerifier, hasAccessFromUserinfo, isBlockedFromUserinfo, isFFIDInquiryCategorySite2026, normalizeRedirectUri, retrieveCodeVerifier, storeCodeVerifier, useFFID, useFFIDAnnouncements, useFFIDContext, useSubscription, withSubscription };
|
|
5149
|
+
export { ACCESS_GRANTING_EFFECTIVE_STATUSES, BLOCKING_EFFECTIVE_STATUSES, DEFAULT_API_BASE_URL, DEFAULT_OAUTH_SCOPES, EFFECTIVE_SUBSCRIPTION_STATUSES, FFIDAnnouncementBadge, FFIDAnnouncementList, FFIDInquiryForm, FFIDLoginButton, FFIDOrganizationSwitcher, FFIDProvider, FFIDSDKError, FFIDSubscriptionBadge, FFIDUserMenu, FFID_ANNOUNCEMENTS_ERROR_CODES, FFID_INQUIRY_CATEGORIES, FFID_INQUIRY_CATEGORIES_SITE_2026, cleanupStateStorage, computeEffectiveStatusFromSession, createFFIDAnnouncementsClient, createFFIDClient, createTokenStore, generateCodeChallenge, generateCodeVerifier, handleRedirectCallback, hasAccessFromUserinfo, isBlockedFromUserinfo, isFFIDInquiryCategorySite2026, normalizeRedirectUri, retrieveCodeVerifier, retrieveState, storeCodeVerifier, storeState, useFFID, useFFIDAnnouncements, useFFIDContext, useSubscription, withSubscription };
|
|
@@ -1,34 +1,34 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunkJFFRO2Q5_cjs = require('../chunk-JFFRO2Q5.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 chunkJFFRO2Q5_cjs.FFIDAnnouncementBadge; }
|
|
10
10
|
});
|
|
11
11
|
Object.defineProperty(exports, "FFIDAnnouncementList", {
|
|
12
12
|
enumerable: true,
|
|
13
|
-
get: function () { return
|
|
13
|
+
get: function () { return chunkJFFRO2Q5_cjs.FFIDAnnouncementList; }
|
|
14
14
|
});
|
|
15
15
|
Object.defineProperty(exports, "FFIDInquiryForm", {
|
|
16
16
|
enumerable: true,
|
|
17
|
-
get: function () { return
|
|
17
|
+
get: function () { return chunkJFFRO2Q5_cjs.FFIDInquiryForm; }
|
|
18
18
|
});
|
|
19
19
|
Object.defineProperty(exports, "FFIDLoginButton", {
|
|
20
20
|
enumerable: true,
|
|
21
|
-
get: function () { return
|
|
21
|
+
get: function () { return chunkJFFRO2Q5_cjs.FFIDLoginButton; }
|
|
22
22
|
});
|
|
23
23
|
Object.defineProperty(exports, "FFIDOrganizationSwitcher", {
|
|
24
24
|
enumerable: true,
|
|
25
|
-
get: function () { return
|
|
25
|
+
get: function () { return chunkJFFRO2Q5_cjs.FFIDOrganizationSwitcher; }
|
|
26
26
|
});
|
|
27
27
|
Object.defineProperty(exports, "FFIDSubscriptionBadge", {
|
|
28
28
|
enumerable: true,
|
|
29
|
-
get: function () { return
|
|
29
|
+
get: function () { return chunkJFFRO2Q5_cjs.FFIDSubscriptionBadge; }
|
|
30
30
|
});
|
|
31
31
|
Object.defineProperty(exports, "FFIDUserMenu", {
|
|
32
32
|
enumerable: true,
|
|
33
|
-
get: function () { return
|
|
33
|
+
get: function () { return chunkJFFRO2Q5_cjs.FFIDUserMenu; }
|
|
34
34
|
});
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { I as FFIDAnnouncementBadge, ai as FFIDAnnouncementBadgeClassNames, aj as FFIDAnnouncementBadgeProps, J as FFIDAnnouncementList, ak as FFIDAnnouncementListClassNames, al as FFIDAnnouncementListProps, S as FFIDInquiryForm, T as FFIDInquiryFormCategoryItem, U as FFIDInquiryFormClassNames, V as FFIDInquiryFormLegalLayout, W as FFIDInquiryFormOrganization, X as FFIDInquiryFormPlaceholderContext, Y as FFIDInquiryFormPrefill, Z as FFIDInquiryFormProps, _ as FFIDInquiryFormSubmitData, $ as FFIDInquiryFormSubmitResult, a1 as FFIDLoginButton, am as FFIDLoginButtonProps, a4 as FFIDOrganizationSwitcher, an as FFIDOrganizationSwitcherClassNames, ao as FFIDOrganizationSwitcherProps, a9 as FFIDSubscriptionBadge, ap as FFIDSubscriptionBadgeClassNames, aq as FFIDSubscriptionBadgeProps, ab as FFIDUserMenu, ar as FFIDUserMenuClassNames, as as FFIDUserMenuProps } from '../index-C6dnexXf.cjs';
|
|
2
2
|
import 'react/jsx-runtime';
|
|
3
3
|
import 'react';
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { I as FFIDAnnouncementBadge, ai as FFIDAnnouncementBadgeClassNames, aj as FFIDAnnouncementBadgeProps, J as FFIDAnnouncementList, ak as FFIDAnnouncementListClassNames, al as FFIDAnnouncementListProps, S as FFIDInquiryForm, T as FFIDInquiryFormCategoryItem, U as FFIDInquiryFormClassNames, V as FFIDInquiryFormLegalLayout, W as FFIDInquiryFormOrganization, X as FFIDInquiryFormPlaceholderContext, Y as FFIDInquiryFormPrefill, Z as FFIDInquiryFormProps, _ as FFIDInquiryFormSubmitData, $ as FFIDInquiryFormSubmitResult, a1 as FFIDLoginButton, am as FFIDLoginButtonProps, a4 as FFIDOrganizationSwitcher, an as FFIDOrganizationSwitcherClassNames, ao as FFIDOrganizationSwitcherProps, a9 as FFIDSubscriptionBadge, ap as FFIDSubscriptionBadgeClassNames, aq as FFIDSubscriptionBadgeProps, ab as FFIDUserMenu, ar as FFIDUserMenuClassNames, as as FFIDUserMenuProps } from '../index-C6dnexXf.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-RATTQIKM.js';
|