@feelflow/ffid-sdk 5.19.0 → 5.21.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.
@@ -16,8 +16,10 @@ var noopLogger = {
16
16
  },
17
17
  info: () => {
18
18
  },
19
- warn: (...args) => console.warn(SDK_LOG_PREFIX, ...args),
20
- error: (...args) => console.error(SDK_LOG_PREFIX, ...args)
19
+ warn: () => {
20
+ },
21
+ error: () => {
22
+ }
21
23
  };
22
24
  var consoleLogger = {
23
25
  debug: (...args) => console.debug(SDK_LOG_PREFIX, ...args),
@@ -15,8 +15,10 @@ var noopLogger = {
15
15
  },
16
16
  info: () => {
17
17
  },
18
- warn: (...args) => console.warn(SDK_LOG_PREFIX, ...args),
19
- error: (...args) => console.error(SDK_LOG_PREFIX, ...args)
18
+ warn: () => {
19
+ },
20
+ error: () => {
21
+ }
20
22
  };
21
23
  var consoleLogger = {
22
24
  debug: (...args) => console.debug(SDK_LOG_PREFIX, ...args),
@@ -20,7 +20,8 @@ var quietLogger = {
20
20
  },
21
21
  warn: () => {
22
22
  },
23
- error: (...args) => console.error(SDK_LOG_PREFIX, ...args)
23
+ error: () => {
24
+ }
24
25
  };
25
26
  var consoleLogger = {
26
27
  debug: (...args) => console.debug(SDK_LOG_PREFIX, ...args),
@@ -18,7 +18,8 @@ var quietLogger = {
18
18
  },
19
19
  warn: () => {
20
20
  },
21
- error: (...args) => console.error(SDK_LOG_PREFIX, ...args)
21
+ error: () => {
22
+ }
22
23
  };
