@feelflow/ffid-sdk 5.22.1 → 5.24.1
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-W4E7YNH5.js → chunk-C4RNFEME.js} +25 -12
- package/dist/{chunk-42Z2WZEI.cjs → chunk-L5SBAHY3.cjs} +25 -12
- package/dist/components/index.cjs +8 -8
- package/dist/components/index.d.cts +1 -1
- package/dist/components/index.d.ts +1 -1
- package/dist/components/index.js +1 -1
- package/dist/{ffid-client-CEJMpqRZ.d.cts → ffid-client-D0Jh4bO7.d.cts} +5 -2
- package/dist/{ffid-client-DM0ZhIDV.d.ts → ffid-client-DX2PTTFr.d.ts} +5 -2
- package/dist/{index-btouGGKr.d.cts → index-GqflEaKi.d.cts} +5 -2
- package/dist/{index-btouGGKr.d.ts → index-GqflEaKi.d.ts} +5 -2
- package/dist/index.cjs +42 -42
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +2 -2
- package/dist/server/index.cjs +290 -10
- package/dist/server/index.d.cts +159 -3
- package/dist/server/index.d.ts +159 -3
- package/dist/server/index.js +287 -11
- package/dist/server/test/index.d.cts +1 -1
- package/dist/server/test/index.d.ts +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { F as FFIDSubscriptionStatus, a as FFIDPrompt, b as FFIDConfig, c as FFIDApiResponse, d as FFIDSessionResponse, e as FFIDSignOutResult, f as FFIDRedirectResult, g as FFIDError, h as FFIDSubscriptionCheckResponse, i as FFIDCheckServiceAccessParams, j as FFIDServiceAccessDecision, k as FFIDAnalyticsConfig, l as FFIDVerifyAccessTokenOptions, m as FFIDOAuthUserInfo, n as FFIDInquiryCreateParams, o as FFIDInquiryCreateResponse, p as FFIDAuthMode, q as FFIDLogger, r as FFIDCacheAdapter, s as FFIDUser, t as FFIDOrganization, u as FFIDSubscription, v as FFIDSubscriptionContextValue, E as EffectiveSubscriptionStatus, w as FFIDOAuthUserInfoSubscription, x as FFIDAnnouncementsClientConfig, L as ListAnnouncementsOptions, y as FFIDAnnouncementsApiResponse, A as AnnouncementListResponse, z as FFIDAnnouncementsLogger } from './index-
|
|
2
|
-
export { B as Announcement, C as AnnouncementStatus, D as AnnouncementType, G as EFFECTIVE_SUBSCRIPTION_STATUSES, H as FFIDAnnouncementBadge, I as FFIDAnnouncementList, J as FFIDAnnouncementsError, K as FFIDAnnouncementsErrorCode, M as FFIDAnnouncementsServerResponse, N as FFIDApiResponseMeta, O as FFIDCacheConfig, P as FFIDContextValue, Q as FFIDInquiryCategory, R as FFIDInquiryCategorySite2026, 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, a0 as FFIDJwtClaims, a1 as FFIDLoginButton, a2 as FFIDOAuthTokenResponse, a3 as FFIDOAuthUserInfoMemberRole, a4 as FFIDOrganizationSwitcher, a5 as FFIDRedirectErrorCode, a6 as FFIDSeatModel, a7 as FFIDServiceAccessDenialReason, a8 as FFIDServiceAccessFailPolicy, a9 as FFIDSubscriptionBadge, aa as FFIDTokenIntrospectionResponse, ab as FFIDUserMenu, ac as FFID_INQUIRY_CATEGORIES, ad as FFID_INQUIRY_CATEGORIES_SITE_2026, ae as UseFFIDAnnouncementsOptions, af as UseFFIDAnnouncementsReturn, ag as isFFIDInquiryCategorySite2026, ah as useFFIDAnnouncements } from './index-
|
|
1
|
+
import { F as FFIDSubscriptionStatus, a as FFIDPrompt, b as FFIDConfig, c as FFIDApiResponse, d as FFIDSessionResponse, e as FFIDSignOutResult, f as FFIDRedirectResult, g as FFIDError, h as FFIDSubscriptionCheckResponse, i as FFIDCheckServiceAccessParams, j as FFIDServiceAccessDecision, k as FFIDAnalyticsConfig, l as FFIDVerifyAccessTokenOptions, m as FFIDOAuthUserInfo, n as FFIDInquiryCreateParams, o as FFIDInquiryCreateResponse, p as FFIDAuthMode, q as FFIDLogger, r as FFIDCacheAdapter, s as FFIDUser, t as FFIDOrganization, u as FFIDSubscription, v as FFIDSubscriptionContextValue, E as EffectiveSubscriptionStatus, w as FFIDOAuthUserInfoSubscription, x as FFIDAnnouncementsClientConfig, L as ListAnnouncementsOptions, y as FFIDAnnouncementsApiResponse, A as AnnouncementListResponse, z as FFIDAnnouncementsLogger } from './index-GqflEaKi.js';
|
|
2
|
+
export { B as Announcement, C as AnnouncementStatus, D as AnnouncementType, G as EFFECTIVE_SUBSCRIPTION_STATUSES, H as FFIDAnnouncementBadge, I as FFIDAnnouncementList, J as FFIDAnnouncementsError, K as FFIDAnnouncementsErrorCode, M as FFIDAnnouncementsServerResponse, N as FFIDApiResponseMeta, O as FFIDCacheConfig, P as FFIDContextValue, Q as FFIDInquiryCategory, R as FFIDInquiryCategorySite2026, 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, a0 as FFIDJwtClaims, a1 as FFIDLoginButton, a2 as FFIDOAuthTokenResponse, a3 as FFIDOAuthUserInfoMemberRole, a4 as FFIDOrganizationSwitcher, a5 as FFIDRedirectErrorCode, a6 as FFIDSeatModel, a7 as FFIDServiceAccessDenialReason, a8 as FFIDServiceAccessFailPolicy, a9 as FFIDSubscriptionBadge, aa as FFIDTokenIntrospectionResponse, ab as FFIDUserMenu, ac as FFID_INQUIRY_CATEGORIES, ad as FFID_INQUIRY_CATEGORIES_SITE_2026, ae as UseFFIDAnnouncementsOptions, af as UseFFIDAnnouncementsReturn, ag as isFFIDInquiryCategorySite2026, ah as useFFIDAnnouncements } from './index-GqflEaKi.js';
|
|
3
3
|
export { A as ALL_DENIED_EXCEPT_NECESSARY, i as CONSENT_COOKIE_NAME, j as CONSENT_DISMISSAL_TIMESTAMP_KEY, l as COOKIE_VERSION, D as DEFAULT_CONSENT_ERROR_MESSAGES, o as FFIDAnalyticsProvider, p as FFIDAnalyticsProviderProps, c as FFIDConsentCategories, r as FFIDConsentCategoryCode, e as FFIDConsentCategoryMetadata, w as FFIDConsentError, x as FFIDConsentErrorCode, y as FFIDConsentMergeStrategy, B as FFIDConsentMergeWarning, E as FFIDConsentMergeWarningEvent, F as FFIDConsentResult, I as FFIDConsentSource, a as FFIDConsentState, d as FFIDConsentSyncResult, M as FFIDCookieBanner, N as FFIDCookieBannerClassNames, O as FFIDCookieBannerProps, Q as FFIDCookieLink, R as FFIDCookieLinkProps, f as FFIDCookiePolicy, T as FFIDCookieSettings, U as FFIDCookieSettingsClassNames, V as FFIDCookieSettingsProps, _ as FFID_CONSENT_ERROR_CODES, ae as UseFFIDConsentPreferencesReturn, af as UseFFIDConsentReturn, ag as clearConsentDismissalTimestamp, aj as decodeConsentCookie, al as encodeConsentCookie, an as readConsentCookie, ao as readConsentDismissalTimestamp, aq as useFFIDConsent, ar as useFFIDConsentPreferences, as as writeConsentCookie, at as writeConsentDismissalTimestamp } from './FFIDCookieLink-BJgVcJyw.js';
|
|
4
4
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
5
5
|
import { ReactNode, ComponentType, FC } from 'react';
|
|
@@ -1643,7 +1643,7 @@ interface FFIDProviderProps extends FFIDConfig {
|
|
|
1643
1643
|
* - Automatic session refresh
|
|
1644
1644
|
* - Token mode: automatic code exchange and token refresh
|
|
1645
1645
|
*/
|
|
1646
|
-
declare function FFIDProvider({ children, serviceCode, scope, apiBaseUrl, debug, logger, refreshInterval, onAuthStateChange, onError, authMode, clientId, cleanCallbackUrl, }: FFIDProviderProps): react_jsx_runtime.JSX.Element;
|
|
1646
|
+
declare function FFIDProvider({ children, serviceCode, scope, apiBaseUrl, debug, logger, refreshInterval, onAuthStateChange, onError, authMode, clientId, timeout, cleanCallbackUrl, }: FFIDProviderProps): react_jsx_runtime.JSX.Element;
|
|
1647
1647
|
|
|
1648
1648
|
/**
|
|
1649
1649
|
* Return type for useFFID hook
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { useFFIDContext, useSubscription } from './chunk-
|
|
2
|
-
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_ERROR_CODES, FFID_INQUIRY_CATEGORIES, FFID_INQUIRY_CATEGORIES_SITE_2026, cleanupStateStorage as clearState, computeEffectiveStatusFromSession, createFFIDAnnouncementsClient, createFFIDClient, createTokenStore, generateCodeChallenge, generateCodeVerifier, handleRedirectCallback, hasAccessFromUserinfo, isBlockedFromUserinfo, isFFIDInquiryCategorySite2026, normalizeRedirectUri, retrieveCodeVerifier, retrieveState, storeCodeVerifier, storeState, useFFID, useFFIDAnnouncements, useSubscription, withSubscription } from './chunk-
|
|
1
|
+
import { useFFIDContext, useSubscription } from './chunk-C4RNFEME.js';
|
|
2
|
+
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_ERROR_CODES, FFID_INQUIRY_CATEGORIES, FFID_INQUIRY_CATEGORIES_SITE_2026, cleanupStateStorage as clearState, computeEffectiveStatusFromSession, createFFIDAnnouncementsClient, createFFIDClient, createTokenStore, generateCodeChallenge, generateCodeVerifier, handleRedirectCallback, hasAccessFromUserinfo, isBlockedFromUserinfo, isFFIDInquiryCategorySite2026, normalizeRedirectUri, retrieveCodeVerifier, retrieveState, storeCodeVerifier, storeState, useFFID, useFFIDAnnouncements, useSubscription, withSubscription } from './chunk-C4RNFEME.js';
|
|
3
3
|
export { ALL_DENIED_EXCEPT_NECESSARY, CONSENT_COOKIE_NAME, CONSENT_DISMISSAL_TIMESTAMP_KEY, COOKIE_VERSION, DEFAULT_CONSENT_ERROR_MESSAGES, FFIDAnalyticsProvider, FFIDConsentError, FFIDCookieBanner, FFIDCookieLink, FFIDCookieSettings, FFID_CONSENT_ERROR_CODES, clearConsentDismissalTimestamp, decodeConsentCookie, encodeConsentCookie, readConsentCookie, readConsentDismissalTimestamp, useFFIDConsent, useFFIDConsentPreferences, writeConsentCookie, writeConsentDismissalTimestamp } from './chunk-G7VOX64X.js';
|
|
4
4
|
import { useEffect, useRef } from 'react';
|
|
5
5
|
import { jsx, Fragment } from 'react/jsx-runtime';
|
package/dist/server/index.cjs
CHANGED
|
@@ -86,7 +86,9 @@ function createMemoryStore() {
|
|
|
86
86
|
function isTokenData(value) {
|
|
87
87
|
if (typeof value !== "object" || value === null) return false;
|
|
88
88
|
const obj = value;
|
|
89
|
-
return typeof obj.accessToken === "string" && typeof obj.refreshToken === "string" &&
|
|
89
|
+
return typeof obj.accessToken === "string" && typeof obj.refreshToken === "string" && // Number.isFinite also rejects NaN/Infinity (e.g. a crafted `1e999` JSON
|
|
90
|
+
// literal parses to Infinity and would never trip the expiry check — #4271)
|
|
91
|
+
typeof obj.expiresAt === "number" && Number.isFinite(obj.expiresAt);
|
|
90
92
|
}
|
|
91
93
|
function createTokenStore(storageType) {
|
|
92
94
|
if (storageType === "memory") {
|
|
@@ -1222,7 +1224,7 @@ function createNonContractMethods(deps) {
|
|
|
1222
1224
|
}
|
|
1223
1225
|
|
|
1224
1226
|
// src/client/version-check.ts
|
|
1225
|
-
var SDK_VERSION = "5.
|
|
1227
|
+
var SDK_VERSION = "5.24.1";
|
|
1226
1228
|
var SDK_USER_AGENT = `FFID-SDK/${SDK_VERSION} (TypeScript)`;
|
|
1227
1229
|
var SDK_VERSION_HEADER = "X-FFID-SDK-Version";
|
|
1228
1230
|
function sdkHeaders() {
|
|
@@ -1277,7 +1279,7 @@ function validateTokenResponse(tokenResponse) {
|
|
|
1277
1279
|
if (!tokenResponse.refresh_token) {
|
|
1278
1280
|
invalid.push("refresh_token");
|
|
1279
1281
|
}
|
|
1280
|
-
if (typeof tokenResponse.expires_in !== "number" || tokenResponse.expires_in <= 0) {
|
|
1282
|
+
if (typeof tokenResponse.expires_in !== "number" || !Number.isFinite(tokenResponse.expires_in) || tokenResponse.expires_in <= 0) {
|
|
1281
1283
|
invalid.push("expires_in");
|
|
1282
1284
|
}
|
|
1283
1285
|
if (invalid.length > 0) {
|
|
@@ -1292,8 +1294,15 @@ function createOAuthTokenMethods(deps) {
|
|
|
1292
1294
|
resolvedRedirectUri,
|
|
1293
1295
|
tokenStore,
|
|
1294
1296
|
logger,
|
|
1297
|
+
timeout,
|
|
1295
1298
|
errorCodes
|
|
1296
1299
|
} = deps;
|
|
1300
|
+
function withTimeout2(init) {
|
|
1301
|
+
if (timeout !== void 0) {
|
|
1302
|
+
return { ...init, signal: AbortSignal.timeout(timeout) };
|
|
1303
|
+
}
|
|
1304
|
+
return init;
|
|
1305
|
+
}
|
|
1297
1306
|
async function exchangeCodeForTokens(code, codeVerifier, opts) {
|
|
1298
1307
|
const url = `${baseUrl}${OAUTH_TOKEN_ENDPOINT}`;
|
|
1299
1308
|
logger.debug("Exchanging code for tokens:", url);
|
|
@@ -1329,12 +1338,12 @@ function createOAuthTokenMethods(deps) {
|
|
|
1329
1338
|
}
|
|
1330
1339
|
let response;
|
|
1331
1340
|
try {
|
|
1332
|
-
response = await fetch(url, {
|
|
1341
|
+
response = await fetch(url, withTimeout2({
|
|
1333
1342
|
method: "POST",
|
|
1334
1343
|
credentials: "omit",
|
|
1335
1344
|
headers: { "Content-Type": "application/x-www-form-urlencoded", ...sdkHeaders() },
|
|
1336
1345
|
body: new URLSearchParams(body).toString()
|
|
1337
|
-
});
|
|
1346
|
+
}));
|
|
1338
1347
|
} catch (error) {
|
|
1339
1348
|
logger.error("Network error during token exchange:", error);
|
|
1340
1349
|
return {
|
|
@@ -1397,7 +1406,7 @@ function createOAuthTokenMethods(deps) {
|
|
|
1397
1406
|
logger.debug("Refreshing access token:", url);
|
|
1398
1407
|
let response;
|
|
1399
1408
|
try {
|
|
1400
|
-
response = await fetch(url, {
|
|
1409
|
+
response = await fetch(url, withTimeout2({
|
|
1401
1410
|
method: "POST",
|
|
1402
1411
|
credentials: "omit",
|
|
1403
1412
|
headers: { "Content-Type": "application/x-www-form-urlencoded", ...sdkHeaders() },
|
|
@@ -1406,7 +1415,7 @@ function createOAuthTokenMethods(deps) {
|
|
|
1406
1415
|
refresh_token: tokens.refreshToken,
|
|
1407
1416
|
client_id: clientId
|
|
1408
1417
|
}).toString()
|
|
1409
|
-
});
|
|
1418
|
+
}));
|
|
1410
1419
|
} catch (error) {
|
|
1411
1420
|
logger.error("Network error during token refresh:", error);
|
|
1412
1421
|
return {
|
|
@@ -1476,7 +1485,7 @@ function createOAuthTokenMethods(deps) {
|
|
|
1476
1485
|
async function revokeOAuthToken(token, tokenTypeHint) {
|
|
1477
1486
|
const url = `${baseUrl}${OAUTH_REVOKE_ENDPOINT}`;
|
|
1478
1487
|
try {
|
|
1479
|
-
const response = await fetch(url, {
|
|
1488
|
+
const response = await fetch(url, withTimeout2({
|
|
1480
1489
|
method: "POST",
|
|
1481
1490
|
credentials: "omit",
|
|
1482
1491
|
headers: { "Content-Type": "application/x-www-form-urlencoded", ...sdkHeaders() },
|
|
@@ -1485,9 +1494,10 @@ function createOAuthTokenMethods(deps) {
|
|
|
1485
1494
|
client_id: clientId,
|
|
1486
1495
|
token_type_hint: tokenTypeHint
|
|
1487
1496
|
}).toString()
|
|
1488
|
-
});
|
|
1497
|
+
}));
|
|
1489
1498
|
return response.ok;
|
|
1490
|
-
} catch {
|
|
1499
|
+
} catch (err) {
|
|
1500
|
+
logger.debug("Token revoke request failed (client-side; e.g. timeout/network):", err);
|
|
1491
1501
|
return false;
|
|
1492
1502
|
}
|
|
1493
1503
|
}
|
|
@@ -2888,6 +2898,7 @@ function createFFIDClient(config) {
|
|
|
2888
2898
|
resolvedRedirectUri,
|
|
2889
2899
|
tokenStore,
|
|
2890
2900
|
logger,
|
|
2901
|
+
timeout,
|
|
2891
2902
|
errorCodes: FFID_ERROR_CODES
|
|
2892
2903
|
});
|
|
2893
2904
|
async function fetchWithAuth(endpoint, options = {}, authOverride) {
|
|
@@ -3382,6 +3393,271 @@ function getFFIDConsentFromCookieHeader(cookieHeader) {
|
|
|
3382
3393
|
return decodeConsentCookie(decodeURIComponent(value));
|
|
3383
3394
|
}
|
|
3384
3395
|
|
|
3396
|
+
// src/server/auth/server-state.ts
|
|
3397
|
+
var STATE_RANDOM_BYTES2 = 16;
|
|
3398
|
+
var HEX_RADIX = 16;
|
|
3399
|
+
function generateServerState() {
|
|
3400
|
+
const bytes = new Uint8Array(STATE_RANDOM_BYTES2);
|
|
3401
|
+
globalThis.crypto.getRandomValues(bytes);
|
|
3402
|
+
return Array.from(bytes, (b) => b.toString(HEX_RADIX).padStart(2, "0")).join("");
|
|
3403
|
+
}
|
|
3404
|
+
|
|
3405
|
+
// src/server/auth/server-oauth-token.ts
|
|
3406
|
+
var OAUTH_TOKEN_ENDPOINT2 = "/api/v1/oauth/token";
|
|
3407
|
+
var MS_PER_SECOND3 = 1e3;
|
|
3408
|
+
function withTimeout(init, timeout) {
|
|
3409
|
+
if (timeout !== void 0) {
|
|
3410
|
+
return { ...init, signal: AbortSignal.timeout(timeout) };
|
|
3411
|
+
}
|
|
3412
|
+
return init;
|
|
3413
|
+
}
|
|
3414
|
+
function validateTokenResponse2(r) {
|
|
3415
|
+
const invalid = [];
|
|
3416
|
+
if (!r.access_token) invalid.push("access_token");
|
|
3417
|
+
if (!r.refresh_token) invalid.push("refresh_token");
|
|
3418
|
+
if (typeof r.expires_in !== "number" || !Number.isFinite(r.expires_in) || r.expires_in <= 0) {
|
|
3419
|
+
invalid.push("expires_in");
|
|
3420
|
+
}
|
|
3421
|
+
return invalid.length > 0 ? `\u30C8\u30FC\u30AF\u30F3\u30EC\u30B9\u30DD\u30F3\u30B9\u306B\u4E0D\u6B63\u306A\u30D5\u30A3\u30FC\u30EB\u30C9\u304C\u3042\u308A\u307E\u3059: ${invalid.join(", ")}` : null;
|
|
3422
|
+
}
|
|
3423
|
+
function toTokens(r) {
|
|
3424
|
+
return {
|
|
3425
|
+
accessToken: r.access_token,
|
|
3426
|
+
refreshToken: r.refresh_token,
|
|
3427
|
+
expiresAt: Date.now() + r.expires_in * MS_PER_SECOND3
|
|
3428
|
+
};
|
|
3429
|
+
}
|
|
3430
|
+
function fail(code, message) {
|
|
3431
|
+
return { error: { code, message } };
|
|
3432
|
+
}
|
|
3433
|
+
async function postToken(deps, body, fallbackErrorCode) {
|
|
3434
|
+
const url = `${deps.apiBaseUrl}${OAUTH_TOKEN_ENDPOINT2}`;
|
|
3435
|
+
let response;
|
|
3436
|
+
try {
|
|
3437
|
+
response = await fetch(
|
|
3438
|
+
url,
|
|
3439
|
+
withTimeout(
|
|
3440
|
+
{
|
|
3441
|
+
method: "POST",
|
|
3442
|
+
credentials: "omit",
|
|
3443
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded", ...sdkHeaders() },
|
|
3444
|
+
body: new URLSearchParams(body).toString()
|
|
3445
|
+
},
|
|
3446
|
+
deps.timeout
|
|
3447
|
+
)
|
|
3448
|
+
);
|
|
3449
|
+
} catch (error) {
|
|
3450
|
+
return fail(
|
|
3451
|
+
FFID_ERROR_CODES.NETWORK_ERROR,
|
|
3452
|
+
error instanceof Error ? error.message : "\u30CD\u30C3\u30C8\u30EF\u30FC\u30AF\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F"
|
|
3453
|
+
);
|
|
3454
|
+
}
|
|
3455
|
+
let json;
|
|
3456
|
+
try {
|
|
3457
|
+
json = await response.json();
|
|
3458
|
+
} catch {
|
|
3459
|
+
return fail(
|
|
3460
|
+
FFID_ERROR_CODES.PARSE_ERROR,
|
|
3461
|
+
`\u30C8\u30FC\u30AF\u30F3\u30EC\u30B9\u30DD\u30F3\u30B9\u306E\u89E3\u6790\u306B\u5931\u6557\u3057\u307E\u3057\u305F (status: ${response.status})`
|
|
3462
|
+
);
|
|
3463
|
+
}
|
|
3464
|
+
if (!response.ok) {
|
|
3465
|
+
const errorBody = json;
|
|
3466
|
+
return fail(errorBody.error ?? fallbackErrorCode, errorBody.error_description ?? "\u30C8\u30FC\u30AF\u30F3\u51E6\u7406\u306B\u5931\u6557\u3057\u307E\u3057\u305F");
|
|
3467
|
+
}
|
|
3468
|
+
const validationError = validateTokenResponse2(json);
|
|
3469
|
+
if (validationError) {
|
|
3470
|
+
return fail(fallbackErrorCode, validationError);
|
|
3471
|
+
}
|
|
3472
|
+
return { tokens: toTokens(json) };
|
|
3473
|
+
}
|
|
3474
|
+
function exchangeAuthorizationCodeServer(deps, code) {
|
|
3475
|
+
return postToken(
|
|
3476
|
+
deps,
|
|
3477
|
+
{
|
|
3478
|
+
grant_type: "authorization_code",
|
|
3479
|
+
code,
|
|
3480
|
+
client_id: deps.clientId,
|
|
3481
|
+
client_secret: deps.clientSecret,
|
|
3482
|
+
redirect_uri: deps.redirectUri
|
|
3483
|
+
},
|
|
3484
|
+
FFID_ERROR_CODES.TOKEN_EXCHANGE_ERROR
|
|
3485
|
+
);
|
|
3486
|
+
}
|
|
3487
|
+
function refreshTokensServer(deps, refreshToken) {
|
|
3488
|
+
return postToken(
|
|
3489
|
+
deps,
|
|
3490
|
+
{
|
|
3491
|
+
grant_type: "refresh_token",
|
|
3492
|
+
refresh_token: refreshToken,
|
|
3493
|
+
client_id: deps.clientId,
|
|
3494
|
+
client_secret: deps.clientSecret
|
|
3495
|
+
},
|
|
3496
|
+
FFID_ERROR_CODES.TOKEN_REFRESH_ERROR
|
|
3497
|
+
);
|
|
3498
|
+
}
|
|
3499
|
+
|
|
3500
|
+
// src/server/auth/server-auth-client.ts
|
|
3501
|
+
var OAUTH_AUTHORIZE_ENDPOINT2 = "/api/v1/oauth/authorize";
|
|
3502
|
+
var SCREEN_HINT_SIGNUP2 = "signup";
|
|
3503
|
+
function createServerAuthClient(config) {
|
|
3504
|
+
if (!config.serviceCode?.trim()) {
|
|
3505
|
+
throw new Error("FFID Server Auth: serviceCode \u304C\u672A\u8A2D\u5B9A\u3067\u3059");
|
|
3506
|
+
}
|
|
3507
|
+
if (!config.clientSecret?.trim()) {
|
|
3508
|
+
throw new Error("FFID Server Auth: clientSecret \u304C\u672A\u8A2D\u5B9A\u3067\u3059");
|
|
3509
|
+
}
|
|
3510
|
+
if (!config.redirectUri?.trim()) {
|
|
3511
|
+
throw new Error("FFID Server Auth: redirectUri \u304C\u672A\u8A2D\u5B9A\u3067\u3059");
|
|
3512
|
+
}
|
|
3513
|
+
if (!config.scope?.trim()) {
|
|
3514
|
+
throw new Error(
|
|
3515
|
+
"FFID Server Auth: scope \u304C\u672A\u8A2D\u5B9A\u3067\u3059\u3002`DEFAULT_OAUTH_SCOPES` \u3092 import \u3059\u308B\u304B\u660E\u793A\u7684\u306B\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044"
|
|
3516
|
+
);
|
|
3517
|
+
}
|
|
3518
|
+
const apiBaseUrl = config.apiBaseUrl ?? chunkMDHKSVLP_cjs.DEFAULT_API_BASE_URL;
|
|
3519
|
+
const clientId = config.clientId ?? config.serviceCode;
|
|
3520
|
+
const deps = {
|
|
3521
|
+
apiBaseUrl,
|
|
3522
|
+
clientId,
|
|
3523
|
+
clientSecret: config.clientSecret,
|
|
3524
|
+
redirectUri: config.redirectUri,
|
|
3525
|
+
timeout: config.timeout
|
|
3526
|
+
};
|
|
3527
|
+
function buildAuthorizeUrl(opts) {
|
|
3528
|
+
const state = generateServerState();
|
|
3529
|
+
const params = new URLSearchParams({
|
|
3530
|
+
response_type: "code",
|
|
3531
|
+
client_id: clientId,
|
|
3532
|
+
scope: config.scope,
|
|
3533
|
+
redirect_uri: config.redirectUri,
|
|
3534
|
+
state
|
|
3535
|
+
});
|
|
3536
|
+
const organizationId = opts?.organizationId?.trim();
|
|
3537
|
+
if (organizationId) params.set("organization_id", organizationId);
|
|
3538
|
+
if (opts?.screenHint === SCREEN_HINT_SIGNUP2) params.set("screen_hint", SCREEN_HINT_SIGNUP2);
|
|
3539
|
+
if (opts?.prompt) params.set("prompt", opts.prompt);
|
|
3540
|
+
return { url: `${apiBaseUrl}${OAUTH_AUTHORIZE_ENDPOINT2}?${params.toString()}`, state };
|
|
3541
|
+
}
|
|
3542
|
+
async function handleCallback(input) {
|
|
3543
|
+
let url;
|
|
3544
|
+
try {
|
|
3545
|
+
url = typeof input.url === "string" ? new URL(input.url) : input.url;
|
|
3546
|
+
} catch {
|
|
3547
|
+
return {
|
|
3548
|
+
error: {
|
|
3549
|
+
code: FFID_ERROR_CODES.TOKEN_EXCHANGE_ERROR,
|
|
3550
|
+
message: "\u30B3\u30FC\u30EB\u30D0\u30C3\u30AF URL \u306E\u89E3\u6790\u306B\u5931\u6557\u3057\u307E\u3057\u305F"
|
|
3551
|
+
}
|
|
3552
|
+
};
|
|
3553
|
+
}
|
|
3554
|
+
const returnedState = url.searchParams.get("state") ?? void 0;
|
|
3555
|
+
if (!input.storedState || !returnedState || input.storedState !== returnedState) {
|
|
3556
|
+
return {
|
|
3557
|
+
error: {
|
|
3558
|
+
code: FFID_ERROR_CODES.STATE_MISMATCH_ERROR,
|
|
3559
|
+
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"
|
|
3560
|
+
}
|
|
3561
|
+
};
|
|
3562
|
+
}
|
|
3563
|
+
const errorParam = url.searchParams.get("error");
|
|
3564
|
+
if (errorParam) {
|
|
3565
|
+
return {
|
|
3566
|
+
error: {
|
|
3567
|
+
code: errorParam,
|
|
3568
|
+
message: url.searchParams.get("error_description") ?? "\u8A8D\u53EF\u30B5\u30FC\u30D0\u30FC\u304C\u30A8\u30E9\u30FC\u3092\u8FD4\u3057\u307E\u3057\u305F"
|
|
3569
|
+
}
|
|
3570
|
+
};
|
|
3571
|
+
}
|
|
3572
|
+
const code = url.searchParams.get("code");
|
|
3573
|
+
if (!code) {
|
|
3574
|
+
return {
|
|
3575
|
+
error: {
|
|
3576
|
+
code: FFID_ERROR_CODES.TOKEN_EXCHANGE_ERROR,
|
|
3577
|
+
message: "authorization code \u304C\u30B3\u30FC\u30EB\u30D0\u30C3\u30AF\u306B\u542B\u307E\u308C\u3066\u3044\u307E\u305B\u3093"
|
|
3578
|
+
}
|
|
3579
|
+
};
|
|
3580
|
+
}
|
|
3581
|
+
return exchangeAuthorizationCodeServer(deps, code);
|
|
3582
|
+
}
|
|
3583
|
+
function refresh(refreshToken) {
|
|
3584
|
+
return refreshTokensServer(deps, refreshToken);
|
|
3585
|
+
}
|
|
3586
|
+
return { buildAuthorizeUrl, handleCallback, refresh };
|
|
3587
|
+
}
|
|
3588
|
+
|
|
3589
|
+
// src/server/auth/session-cookies.ts
|
|
3590
|
+
var FFID_SESSION_COOKIE_NAME = "ffid_session";
|
|
3591
|
+
var DEFAULT_PATH = "/";
|
|
3592
|
+
var DEFAULT_SAME_SITE = "lax";
|
|
3593
|
+
var HOURS_PER_DAY2 = 24;
|
|
3594
|
+
var DEFAULT_MAX_AGE_DAYS = 30;
|
|
3595
|
+
var DEFAULT_MAX_AGE_SECONDS = 60 * 60 * HOURS_PER_DAY2 * DEFAULT_MAX_AGE_DAYS;
|
|
3596
|
+
var BASE64_GROUP = 4;
|
|
3597
|
+
function base64UrlEncode2(input) {
|
|
3598
|
+
const bytes = new TextEncoder().encode(input);
|
|
3599
|
+
const binary = Array.from(bytes, (byte) => String.fromCharCode(byte)).join("");
|
|
3600
|
+
return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
3601
|
+
}
|
|
3602
|
+
function base64UrlDecode(input) {
|
|
3603
|
+
const b64 = input.replace(/-/g, "+").replace(/_/g, "/");
|
|
3604
|
+
const remainder = b64.length % BASE64_GROUP;
|
|
3605
|
+
const padded = remainder === 0 ? b64 : b64 + "=".repeat(BASE64_GROUP - remainder);
|
|
3606
|
+
const bytes = Uint8Array.from(atob(padded), (char) => char.charCodeAt(0));
|
|
3607
|
+
return new TextDecoder().decode(bytes);
|
|
3608
|
+
}
|
|
3609
|
+
function sameSiteLabel(value) {
|
|
3610
|
+
return value.charAt(0).toUpperCase() + value.slice(1);
|
|
3611
|
+
}
|
|
3612
|
+
function buildSetCookie(nameValue, opts, maxAge) {
|
|
3613
|
+
const path = opts.path ?? DEFAULT_PATH;
|
|
3614
|
+
const sameSite = opts.sameSite ?? DEFAULT_SAME_SITE;
|
|
3615
|
+
const secure = opts.secure ?? true;
|
|
3616
|
+
const httpOnly = opts.httpOnly ?? true;
|
|
3617
|
+
const attrs = [nameValue, `Path=${path}`, `Max-Age=${maxAge}`, `SameSite=${sameSiteLabel(sameSite)}`];
|
|
3618
|
+
if (httpOnly) attrs.push("HttpOnly");
|
|
3619
|
+
if (secure) attrs.push("Secure");
|
|
3620
|
+
if (opts.domain) attrs.push(`Domain=${opts.domain}`);
|
|
3621
|
+
return attrs.join("; ");
|
|
3622
|
+
}
|
|
3623
|
+
function parseCookieHeader(header) {
|
|
3624
|
+
const out = {};
|
|
3625
|
+
for (const part of header.split(";")) {
|
|
3626
|
+
const eq = part.indexOf("=");
|
|
3627
|
+
if (eq === -1) continue;
|
|
3628
|
+
const key = part.slice(0, eq).trim();
|
|
3629
|
+
if (key) out[key] = part.slice(eq + 1).trim();
|
|
3630
|
+
}
|
|
3631
|
+
return out;
|
|
3632
|
+
}
|
|
3633
|
+
function serialize(tokens, opts = {}) {
|
|
3634
|
+
const value = base64UrlEncode2(JSON.stringify(tokens));
|
|
3635
|
+
const maxAge = opts.maxAge ?? DEFAULT_MAX_AGE_SECONDS;
|
|
3636
|
+
return [buildSetCookie(`${FFID_SESSION_COOKIE_NAME}=${value}`, opts, maxAge)];
|
|
3637
|
+
}
|
|
3638
|
+
function read(cookieHeader) {
|
|
3639
|
+
if (!cookieHeader) return null;
|
|
3640
|
+
const raw = parseCookieHeader(cookieHeader)[FFID_SESSION_COOKIE_NAME];
|
|
3641
|
+
if (!raw) return null;
|
|
3642
|
+
try {
|
|
3643
|
+
const parsed = JSON.parse(base64UrlDecode(raw));
|
|
3644
|
+
if (typeof parsed.accessToken !== "string" || typeof parsed.refreshToken !== "string" || typeof parsed.expiresAt !== "number") {
|
|
3645
|
+
return null;
|
|
3646
|
+
}
|
|
3647
|
+
return {
|
|
3648
|
+
accessToken: parsed.accessToken,
|
|
3649
|
+
refreshToken: parsed.refreshToken,
|
|
3650
|
+
expiresAt: parsed.expiresAt
|
|
3651
|
+
};
|
|
3652
|
+
} catch {
|
|
3653
|
+
return null;
|
|
3654
|
+
}
|
|
3655
|
+
}
|
|
3656
|
+
function clear(opts = {}) {
|
|
3657
|
+
return [buildSetCookie(`${FFID_SESSION_COOKIE_NAME}=`, opts, 0)];
|
|
3658
|
+
}
|
|
3659
|
+
var sessionCookies = { serialize, read, clear };
|
|
3660
|
+
|
|
3385
3661
|
Object.defineProperty(exports, "DEFAULT_API_BASE_URL", {
|
|
3386
3662
|
enumerable: true,
|
|
3387
3663
|
get: function () { return chunkMDHKSVLP_cjs.DEFAULT_API_BASE_URL; }
|
|
@@ -3394,12 +3670,16 @@ exports.ALL_DENIED_EXCEPT_NECESSARY = ALL_DENIED_EXCEPT_NECESSARY;
|
|
|
3394
3670
|
exports.CONSENT_COOKIE_NAME = CONSENT_COOKIE_NAME;
|
|
3395
3671
|
exports.COOKIE_VERSION = COOKIE_VERSION;
|
|
3396
3672
|
exports.FFID_ERROR_CODES = FFID_ERROR_CODES;
|
|
3673
|
+
exports.FFID_SESSION_COOKIE_NAME = FFID_SESSION_COOKIE_NAME;
|
|
3397
3674
|
exports.createFFIDClient = createFFIDClient;
|
|
3398
3675
|
exports.createKVCacheAdapter = createKVCacheAdapter;
|
|
3399
3676
|
exports.createMemoryCacheAdapter = createMemoryCacheAdapter;
|
|
3677
|
+
exports.createServerAuthClient = createServerAuthClient;
|
|
3400
3678
|
exports.createTokenStore = createTokenStore;
|
|
3401
3679
|
exports.createVerifyAccessToken = createVerifyAccessToken;
|
|
3402
3680
|
exports.decodeConsentCookie = decodeConsentCookie;
|
|
3403
3681
|
exports.encodeConsentCookie = encodeConsentCookie;
|
|
3682
|
+
exports.generateServerState = generateServerState;
|
|
3404
3683
|
exports.getFFIDConsentFromCookieHeader = getFFIDConsentFromCookieHeader;
|
|
3405
3684
|
exports.getFFIDConsentFromCookies = getFFIDConsentFromCookies;
|
|
3685
|
+
exports.sessionCookies = sessionCookies;
|
package/dist/server/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { F as FFIDLogger, a as FFIDError, b as FFIDCacheAdapter, c as FFIDVerifyAccessTokenOptions, d as FFIDApiResponse, e as FFIDOAuthUserInfo } from '../ffid-client-
|
|
2
|
-
export { f as FFIDAddMemberParams, g as FFIDAddMemberRequest, h as FFIDAddMemberResponse, i as FFIDAssignableMemberRole, j as FFIDCacheConfig, k as FFIDClient, l as FFIDConfig, m as FFIDListMembersResponse, n as FFIDMemberRole, o as FFIDOrganization, p as FFIDOrganizationMember, q as FFIDOtpSendResponse, r as FFIDOtpVerifyResponse, s as FFIDPasswordResetConfirmResponse, t as FFIDPasswordResetResponse, u as FFIDPasswordResetVerifyResponse, v as FFIDProfileCallOptions, w as FFIDProvisionMemberPlan, x as FFIDProvisionMemberPlanStatus, y as FFIDProvisionMemberResult, z as FFIDProvisionMemberStatus, A as FFIDProvisionOrganizationDryRun, B as FFIDProvisionOrganizationMemberInput, C as FFIDProvisionOrganizationOutcome, D as FFIDProvisionOrganizationParams, E as FFIDProvisionOrganizationResponse, G as FFIDProvisionUserDryRun, H as FFIDProvisionUserOutcome, I as FFIDProvisionUserParams, J as FFIDProvisionUserProfileInput, K as FFIDProvisionUserResponse, L as FFIDProvisionedOrganization, M as FFIDProvisionedUser, N as FFIDRemoveMemberResponse, O as FFIDResetSessionResponse, P as FFIDSubscription, Q as FFIDUpdateMemberRoleResponse, R as FFIDUpdateUserProfileRequest, S as FFIDUser, T as FFIDUserProfile, U as TokenData, V as TokenStore, W as createFFIDClient, X as createTokenStore } from '../ffid-client-
|
|
1
|
+
import { F as FFIDLogger, a as FFIDError, b as FFIDCacheAdapter, c as FFIDVerifyAccessTokenOptions, d as FFIDApiResponse, e as FFIDOAuthUserInfo } from '../ffid-client-D0Jh4bO7.cjs';
|
|
2
|
+
export { f as FFIDAddMemberParams, g as FFIDAddMemberRequest, h as FFIDAddMemberResponse, i as FFIDAssignableMemberRole, j as FFIDCacheConfig, k as FFIDClient, l as FFIDConfig, m as FFIDListMembersResponse, n as FFIDMemberRole, o as FFIDOrganization, p as FFIDOrganizationMember, q as FFIDOtpSendResponse, r as FFIDOtpVerifyResponse, s as FFIDPasswordResetConfirmResponse, t as FFIDPasswordResetResponse, u as FFIDPasswordResetVerifyResponse, v as FFIDProfileCallOptions, w as FFIDProvisionMemberPlan, x as FFIDProvisionMemberPlanStatus, y as FFIDProvisionMemberResult, z as FFIDProvisionMemberStatus, A as FFIDProvisionOrganizationDryRun, B as FFIDProvisionOrganizationMemberInput, C as FFIDProvisionOrganizationOutcome, D as FFIDProvisionOrganizationParams, E as FFIDProvisionOrganizationResponse, G as FFIDProvisionUserDryRun, H as FFIDProvisionUserOutcome, I as FFIDProvisionUserParams, J as FFIDProvisionUserProfileInput, K as FFIDProvisionUserResponse, L as FFIDProvisionedOrganization, M as FFIDProvisionedUser, N as FFIDRemoveMemberResponse, O as FFIDResetSessionResponse, P as FFIDSubscription, Q as FFIDUpdateMemberRoleResponse, R as FFIDUpdateUserProfileRequest, S as FFIDUser, T as FFIDUserProfile, U as TokenData, V as TokenStore, W as createFFIDClient, X as createTokenStore } from '../ffid-client-D0Jh4bO7.cjs';
|
|
3
3
|
export { D as DEFAULT_API_BASE_URL, a as DEFAULT_OAUTH_SCOPES } from '../constants-D61jqRIO.cjs';
|
|
4
4
|
import { z } from 'zod';
|
|
5
5
|
import '../types-Cjb9J0rL.cjs';
|
|
@@ -233,4 +233,160 @@ declare function decodeConsentCookie(raw: string | null | undefined): FFIDConsen
|
|
|
233
233
|
|
|
234
234
|
declare const CONSENT_COOKIE_NAME = "ffid_consent";
|
|
235
235
|
|
|
236
|
-
|
|
236
|
+
/**
|
|
237
|
+
* Public types for the confidential / BFF server auth helpers (#4261 / #4269).
|
|
238
|
+
* React/DOM-free — see `src/server/auth/server-auth-client.ts`.
|
|
239
|
+
*/
|
|
240
|
+
|
|
241
|
+
/** Configuration for {@link createServerAuthClient}. */
|
|
242
|
+
interface ServerAuthConfig {
|
|
243
|
+
/** FFID service code (== OAuth `client_id` unless `clientId` is overridden). */
|
|
244
|
+
serviceCode: string;
|
|
245
|
+
/** OAuth `client_id`. Defaults to `serviceCode`. */
|
|
246
|
+
clientId?: string;
|
|
247
|
+
/**
|
|
248
|
+
* confidential-client secret — a `service_api_key` (`ffid_sk_live_…`) carrying
|
|
249
|
+
* the `oauth:client_auth` scope (PR-A1 #4267). Read from a server-only env var;
|
|
250
|
+
* never sent to the browser.
|
|
251
|
+
*/
|
|
252
|
+
clientSecret: string;
|
|
253
|
+
/** FFID API base URL. Defaults to `DEFAULT_API_BASE_URL` (`https://id.feelflow.net`). */
|
|
254
|
+
apiBaseUrl?: string;
|
|
255
|
+
/** Server callback URL, registered in the service's `allowed_redirect_uris`. */
|
|
256
|
+
redirectUri: string;
|
|
257
|
+
/** OAuth scope (space-separated), forwarded to `/oauth/authorize?scope=…`. */
|
|
258
|
+
scope: string;
|
|
259
|
+
/**
|
|
260
|
+
* Optional request timeout (ms) applied to token fetches via
|
|
261
|
+
* `AbortSignal.timeout` (#4260). Unset → no timeout (fetch default).
|
|
262
|
+
*/
|
|
263
|
+
timeout?: number | undefined;
|
|
264
|
+
}
|
|
265
|
+
/** Session tokens established by the BFF flow. Persist via {@link sessionCookies}. */
|
|
266
|
+
interface ServerSessionTokens {
|
|
267
|
+
accessToken: string;
|
|
268
|
+
refreshToken: string;
|
|
269
|
+
/** Access-token expiry as epoch milliseconds. */
|
|
270
|
+
expiresAt: number;
|
|
271
|
+
}
|
|
272
|
+
/** Options for {@link ServerAuthClient.buildAuthorizeUrl}. */
|
|
273
|
+
interface BuildAuthorizeUrlOptions {
|
|
274
|
+
/**
|
|
275
|
+
* `'signup'` は `?screen_hint=signup` を転送する(OIDC `prompt=create`
|
|
276
|
+
* 相当の意図の FFID 独自パラメータ)。
|
|
277
|
+
*/
|
|
278
|
+
screenHint?: 'signup';
|
|
279
|
+
/** Forwards `?organization_id=…` (trimmed; empty is ignored). */
|
|
280
|
+
organizationId?: string;
|
|
281
|
+
/** Forwards OIDC `prompt` (`select_account` / `login`) to the upstream chooser (#4027). */
|
|
282
|
+
prompt?: 'login' | 'select_account';
|
|
283
|
+
}
|
|
284
|
+
/** Result of {@link ServerAuthClient.buildAuthorizeUrl}. */
|
|
285
|
+
interface BuildAuthorizeUrlResult {
|
|
286
|
+
/** Authorize URL to 302 the browser to. */
|
|
287
|
+
url: string;
|
|
288
|
+
/** Random `state` — persist in an httpOnly cookie and pass back as `storedState`. */
|
|
289
|
+
state: string;
|
|
290
|
+
}
|
|
291
|
+
/** Input to {@link ServerAuthClient.handleCallback}. */
|
|
292
|
+
interface HandleCallbackInput {
|
|
293
|
+
/** The callback request URL (absolute). `code` / `state` are read from its query. */
|
|
294
|
+
url: string | URL;
|
|
295
|
+
/** The `state` previously stored in the httpOnly cookie (undefined if absent). */
|
|
296
|
+
storedState: string | undefined;
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Discriminated result of a server token operation. Mirrors {@link FFIDApiResponse}:
|
|
300
|
+
* exactly one of `tokens` / `error` is present.
|
|
301
|
+
*/
|
|
302
|
+
type ServerTokenResult = {
|
|
303
|
+
tokens: ServerSessionTokens;
|
|
304
|
+
error?: undefined;
|
|
305
|
+
} | {
|
|
306
|
+
tokens?: undefined;
|
|
307
|
+
error: FFIDError;
|
|
308
|
+
};
|
|
309
|
+
/** Framework-agnostic confidential / BFF auth client. */
|
|
310
|
+
interface ServerAuthClient {
|
|
311
|
+
/**
|
|
312
|
+
* Build the authorize URL and a fresh `state`. The caller stores `state` in an
|
|
313
|
+
* httpOnly cookie, then 302s to `url`. No `code_challenge` is sent (v1 uses
|
|
314
|
+
* `client_secret`, not PKCE).
|
|
315
|
+
*/
|
|
316
|
+
buildAuthorizeUrl(opts?: BuildAuthorizeUrlOptions): BuildAuthorizeUrlResult;
|
|
317
|
+
/**
|
|
318
|
+
* Validate `state` (CSRF, fail-closed) then exchange `code` for tokens using
|
|
319
|
+
* the `client_secret` (server→server, browser never sees the secret or tokens).
|
|
320
|
+
*/
|
|
321
|
+
handleCallback(input: HandleCallbackInput): Promise<ServerTokenResult>;
|
|
322
|
+
/** Refresh tokens (`refresh_token` grant, `client_secret`-authenticated). */
|
|
323
|
+
refresh(refreshToken: string): Promise<ServerTokenResult>;
|
|
324
|
+
}
|
|
325
|
+
/** Attributes for the session `Set-Cookie` (name is fixed — see {@link FFID_SESSION_COOKIE_NAME}). */
|
|
326
|
+
interface CookieOptions {
|
|
327
|
+
/** `Path` attribute. Default `/`. */
|
|
328
|
+
path?: string;
|
|
329
|
+
/** `Domain` attribute. Omitted by default (host-only cookie). */
|
|
330
|
+
domain?: string;
|
|
331
|
+
/** `SameSite` attribute. Default `lax` (required for the cross-site callback redirect). */
|
|
332
|
+
sameSite?: 'lax' | 'strict' | 'none';
|
|
333
|
+
/**
|
|
334
|
+
* `Secure` attribute. Default `true`. ⚠️ `false` にすると cookie が平文 HTTP
|
|
335
|
+
* でも送信されるようになり、本番環境では非推奨(通常は変更しないこと)。
|
|
336
|
+
*/
|
|
337
|
+
secure?: boolean;
|
|
338
|
+
/**
|
|
339
|
+
* `HttpOnly` attribute. Default `true`. ⚠️ `false` にするとトークンが
|
|
340
|
+
* ページ JS(`document.cookie`)から読めるようになり、本モジュールの
|
|
341
|
+
* 機密性モデルが破れる — 通常は変更しないこと。
|
|
342
|
+
*/
|
|
343
|
+
httpOnly?: boolean;
|
|
344
|
+
/** `Max-Age` in seconds. Default 30 days. */
|
|
345
|
+
maxAge?: number;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/** Create a confidential / BFF auth client bound to one service's credentials. */
|
|
349
|
+
declare function createServerAuthClient(config: ServerAuthConfig): ServerAuthClient;
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* httpOnly session cookie codec for the confidential / BFF flow (#4261 / #4269).
|
|
353
|
+
*
|
|
354
|
+
* v1 stores the whole {@link ServerSessionTokens} in a single base64url-JSON
|
|
355
|
+
* cookie (`ffid_session`). httpOnly + Secure + SameSite=Lax by default so the
|
|
356
|
+
* browser can never read the tokens and the cookie survives the top-level
|
|
357
|
+
* callback redirect. The 4 KB per-cookie limit applies; access-token encryption
|
|
358
|
+
* and access/refresh split are documented future extensions (設計 spec
|
|
359
|
+
* (docs/02-design/2026-07-08-sdk-confidential-bff-design.md) §11).
|
|
360
|
+
*
|
|
361
|
+
* React/DOM-free: pure string I/O. The caller applies the returned `Set-Cookie`
|
|
362
|
+
* strings (e.g. `res.headers.append('Set-Cookie', c)`).
|
|
363
|
+
*/
|
|
364
|
+
|
|
365
|
+
/** Fixed session cookie name (read/serialize must agree — not configurable). */
|
|
366
|
+
declare const FFID_SESSION_COOKIE_NAME = "ffid_session";
|
|
367
|
+
declare function serialize(tokens: ServerSessionTokens, opts?: CookieOptions): string[];
|
|
368
|
+
declare function read(cookieHeader: string | undefined): ServerSessionTokens | null;
|
|
369
|
+
declare function clear(opts?: Omit<CookieOptions, 'maxAge'>): string[];
|
|
370
|
+
/** httpOnly session cookie serialize / read / clear (returns `Set-Cookie` values). */
|
|
371
|
+
declare const sessionCookies: {
|
|
372
|
+
serialize: typeof serialize;
|
|
373
|
+
read: typeof read;
|
|
374
|
+
clear: typeof clear;
|
|
375
|
+
};
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* Server-side OAuth `state` generation for the confidential / BFF flow (#4261).
|
|
379
|
+
*
|
|
380
|
+
* React/DOM-free: uses Web Crypto (`globalThis.crypto.getRandomValues`), which
|
|
381
|
+
* is available in Node 18+ and Edge runtimes. Unlike `src/auth/state.ts` (the
|
|
382
|
+
* browser flow), this never touches `window` / web storage — the caller persists
|
|
383
|
+
* the returned `state` in an httpOnly cookie instead.
|
|
384
|
+
*/
|
|
385
|
+
/**
|
|
386
|
+
* Generate a cryptographically-random, URL-safe OAuth `state` (32 hex chars).
|
|
387
|
+
* The caller stores this in an httpOnly `Secure` `SameSite=Lax` cookie and
|
|
388
|
+
* validates it on the callback (CSRF defense, RFC 6749 §10.12).
|
|
389
|
+
*/
|
|
390
|
+
declare function generateServerState(): string;
|
|
391
|
+
|
|
392
|
+
export { ALL_DENIED_EXCEPT_NECESSARY, type BuildAuthorizeUrlOptions, type BuildAuthorizeUrlResult, CONSENT_COOKIE_NAME, COOKIE_VERSION, type CookieOptions, FFIDCacheAdapter, type FFIDConsentCategories, type FFIDConsentCategoryCode, type FFIDCookieStoreLike, type FFIDErrorCode, FFIDOAuthUserInfo, FFIDVerifyAccessTokenOptions, FFID_ERROR_CODES, FFID_SESSION_COOKIE_NAME, type HandleCallbackInput, type KVNamespaceLike, type ServerAuthClient, type ServerAuthConfig, type ServerSessionTokens, type ServerTokenResult, createKVCacheAdapter, createMemoryCacheAdapter, createServerAuthClient, createVerifyAccessToken, decodeConsentCookie, encodeConsentCookie, generateServerState, getFFIDConsentFromCookieHeader, getFFIDConsentFromCookies, sessionCookies };
|