@feelflow/ffid-sdk 5.24.2 → 5.25.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-Q7ZEF3EN.js → chunk-HIKX4AVY.js} +68 -33
- package/dist/{chunk-QMWUOVZK.cjs → chunk-ZFKXOTFF.cjs} +68 -33
- 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-D0Jh4bO7.d.cts → ffid-client-BZERrMAF.d.cts} +133 -82
- package/dist/{ffid-client-DX2PTTFr.d.ts → ffid-client-CFZPX5ej.d.ts} +133 -82
- package/dist/{index-GqflEaKi.d.cts → index-Dd6CMIBd.d.cts} +90 -92
- package/dist/{index-GqflEaKi.d.ts → index-Dd6CMIBd.d.ts} +90 -92
- package/dist/index.cjs +42 -42
- package/dist/index.d.cts +123 -45
- package/dist/index.d.ts +123 -45
- package/dist/index.js +2 -2
- package/dist/server/index.cjs +58 -51
- package/dist/server/index.d.cts +2 -2
- package/dist/server/index.d.ts +2 -2
- package/dist/server/index.js +58 -51
- package/dist/server/test/index.d.cts +1 -1
- package/dist/server/test/index.d.ts +1 -1
- package/package.json +1 -1
|
@@ -270,6 +270,47 @@ interface FFIDServiceAccessDecision {
|
|
|
270
270
|
error?: FFIDServiceAccessError;
|
|
271
271
|
}
|
|
272
272
|
|
|
273
|
+
/**
|
|
274
|
+
* Token Store
|
|
275
|
+
*
|
|
276
|
+
* Manages OAuth 2.0 tokens (access + refresh) with dual-storage support.
|
|
277
|
+
* Falls back to in-memory storage when localStorage is unavailable
|
|
278
|
+
* (e.g., Safari private browsing mode).
|
|
279
|
+
*/
|
|
280
|
+
/**
|
|
281
|
+
* Token data stored by the token store
|
|
282
|
+
*/
|
|
283
|
+
interface TokenData {
|
|
284
|
+
/** OAuth 2.0 access token */
|
|
285
|
+
accessToken: string;
|
|
286
|
+
/** OAuth 2.0 refresh token */
|
|
287
|
+
refreshToken: string;
|
|
288
|
+
/** Expiration timestamp in milliseconds (Unix epoch) */
|
|
289
|
+
expiresAt: number;
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* Token store interface for managing OAuth tokens
|
|
293
|
+
*/
|
|
294
|
+
interface TokenStore {
|
|
295
|
+
/** Get stored tokens (null if not stored) */
|
|
296
|
+
getTokens(): TokenData | null;
|
|
297
|
+
/** Store new tokens */
|
|
298
|
+
setTokens(tokens: TokenData): void;
|
|
299
|
+
/** Clear all stored tokens */
|
|
300
|
+
clearTokens(): void;
|
|
301
|
+
/** Check if access token is expired (with 30s buffer) */
|
|
302
|
+
isAccessTokenExpired(): boolean;
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Create a token store with the specified storage type.
|
|
306
|
+
*
|
|
307
|
+
* When storageType is 'localStorage' (default in browser), falls back
|
|
308
|
+
* to memory if localStorage is not available (e.g., Safari private mode).
|
|
309
|
+
*
|
|
310
|
+
* @param storageType - 'localStorage' (default) or 'memory'
|
|
311
|
+
*/
|
|
312
|
+
declare function createTokenStore(storageType?: 'localStorage' | 'memory'): TokenStore;
|
|
313
|
+
|
|
273
314
|
/**
|
|
274
315
|
* Billing checkout / portal session types.
|
|
275
316
|
*
|
|
@@ -1304,6 +1345,57 @@ interface FFIDUpdateUserProfileRequest {
|
|
|
1304
1345
|
preferences?: Record<string, unknown> | null;
|
|
1305
1346
|
}
|
|
1306
1347
|
|
|
1348
|
+
/**
|
|
1349
|
+
* OAuth flow types: client-side redirect results and OAuth token / RFC 7662
|
|
1350
|
+
* introspection payloads.
|
|
1351
|
+
*
|
|
1352
|
+
* Extracted from `types/index.ts` to keep that barrel under the 800-line hard
|
|
1353
|
+
* limit (`scripts/check-file-size.sh`). Re-exported from `types/index.ts`, so
|
|
1354
|
+
* the public API is unchanged — import from `@feelflow/ffid-sdk` as before.
|
|
1355
|
+
*/
|
|
1356
|
+
|
|
1357
|
+
/**
|
|
1358
|
+
* Discriminant for redirect failures that callers need to handle
|
|
1359
|
+
* programmatically (vs. logging the human-readable `error` string).
|
|
1360
|
+
*
|
|
1361
|
+
* - `'redirect_loop_detected'`: `redirectToAuthorize()` detected that the
|
|
1362
|
+
* same authorize URL (keyed by `baseUrl + client_id + organization_id`)
|
|
1363
|
+
* has been fired **3 times within 60 seconds**. Caller should surface a
|
|
1364
|
+
* manual "再度ログインする" UI rather than retry automatically (#2406 / #2411).
|
|
1365
|
+
*
|
|
1366
|
+
* - `'state_persistence_failed'`: `redirectToAuthorize()` could not persist
|
|
1367
|
+
* the OAuth CSRF `state` (storage unavailable / quota). Redirect is aborted
|
|
1368
|
+
* fail-closed (#4136) — callers should surface a manual retry UI.
|
|
1369
|
+
*
|
|
1370
|
+
* Other failure paths (SSR environment, PKCE generation failure, empty
|
|
1371
|
+
* organizationId) currently return `error` without a `code`. Treat this union
|
|
1372
|
+
* as forward-extensible and do NOT exhaustively `switch` over it without a
|
|
1373
|
+
* `default` branch for consumer code that must stay compatible across SDK
|
|
1374
|
+
* upgrades.
|
|
1375
|
+
*/
|
|
1376
|
+
type FFIDRedirectErrorCode = 'redirect_loop_detected' | 'state_persistence_failed';
|
|
1377
|
+
/**
|
|
1378
|
+
* Result of a redirect operation (redirectToLogin / redirectToAuthorize / redirectToLogout)
|
|
1379
|
+
*
|
|
1380
|
+
* Structured return type so callers can inspect failure reasons
|
|
1381
|
+
* instead of receiving a bare `false`. When `code` is set, branch on it
|
|
1382
|
+
* for programmatic handling; otherwise log `error` for humans.
|
|
1383
|
+
*
|
|
1384
|
+
* `loopDetectionDisabled` (#4119): set to `true` when sessionStorage is
|
|
1385
|
+
* unavailable (iOS WebKit private mode / eviction) so redirect-loop detection
|
|
1386
|
+
* ran **fail-open** — the redirect fired, but repeated-redirect protection is
|
|
1387
|
+
* disabled for this browser session. Callers that need loop protection on
|
|
1388
|
+
* such browsers should add their own max-retry guard.
|
|
1389
|
+
*/
|
|
1390
|
+
type FFIDRedirectResult = {
|
|
1391
|
+
success: true;
|
|
1392
|
+
loopDetectionDisabled?: boolean;
|
|
1393
|
+
} | {
|
|
1394
|
+
success: false;
|
|
1395
|
+
error: string;
|
|
1396
|
+
code?: FFIDRedirectErrorCode;
|
|
1397
|
+
};
|
|
1398
|
+
|
|
1307
1399
|
/**
|
|
1308
1400
|
* FFID SDK Type Definitions
|
|
1309
1401
|
*
|
|
@@ -1447,6 +1539,19 @@ interface FFIDOAuthUserInfoSubscription {
|
|
|
1447
1539
|
* round-trip.
|
|
1448
1540
|
*/
|
|
1449
1541
|
currentPeriodEnd: string | null;
|
|
1542
|
+
/**
|
|
1543
|
+
* Per-service entitlement codes (e.g. `praxis.pack.<slug>.access`) granting
|
|
1544
|
+
* access to one-time / single-pack purchases not encoded in the plan tier
|
|
1545
|
+
* (#4333, SDK 5.25.0). Read this list to gate per-pack access; an empty
|
|
1546
|
+
* array means "no pack entitlements" (the normal case for Pro / Team tiers
|
|
1547
|
+
* that unlock packs via `planCode`).
|
|
1548
|
+
*
|
|
1549
|
+
* The `normalizeUserinfo` boundary defaults this to `[]` when the server
|
|
1550
|
+
* omits it (older FFID pre-#4333) or when the introspect path uses a service
|
|
1551
|
+
* API key without `subscription:read`, so consumers always see a plain
|
|
1552
|
+
* `readonly string[]` and never branch on an "unknown" sentinel.
|
|
1553
|
+
*/
|
|
1554
|
+
entitlements: readonly string[];
|
|
1450
1555
|
}
|
|
1451
1556
|
/**
|
|
1452
1557
|
* Unified user-identity shape returned by `getSession()` (→ `/oauth/userinfo`)
|
|
@@ -1608,6 +1713,29 @@ interface FFIDConfig {
|
|
|
1608
1713
|
* @default undefined (no timeout, uses fetch default)
|
|
1609
1714
|
*/
|
|
1610
1715
|
timeout?: number | undefined;
|
|
1716
|
+
/**
|
|
1717
|
+
* Token mode only: inject a shared {@link TokenStore} instance so the SDK and
|
|
1718
|
+
* the host app read/write the *same* token source (#4318).
|
|
1719
|
+
*
|
|
1720
|
+
* By default the client creates its own store internally
|
|
1721
|
+
* (`createTokenStore()` in `token` mode, `createTokenStore('memory')`
|
|
1722
|
+
* otherwise). That is fine when `localStorage` is available, because a
|
|
1723
|
+
* separately-created `localStorage` store still points at the same
|
|
1724
|
+
* `ffid_tokens` key. But when `localStorage` is unavailable (e.g. Safari
|
|
1725
|
+
* Private Mode) the SDK falls back to an *in-memory* store, and a store the
|
|
1726
|
+
* host app creates independently is a **different** object — so the app would
|
|
1727
|
+
* see the user as signed-out even though the SDK holds valid tokens.
|
|
1728
|
+
*
|
|
1729
|
+
* Passing a single instance here (typically `createTokenStore()` created once
|
|
1730
|
+
* by the host) guarantees both sides share one source of truth regardless of
|
|
1731
|
+
* storage backend. Prefer reading the token via `useFFID().getAccessToken()`
|
|
1732
|
+
* so refresh/logout stay reflected without the host polling the store.
|
|
1733
|
+
*
|
|
1734
|
+
* Ignored in `cookie` and `service-key` modes (neither exposes a browser
|
|
1735
|
+
* access-token store). When omitted, behavior is unchanged from previous
|
|
1736
|
+
* versions.
|
|
1737
|
+
*/
|
|
1738
|
+
tokenStore?: TokenStore | undefined;
|
|
1611
1739
|
}
|
|
1612
1740
|
/**
|
|
1613
1741
|
* OIDC `prompt` values the SDK forwards to FFID `/oauth/authorize` (#4027).
|
|
@@ -1725,47 +1853,6 @@ interface FFIDAnalyticsConfig {
|
|
|
1725
1853
|
*/
|
|
1726
1854
|
isActive: boolean;
|
|
1727
1855
|
}
|
|
1728
|
-
/**
|
|
1729
|
-
* Discriminant for redirect failures that callers need to handle
|
|
1730
|
-
* programmatically (vs. logging the human-readable `error` string).
|
|
1731
|
-
*
|
|
1732
|
-
* - `'redirect_loop_detected'`: `redirectToAuthorize()` detected that the
|
|
1733
|
-
* same authorize URL (keyed by `baseUrl + client_id + organization_id`)
|
|
1734
|
-
* has been fired **3 times within 60 seconds**. Caller should surface a
|
|
1735
|
-
* manual "再度ログインする" UI rather than retry automatically (#2406 / #2411).
|
|
1736
|
-
*
|
|
1737
|
-
* - `'state_persistence_failed'`: `redirectToAuthorize()` could not persist
|
|
1738
|
-
* the OAuth CSRF `state` (storage unavailable / quota). Redirect is aborted
|
|
1739
|
-
* fail-closed (#4136) — callers should surface a manual retry UI.
|
|
1740
|
-
*
|
|
1741
|
-
* Other failure paths (SSR environment, PKCE generation failure, empty
|
|
1742
|
-
* organizationId) currently return `error` without a `code`. Treat this union
|
|
1743
|
-
* as forward-extensible and do NOT exhaustively `switch` over it without a
|
|
1744
|
-
* `default` branch for consumer code that must stay compatible across SDK
|
|
1745
|
-
* upgrades.
|
|
1746
|
-
*/
|
|
1747
|
-
type FFIDRedirectErrorCode = 'redirect_loop_detected' | 'state_persistence_failed';
|
|
1748
|
-
/**
|
|
1749
|
-
* Result of a redirect operation (redirectToLogin / redirectToAuthorize / redirectToLogout)
|
|
1750
|
-
*
|
|
1751
|
-
* Structured return type so callers can inspect failure reasons
|
|
1752
|
-
* instead of receiving a bare `false`. When `code` is set, branch on it
|
|
1753
|
-
* for programmatic handling; otherwise log `error` for humans.
|
|
1754
|
-
*
|
|
1755
|
-
* `loopDetectionDisabled` (#4119): set to `true` when sessionStorage is
|
|
1756
|
-
* unavailable (iOS WebKit private mode / eviction) so redirect-loop detection
|
|
1757
|
-
* ran **fail-open** — the redirect fired, but repeated-redirect protection is
|
|
1758
|
-
* disabled for this browser session. Callers that need loop protection on
|
|
1759
|
-
* such browsers should add their own max-retry guard.
|
|
1760
|
-
*/
|
|
1761
|
-
type FFIDRedirectResult = {
|
|
1762
|
-
success: true;
|
|
1763
|
-
loopDetectionDisabled?: boolean;
|
|
1764
|
-
} | {
|
|
1765
|
-
success: false;
|
|
1766
|
-
error: string;
|
|
1767
|
-
code?: FFIDRedirectErrorCode;
|
|
1768
|
-
};
|
|
1769
1856
|
|
|
1770
1857
|
/** OTP / magic link methods - sendOtp / verifyOtp */
|
|
1771
1858
|
|
|
@@ -1850,47 +1937,6 @@ interface FFIDGetLoginHistoryParams {
|
|
|
1850
1937
|
limit?: number;
|
|
1851
1938
|
}
|
|
1852
1939
|
|
|
1853
|
-
/**
|
|
1854
|
-
* Token Store
|
|
1855
|
-
*
|
|
1856
|
-
* Manages OAuth 2.0 tokens (access + refresh) with dual-storage support.
|
|
1857
|
-
* Falls back to in-memory storage when localStorage is unavailable
|
|
1858
|
-
* (e.g., Safari private browsing mode).
|
|
1859
|
-
*/
|
|
1860
|
-
/**
|
|
1861
|
-
* Token data stored by the token store
|
|
1862
|
-
*/
|
|
1863
|
-
interface TokenData {
|
|
1864
|
-
/** OAuth 2.0 access token */
|
|
1865
|
-
accessToken: string;
|
|
1866
|
-
/** OAuth 2.0 refresh token */
|
|
1867
|
-
refreshToken: string;
|
|
1868
|
-
/** Expiration timestamp in milliseconds (Unix epoch) */
|
|
1869
|
-
expiresAt: number;
|
|
1870
|
-
}
|
|
1871
|
-
/**
|
|
1872
|
-
* Token store interface for managing OAuth tokens
|
|
1873
|
-
*/
|
|
1874
|
-
interface TokenStore {
|
|
1875
|
-
/** Get stored tokens (null if not stored) */
|
|
1876
|
-
getTokens(): TokenData | null;
|
|
1877
|
-
/** Store new tokens */
|
|
1878
|
-
setTokens(tokens: TokenData): void;
|
|
1879
|
-
/** Clear all stored tokens */
|
|
1880
|
-
clearTokens(): void;
|
|
1881
|
-
/** Check if access token is expired (with 30s buffer) */
|
|
1882
|
-
isAccessTokenExpired(): boolean;
|
|
1883
|
-
}
|
|
1884
|
-
/**
|
|
1885
|
-
* Create a token store with the specified storage type.
|
|
1886
|
-
*
|
|
1887
|
-
* When storageType is 'localStorage' (default in browser), falls back
|
|
1888
|
-
* to memory if localStorage is not available (e.g., Safari private mode).
|
|
1889
|
-
*
|
|
1890
|
-
* @param storageType - 'localStorage' (default) or 'memory'
|
|
1891
|
-
*/
|
|
1892
|
-
declare function createTokenStore(storageType?: 'localStorage' | 'memory'): TokenStore;
|
|
1893
|
-
|
|
1894
1940
|
/** OAuth token operations - exchangeCodeForTokens / refreshAccessToken / signOutToken */
|
|
1895
1941
|
|
|
1896
1942
|
/**
|
|
@@ -2065,6 +2111,11 @@ declare function createFFIDClient(config: FFIDConfig): {
|
|
|
2065
2111
|
};
|
|
2066
2112
|
/** Token store (token mode only) */
|
|
2067
2113
|
tokenStore: TokenStore;
|
|
2114
|
+
/**
|
|
2115
|
+
* Return the currently-valid access token, or null (token mode only; #4318).
|
|
2116
|
+
* Live-reads the store and applies the 30s expiry buffer.
|
|
2117
|
+
*/
|
|
2118
|
+
getAccessToken: () => string | null;
|
|
2068
2119
|
/** Resolved auth mode */
|
|
2069
2120
|
authMode: FFIDAuthMode;
|
|
2070
2121
|
/** Resolved logger instance */
|
|
@@ -270,6 +270,47 @@ interface FFIDServiceAccessDecision {
|
|
|
270
270
|
error?: FFIDServiceAccessError;
|
|
271
271
|
}
|
|
272
272
|
|
|
273
|
+
/**
|
|
274
|
+
* Token Store
|
|
275
|
+
*
|
|
276
|
+
* Manages OAuth 2.0 tokens (access + refresh) with dual-storage support.
|
|
277
|
+
* Falls back to in-memory storage when localStorage is unavailable
|
|
278
|
+
* (e.g., Safari private browsing mode).
|
|
279
|
+
*/
|
|
280
|
+
/**
|
|
281
|
+
* Token data stored by the token store
|
|
282
|
+
*/
|
|
283
|
+
interface TokenData {
|
|
284
|
+
/** OAuth 2.0 access token */
|
|
285
|
+
accessToken: string;
|
|
286
|
+
/** OAuth 2.0 refresh token */
|
|
287
|
+
refreshToken: string;
|
|
288
|
+
/** Expiration timestamp in milliseconds (Unix epoch) */
|
|
289
|
+
expiresAt: number;
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* Token store interface for managing OAuth tokens
|
|
293
|
+
*/
|
|
294
|
+
interface TokenStore {
|
|
295
|
+
/** Get stored tokens (null if not stored) */
|
|
296
|
+
getTokens(): TokenData | null;
|
|
297
|
+
/** Store new tokens */
|
|
298
|
+
setTokens(tokens: TokenData): void;
|
|
299
|
+
/** Clear all stored tokens */
|
|
300
|
+
clearTokens(): void;
|
|
301
|
+
/** Check if access token is expired (with 30s buffer) */
|
|
302
|
+
isAccessTokenExpired(): boolean;
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Create a token store with the specified storage type.
|
|
306
|
+
*
|
|
307
|
+
* When storageType is 'localStorage' (default in browser), falls back
|
|
308
|
+
* to memory if localStorage is not available (e.g., Safari private mode).
|
|
309
|
+
*
|
|
310
|
+
* @param storageType - 'localStorage' (default) or 'memory'
|
|
311
|
+
*/
|
|
312
|
+
declare function createTokenStore(storageType?: 'localStorage' | 'memory'): TokenStore;
|
|
313
|
+
|
|
273
314
|
/**
|
|
274
315
|
* Billing checkout / portal session types.
|
|
275
316
|
*
|
|
@@ -1304,6 +1345,57 @@ interface FFIDUpdateUserProfileRequest {
|
|
|
1304
1345
|
preferences?: Record<string, unknown> | null;
|
|
1305
1346
|
}
|
|
1306
1347
|
|
|
1348
|
+
/**
|
|
1349
|
+
* OAuth flow types: client-side redirect results and OAuth token / RFC 7662
|
|
1350
|
+
* introspection payloads.
|
|
1351
|
+
*
|
|
1352
|
+
* Extracted from `types/index.ts` to keep that barrel under the 800-line hard
|
|
1353
|
+
* limit (`scripts/check-file-size.sh`). Re-exported from `types/index.ts`, so
|
|
1354
|
+
* the public API is unchanged — import from `@feelflow/ffid-sdk` as before.
|
|
1355
|
+
*/
|
|
1356
|
+
|
|
1357
|
+
/**
|
|
1358
|
+
* Discriminant for redirect failures that callers need to handle
|
|
1359
|
+
* programmatically (vs. logging the human-readable `error` string).
|
|
1360
|
+
*
|
|
1361
|
+
* - `'redirect_loop_detected'`: `redirectToAuthorize()` detected that the
|
|
1362
|
+
* same authorize URL (keyed by `baseUrl + client_id + organization_id`)
|
|
1363
|
+
* has been fired **3 times within 60 seconds**. Caller should surface a
|
|
1364
|
+
* manual "再度ログインする" UI rather than retry automatically (#2406 / #2411).
|
|
1365
|
+
*
|
|
1366
|
+
* - `'state_persistence_failed'`: `redirectToAuthorize()` could not persist
|
|
1367
|
+
* the OAuth CSRF `state` (storage unavailable / quota). Redirect is aborted
|
|
1368
|
+
* fail-closed (#4136) — callers should surface a manual retry UI.
|
|
1369
|
+
*
|
|
1370
|
+
* Other failure paths (SSR environment, PKCE generation failure, empty
|
|
1371
|
+
* organizationId) currently return `error` without a `code`. Treat this union
|
|
1372
|
+
* as forward-extensible and do NOT exhaustively `switch` over it without a
|
|
1373
|
+
* `default` branch for consumer code that must stay compatible across SDK
|
|
1374
|
+
* upgrades.
|
|
1375
|
+
*/
|
|
1376
|
+
type FFIDRedirectErrorCode = 'redirect_loop_detected' | 'state_persistence_failed';
|
|
1377
|
+
/**
|
|
1378
|
+
* Result of a redirect operation (redirectToLogin / redirectToAuthorize / redirectToLogout)
|
|
1379
|
+
*
|
|
1380
|
+
* Structured return type so callers can inspect failure reasons
|
|
1381
|
+
* instead of receiving a bare `false`. When `code` is set, branch on it
|
|
1382
|
+
* for programmatic handling; otherwise log `error` for humans.
|
|
1383
|
+
*
|
|
1384
|
+
* `loopDetectionDisabled` (#4119): set to `true` when sessionStorage is
|
|
1385
|
+
* unavailable (iOS WebKit private mode / eviction) so redirect-loop detection
|
|
1386
|
+
* ran **fail-open** — the redirect fired, but repeated-redirect protection is
|
|
1387
|
+
* disabled for this browser session. Callers that need loop protection on
|
|
1388
|
+
* such browsers should add their own max-retry guard.
|
|
1389
|
+
*/
|
|
1390
|
+
type FFIDRedirectResult = {
|
|
1391
|
+
success: true;
|
|
1392
|
+
loopDetectionDisabled?: boolean;
|
|
1393
|
+
} | {
|
|
1394
|
+
success: false;
|
|
1395
|
+
error: string;
|
|
1396
|
+
code?: FFIDRedirectErrorCode;
|
|
1397
|
+
};
|
|
1398
|
+
|
|
1307
1399
|
/**
|
|
1308
1400
|
* FFID SDK Type Definitions
|
|
1309
1401
|
*
|
|
@@ -1447,6 +1539,19 @@ interface FFIDOAuthUserInfoSubscription {
|
|
|
1447
1539
|
* round-trip.
|
|
1448
1540
|
*/
|
|
1449
1541
|
currentPeriodEnd: string | null;
|
|
1542
|
+
/**
|
|
1543
|
+
* Per-service entitlement codes (e.g. `praxis.pack.<slug>.access`) granting
|
|
1544
|
+
* access to one-time / single-pack purchases not encoded in the plan tier
|
|
1545
|
+
* (#4333, SDK 5.25.0). Read this list to gate per-pack access; an empty
|
|
1546
|
+
* array means "no pack entitlements" (the normal case for Pro / Team tiers
|
|
1547
|
+
* that unlock packs via `planCode`).
|
|
1548
|
+
*
|
|
1549
|
+
* The `normalizeUserinfo` boundary defaults this to `[]` when the server
|
|
1550
|
+
* omits it (older FFID pre-#4333) or when the introspect path uses a service
|
|
1551
|
+
* API key without `subscription:read`, so consumers always see a plain
|
|
1552
|
+
* `readonly string[]` and never branch on an "unknown" sentinel.
|
|
1553
|
+
*/
|
|
1554
|
+
entitlements: readonly string[];
|
|
1450
1555
|
}
|
|
1451
1556
|
/**
|
|
1452
1557
|
* Unified user-identity shape returned by `getSession()` (→ `/oauth/userinfo`)
|
|
@@ -1608,6 +1713,29 @@ interface FFIDConfig {
|
|
|
1608
1713
|
* @default undefined (no timeout, uses fetch default)
|
|
1609
1714
|
*/
|
|
1610
1715
|
timeout?: number | undefined;
|
|
1716
|
+
/**
|
|
1717
|
+
* Token mode only: inject a shared {@link TokenStore} instance so the SDK and
|
|
1718
|
+
* the host app read/write the *same* token source (#4318).
|
|
1719
|
+
*
|
|
1720
|
+
* By default the client creates its own store internally
|
|
1721
|
+
* (`createTokenStore()` in `token` mode, `createTokenStore('memory')`
|
|
1722
|
+
* otherwise). That is fine when `localStorage` is available, because a
|
|
1723
|
+
* separately-created `localStorage` store still points at the same
|
|
1724
|
+
* `ffid_tokens` key. But when `localStorage` is unavailable (e.g. Safari
|
|
1725
|
+
* Private Mode) the SDK falls back to an *in-memory* store, and a store the
|
|
1726
|
+
* host app creates independently is a **different** object — so the app would
|
|
1727
|
+
* see the user as signed-out even though the SDK holds valid tokens.
|
|
1728
|
+
*
|
|
1729
|
+
* Passing a single instance here (typically `createTokenStore()` created once
|
|
1730
|
+
* by the host) guarantees both sides share one source of truth regardless of
|
|
1731
|
+
* storage backend. Prefer reading the token via `useFFID().getAccessToken()`
|
|
1732
|
+
* so refresh/logout stay reflected without the host polling the store.
|
|
1733
|
+
*
|
|
1734
|
+
* Ignored in `cookie` and `service-key` modes (neither exposes a browser
|
|
1735
|
+
* access-token store). When omitted, behavior is unchanged from previous
|
|
1736
|
+
* versions.
|
|
1737
|
+
*/
|
|
1738
|
+
tokenStore?: TokenStore | undefined;
|
|
1611
1739
|
}
|
|
1612
1740
|
/**
|
|
1613
1741
|
* OIDC `prompt` values the SDK forwards to FFID `/oauth/authorize` (#4027).
|
|
@@ -1725,47 +1853,6 @@ interface FFIDAnalyticsConfig {
|
|
|
1725
1853
|
*/
|
|
1726
1854
|
isActive: boolean;
|
|
1727
1855
|
}
|
|
1728
|
-
/**
|
|
1729
|
-
* Discriminant for redirect failures that callers need to handle
|
|
1730
|
-
* programmatically (vs. logging the human-readable `error` string).
|
|
1731
|
-
*
|
|
1732
|
-
* - `'redirect_loop_detected'`: `redirectToAuthorize()` detected that the
|
|
1733
|
-
* same authorize URL (keyed by `baseUrl + client_id + organization_id`)
|
|
1734
|
-
* has been fired **3 times within 60 seconds**. Caller should surface a
|
|
1735
|
-
* manual "再度ログインする" UI rather than retry automatically (#2406 / #2411).
|
|
1736
|
-
*
|
|
1737
|
-
* - `'state_persistence_failed'`: `redirectToAuthorize()` could not persist
|
|
1738
|
-
* the OAuth CSRF `state` (storage unavailable / quota). Redirect is aborted
|
|
1739
|
-
* fail-closed (#4136) — callers should surface a manual retry UI.
|
|
1740
|
-
*
|
|
1741
|
-
* Other failure paths (SSR environment, PKCE generation failure, empty
|
|
1742
|
-
* organizationId) currently return `error` without a `code`. Treat this union
|
|
1743
|
-
* as forward-extensible and do NOT exhaustively `switch` over it without a
|
|
1744
|
-
* `default` branch for consumer code that must stay compatible across SDK
|
|
1745
|
-
* upgrades.
|
|
1746
|
-
*/
|
|
1747
|
-
type FFIDRedirectErrorCode = 'redirect_loop_detected' | 'state_persistence_failed';
|
|
1748
|
-
/**
|
|
1749
|
-
* Result of a redirect operation (redirectToLogin / redirectToAuthorize / redirectToLogout)
|
|
1750
|
-
*
|
|
1751
|
-
* Structured return type so callers can inspect failure reasons
|
|
1752
|
-
* instead of receiving a bare `false`. When `code` is set, branch on it
|
|
1753
|
-
* for programmatic handling; otherwise log `error` for humans.
|
|
1754
|
-
*
|
|
1755
|
-
* `loopDetectionDisabled` (#4119): set to `true` when sessionStorage is
|
|
1756
|
-
* unavailable (iOS WebKit private mode / eviction) so redirect-loop detection
|
|
1757
|
-
* ran **fail-open** — the redirect fired, but repeated-redirect protection is
|
|
1758
|
-
* disabled for this browser session. Callers that need loop protection on
|
|
1759
|
-
* such browsers should add their own max-retry guard.
|
|
1760
|
-
*/
|
|
1761
|
-
type FFIDRedirectResult = {
|
|
1762
|
-
success: true;
|
|
1763
|
-
loopDetectionDisabled?: boolean;
|
|
1764
|
-
} | {
|
|
1765
|
-
success: false;
|
|
1766
|
-
error: string;
|
|
1767
|
-
code?: FFIDRedirectErrorCode;
|
|
1768
|
-
};
|
|
1769
1856
|
|
|
1770
1857
|
/** OTP / magic link methods - sendOtp / verifyOtp */
|
|
1771
1858
|
|
|
@@ -1850,47 +1937,6 @@ interface FFIDGetLoginHistoryParams {
|
|
|
1850
1937
|
limit?: number;
|
|
1851
1938
|
}
|
|
1852
1939
|
|
|
1853
|
-
/**
|
|
1854
|
-
* Token Store
|
|
1855
|
-
*
|
|
1856
|
-
* Manages OAuth 2.0 tokens (access + refresh) with dual-storage support.
|
|
1857
|
-
* Falls back to in-memory storage when localStorage is unavailable
|
|
1858
|
-
* (e.g., Safari private browsing mode).
|
|
1859
|
-
*/
|
|
1860
|
-
/**
|
|
1861
|
-
* Token data stored by the token store
|
|
1862
|
-
*/
|
|
1863
|
-
interface TokenData {
|
|
1864
|
-
/** OAuth 2.0 access token */
|
|
1865
|
-
accessToken: string;
|
|
1866
|
-
/** OAuth 2.0 refresh token */
|
|
1867
|
-
refreshToken: string;
|
|
1868
|
-
/** Expiration timestamp in milliseconds (Unix epoch) */
|
|
1869
|
-
expiresAt: number;
|
|
1870
|
-
}
|
|
1871
|
-
/**
|
|
1872
|
-
* Token store interface for managing OAuth tokens
|
|
1873
|
-
*/
|
|
1874
|
-
interface TokenStore {
|
|
1875
|
-
/** Get stored tokens (null if not stored) */
|
|
1876
|
-
getTokens(): TokenData | null;
|
|
1877
|
-
/** Store new tokens */
|
|
1878
|
-
setTokens(tokens: TokenData): void;
|
|
1879
|
-
/** Clear all stored tokens */
|
|
1880
|
-
clearTokens(): void;
|
|
1881
|
-
/** Check if access token is expired (with 30s buffer) */
|
|
1882
|
-
isAccessTokenExpired(): boolean;
|
|
1883
|
-
}
|
|
1884
|
-
/**
|
|
1885
|
-
* Create a token store with the specified storage type.
|
|
1886
|
-
*
|
|
1887
|
-
* When storageType is 'localStorage' (default in browser), falls back
|
|
1888
|
-
* to memory if localStorage is not available (e.g., Safari private mode).
|
|
1889
|
-
*
|
|
1890
|
-
* @param storageType - 'localStorage' (default) or 'memory'
|
|
1891
|
-
*/
|
|
1892
|
-
declare function createTokenStore(storageType?: 'localStorage' | 'memory'): TokenStore;
|
|
1893
|
-
|
|
1894
1940
|
/** OAuth token operations - exchangeCodeForTokens / refreshAccessToken / signOutToken */
|
|
1895
1941
|
|
|
1896
1942
|
/**
|
|
@@ -2065,6 +2111,11 @@ declare function createFFIDClient(config: FFIDConfig): {
|
|
|
2065
2111
|
};
|
|
2066
2112
|
/** Token store (token mode only) */
|
|
2067
2113
|
tokenStore: TokenStore;
|
|
2114
|
+
/**
|
|
2115
|
+
* Return the currently-valid access token, or null (token mode only; #4318).
|
|
2116
|
+
* Live-reads the store and applies the 30s expiry buffer.
|
|
2117
|
+
*/
|
|
2118
|
+
getAccessToken: () => string | null;
|
|
2068
2119
|
/** Resolved auth mode */
|
|
2069
2120
|
authMode: FFIDAuthMode;
|
|
2070
2121
|
/** Resolved logger instance */
|