23
24
  var consoleLogger = {
24
25
  debug: (...args) => console.debug(SDK_LOG_PREFIX, ...args),
@@ -266,10 +266,12 @@ var OAUTH_INTROSPECT_ENDPOINT = "/api/v1/oauth/introspect";
266
266
  var CACHE_KEY_PREFIX = "ffid:introspect:";
267
267
  var HEX_BASE = 16;
268
268
  var HEX_BYTE_WIDTH = 2;
269
- var TOKEN_LOG_PREFIX_LENGTH = 8;
270
269
  function createVerifyAccessToken(deps) {
271
270
  const { authMode, baseUrl, serviceCode, serviceApiKey, verifyStrategy, logger, createError, errorCodes, cache, timeout } = deps;
272
271
  let jwtVerify2 = null;
272
+ let cacheReadFailureEscalated = false;
273
+ let cacheWriteFailureEscalated = false;
274
+ let missingSubtleCryptoLogged = false;
273
275
  function getJwtVerifier() {
274
276
  if (!jwtVerify2) {
275
277
  jwtVerify2 = createJwtVerifier({
@@ -329,8 +331,9 @@ function createVerifyAccessToken(deps) {
329
331
  }
330
332
  const url = `${baseUrl}${OAUTH_INTROSPECT_ENDPOINT}`;
331
333
  logger.debug("Verifying access token:", url);
332
- const cacheKey = cache ? await buildCacheKey(accessToken, cacheKeySuffix) : "";
333
- if (cache) {
334
+ const cacheKey = cache ? await buildCacheKey(accessToken, cacheKeySuffix) : null;
335
+ let verificationCacheDegraded = cache !== void 0 && cacheKey === null;
336
+ if (cache && cacheKey !== null) {
334
337
  try {
335
338
  const cached = await cache.adapter.get(cacheKey);
336
339
  if (cached && typeof cached === "object" && "sub" in cached) {
@@ -338,7 +341,16 @@ function createVerifyAccessToken(deps) {
338
341
  return { data: cached };
339
342
  }
340
343
  } catch (cacheError) {
341
- logger.warn("Cache read failed, falling back to API call:", cacheError);
344
+ verificationCacheDegraded = true;
345
+ if (!cacheReadFailureEscalated) {
346
+ cacheReadFailureEscalated = true;
347
+ logger.error(
348
+ "Cache read failed \u2014 falling back to API call. Redis \u7B49\u306E cache adapter \u969C\u5BB3\u6642\u306F\u5168 verification \u304C introspect \u306B degrade \u3057\u307E\u3059\uFF08\u3053\u306E error \u306F instance \u3054\u3068\u306B\u521D\u56DE\u306E\u307F\u3001\u4EE5\u964D\u306F warn\uFF09:",
349
+ cacheError
350
+ );
351
+ } else {
352
+ logger.warn("Cache read failed, falling back to API call:", cacheError);
353
+ }
342
354
  }
343
355
  }
344
356
  const fetchOptions = {
@@ -433,31 +445,40 @@ function createVerifyAccessToken(deps) {
433
445
  }
434
446
  } : base;
435
447
  const userinfo = normalizeUserinfo(raw);
436
- if (cache) {
448
+ if (cache && cacheKey !== null) {
437
449
  try {
438
450
  await cache.adapter.set(cacheKey, userinfo, cache.ttl);
439
451
  } catch (cacheError) {
440
- logger.warn("Cache write failed (result still returned):", cacheError);
452
+ verificationCacheDegraded = true;
453
+ if (!cacheWriteFailureEscalated) {
454
+ cacheWriteFailureEscalated = true;
455
+ logger.error(
456
+ "Cache write failed \u2014 result still returned. cache adapter \u969C\u5BB3\u6642\u306F introspect \u7D50\u679C\u304C\u30AD\u30E3\u30C3\u30B7\u30E5\u3055\u308C\u305A\u5168 verification \u304C API \u547C\u3073\u51FA\u3057\u306B degrade \u3057\u307E\u3059\uFF08\u3053\u306E error \u306F instance \u3054\u3068\u306B\u521D\u56DE\u306E\u307F\u3001\u4EE5\u964D\u306F warn\uFF09:",
457
+ cacheError
458
+ );
459
+ } else {
460
+ logger.warn("Cache write failed (result still returned):", cacheError);
461
+ }
441
462
  }
442
463
  }
443
- return { data: userinfo };
464
+ return verificationCacheDegraded ? { data: userinfo, meta: { verificationCacheDegraded: true } } : { data: userinfo };
444
465
  }
445
466
  async function buildCacheKey(token, suffix) {
446
- let key;
447
- if (typeof globalThis.crypto?.subtle?.digest === "function") {
448
- const encoder = new TextEncoder();
449
- const data = encoder.encode(token);
450
- const hashBuffer = await crypto.subtle.digest("SHA-256", data);
451
- const hashArray = Array.from(new Uint8Array(hashBuffer));
452
- const hashHex = hashArray.map((b) => b.toString(HEX_BASE).padStart(HEX_BYTE_WIDTH, "0")).join("");
453
- key = CACHE_KEY_PREFIX + hashHex;
454
- } else {
455
- const tokenPrefix = token.length > TOKEN_LOG_PREFIX_LENGTH ? token.substring(0, TOKEN_LOG_PREFIX_LENGTH) + "..." : "***";
456
- logger.warn(
457
- `crypto.subtle \u304C\u5229\u7528\u3067\u304D\u306A\u3044\u305F\u3081\u3001\u30AD\u30E3\u30C3\u30B7\u30E5\u30AD\u30FC\u306B\u30CF\u30C3\u30B7\u30E5\u5316\u3055\u308C\u3066\u3044\u306A\u3044\u30C8\u30FC\u30AF\u30F3\u3092\u4F7F\u7528\u3057\u307E\u3059 (token prefix: ${tokenPrefix})`
458
- );
459
- key = CACHE_KEY_PREFIX + token;
460
- }
467
+ if (typeof globalThis.crypto?.subtle?.digest !== "function") {
468
+ if (!missingSubtleCryptoLogged) {
469
+ missingSubtleCryptoLogged = true;
470
+ logger.error(
471
+ "crypto.subtle \u304C\u5229\u7528\u3067\u304D\u306A\u3044\u305F\u3081 introspect \u7D50\u679C\u306E\u30AD\u30E3\u30C3\u30B7\u30E5\u3092\u7121\u52B9\u5316\u3057\u307E\u3057\u305F\uFF08\u30CF\u30C3\u30B7\u30E5\u5316\u3055\u308C\u3066\u3044\u306A\u3044\u30C8\u30FC\u30AF\u30F3\u3092 cache key \u306B\u4F7F\u308F\u306A\u3044\u305F\u3081\u306E\u63AA\u7F6E #4119\uFF09\u3002\u5168 verification \u304C API \u547C\u3073\u51FA\u3057\u306B\u306A\u308A\u307E\u3059"
472
+ );
473
+ }
474
+ return null;
475
+ }
476
+ const encoder = new TextEncoder();
477
+ const data = encoder.encode(token);
478
+ const hashBuffer = await crypto.subtle.digest("SHA-256", data);
479
+ const hashArray = Array.from(new Uint8Array(hashBuffer));
480
+ const hashHex = hashArray.map((b) => b.toString(HEX_BASE).padStart(HEX_BYTE_WIDTH, "0")).join("");
481
+ const key = CACHE_KEY_PREFIX + hashHex;
461
482
  return suffix ? `${key}:${suffix}` : key;
462
483
  }
463
484
  return verifyAccessToken;
@@ -1206,7 +1227,7 @@ function createNonContractMethods(deps) {
1206
1227
  }
1207
1228
 
1208
1229
  // src/client/version-check.ts
1209
- var SDK_VERSION = "5.19.0";
1230
+ var SDK_VERSION = "5.21.0";
1210
1231
  var SDK_USER_AGENT = `FFID-SDK/${SDK_VERSION} (TypeScript)`;
1211
1232
  var SDK_VERSION_HEADER = "X-FFID-SDK-Version";
1212
1233
  function sdkHeaders() {
@@ -1448,33 +1469,48 @@ function createOAuthTokenMethods(deps) {
1448
1469
  async function signOutToken() {
1449
1470
  const tokens = tokenStore.getTokens();
1450
1471
  tokenStore.clearTokens();
1472
+ const allRevoked = {
1473
+ revoked: true,
1474
+ accessTokenRevoked: true,
1475
+ refreshTokenRevoked: true
1476
+ };
1451
1477
  if (!tokens) {
1452
1478
  logger.debug("No tokens to revoke");
1453
- return { data: void 0 };
1479
+ return { data: allRevoked };
1454
1480
  }
1455
- const url = `${baseUrl}${OAUTH_REVOKE_ENDPOINT}`;
1456
- logger.debug("Revoking token:", url);
1457
- try {
1458
- const response = await fetch(url, {
1459
- method: "POST",
1460
- credentials: "omit",
1461
- headers: { "Content-Type": "application/x-www-form-urlencoded", ...sdkHeaders() },
1462
- body: new URLSearchParams({
1463
- token: tokens.accessToken,
1464
- client_id: clientId
1465
- }).toString()
1466
- });
1467
- if (!response.ok) {
1468
- logger.warn(
1469
- "\u30C8\u30FC\u30AF\u30F3\u7121\u52B9\u5316\u30EA\u30AF\u30A8\u30B9\u30C8\u304C\u30B5\u30FC\u30D0\u30FC\u3067\u5931\u6557\u3057\u307E\u3057\u305F:",
1470
- `status=${response.status}`
1471
- );
1481
+ async function revokeOAuthToken(token, tokenTypeHint) {
1482
+ const url = `${baseUrl}${OAUTH_REVOKE_ENDPOINT}`;
1483
+ try {
1484
+ const response = await fetch(url, {
1485
+ method: "POST",
1486
+ credentials: "omit",
1487
+ headers: { "Content-Type": "application/x-www-form-urlencoded", ...sdkHeaders() },
1488
+ body: new URLSearchParams({
1489
+ token,
1490
+ client_id: clientId,
1491
+ token_type_hint: tokenTypeHint
1492
+ }).toString()
1493
+ });
1494
+ return response.ok;
1495
+ } catch {
1496
+ return false;
1472
1497
  }
1473
- } catch (error) {
1474
- logger.warn("\u30C8\u30FC\u30AF\u30F3\u7121\u52B9\u5316\u306E\u30CD\u30C3\u30C8\u30EF\u30FC\u30AF\u30EA\u30AF\u30A8\u30B9\u30C8\u306B\u5931\u6557\u3057\u307E\u3057\u305F:", error);
1498
+ }
1499
+ logger.debug("Revoking tokens:", `${baseUrl}${OAUTH_REVOKE_ENDPOINT}`);
1500
+ const accessTokenRevoked = await revokeOAuthToken(tokens.accessToken, "access_token");
1501
+ if (!accessTokenRevoked) {
1502
+ logger.warn("\u30A2\u30AF\u30BB\u30B9\u30C8\u30FC\u30AF\u30F3\u306E\u7121\u52B9\u5316\u30EA\u30AF\u30A8\u30B9\u30C8\u304C\u30B5\u30FC\u30D0\u30FC\u3067\u5931\u6557\u3057\u307E\u3057\u305F");
1503
+ }
1504
+ const refreshTokenRevoked = tokens.refreshToken ? await revokeOAuthToken(tokens.refreshToken, "refresh_token") : true;
1505
+ if (tokens.refreshToken && !refreshTokenRevoked) {
1506
+ logger.warn("\u30EA\u30D5\u30EC\u30C3\u30B7\u30E5\u30C8\u30FC\u30AF\u30F3\u306E\u7121\u52B9\u5316\u30EA\u30AF\u30A8\u30B9\u30C8\u304C\u30B5\u30FC\u30D0\u30FC\u3067\u5931\u6557\u3057\u307E\u3057\u305F");
1507
+ }
1508
+ const revoked = accessTokenRevoked && refreshTokenRevoked;
1509
+ if (!revoked) {
1510
+ logger.warn("\u30C8\u30FC\u30AF\u30F3\u7121\u52B9\u5316\u304C\u4E00\u90E8\u307E\u305F\u306F\u5168\u90E8\u5931\u6557\u3057\u307E\u3057\u305F\uFF08\u30ED\u30FC\u30AB\u30EB\u30C8\u30FC\u30AF\u30F3\u306F\u30AF\u30EA\u30A2\u6E08\u307F\uFF09");
1475
1511
  }
1476
1512
  logger.debug("Token sign-out completed");
1477
- return { data: void 0 };
1513
+ return { data: { revoked, accessTokenRevoked, refreshTokenRevoked } };
1478
1514
  }
1479
1515
  return { exchangeCodeForTokens, refreshAccessToken, signOutToken };
1480
1516
  }
@@ -1638,7 +1674,7 @@ function createSessionMethods(deps) {
1638
1674
  }
1639
1675
  if (response.status === NO_CONTENT_STATUS) {
1640
1676
  logger.debug("Response: 204 No Content");
1641
- return { data: void 0 };
1677
+ return { data: { revoked: true } };
1642
1678
  }
1643
1679
  if (!response.ok) {
1644
1680
  try {
@@ -1665,7 +1701,7 @@ function createSessionMethods(deps) {
1665
1701
  }
1666
1702
  }
1667
1703
  logger.debug("Response:", response.status);
1668
- return { data: void 0 };
1704
+ return { data: { revoked: true } };
1669
1705
  }
1670
1706
  return { getSession, signOutCookie };
1671
1707
  }
@@ -1883,6 +1919,9 @@ var REDIRECT_LOOP_WINDOW_MS = 6e4;
1883
1919
  var REDIRECT_LOOP_THRESHOLD = 3;
1884
1920
  var storageReadFailureLogged = false;
1885
1921
  var storageWriteFailureLogged = false;
1922
+ function isLoopDetectionDegraded() {
1923
+ return storageReadFailureLogged || storageWriteFailureLogged;
1924
+ }
1886
1925
  function logStorageReadFailure(logger, err) {
1887
1926
  if (storageReadFailureLogged) return;
1888
1927
  storageReadFailureLogged = true;
@@ -2027,9 +2066,16 @@ function createRedirectMethods(deps) {
2027
2066
  }
2028
2067
  const state = generateRandomState();
2029
2068
  if (!storeState(state, logger)) {
2069
+ cleanupVerifierStorage(logger);
2070
+ cleanupStateStorage(logger);
2030
2071
  logger.error(
2031
- "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"
2072
+ "redirectToAuthorize: OAuth state \u3092\u4FDD\u5B58\u3067\u304D\u307E\u305B\u3093\u3067\u3057\u305F\uFF08storage \u5229\u7528\u4E0D\u53EF\uFF09\u3002\u8A8D\u53EF\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3092\u4E2D\u6B62\u3057\u307E\u3057\u305F"
2032
2073
  );
2074
+ return {
2075
+ success: false,
2076
+ code: "state_persistence_failed",
2077
+ error: "OAuth state \u3092\u4FDD\u5B58\u3067\u304D\u307E\u305B\u3093\u3067\u3057\u305F\uFF08storage \u5229\u7528\u4E0D\u53EF\uFF09\u3002\u8A8D\u53EF\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3092\u4E2D\u6B62\u3057\u307E\u3057\u305F"
2078
+ };
2033
2079
  }
2034
2080
  const redirectUri = resolvedRedirectUri ?? window.location.origin + window.location.pathname;
2035
2081
  const params = new URLSearchParams({
@@ -2064,7 +2110,7 @@ function createRedirectMethods(deps) {
2064
2110
  rollbackLastRedirectAttempt(authorizeKey, logger);
2065
2111
  throw err;
2066
2112
  }
2067
- return { success: true };
2113
+ return isLoopDetectionDegraded() ? { success: true, loopDetectionDisabled: true } : { success: true };
2068
2114
  }
2069
2115
  async function redirectToLogin(options) {
2070
2116
  if (typeof window === "undefined") {
@@ -4331,7 +4377,8 @@ var quietLogger = {
4331
4377
  },
4332
4378
  warn: () => {
4333
4379
  },
4334
- error: (...args) => console.error(SDK_LOG_PREFIX2, ...args)
4380
+ error: () => {
4381
+ }
4335
4382
  };
4336
4383
  var consoleLogger2 = {
4337
4384
  debug: (...args) => console.debug(SDK_LOG_PREFIX2, ...args),
@@ -264,10 +264,12 @@ var OAUTH_INTROSPECT_ENDPOINT = "/api/v1/oauth/introspect";
264
264
  var CACHE_KEY_PREFIX = "ffid:introspect:";
265
265
  var HEX_BASE = 16;
266
266
  var HEX_BYTE_WIDTH = 2;
267
- var TOKEN_LOG_PREFIX_LENGTH = 8;
268
267
  function createVerifyAccessToken(deps) {
269
268
  const { authMode, baseUrl, serviceCode, serviceApiKey, verifyStrategy, logger, createError, errorCodes, cache, timeout } = deps;
270
269
  let jwtVerify2 = null;
270
+ let cacheReadFailureEscalated = false;
271
+ let cacheWriteFailureEscalated = false;
272
+ let missingSubtleCryptoLogged = false;
271
273
  function getJwtVerifier() {
272
274
  if (!jwtVerify2) {
273
275
  jwtVerify2 = createJwtVerifier({
@@ -327,8 +329,9 @@ function createVerifyAccessToken(deps) {
327
329
  }
328
330
  const url = `${baseUrl}${OAUTH_INTROSPECT_ENDPOINT}`;
329
331
  logger.debug("Verifying access token:", url);
330
- const cacheKey = cache ? await buildCacheKey(accessToken, cacheKeySuffix) : "";
331
- if (cache) {
332
+ const cacheKey = cache ? await buildCacheKey(accessToken, cacheKeySuffix) : null;
333
+ let verificationCacheDegraded = cache !== void 0 && cacheKey === null;
334
+ if (cache && cacheKey !== null) {
332
335
  try {
333
336
  const cached = await cache.adapter.get(cacheKey);
334
337
  if (cached && typeof cached === "object" && "sub" in cached) {
@@ -336,7 +339,16 @@ function createVerifyAccessToken(deps) {
336
339
  return { data: cached };
337
340
  }
338
341
  } catch (cacheError) {
339
- logger.warn("Cache read failed, falling back to API call:", cacheError);
342
+ verificationCacheDegraded = true;
343
+ if (!cacheReadFailureEscalated) {
344
+ cacheReadFailureEscalated = true;
345
+ logger.error(
346
+ "Cache read failed \u2014 falling back to API call. Redis \u7B49\u306E cache adapter \u969C\u5BB3\u6642\u306F\u5168 verification \u304C introspect \u306B degrade \u3057\u307E\u3059\uFF08\u3053\u306E error \u306F instance \u3054\u3068\u306B\u521D\u56DE\u306E\u307F\u3001\u4EE5\u964D\u306F warn\uFF09:",
347
+ cacheError
348
+ );
349
+ } else {
350
+ logger.warn("Cache read failed, falling back to API call:", cacheError);
351
+ }
340
352
  }
341
353
  }
342
354
  const fetchOptions = {
@@ -431,31 +443,40 @@ function createVerifyAccessToken(deps) {
431
443
  }
432
444
  } : base;
433
445
  const userinfo = normalizeUserinfo(raw);
434
- if (cache) {
446
+ if (cache && cacheKey !== null) {
435
447
  try {
436
448
  await cache.adapter.set(cacheKey, userinfo, cache.ttl);
437
449
  } catch (cacheError) {
438
- logger.warn("Cache write failed (result still returned):", cacheError);
450
+ verificationCacheDegraded = true;
451
+ if (!cacheWriteFailureEscalated) {
452
+ cacheWriteFailureEscalated = true;
453
+ logger.error(
454
+ "Cache write failed \u2014 result still returned. cache adapter \u969C\u5BB3\u6642\u306F introspect \u7D50\u679C\u304C\u30AD\u30E3\u30C3\u30B7\u30E5\u3055\u308C\u305A\u5168 verification \u304C API \u547C\u3073\u51FA\u3057\u306B degrade \u3057\u307E\u3059\uFF08\u3053\u306E error \u306F instance \u3054\u3068\u306B\u521D\u56DE\u306E\u307F\u3001\u4EE5\u964D\u306F warn\uFF09:",
455
+ cacheError
456
+ );
457
+ } else {
458
+ logger.warn("Cache write failed (result still returned):", cacheError);
459
+ }
439
460
  }
440
461
  }
441
- return { data: userinfo };
462
+ return verificationCacheDegraded ? { data: userinfo, meta: { verificationCacheDegraded: true } } : { data: userinfo };
442
463
  }
443
464
  async function buildCacheKey(token, suffix) {
444
- let key;
445
- if (typeof globalThis.crypto?.subtle?.digest === "function") {
446
- const encoder = new TextEncoder();
447
- const data = encoder.encode(token);
448
- const hashBuffer = await crypto.subtle.digest("SHA-256", data);
449
- const hashArray = Array.from(new Uint8Array(hashBuffer));
450
- const hashHex = hashArray.map((b) => b.toString(HEX_BASE).padStart(HEX_BYTE_WIDTH, "0")).join("");
451
- key = CACHE_KEY_PREFIX + hashHex;
452
- } else {
453
- const tokenPrefix = token.length > TOKEN_LOG_PREFIX_LENGTH ? token.substring(0, TOKEN_LOG_PREFIX_LENGTH) + "..." : "***";
454
- logger.warn(
455
- `crypto.subtle \u304C\u5229\u7528\u3067\u304D\u306A\u3044\u305F\u3081\u3001\u30AD\u30E3\u30C3\u30B7\u30E5\u30AD\u30FC\u306B\u30CF\u30C3\u30B7\u30E5\u5316\u3055\u308C\u3066\u3044\u306A\u3044\u30C8\u30FC\u30AF\u30F3\u3092\u4F7F\u7528\u3057\u307E\u3059 (token prefix: ${tokenPrefix})`
456
- );
457
- key = CACHE_KEY_PREFIX + token;
458
- }
465
+ if (typeof globalThis.crypto?.subtle?.digest !== "function") {
466
+ if (!missingSubtleCryptoLogged) {
467
+ missingSubtleCryptoLogged = true;
468
+ logger.error(
469
+ "crypto.subtle \u304C\u5229\u7528\u3067\u304D\u306A\u3044\u305F\u3081 introspect \u7D50\u679C\u306E\u30AD\u30E3\u30C3\u30B7\u30E5\u3092\u7121\u52B9\u5316\u3057\u307E\u3057\u305F\uFF08\u30CF\u30C3\u30B7\u30E5\u5316\u3055\u308C\u3066\u3044\u306A\u3044\u30C8\u30FC\u30AF\u30F3\u3092 cache key \u306B\u4F7F\u308F\u306A\u3044\u305F\u3081\u306E\u63AA\u7F6E #4119\uFF09\u3002\u5168 verification \u304C API \u547C\u3073\u51FA\u3057\u306B\u306A\u308A\u307E\u3059"
470
+ );
471
+ }
472
+ return null;
473
+ }
474
+ const encoder = new TextEncoder();
475
+ const data = encoder.encode(token);
476
+ const hashBuffer = await crypto.subtle.digest("SHA-256", data);
477
+ const hashArray = Array.from(new Uint8Array(hashBuffer));
478
+ const hashHex = hashArray.map((b) => b.toString(HEX_BASE).padStart(HEX_BYTE_WIDTH, "0")).join("");
479
+ const key = CACHE_KEY_PREFIX + hashHex;
459
480
  return suffix ? `${key}:${suffix}` : key;
460
481
  }
461
482
  return verifyAccessToken;
@@ -1204,7 +1225,7 @@ function createNonContractMethods(deps) {
1204
1225
  }
1205
1226
 
1206
1227
  // src/client/version-check.ts
1207
- var SDK_VERSION = "5.19.0";
1228
+ var SDK_VERSION = "5.21.0";
1208
1229
  var SDK_USER_AGENT = `FFID-SDK/${SDK_VERSION} (TypeScript)`;
1209
1230
  var SDK_VERSION_HEADER = "X-FFID-SDK-Version";
1210
1231
  function sdkHeaders() {
@@ -1446,33 +1467,48 @@ function createOAuthTokenMethods(deps) {
1446
1467
  async function signOutToken() {
1447
1468
  const tokens = tokenStore.getTokens();
1448
1469
  tokenStore.clearTokens();
1470
+ const allRevoked = {
1471
+ revoked: true,
1472
+ accessTokenRevoked: true,
1473
+ refreshTokenRevoked: true
1474
+ };
1449
1475
  if (!tokens) {
1450
1476
  logger.debug("No tokens to revoke");
1451
- return { data: void 0 };
1477
+ return { data: allRevoked };
1452
1478
  }
1453
- const url = `${baseUrl}${OAUTH_REVOKE_ENDPOINT}`;
1454
- logger.debug("Revoking token:", url);
1455
- try {
1456
- const response = await fetch(url, {
1457
- method: "POST",
1458
- credentials: "omit",
1459
- headers: { "Content-Type": "application/x-www-form-urlencoded", ...sdkHeaders() },
1460
- body: new URLSearchParams({
1461
- token: tokens.accessToken,
1462
- client_id: clientId
1463
- }).toString()
1464
- });
1465
- if (!response.ok) {
1466
- logger.warn(
1467
- "\u30C8\u30FC\u30AF\u30F3\u7121\u52B9\u5316\u30EA\u30AF\u30A8\u30B9\u30C8\u304C\u30B5\u30FC\u30D0\u30FC\u3067\u5931\u6557\u3057\u307E\u3057\u305F:",
1468
- `status=${response.status}`
1469
- );
1479
+ async function revokeOAuthToken(token, tokenTypeHint) {
1480
+ const url = `${baseUrl}${OAUTH_REVOKE_ENDPOINT}`;
1481
+ try {
1482
+ const response = await fetch(url, {
1483
+ method: "POST",
1484
+ credentials: "omit",
1485
+ headers: { "Content-Type": "application/x-www-form-urlencoded", ...sdkHeaders() },
1486
+ body: new URLSearchParams({
1487
+ token,
1488
+ client_id: clientId,
1489
+ token_type_hint: tokenTypeHint
1490
+ }).toString()
1491
+ });
1492
+ return response.ok;
1493
+ } catch {
1494
+ return false;
1470
1495
  }
1471
- } catch (error) {
1472
- logger.warn("\u30C8\u30FC\u30AF\u30F3\u7121\u52B9\u5316\u306E\u30CD\u30C3\u30C8\u30EF\u30FC\u30AF\u30EA\u30AF\u30A8\u30B9\u30C8\u306B\u5931\u6557\u3057\u307E\u3057\u305F:", error);
1496
+ }
1497
+ logger.debug("Revoking tokens:", `${baseUrl}${OAUTH_REVOKE_ENDPOINT}`);
1498
+ const accessTokenRevoked = await revokeOAuthToken(tokens.accessToken, "access_token");
1499
+ if (!accessTokenRevoked) {
1500
+ logger.warn("\u30A2\u30AF\u30BB\u30B9\u30C8\u30FC\u30AF\u30F3\u306E\u7121\u52B9\u5316\u30EA\u30AF\u30A8\u30B9\u30C8\u304C\u30B5\u30FC\u30D0\u30FC\u3067\u5931\u6557\u3057\u307E\u3057\u305F");
1501
+ }
1502
+ const refreshTokenRevoked = tokens.refreshToken ? await revokeOAuthToken(tokens.refreshToken, "refresh_token") : true;
1503
+ if (tokens.refreshToken && !refreshTokenRevoked) {
1504
+ logger.warn("\u30EA\u30D5\u30EC\u30C3\u30B7\u30E5\u30C8\u30FC\u30AF\u30F3\u306E\u7121\u52B9\u5316\u30EA\u30AF\u30A8\u30B9\u30C8\u304C\u30B5\u30FC\u30D0\u30FC\u3067\u5931\u6557\u3057\u307E\u3057\u305F");
1505
+ }
1506
+ const revoked = accessTokenRevoked && refreshTokenRevoked;
1507
+ if (!revoked) {
1508
+ logger.warn("\u30C8\u30FC\u30AF\u30F3\u7121\u52B9\u5316\u304C\u4E00\u90E8\u307E\u305F\u306F\u5168\u90E8\u5931\u6557\u3057\u307E\u3057\u305F\uFF08\u30ED\u30FC\u30AB\u30EB\u30C8\u30FC\u30AF\u30F3\u306F\u30AF\u30EA\u30A2\u6E08\u307F\uFF09");
1473
1509
  }
1474
1510
  logger.debug("Token sign-out completed");
1475
- return { data: void 0 };
1511
+ return { data: { revoked, accessTokenRevoked, refreshTokenRevoked } };
1476
1512
  }
1477
1513
  return { exchangeCodeForTokens, refreshAccessToken, signOutToken };
1478
1514
  }
@@ -1636,7 +1672,7 @@ function createSessionMethods(deps) {
1636
1672
  }
1637
1673
  if (response.status === NO_CONTENT_STATUS) {
1638
1674
  logger.debug("Response: 204 No Content");
1639
- return { data: void 0 };
1675
+ return { data: { revoked: true } };
1640
1676
  }
1641
1677
  if (!response.ok) {
1642
1678
  try {
@@ -1663,7 +1699,7 @@ function createSessionMethods(deps) {
1663
1699
  }
1664
1700
  }
1665
1701
  logger.debug("Response:", response.status);
1666
- return { data: void 0 };
1702
+ return { data: { revoked: true } };
1667
1703
  }
1668
1704
  return { getSession, signOutCookie };
1669
1705
  }
@@ -1881,6 +1917,9 @@ var REDIRECT_LOOP_WINDOW_MS = 6e4;
1881
1917
  var REDIRECT_LOOP_THRESHOLD = 3;
1882
1918
  var storageReadFailureLogged = false;
1883
1919
  var storageWriteFailureLogged = false;
1920
+ function isLoopDetectionDegraded() {
1921
+ return storageReadFailureLogged || storageWriteFailureLogged;
1922
+ }
1884
1923
  function logStorageReadFailure(logger, err) {
1885
1924
  if (storageReadFailureLogged) return;
1886
1925
  storageReadFailureLogged = true;
@@ -2025,9 +2064,16 @@ function createRedirectMethods(deps) {
2025
2064
  }
2026
2065
  const state = generateRandomState();
2027
2066
  if (!storeState(state, logger)) {
2067
+ cleanupVerifierStorage(logger);
2068
+ cleanupStateStorage(logger);
2028
2069
  logger.error(
2029
- "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"
2070
+ "redirectToAuthorize: OAuth state \u3092\u4FDD\u5B58\u3067\u304D\u307E\u305B\u3093\u3067\u3057\u305F\uFF08storage \u5229\u7528\u4E0D\u53EF\uFF09\u3002\u8A8D\u53EF\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3092\u4E2D\u6B62\u3057\u307E\u3057\u305F"
2030
2071
  );
2072
+ return {
2073
+ success: false,
2074
+ code: "state_persistence_failed",
2075
+ error: "OAuth state \u3092\u4FDD\u5B58\u3067\u304D\u307E\u305B\u3093\u3067\u3057\u305F\uFF08storage \u5229\u7528\u4E0D\u53EF\uFF09\u3002\u8A8D\u53EF\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3092\u4E2D\u6B62\u3057\u307E\u3057\u305F"
2076
+ };
2031
2077
  }
2032
2078
  const redirectUri = resolvedRedirectUri ?? window.location.origin + window.location.pathname;
2033
2079
  const params = new URLSearchParams({
@@ -2062,7 +2108,7 @@ function createRedirectMethods(deps) {
2062
2108
  rollbackLastRedirectAttempt(authorizeKey, logger);
2063
2109
  throw err;
2064
2110
  }
2065
- return { success: true };
2111
+ return isLoopDetectionDegraded() ? { success: true, loopDetectionDisabled: true } : { success: true };
2066
2112
  }
2067
2113
  async function redirectToLogin(options) {
2068
2114
  if (typeof window === "undefined") {
@@ -4329,7 +4375,8 @@ var quietLogger = {
4329
4375
  },
4330
4376
  warn: () => {
4331
4377
  },
4332
- error: (...args) => console.error(SDK_LOG_PREFIX2, ...args)
4378
+ error: () => {
4379
+ }
4333
4380
  };
4334
4381
  var consoleLogger2 = {
4335
4382
  debug: (...args) => console.debug(SDK_LOG_PREFIX2, ...args),
@@ -1,34 +1,34 @@
1
1
  'use strict';
2
2
 
3
- var chunkTW2FXASO_cjs = require('../chunk-TW2FXASO.cjs');
3
+ var chunkRDQ4R3XC_cjs = require('../chunk-RDQ4R3XC.cjs');
4
4
 
5
5
 
6
6
 
7
7
  Object.defineProperty(exports, "FFIDAnnouncementBadge", {
8
8
  enumerable: true,
9
- get: function () { return chunkTW2FXASO_cjs.FFIDAnnouncementBadge; }
9
+ get: function () { return chunkRDQ4R3XC_cjs.FFIDAnnouncementBadge; }
10
10
  });
11
11
  Object.defineProperty(exports, "FFIDAnnouncementList", {
12
12
  enumerable: true,
13
- get: function () { return chunkTW2FXASO_cjs.FFIDAnnouncementList; }
13
+ get: function () { return chunkRDQ4R3XC_cjs.FFIDAnnouncementList; }
14
14
  });
15
15
  Object.defineProperty(exports, "FFIDInquiryForm", {
16
16
  enumerable: true,
17
- get: function () { return chunkTW2FXASO_cjs.FFIDInquiryForm; }
17
+ get: function () { return chunkRDQ4R3XC_cjs.FFIDInquiryForm; }
18
18
  });
19
19
  Object.defineProperty(exports, "FFIDLoginButton", {
20
20
  enumerable: true,
21
- get: function () { return chunkTW2FXASO_cjs.FFIDLoginButton; }
21
+ get: function () { return chunkRDQ4R3XC_cjs.FFIDLoginButton; }
22
22
  });
23
23
  Object.defineProperty(exports, "FFIDOrganizationSwitcher", {
24
24
  enumerable: true,
25
- get: function () { return chunkTW2FXASO_cjs.FFIDOrganizationSwitcher; }
25
+ get: function () { return chunkRDQ4R3XC_cjs.FFIDOrganizationSwitcher; }
26
26
  });
27
27
  Object.defineProperty(exports, "FFIDSubscriptionBadge", {
28
28
  enumerable: true,
29
- get: function () { return chunkTW2FXASO_cjs.FFIDSubscriptionBadge; }
29
+ get: function () { return chunkRDQ4R3XC_cjs.FFIDSubscriptionBadge; }
30
30
  });
31
31
  Object.defineProperty(exports, "FFIDUserMenu", {
32
32
  enumerable: true,
33
- get: function () { return chunkTW2FXASO_cjs.FFIDUserMenu; }
33
+ get: function () { return chunkRDQ4R3XC_cjs.FFIDUserMenu; }
34
34
  });
@@ -1,3 +1,3 @@
1
- export { G as FFIDAnnouncementBadge, ag as FFIDAnnouncementBadgeClassNames, ah as FFIDAnnouncementBadgeProps, H as FFIDAnnouncementList, ai as FFIDAnnouncementListClassNames, aj as FFIDAnnouncementListProps, Q as FFIDInquiryForm, R as FFIDInquiryFormCategoryItem, S as FFIDInquiryFormClassNames, T as FFIDInquiryFormLegalLayout, U as FFIDInquiryFormOrganization, V as FFIDInquiryFormPlaceholderContext, W as FFIDInquiryFormPrefill, X as FFIDInquiryFormProps, Y as FFIDInquiryFormSubmitData, Z as FFIDInquiryFormSubmitResult, $ as FFIDLoginButton, ak as FFIDLoginButtonProps, a2 as FFIDOrganizationSwitcher, al as FFIDOrganizationSwitcherClassNames, am as FFIDOrganizationSwitcherProps, a7 as FFIDSubscriptionBadge, an as FFIDSubscriptionBadgeClassNames, ao as FFIDSubscriptionBadgeProps, a9 as FFIDUserMenu, ap as FFIDUserMenuClassNames, aq as FFIDUserMenuProps } from '../index-Bkul-fRw.cjs';
1
+ export { H as FFIDAnnouncementBadge, ai as FFIDAnnouncementBadgeClassNames, aj as FFIDAnnouncementBadgeProps, I 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--wRiPlpi.cjs';
2
2
  import 'react/jsx-runtime';
3
3
  import 'react';
@@ -1,3 +1,3 @@
1
- export { G as FFIDAnnouncementBadge, ag as FFIDAnnouncementBadgeClassNames, ah as FFIDAnnouncementBadgeProps, H as FFIDAnnouncementList, ai as FFIDAnnouncementListClassNames, aj as FFIDAnnouncementListProps, Q as FFIDInquiryForm, R as FFIDInquiryFormCategoryItem, S as FFIDInquiryFormClassNames, T as FFIDInquiryFormLegalLayout, U as FFIDInquiryFormOrganization, V as FFIDInquiryFormPlaceholderContext, W as FFIDInquiryFormPrefill, X as FFIDInquiryFormProps, Y as FFIDInquiryFormSubmitData, Z as FFIDInquiryFormSubmitResult, $ as FFIDLoginButton, ak as FFIDLoginButtonProps, a2 as FFIDOrganizationSwitcher, al as FFIDOrganizationSwitcherClassNames, am as FFIDOrganizationSwitcherProps, a7 as FFIDSubscriptionBadge, an as FFIDSubscriptionBadgeClassNames, ao as FFIDSubscriptionBadgeProps, a9 as FFIDUserMenu, ap as FFIDUserMenuClassNames, aq as FFIDUserMenuProps } from '../index-Bkul-fRw.js';
1
+ export { H as FFIDAnnouncementBadge, ai as FFIDAnnouncementBadgeClassNames, aj as FFIDAnnouncementBadgeProps, I 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--wRiPlpi.js';
2
2
  import 'react/jsx-runtime';
3
3
  import 'react';
@@ -1 +1 @@
1
- export { FFIDAnnouncementBadge, FFIDAnnouncementList, FFIDInquiryForm, FFIDLoginButton, FFIDOrganizationSwitcher, FFIDSubscriptionBadge, FFIDUserMenu } from '../chunk-D3PZ6SZB.js';
1
+ export { FFIDAnnouncementBadge, FFIDAnnouncementList, FFIDInquiryForm, FFIDLoginButton, FFIDOrganizationSwitcher, FFIDSubscriptionBadge, FFIDUserMenu } from '../chunk-U57ZDFEN.js';