@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.
@@ -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
- return { createCheckoutSession, createPortalSession };
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.10.0";
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
- const codeVerifier = retrieveCodeVerifier(client.logger);
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 chunkIZSL6DI7_cjs = require('../chunk-IZSL6DI7.cjs');
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 chunkIZSL6DI7_cjs.FFIDAnnouncementBadge; }
9
+ get: function () { return chunkJFFRO2Q5_cjs.FFIDAnnouncementBadge; }
10
10
  });
11
11
  Object.defineProperty(exports, "FFIDAnnouncementList", {
12
12
  enumerable: true,
13
- get: function () { return chunkIZSL6DI7_cjs.FFIDAnnouncementList; }
13
+ get: function () { return chunkJFFRO2Q5_cjs.FFIDAnnouncementList; }
14
14
  });
15
15
  Object.defineProperty(exports, "FFIDInquiryForm", {
16
16
  enumerable: true,
17
- get: function () { return chunkIZSL6DI7_cjs.FFIDInquiryForm; }
17
+ get: function () { return chunkJFFRO2Q5_cjs.FFIDInquiryForm; }
18
18
  });
19
19
  Object.defineProperty(exports, "FFIDLoginButton", {
20
20
  enumerable: true,
21
- get: function () { return chunkIZSL6DI7_cjs.FFIDLoginButton; }
21
+ get: function () { return chunkJFFRO2Q5_cjs.FFIDLoginButton; }
22
22
  });
23
23
  Object.defineProperty(exports, "FFIDOrganizationSwitcher", {
24
24
  enumerable: true,
25
- get: function () { return chunkIZSL6DI7_cjs.FFIDOrganizationSwitcher; }
25
+ get: function () { return chunkJFFRO2Q5_cjs.FFIDOrganizationSwitcher; }
26
26
  });
27
27
  Object.defineProperty(exports, "FFIDSubscriptionBadge", {
28
28
  enumerable: true,
29
- get: function () { return chunkIZSL6DI7_cjs.FFIDSubscriptionBadge; }
29
+ get: function () { return chunkJFFRO2Q5_cjs.FFIDSubscriptionBadge; }
30
30
  });
31
31
  Object.defineProperty(exports, "FFIDUserMenu", {
32
32
  enumerable: true,
33
- get: function () { return chunkIZSL6DI7_cjs.FFIDUserMenu; }
33
+ get: function () { return chunkJFFRO2Q5_cjs.FFIDUserMenu; }
34
34
  });
@@ -1,3 +1,3 @@
1
- export { M as FFIDAnnouncementBadge, al as FFIDAnnouncementBadgeClassNames, am as FFIDAnnouncementBadgeProps, N as FFIDAnnouncementList, an as FFIDAnnouncementListClassNames, ao as FFIDAnnouncementListProps, V as FFIDInquiryForm, W as FFIDInquiryFormCategoryItem, X as FFIDInquiryFormClassNames, Y as FFIDInquiryFormLegalLayout, Z as FFIDInquiryFormOrganization, _ as FFIDInquiryFormPlaceholderContext, $ as FFIDInquiryFormPrefill, a0 as FFIDInquiryFormProps, a1 as FFIDInquiryFormSubmitData, a2 as FFIDInquiryFormSubmitResult, a4 as FFIDLoginButton, ap as FFIDLoginButtonProps, a7 as FFIDOrganizationSwitcher, aq as FFIDOrganizationSwitcherClassNames, ar as FFIDOrganizationSwitcherProps, ac as FFIDSubscriptionBadge, as as FFIDSubscriptionBadgeClassNames, at as FFIDSubscriptionBadgeProps, ae as FFIDUserMenu, au as FFIDUserMenuClassNames, av as FFIDUserMenuProps } from '../index-DSvX4q8E.cjs';
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 { M as FFIDAnnouncementBadge, al as FFIDAnnouncementBadgeClassNames, am as FFIDAnnouncementBadgeProps, N as FFIDAnnouncementList, an as FFIDAnnouncementListClassNames, ao as FFIDAnnouncementListProps, V as FFIDInquiryForm, W as FFIDInquiryFormCategoryItem, X as FFIDInquiryFormClassNames, Y as FFIDInquiryFormLegalLayout, Z as FFIDInquiryFormOrganization, _ as FFIDInquiryFormPlaceholderContext, $ as FFIDInquiryFormPrefill, a0 as FFIDInquiryFormProps, a1 as FFIDInquiryFormSubmitData, a2 as FFIDInquiryFormSubmitResult, a4 as FFIDLoginButton, ap as FFIDLoginButtonProps, a7 as FFIDOrganizationSwitcher, aq as FFIDOrganizationSwitcherClassNames, ar as FFIDOrganizationSwitcherProps, ac as FFIDSubscriptionBadge, as as FFIDSubscriptionBadgeClassNames, at as FFIDSubscriptionBadgeProps, ae as FFIDUserMenu, au as FFIDUserMenuClassNames, av as FFIDUserMenuProps } from '../index-DSvX4q8E.js';
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';
@@ -1 +1 @@
1
- export { FFIDAnnouncementBadge, FFIDAnnouncementList, FFIDInquiryForm, FFIDLoginButton, FFIDOrganizationSwitcher, FFIDSubscriptionBadge, FFIDUserMenu } from '../chunk-OJ3OJTIE.js';
1
+ export { FFIDAnnouncementBadge, FFIDAnnouncementList, FFIDInquiryForm, FFIDLoginButton, FFIDOrganizationSwitcher, FFIDSubscriptionBadge, FFIDUserMenu } from '../chunk-RATTQIKM.js';