@feelflow/ffid-sdk 5.12.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-AOJDV3UO.cjs → chunk-JFFRO2Q5.cjs} +221 -9
- package/dist/{chunk-KB2JG64Z.js → chunk-RATTQIKM.js} +218 -10
- 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-5IprSUVR.d.ts → ffid-client-BG0Qr86U.d.ts} +59 -29
- package/dist/{ffid-client-Dx4oc4Cw.d.cts → ffid-client-C3K3sAqq.d.cts} +59 -29
- package/dist/{index-DqsWKU16.d.cts → index-C6dnexXf.d.cts} +13 -0
- package/dist/{index-DqsWKU16.d.ts → index-C6dnexXf.d.ts} +13 -0
- package/dist/index.cjs +53 -37
- package/dist/index.d.cts +98 -4
- package/dist/index.d.ts +98 -4
- package/dist/index.js +2 -2
- package/dist/server/index.cjs +67 -3
- package/dist/server/index.d.cts +2 -2
- package/dist/server/index.d.ts +2 -2
- package/dist/server/index.js +67 -3
- package/dist/server/test/index.d.cts +1 -1
- package/dist/server/test/index.d.ts +1 -1
- package/dist/webhooks/index.cjs +5 -2
- package/dist/webhooks/index.js +5 -2
- package/package.json +1 -1
|
@@ -124,6 +124,9 @@ function normalizeUserinfo(raw) {
|
|
|
124
124
|
return {
|
|
125
125
|
sub: raw.sub,
|
|
126
126
|
email: raw.email,
|
|
127
|
+
// #3791: undefined → undefined を保つ(false に丸めない)。古い FFID は wire に
|
|
128
|
+
// 含めないため、consumer は undefined を「未確認」ではなく「不明」として扱える。
|
|
129
|
+
emailVerified: raw.email_verified,
|
|
127
130
|
name: raw.name,
|
|
128
131
|
picture: raw.picture,
|
|
129
132
|
companyName: raw.company_name ?? null,
|
|
@@ -379,6 +382,9 @@ function createVerifyAccessToken(deps) {
|
|
|
379
382
|
const base = {
|
|
380
383
|
sub: introspectResponse.sub,
|
|
381
384
|
email: introspectResponse.email ?? null,
|
|
385
|
+
// #3791: preserve undefined (do NOT coerce to false) so introspect-strategy
|
|
386
|
+
// callers can distinguish "verified" / "not verified" / "older server".
|
|
387
|
+
email_verified: introspectResponse.email_verified,
|
|
382
388
|
name: introspectResponse.name ?? null,
|
|
383
389
|
picture: introspectResponse.picture ?? null,
|
|
384
390
|
company_name: introspectResponse.company_name ?? null,
|
|
@@ -934,7 +940,7 @@ function createProfileMethods(deps) {
|
|
|
934
940
|
}
|
|
935
941
|
|
|
936
942
|
// src/client/version-check.ts
|
|
937
|
-
var SDK_VERSION = "5.
|
|
943
|
+
var SDK_VERSION = "5.13.0";
|
|
938
944
|
var SDK_USER_AGENT = `FFID-SDK/${SDK_VERSION} (TypeScript)`;
|
|
939
945
|
var SDK_VERSION_HEADER = "X-FFID-SDK-Version";
|
|
940
946
|
function sdkHeaders() {
|
|
@@ -1006,9 +1012,20 @@ function createOAuthTokenMethods(deps) {
|
|
|
1006
1012
|
logger,
|
|
1007
1013
|
errorCodes
|
|
1008
1014
|
} = deps;
|
|
1009
|
-
async function exchangeCodeForTokens(code, codeVerifier) {
|
|
1015
|
+
async function exchangeCodeForTokens(code, codeVerifier, opts) {
|
|
1010
1016
|
const url = `${baseUrl}${OAUTH_TOKEN_ENDPOINT}`;
|
|
1011
1017
|
logger.debug("Exchanging code for tokens:", url);
|
|
1018
|
+
if (opts?.expectedState !== void 0 || opts?.actualState !== void 0) {
|
|
1019
|
+
if (opts?.expectedState !== opts?.actualState) {
|
|
1020
|
+
logger.error("Token exchange aborted: OAuth state mismatch or omitted (possible CSRF)");
|
|
1021
|
+
return {
|
|
1022
|
+
error: {
|
|
1023
|
+
code: errorCodes.STATE_MISMATCH_ERROR,
|
|
1024
|
+
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"
|
|
1025
|
+
}
|
|
1026
|
+
};
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1012
1029
|
const effectiveRedirectUri = resolvedRedirectUri ?? (typeof window !== "undefined" ? window.location.origin + window.location.pathname : null);
|
|
1013
1030
|
if (!effectiveRedirectUri) {
|
|
1014
1031
|
logger.error("redirectUri is required for token exchange in SSR environments. Set config.redirectUri explicitly.");
|
|
@@ -1492,6 +1509,102 @@ function base64UrlEncode(buffer) {
|
|
|
1492
1509
|
return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
1493
1510
|
}
|
|
1494
1511
|
|
|
1512
|
+
// src/auth/state.ts
|
|
1513
|
+
var STATE_STORAGE_KEY = "ffid_oauth_state";
|
|
1514
|
+
var STATE_FALLBACK_STORAGE_KEY = "ffid_oauth_state_fb";
|
|
1515
|
+
var STATE_FALLBACK_TIMESTAMP_KEY = "ffid_oauth_state_fb_ts";
|
|
1516
|
+
var OAUTH_STATE_MAX_AGE_MS = 5 * 60 * 1e3;
|
|
1517
|
+
function storeState(state, logger) {
|
|
1518
|
+
if (typeof window === "undefined") {
|
|
1519
|
+
logger?.warn("storeState: storage is not available in SSR context");
|
|
1520
|
+
return false;
|
|
1521
|
+
}
|
|
1522
|
+
let sessionStored = false;
|
|
1523
|
+
try {
|
|
1524
|
+
window.sessionStorage.setItem(STATE_STORAGE_KEY, state);
|
|
1525
|
+
sessionStored = true;
|
|
1526
|
+
} catch (error) {
|
|
1527
|
+
logger?.warn("storeState: sessionStorage \u3078\u306E\u4FDD\u5B58\u306B\u5931\u6557\u3057\u307E\u3057\u305F:", error);
|
|
1528
|
+
}
|
|
1529
|
+
let localStored = false;
|
|
1530
|
+
try {
|
|
1531
|
+
window.localStorage.setItem(STATE_FALLBACK_STORAGE_KEY, state);
|
|
1532
|
+
window.localStorage.setItem(STATE_FALLBACK_TIMESTAMP_KEY, String(Date.now()));
|
|
1533
|
+
localStored = true;
|
|
1534
|
+
} catch (error) {
|
|
1535
|
+
logger?.warn("storeState: localStorage fallback \u3078\u306E\u4FDD\u5B58\u306B\u5931\u6557\u3057\u307E\u3057\u305F:", error);
|
|
1536
|
+
}
|
|
1537
|
+
return sessionStored || localStored;
|
|
1538
|
+
}
|
|
1539
|
+
function retrieveState(logger) {
|
|
1540
|
+
if (typeof window === "undefined") return null;
|
|
1541
|
+
let sessionState = null;
|
|
1542
|
+
try {
|
|
1543
|
+
sessionState = window.sessionStorage.getItem(STATE_STORAGE_KEY);
|
|
1544
|
+
} catch (error) {
|
|
1545
|
+
logger?.warn("retrieveState: sessionStorage \u304B\u3089\u306E\u53D6\u5F97\u306B\u5931\u6557\u3057\u307E\u3057\u305F:", error);
|
|
1546
|
+
}
|
|
1547
|
+
if (sessionState) {
|
|
1548
|
+
cleanupStateStorage(logger);
|
|
1549
|
+
return sessionState;
|
|
1550
|
+
}
|
|
1551
|
+
let fallbackState = null;
|
|
1552
|
+
let fallbackTimestamp = null;
|
|
1553
|
+
try {
|
|
1554
|
+
fallbackState = window.localStorage.getItem(STATE_FALLBACK_STORAGE_KEY);
|
|
1555
|
+
fallbackTimestamp = window.localStorage.getItem(STATE_FALLBACK_TIMESTAMP_KEY);
|
|
1556
|
+
} catch (error) {
|
|
1557
|
+
logger?.warn("retrieveState: localStorage fallback \u304B\u3089\u306E\u53D6\u5F97\u306B\u5931\u6557\u3057\u307E\u3057\u305F:", error);
|
|
1558
|
+
cleanupStateStorage(logger);
|
|
1559
|
+
return null;
|
|
1560
|
+
}
|
|
1561
|
+
if (!fallbackState || !fallbackTimestamp) {
|
|
1562
|
+
cleanupStateStorage(logger);
|
|
1563
|
+
return null;
|
|
1564
|
+
}
|
|
1565
|
+
const ts = Number(fallbackTimestamp);
|
|
1566
|
+
const age = Date.now() - ts;
|
|
1567
|
+
if (!Number.isFinite(ts) || age < 0 || age > OAUTH_STATE_MAX_AGE_MS) {
|
|
1568
|
+
cleanupStateStorage(logger);
|
|
1569
|
+
return null;
|
|
1570
|
+
}
|
|
1571
|
+
logger?.warn(
|
|
1572
|
+
"retrieveState: sessionStorage was empty, recovered OAuth state from localStorage fallback"
|
|
1573
|
+
);
|
|
1574
|
+
cleanupStateStorage(logger);
|
|
1575
|
+
return fallbackState;
|
|
1576
|
+
}
|
|
1577
|
+
function isStateStorageAvailable() {
|
|
1578
|
+
if (typeof window === "undefined") return false;
|
|
1579
|
+
const probeKey = "__ffid_oauth_state_probe__";
|
|
1580
|
+
try {
|
|
1581
|
+
window.sessionStorage.setItem(probeKey, "1");
|
|
1582
|
+
window.sessionStorage.removeItem(probeKey);
|
|
1583
|
+
return true;
|
|
1584
|
+
} catch {
|
|
1585
|
+
}
|
|
1586
|
+
try {
|
|
1587
|
+
window.localStorage.setItem(probeKey, "1");
|
|
1588
|
+
window.localStorage.removeItem(probeKey);
|
|
1589
|
+
return true;
|
|
1590
|
+
} catch {
|
|
1591
|
+
return false;
|
|
1592
|
+
}
|
|
1593
|
+
}
|
|
1594
|
+
function cleanupStateStorage(logger) {
|
|
1595
|
+
try {
|
|
1596
|
+
window.sessionStorage.removeItem(STATE_STORAGE_KEY);
|
|
1597
|
+
} catch (error) {
|
|
1598
|
+
logger?.warn("retrieveState: sessionStorage \u306E\u30AF\u30EA\u30FC\u30F3\u30A2\u30C3\u30D7\u306B\u5931\u6557\u3057\u307E\u3057\u305F:", error);
|
|
1599
|
+
}
|
|
1600
|
+
try {
|
|
1601
|
+
window.localStorage.removeItem(STATE_FALLBACK_STORAGE_KEY);
|
|
1602
|
+
window.localStorage.removeItem(STATE_FALLBACK_TIMESTAMP_KEY);
|
|
1603
|
+
} catch (error) {
|
|
1604
|
+
logger?.warn("retrieveState: localStorage \u306E\u30AF\u30EA\u30FC\u30F3\u30A2\u30C3\u30D7\u306B\u5931\u6557\u3057\u307E\u3057\u305F:", error);
|
|
1605
|
+
}
|
|
1606
|
+
}
|
|
1607
|
+
|
|
1495
1608
|
// src/client/redirect.ts
|
|
1496
1609
|
var OAUTH_AUTHORIZE_ENDPOINT = "/api/v1/oauth/authorize";
|
|
1497
1610
|
var AUTH_LOGOUT_ENDPOINT = "/api/v1/auth/logout";
|
|
@@ -1623,6 +1736,7 @@ function createRedirectMethods(deps) {
|
|
|
1623
1736
|
const recentCount = getRecentRedirectCount(authorizeKey, now, logger);
|
|
1624
1737
|
if (recentCount >= REDIRECT_LOOP_THRESHOLD) {
|
|
1625
1738
|
cleanupVerifierStorage(logger);
|
|
1739
|
+
cleanupStateStorage(logger);
|
|
1626
1740
|
logger.warn("[FFID SDK] redirect loop detected \u2014 \u81EA\u52D5\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3092\u505C\u6B62\u3057\u307E\u3057\u305F", {
|
|
1627
1741
|
authorizeKey,
|
|
1628
1742
|
recentCount,
|
|
@@ -1646,6 +1760,11 @@ function createRedirectMethods(deps) {
|
|
|
1646
1760
|
return { success: false, error: errorMessage };
|
|
1647
1761
|
}
|
|
1648
1762
|
const state = generateRandomState();
|
|
1763
|
+
if (!storeState(state, logger)) {
|
|
1764
|
+
logger.error(
|
|
1765
|
+
"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"
|
|
1766
|
+
);
|
|
1767
|
+
}
|
|
1649
1768
|
const redirectUri = resolvedRedirectUri ?? window.location.origin + window.location.pathname;
|
|
1650
1769
|
const params = new URLSearchParams({
|
|
1651
1770
|
response_type: "code",
|
|
@@ -2435,7 +2554,8 @@ var FFID_ERROR_CODES = {
|
|
|
2435
2554
|
TOKEN_EXCHANGE_ERROR: "TOKEN_EXCHANGE_ERROR",
|
|
2436
2555
|
TOKEN_REFRESH_ERROR: "TOKEN_REFRESH_ERROR",
|
|
2437
2556
|
NO_TOKENS: "NO_TOKENS",
|
|
2438
|
-
TOKEN_VERIFICATION_ERROR: "TOKEN_VERIFICATION_ERROR"
|
|
2557
|
+
TOKEN_VERIFICATION_ERROR: "TOKEN_VERIFICATION_ERROR",
|
|
2558
|
+
STATE_MISMATCH_ERROR: "STATE_MISMATCH_ERROR"
|
|
2439
2559
|
};
|
|
2440
2560
|
var EXT_CHECK_ENDPOINT = "/api/v1/subscriptions/ext/check";
|
|
2441
2561
|
var DEFAULT_ALLOW_GRACE = true;
|
|
@@ -2953,6 +3073,99 @@ function createFFIDClient(config) {
|
|
|
2953
3073
|
redirectUri: resolvedRedirectUri
|
|
2954
3074
|
};
|
|
2955
3075
|
}
|
|
3076
|
+
|
|
3077
|
+
// src/client/callback.ts
|
|
3078
|
+
var CALLBACK_ERROR_CODES = {
|
|
3079
|
+
/** Stored state missing or did not match the callback `state` (CSRF guard) */
|
|
3080
|
+
STATE_MISMATCH: "STATE_MISMATCH_ERROR",
|
|
3081
|
+
/**
|
|
3082
|
+
* Web storage is unavailable, so `state` could never be persisted across the
|
|
3083
|
+
* OAuth bounce. An environment issue (e.g. iOS private mode), not an attack —
|
|
3084
|
+
* surfaced distinctly so callers don't mistake it for a CSRF mismatch.
|
|
3085
|
+
*/
|
|
3086
|
+
STATE_UNAVAILABLE: "STATE_STORAGE_UNAVAILABLE",
|
|
3087
|
+
/** Callback URL had no `code` param */
|
|
3088
|
+
MISSING_CODE: "MISSING_AUTHORIZATION_CODE",
|
|
3089
|
+
/** SSR with no `options.url` — cannot resolve callback params */
|
|
3090
|
+
CALLBACK_URL_UNAVAILABLE: "CALLBACK_URL_UNAVAILABLE"
|
|
3091
|
+
};
|
|
3092
|
+
function extractSearchParams(source) {
|
|
3093
|
+
try {
|
|
3094
|
+
return new URL(source).searchParams;
|
|
3095
|
+
} catch {
|
|
3096
|
+
const queryStart = source.indexOf("?");
|
|
3097
|
+
const query = queryStart >= 0 ? source.slice(queryStart + 1) : source;
|
|
3098
|
+
return new URLSearchParams(query);
|
|
3099
|
+
}
|
|
3100
|
+
}
|
|
3101
|
+
function cleanCallbackUrlParams(logger) {
|
|
3102
|
+
try {
|
|
3103
|
+
const cleanUrl = new URL(window.location.href);
|
|
3104
|
+
cleanUrl.searchParams.delete("code");
|
|
3105
|
+
cleanUrl.searchParams.delete("state");
|
|
3106
|
+
window.history.replaceState({}, "", cleanUrl.toString());
|
|
3107
|
+
} catch (error) {
|
|
3108
|
+
logger.warn("handleRedirectCallback: URL \u306E\u30AF\u30EA\u30FC\u30F3\u30A2\u30C3\u30D7\u306B\u5931\u6557\u3057\u307E\u3057\u305F:", error);
|
|
3109
|
+
}
|
|
3110
|
+
}
|
|
3111
|
+
async function handleRedirectCallback(client, options) {
|
|
3112
|
+
const { logger } = client;
|
|
3113
|
+
const hasWindow = typeof window !== "undefined";
|
|
3114
|
+
const source = options?.url ?? (hasWindow ? window.location.search : null);
|
|
3115
|
+
if (source === null) {
|
|
3116
|
+
logger.warn("handleRedirectCallback: SSR \u74B0\u5883\u3067\u306F options.url \u304C\u5FC5\u8981\u3067\u3059");
|
|
3117
|
+
return {
|
|
3118
|
+
error: client.createError(
|
|
3119
|
+
CALLBACK_ERROR_CODES.CALLBACK_URL_UNAVAILABLE,
|
|
3120
|
+
"\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"
|
|
3121
|
+
)
|
|
3122
|
+
};
|
|
3123
|
+
}
|
|
3124
|
+
const params = extractSearchParams(source);
|
|
3125
|
+
const code = params.get("code");
|
|
3126
|
+
const urlState = params.get("state");
|
|
3127
|
+
if (!code) {
|
|
3128
|
+
logger.warn("handleRedirectCallback: \u8A8D\u53EF\u30B3\u30FC\u30C9\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093");
|
|
3129
|
+
return {
|
|
3130
|
+
error: client.createError(
|
|
3131
|
+
CALLBACK_ERROR_CODES.MISSING_CODE,
|
|
3132
|
+
"\u30B3\u30FC\u30EB\u30D0\u30C3\u30AF URL \u306B\u8A8D\u53EF\u30B3\u30FC\u30C9\u304C\u542B\u307E\u308C\u3066\u3044\u307E\u305B\u3093"
|
|
3133
|
+
)
|
|
3134
|
+
};
|
|
3135
|
+
}
|
|
3136
|
+
const storedState = retrieveState(logger);
|
|
3137
|
+
if (storedState !== null || urlState !== null) {
|
|
3138
|
+
const stateMatches = storedState !== null && storedState === urlState;
|
|
3139
|
+
if (!stateMatches) {
|
|
3140
|
+
if (storedState === null && !isStateStorageAvailable()) {
|
|
3141
|
+
logger.error(
|
|
3142
|
+
"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"
|
|
3143
|
+
);
|
|
3144
|
+
return {
|
|
3145
|
+
error: client.createError(
|
|
3146
|
+
CALLBACK_ERROR_CODES.STATE_UNAVAILABLE,
|
|
3147
|
+
"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"
|
|
3148
|
+
)
|
|
3149
|
+
};
|
|
3150
|
+
}
|
|
3151
|
+
logger.error(
|
|
3152
|
+
"handleRedirectCallback: OAuth state mismatch or omitted (possible CSRF) \u2014 \u8A8D\u8A3C\u3092\u4E2D\u6B62\u3057\u307E\u3057\u305F"
|
|
3153
|
+
);
|
|
3154
|
+
return {
|
|
3155
|
+
error: client.createError(
|
|
3156
|
+
CALLBACK_ERROR_CODES.STATE_MISMATCH,
|
|
3157
|
+
"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"
|
|
3158
|
+
)
|
|
3159
|
+
};
|
|
3160
|
+
}
|
|
3161
|
+
}
|
|
3162
|
+
const verifier = retrieveCodeVerifier(logger);
|
|
3163
|
+
const result = await client.exchangeCodeForTokens(code, verifier ?? void 0);
|
|
3164
|
+
if (!result.error && hasWindow) {
|
|
3165
|
+
cleanCallbackUrlParams(logger);
|
|
3166
|
+
}
|
|
3167
|
+
return result;
|
|
3168
|
+
}
|
|
2956
3169
|
var DEFAULT_REFRESH_INTERVAL_MS = 5 * 60 * 1e3;
|
|
2957
3170
|
var TOKEN_REFRESH_RATIO = 0.8;
|
|
2958
3171
|
var FFIDContext = react.createContext(null);
|
|
@@ -3127,8 +3340,7 @@ function FFIDProvider({
|
|
|
3127
3340
|
const code = urlParams.get("code");
|
|
3128
3341
|
if (!code) return;
|
|
3129
3342
|
client.logger.debug("Authorization code detected, exchanging for tokens");
|
|
3130
|
-
|
|
3131
|
-
client.exchangeCodeForTokens(code, codeVerifier ?? void 0).then((result) => {
|
|
3343
|
+
handleRedirectCallback(client).then((result) => {
|
|
3132
3344
|
if (result.error) {
|
|
3133
3345
|
client.logger.error("Token exchange failed:", result.error);
|
|
3134
3346
|
setError(result.error);
|
|
@@ -3136,10 +3348,6 @@ function FFIDProvider({
|
|
|
3136
3348
|
setIsLoading(false);
|
|
3137
3349
|
return;
|
|
3138
3350
|
}
|
|
3139
|
-
const cleanUrl = new URL(window.location.href);
|
|
3140
|
-
cleanUrl.searchParams.delete("code");
|
|
3141
|
-
cleanUrl.searchParams.delete("state");
|
|
3142
|
-
window.history.replaceState({}, "", cleanUrl.toString());
|
|
3143
3351
|
client.logger.debug("Token exchange successful, refreshing session");
|
|
3144
3352
|
return refresh();
|
|
3145
3353
|
}).catch((err) => {
|
|
@@ -4957,18 +5165,22 @@ exports.FFIDUserMenu = FFIDUserMenu;
|
|
|
4957
5165
|
exports.FFID_ANNOUNCEMENTS_ERROR_CODES = FFID_ANNOUNCEMENTS_ERROR_CODES;
|
|
4958
5166
|
exports.FFID_INQUIRY_CATEGORIES = FFID_INQUIRY_CATEGORIES;
|
|
4959
5167
|
exports.FFID_INQUIRY_CATEGORIES_SITE_2026 = FFID_INQUIRY_CATEGORIES_SITE_2026;
|
|
5168
|
+
exports.cleanupStateStorage = cleanupStateStorage;
|
|
4960
5169
|
exports.computeEffectiveStatusFromSession = computeEffectiveStatusFromSession;
|
|
4961
5170
|
exports.createFFIDAnnouncementsClient = createFFIDAnnouncementsClient;
|
|
4962
5171
|
exports.createFFIDClient = createFFIDClient;
|
|
4963
5172
|
exports.createTokenStore = createTokenStore;
|
|
4964
5173
|
exports.generateCodeChallenge = generateCodeChallenge;
|
|
4965
5174
|
exports.generateCodeVerifier = generateCodeVerifier;
|
|
5175
|
+
exports.handleRedirectCallback = handleRedirectCallback;
|
|
4966
5176
|
exports.hasAccessFromUserinfo = hasAccessFromUserinfo;
|
|
4967
5177
|
exports.isBlockedFromUserinfo = isBlockedFromUserinfo;
|
|
4968
5178
|
exports.isFFIDInquiryCategorySite2026 = isFFIDInquiryCategorySite2026;
|
|
4969
5179
|
exports.normalizeRedirectUri = normalizeRedirectUri;
|
|
4970
5180
|
exports.retrieveCodeVerifier = retrieveCodeVerifier;
|
|
5181
|
+
exports.retrieveState = retrieveState;
|
|
4971
5182
|
exports.storeCodeVerifier = storeCodeVerifier;
|
|
5183
|
+
exports.storeState = storeState;
|
|
4972
5184
|
exports.useFFID = useFFID;
|
|
4973
5185
|
exports.useFFIDAnnouncements = useFFIDAnnouncements;
|
|
4974
5186
|
exports.useFFIDContext = useFFIDContext;
|
|
@@ -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,
|
|
@@ -932,7 +938,7 @@ function createProfileMethods(deps) {
|
|
|
932
938
|
}
|
|
933
939
|
|
|
934
940
|
// src/client/version-check.ts
|
|
935
|
-
var SDK_VERSION = "5.
|
|
941
|
+
var SDK_VERSION = "5.13.0";
|
|
936
942
|
var SDK_USER_AGENT = `FFID-SDK/${SDK_VERSION} (TypeScript)`;
|
|
937
943
|
var SDK_VERSION_HEADER = "X-FFID-SDK-Version";
|
|
938
944
|
function sdkHeaders() {
|
|
@@ -1004,9 +1010,20 @@ function createOAuthTokenMethods(deps) {
|
|
|
1004
1010
|
logger,
|
|
1005
1011
|
errorCodes
|
|
1006
1012
|
} = deps;
|
|
1007
|
-
async function exchangeCodeForTokens(code, codeVerifier) {
|
|
1013
|
+
async function exchangeCodeForTokens(code, codeVerifier, opts) {
|
|
1008
1014
|
const url = `${baseUrl}${OAUTH_TOKEN_ENDPOINT}`;
|
|
1009
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
|
+
}
|
|
1010
1027
|
const effectiveRedirectUri = resolvedRedirectUri ?? (typeof window !== "undefined" ? window.location.origin + window.location.pathname : null);
|
|
1011
1028
|
if (!effectiveRedirectUri) {
|
|
1012
1029
|
logger.error("redirectUri is required for token exchange in SSR environments. Set config.redirectUri explicitly.");
|
|
@@ -1490,6 +1507,102 @@ function base64UrlEncode(buffer) {
|
|
|
1490
1507
|
return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
1491
1508
|
}
|
|
1492
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
|
+
|
|
1493
1606
|
// src/client/redirect.ts
|
|
1494
1607
|
var OAUTH_AUTHORIZE_ENDPOINT = "/api/v1/oauth/authorize";
|
|
1495
1608
|
var AUTH_LOGOUT_ENDPOINT = "/api/v1/auth/logout";
|
|
@@ -1621,6 +1734,7 @@ function createRedirectMethods(deps) {
|
|
|
1621
1734
|
const recentCount = getRecentRedirectCount(authorizeKey, now, logger);
|
|
1622
1735
|
if (recentCount >= REDIRECT_LOOP_THRESHOLD) {
|
|
1623
1736
|
cleanupVerifierStorage(logger);
|
|
1737
|
+
cleanupStateStorage(logger);
|
|
1624
1738
|
logger.warn("[FFID SDK] redirect loop detected \u2014 \u81EA\u52D5\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3092\u505C\u6B62\u3057\u307E\u3057\u305F", {
|
|
1625
1739
|
authorizeKey,
|
|
1626
1740
|
recentCount,
|
|
@@ -1644,6 +1758,11 @@ function createRedirectMethods(deps) {
|
|
|
1644
1758
|
return { success: false, error: errorMessage };
|
|
1645
1759
|
}
|
|
1646
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
|
+
}
|
|
1647
1766
|
const redirectUri = resolvedRedirectUri ?? window.location.origin + window.location.pathname;
|
|
1648
1767
|
const params = new URLSearchParams({
|
|
1649
1768
|
response_type: "code",
|
|
@@ -2433,7 +2552,8 @@ var FFID_ERROR_CODES = {
|
|
|
2433
2552
|
TOKEN_EXCHANGE_ERROR: "TOKEN_EXCHANGE_ERROR",
|
|
2434
2553
|
TOKEN_REFRESH_ERROR: "TOKEN_REFRESH_ERROR",
|
|
2435
2554
|
NO_TOKENS: "NO_TOKENS",
|
|
2436
|
-
TOKEN_VERIFICATION_ERROR: "TOKEN_VERIFICATION_ERROR"
|
|
2555
|
+
TOKEN_VERIFICATION_ERROR: "TOKEN_VERIFICATION_ERROR",
|
|
2556
|
+
STATE_MISMATCH_ERROR: "STATE_MISMATCH_ERROR"
|
|
2437
2557
|
};
|
|
2438
2558
|
var EXT_CHECK_ENDPOINT = "/api/v1/subscriptions/ext/check";
|
|
2439
2559
|
var DEFAULT_ALLOW_GRACE = true;
|
|
@@ -2951,6 +3071,99 @@ function createFFIDClient(config) {
|
|
|
2951
3071
|
redirectUri: resolvedRedirectUri
|
|
2952
3072
|
};
|
|
2953
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
|
+
}
|
|
2954
3167
|
var DEFAULT_REFRESH_INTERVAL_MS = 5 * 60 * 1e3;
|
|
2955
3168
|
var TOKEN_REFRESH_RATIO = 0.8;
|
|
2956
3169
|
var FFIDContext = createContext(null);
|
|
@@ -3125,8 +3338,7 @@ function FFIDProvider({
|
|
|
3125
3338
|
const code = urlParams.get("code");
|
|
3126
3339
|
if (!code) return;
|
|
3127
3340
|
client.logger.debug("Authorization code detected, exchanging for tokens");
|
|
3128
|
-
|
|
3129
|
-
client.exchangeCodeForTokens(code, codeVerifier ?? void 0).then((result) => {
|
|
3341
|
+
handleRedirectCallback(client).then((result) => {
|
|
3130
3342
|
if (result.error) {
|
|
3131
3343
|
client.logger.error("Token exchange failed:", result.error);
|
|
3132
3344
|
setError(result.error);
|
|
@@ -3134,10 +3346,6 @@ function FFIDProvider({
|
|
|
3134
3346
|
setIsLoading(false);
|
|
3135
3347
|
return;
|
|
3136
3348
|
}
|
|
3137
|
-
const cleanUrl = new URL(window.location.href);
|
|
3138
|
-
cleanUrl.searchParams.delete("code");
|
|
3139
|
-
cleanUrl.searchParams.delete("state");
|
|
3140
|
-
window.history.replaceState({}, "", cleanUrl.toString());
|
|
3141
3349
|
client.logger.debug("Token exchange successful, refreshing session");
|
|
3142
3350
|
return refresh();
|
|
3143
3351
|
}).catch((err) => {
|
|
@@ -4938,4 +5146,4 @@ function FFIDInquiryForm({
|
|
|
4938
5146
|
);
|
|
4939
5147
|
}
|
|
4940
5148
|
|
|
4941
|
-
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, 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
|
});
|