@feelflow/ffid-sdk 1.10.0 → 1.11.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.
@@ -5,6 +5,7 @@ var jose = require('jose');
5
5
 
6
6
  // src/auth/token-store.ts
7
7
  var STORAGE_KEY = "ffid_tokens";
8
+ var TOKEN_STORE_LOG_PREFIX = "[FFID SDK] TokenStore:";
8
9
  var EXPIRY_BUFFER_SECONDS = 30;
9
10
  var EXPIRY_BUFFER_MS = EXPIRY_BUFFER_SECONDS * 1e3;
10
11
  function isLocalStorageAvailable() {
@@ -28,22 +29,33 @@ function createLocalStorageStore() {
28
29
  const raw = storage.getItem(STORAGE_KEY);
29
30
  if (!raw) return null;
30
31
  const parsed = JSON.parse(raw);
31
- if (!isTokenData(parsed)) return null;
32
+ if (!isTokenData(parsed)) {
33
+ console.warn(TOKEN_STORE_LOG_PREFIX, "\u30C8\u30FC\u30AF\u30F3\u30C7\u30FC\u30BF\u304C\u4E0D\u6B63\u306A\u5F62\u5F0F\u3067\u3059\u3002\u30B9\u30C8\u30EC\u30FC\u30B8\u3092\u30AF\u30EA\u30A2\u3057\u307E\u3059");
34
+ storage.removeItem(STORAGE_KEY);
35
+ return null;
36
+ }
32
37
  return parsed;
33
- } catch {
38
+ } catch (error) {
39
+ console.warn(TOKEN_STORE_LOG_PREFIX, "\u30C8\u30FC\u30AF\u30F3\u8AAD\u307F\u53D6\u308A\u306B\u5931\u6557\u3057\u307E\u3057\u305F\u3002\u7834\u640D\u30C7\u30FC\u30BF\u3092\u30AF\u30EA\u30A2\u3057\u307E\u3059", error);
40
+ try {
41
+ storage.removeItem(STORAGE_KEY);
42
+ } catch {
43
+ }
34
44
  return null;
35
45
  }
36
46
  },
37
47
  setTokens(tokens) {
38
48
  try {
39
49
  storage.setItem(STORAGE_KEY, JSON.stringify(tokens));
40
- } catch {
50
+ } catch (error) {
51
+ console.warn(TOKEN_STORE_LOG_PREFIX, "\u30C8\u30FC\u30AF\u30F3\u4FDD\u5B58\u306B\u5931\u6557\u3057\u307E\u3057\u305F\uFF08\u30B9\u30C8\u30EC\u30FC\u30B8\u5BB9\u91CF\u8D85\u904E\u306E\u53EF\u80FD\u6027\uFF09", error);
41
52
  }
42
53
  },
43
54
  clearTokens() {
44
55
  try {
45
56
  storage.removeItem(STORAGE_KEY);
46
- } catch {
57
+ } catch (error) {
58
+ console.warn(TOKEN_STORE_LOG_PREFIX, "\u30C8\u30FC\u30AF\u30F3\u524A\u9664\u306B\u5931\u6557\u3057\u307E\u3057\u305F", error);
47
59
  }
48
60
  },
49
61
  isAccessTokenExpired() {
@@ -86,42 +98,6 @@ function createTokenStore(storageType) {
86
98
  return createMemoryStore();
87
99
  }
88
100
 
89
- // src/auth/pkce.ts
90
- var VERIFIER_STORAGE_KEY = "ffid_code_verifier";
91
- var CODE_VERIFIER_MIN_LENGTH = 43;
92
- var UNRESERVED_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~";
93
- function generateCodeVerifier() {
94
- const length = CODE_VERIFIER_MIN_LENGTH;
95
- const randomValues = new Uint8Array(length);
96
- crypto.getRandomValues(randomValues);
97
- let verifier = "";
98
- for (let i = 0; i < length; i++) {
99
- verifier += UNRESERVED_CHARS[randomValues[i] % UNRESERVED_CHARS.length];
100
- }
101
- return verifier;
102
- }
103
- async function generateCodeChallenge(verifier) {
104
- const encoder = new TextEncoder();
105
- const data = encoder.encode(verifier);
106
- const digest = await crypto.subtle.digest("SHA-256", data);
107
- return base64UrlEncode(digest);
108
- }
109
- function storeCodeVerifier(verifier) {
110
- try {
111
- if (typeof window === "undefined") return;
112
- window.sessionStorage.setItem(VERIFIER_STORAGE_KEY, verifier);
113
- } catch {
114
- }
115
- }
116
- function base64UrlEncode(buffer) {
117
- const bytes = new Uint8Array(buffer);
118
- let binary = "";
119
- for (let i = 0; i < bytes.length; i++) {
120
- binary += String.fromCharCode(bytes[i]);
121
- }
122
- return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
123
- }
124
-
125
101
  // src/client/oauth-userinfo.ts
126
102
  var VALID_SUBSCRIPTION_STATUSES = ["trialing", "active", "past_due", "canceled", "paused"];
127
103
  function isValidSubscriptionStatus(value) {
@@ -225,6 +201,7 @@ var OAUTH_INTROSPECT_ENDPOINT = "/api/v1/oauth/introspect";
225
201
  var CACHE_KEY_PREFIX = "ffid:introspect:";
226
202
  var HEX_BASE = 16;
227
203
  var HEX_BYTE_WIDTH = 2;
204
+ var TOKEN_LOG_PREFIX_LENGTH = 8;
228
205
  function createVerifyAccessToken(deps) {
229
206
  const { authMode, baseUrl, serviceCode, serviceApiKey, verifyStrategy, logger, createError, errorCodes, cache, timeout } = deps;
230
207
  let jwtVerify2 = null;
@@ -240,7 +217,7 @@ function createVerifyAccessToken(deps) {
240
217
  }
241
218
  return jwtVerify2;
242
219
  }
243
- async function verifyAccessToken(accessToken) {
220
+ async function verifyAccessToken(accessToken, options) {
244
221
  if (authMode !== "service-key") {
245
222
  return {
246
223
  error: createError(
@@ -257,12 +234,24 @@ function createVerifyAccessToken(deps) {
257
234
  )
258
235
  };
259
236
  }
237
+ if (options?.includeProfile && verifyStrategy === "jwt" && !serviceApiKey) {
238
+ return {
239
+ error: createError(
240
+ errorCodes.TOKEN_VERIFICATION_ERROR,
241
+ "includeProfile: true \u3092\u4F7F\u7528\u3059\u308B\u306B\u306F serviceApiKey \u306E\u8A2D\u5B9A\u304C\u5FC5\u8981\u3067\u3059"
242
+ )
243
+ };
244
+ }
260
245
  if (verifyStrategy === "jwt") {
261
- return getJwtVerifier()(accessToken);
246
+ const jwtResult = await getJwtVerifier()(accessToken);
247
+ if (!options?.includeProfile || jwtResult.error) {
248
+ return jwtResult;
249
+ }
250
+ return verifyViaIntrospect(accessToken, "profile");
262
251
  }
263
252
  return verifyViaIntrospect(accessToken);
264
253
  }
265
- async function verifyViaIntrospect(accessToken) {
254
+ async function verifyViaIntrospect(accessToken, cacheKeySuffix) {
266
255
  if (!serviceApiKey) {
267
256
  return {
268
257
  error: createError(
@@ -273,7 +262,7 @@ function createVerifyAccessToken(deps) {
273
262
  }
274
263
  const url = `${baseUrl}${OAUTH_INTROSPECT_ENDPOINT}`;
275
264
  logger.debug("Verifying access token:", url);
276
- const cacheKey = await buildCacheKey(accessToken);
265
+ const cacheKey = cache ? await buildCacheKey(accessToken, cacheKeySuffix) : "";
277
266
  if (cache) {
278
267
  try {
279
268
  const cached = await cache.adapter.get(cacheKey);
@@ -375,16 +364,23 @@ function createVerifyAccessToken(deps) {
375
364
  }
376
365
  return { data: userinfo };
377
366
  }
378
- async function buildCacheKey(token) {
367
+ async function buildCacheKey(token, suffix) {
368
+ let key;
379
369
  if (typeof globalThis.crypto?.subtle?.digest === "function") {
380
370
  const encoder = new TextEncoder();
381
371
  const data = encoder.encode(token);
382
372
  const hashBuffer = await crypto.subtle.digest("SHA-256", data);
383
373
  const hashArray = Array.from(new Uint8Array(hashBuffer));
384
374
  const hashHex = hashArray.map((b) => b.toString(HEX_BASE).padStart(HEX_BYTE_WIDTH, "0")).join("");
385
- return CACHE_KEY_PREFIX + hashHex;
375
+ key = CACHE_KEY_PREFIX + hashHex;
376
+ } else {
377
+ const tokenPrefix = token.length > TOKEN_LOG_PREFIX_LENGTH ? token.substring(0, TOKEN_LOG_PREFIX_LENGTH) + "..." : "***";
378
+ logger.warn(
379
+ `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})`
380
+ );
381
+ key = CACHE_KEY_PREFIX + token;
386
382
  }
387
- return CACHE_KEY_PREFIX + token;
383
+ return suffix ? `${key}:${suffix}` : key;
388
384
  }
389
385
  return verifyAccessToken;
390
386
  }
@@ -437,9 +433,15 @@ function createBillingMethods(deps) {
437
433
  }
438
434
 
439
435
  // src/client/version-check.ts
440
- var SDK_VERSION = "1.10.0";
436
+ var SDK_VERSION = "1.11.0";
441
437
  var SDK_USER_AGENT = `FFID-SDK/${SDK_VERSION} (TypeScript)`;
442
438
  var SDK_VERSION_HEADER = "X-FFID-SDK-Version";
439
+ function sdkHeaders() {
440
+ return {
441
+ "User-Agent": SDK_USER_AGENT,
442
+ [SDK_VERSION_HEADER]: SDK_VERSION
443
+ };
444
+ }
443
445
  var LATEST_VERSION_HEADER = "X-FFID-SDK-Latest-Version";
444
446
  var SEMVER_PATTERN = /^\d+(\.\d+)*$/;
445
447
  var _versionWarningShown = false;
@@ -473,178 +475,213 @@ npm install @feelflow/ffid-sdk@latest \u3067\u30A2\u30C3\u30D7\u30C7\u30FC\u30C8
473
475
  }
474
476
  }
475
477
 
476
- // src/client/ffid-client.ts
477
- var NO_CONTENT_STATUS = 204;
478
- var SESSION_ENDPOINT = "/api/v1/auth/session";
479
- var LOGOUT_ENDPOINT = "/api/v1/auth/signout";
478
+ // src/client/oauth-token.ts
480
479
  var OAUTH_TOKEN_ENDPOINT = "/api/v1/oauth/token";
481
- var OAUTH_USERINFO_ENDPOINT = "/api/v1/oauth/userinfo";
482
- var OAUTH_AUTHORIZE_ENDPOINT = "/api/v1/oauth/authorize";
483
480
  var OAUTH_REVOKE_ENDPOINT = "/api/v1/oauth/revoke";
484
- var EXT_CHECK_ENDPOINT = "/api/v1/subscriptions/ext/check";
485
- var SDK_LOG_PREFIX = "[FFID SDK]";
486
481
  var MS_PER_SECOND = 1e3;
487
- var UNAUTHORIZED_STATUS = 401;
488
- var STATE_RANDOM_BYTES = 16;
489
- var HEX_BASE2 = 16;
490
- var noopLogger = {
491
- debug: () => {
492
- },
493
- info: () => {
494
- },
495
- warn: () => {
496
- },
497
- error: (...args) => console.error(SDK_LOG_PREFIX, ...args)
498
- };
499
- var consoleLogger = {
500
- debug: (...args) => console.debug(SDK_LOG_PREFIX, ...args),
501
- info: (...args) => console.info(SDK_LOG_PREFIX, ...args),
502
- warn: (...args) => console.warn(SDK_LOG_PREFIX, ...args),
503
- error: (...args) => console.error(SDK_LOG_PREFIX, ...args)
504
- };
505
- var FFID_ERROR_CODES = {
506
- NETWORK_ERROR: "NETWORK_ERROR",
507
- PARSE_ERROR: "PARSE_ERROR",
508
- UNKNOWN_ERROR: "UNKNOWN_ERROR",
509
- TOKEN_EXCHANGE_ERROR: "TOKEN_EXCHANGE_ERROR",
510
- TOKEN_REFRESH_ERROR: "TOKEN_REFRESH_ERROR",
511
- NO_TOKENS: "NO_TOKENS",
512
- TOKEN_VERIFICATION_ERROR: "TOKEN_VERIFICATION_ERROR"
513
- };
514
- function createFFIDClient(config) {
515
- if (!config.serviceCode || !config.serviceCode.trim()) {
516
- throw new Error("FFID Client: serviceCode \u304C\u672A\u8A2D\u5B9A\u3067\u3059");
517
- }
518
- const baseUrl = config.apiBaseUrl ?? chunkYUIITYBE_cjs.DEFAULT_API_BASE_URL;
519
- const authMode = config.authMode ?? "cookie";
520
- const clientId = config.clientId ?? config.serviceCode;
521
- const resolvedRedirectUri = config.redirectUri ?? null;
522
- const serviceApiKey = config.serviceApiKey?.trim();
523
- const verifyStrategy = config.verifyStrategy ?? "jwt";
524
- const cache = config.cache;
525
- const timeout = config.timeout;
526
- if (authMode === "service-key" && !serviceApiKey) {
527
- throw new Error("FFID Client: service-key \u30E2\u30FC\u30C9\u3067\u306F serviceApiKey \u304C\u5FC5\u9808\u3067\u3059");
528
- }
529
- if (cache && cache.ttl <= 0) {
530
- throw new Error("FFID Client: cache.ttl \u306F\u6B63\u306E\u6570\u5024\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044");
531
- }
532
- if (timeout !== void 0 && timeout <= 0) {
533
- throw new Error("FFID Client: timeout \u306F\u6B63\u306E\u6570\u5024\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044");
534
- }
535
- const logger = config.logger ?? (config.debug ? consoleLogger : noopLogger);
536
- const tokenStore = authMode === "token" ? createTokenStore() : createTokenStore("memory");
537
- async function fetchWithAuth(endpoint, options = {}) {
538
- const url = `${baseUrl}${endpoint}`;
539
- logger.debug("Fetching:", url);
540
- const fetchOptions = buildFetchOptions(options);
482
+ function createOAuthTokenMethods(deps) {
483
+ const {
484
+ baseUrl,
485
+ clientId,
486
+ resolvedRedirectUri,
487
+ tokenStore,
488
+ logger,
489
+ errorCodes
490
+ } = deps;
491
+ async function exchangeCodeForTokens(code, codeVerifier) {
492
+ const url = `${baseUrl}${OAUTH_TOKEN_ENDPOINT}`;
493
+ logger.debug("Exchanging code for tokens:", url);
494
+ const effectiveRedirectUri = resolvedRedirectUri ?? (typeof window !== "undefined" ? window.location.origin + window.location.pathname : null);
495
+ if (!effectiveRedirectUri) {
496
+ logger.error("redirectUri is required for token exchange in SSR environments. Set config.redirectUri explicitly.");
497
+ return {
498
+ error: {
499
+ code: errorCodes.TOKEN_EXCHANGE_ERROR,
500
+ message: "redirectUri \u304C\u672A\u8A2D\u5B9A\u3067\u3059\u3002SSR\u74B0\u5883\u3067\u306F config.redirectUri \u3092\u660E\u793A\u7684\u306B\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044"
501
+ }
502
+ };
503
+ }
504
+ const body = {
505
+ grant_type: "authorization_code",
506
+ code,
507
+ client_id: clientId,
508
+ redirect_uri: effectiveRedirectUri
509
+ };
510
+ if (codeVerifier) {
511
+ body.code_verifier = codeVerifier;
512
+ }
541
513
  let response;
542
514
  try {
543
- response = await fetch(url, fetchOptions);
515
+ response = await fetch(url, {
516
+ method: "POST",
517
+ credentials: "omit",
518
+ headers: { "Content-Type": "application/x-www-form-urlencoded", ...sdkHeaders() },
519
+ body: new URLSearchParams(body).toString()
520
+ });
544
521
  } catch (error) {
545
- logger.error("Network error:", error);
522
+ logger.error("Network error during token exchange:", error);
546
523
  return {
547
524
  error: {
548
- code: FFID_ERROR_CODES.NETWORK_ERROR,
525
+ code: errorCodes.NETWORK_ERROR,
549
526
  message: error instanceof Error ? error.message : "\u30CD\u30C3\u30C8\u30EF\u30FC\u30AF\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F"
550
527
  }
551
528
  };
552
529
  }
553
- if (authMode === "token" && response.status === UNAUTHORIZED_STATUS) {
554
- const refreshResult = await refreshAccessToken();
555
- if (!refreshResult.error) {
556
- logger.debug("Token refreshed, retrying request");
557
- const retryOptions = buildFetchOptions(options);
558
- try {
559
- response = await fetch(url, retryOptions);
560
- } catch (retryError) {
561
- logger.error("Network error on retry:", retryError);
562
- return {
563
- error: {
564
- code: FFID_ERROR_CODES.NETWORK_ERROR,
565
- message: retryError instanceof Error ? retryError.message : "\u30CD\u30C3\u30C8\u30EF\u30FC\u30AF\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F"
566
- }
567
- };
568
- }
569
- }
570
- }
571
- let raw;
530
+ let tokenResponse;
572
531
  try {
573
- raw = await response.json();
532
+ tokenResponse = await response.json();
574
533
  } catch (parseError) {
575
- logger.error("Parse error:", parseError, "Status:", response.status);
534
+ logger.error("Parse error during token exchange:", parseError);
576
535
  return {
577
536
  error: {
578
- code: FFID_ERROR_CODES.PARSE_ERROR,
579
- message: `\u30B5\u30FC\u30D0\u30FC\u304B\u3089\u4E0D\u6B63\u306A\u30EC\u30B9\u30DD\u30F3\u30B9\u3092\u53D7\u4FE1\u3057\u307E\u3057\u305F (status: ${response.status})`
537
+ code: errorCodes.PARSE_ERROR,
538
+ message: `\u30C8\u30FC\u30AF\u30F3\u30EC\u30B9\u30DD\u30F3\u30B9\u306E\u89E3\u6790\u306B\u5931\u6557\u3057\u307E\u3057\u305F (status: ${response.status})`
580
539
  }
581
540
  };
582
541
  }
583
- logger.debug("Response:", response.status, raw);
584
- checkVersionHeader(response, logger);
585
542
  if (!response.ok) {
543
+ const errorBody = tokenResponse;
586
544
  return {
587
- error: raw.error ?? {
588
- code: FFID_ERROR_CODES.UNKNOWN_ERROR,
589
- message: "\u4E0D\u660E\u306A\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F"
545
+ error: {
546
+ code: errorBody.error ?? errorCodes.TOKEN_EXCHANGE_ERROR,
547
+ message: errorBody.error_description ?? "\u30C8\u30FC\u30AF\u30F3\u4EA4\u63DB\u306B\u5931\u6557\u3057\u307E\u3057\u305F"
590
548
  }
591
549
  };
592
550
  }
593
- if (raw.data === void 0) {
551
+ tokenStore.setTokens({
552
+ accessToken: tokenResponse.access_token,
553
+ refreshToken: tokenResponse.refresh_token,
554
+ expiresAt: Date.now() + tokenResponse.expires_in * MS_PER_SECOND
555
+ });
556
+ logger.debug("Token exchange successful");
557
+ return { data: void 0 };
558
+ }
559
+ async function refreshAccessToken() {
560
+ const tokens = tokenStore.getTokens();
561
+ if (!tokens) {
594
562
  return {
595
563
  error: {
596
- code: FFID_ERROR_CODES.UNKNOWN_ERROR,
597
- message: "\u30B5\u30FC\u30D0\u30FC\u304B\u3089\u30C7\u30FC\u30BF\u304C\u8FD4\u3055\u308C\u307E\u305B\u3093\u3067\u3057\u305F"
564
+ code: errorCodes.NO_TOKENS,
565
+ message: "\u30EA\u30D5\u30EC\u30C3\u30B7\u30E5\u30C8\u30FC\u30AF\u30F3\u304C\u3042\u308A\u307E\u305B\u3093"
598
566
  }
599
567
  };
600
568
  }
601
- return { data: raw.data };
602
- }
603
- function sdkHeaders() {
604
- return {
605
- "User-Agent": SDK_USER_AGENT,
606
- [SDK_VERSION_HEADER]: SDK_VERSION
607
- };
608
- }
609
- function buildFetchOptions(options) {
610
- if (authMode === "service-key") {
611
- return {
612
- ...options,
569
+ const url = `${baseUrl}${OAUTH_TOKEN_ENDPOINT}`;
570
+ logger.debug("Refreshing access token:", url);
571
+ let response;
572
+ try {
573
+ response = await fetch(url, {
574
+ method: "POST",
613
575
  credentials: "omit",
614
- headers: {
615
- "Content-Type": "application/json",
616
- ...sdkHeaders(),
617
- "X-Service-Api-Key": serviceApiKey,
618
- ...options.headers
576
+ headers: { "Content-Type": "application/x-www-form-urlencoded", ...sdkHeaders() },
577
+ body: new URLSearchParams({
578
+ grant_type: "refresh_token",
579
+ refresh_token: tokens.refreshToken,
580
+ client_id: clientId
581
+ }).toString()
582
+ });
583
+ } catch (error) {
584
+ logger.error("Network error during token refresh:", error);
585
+ return {
586
+ error: {
587
+ code: errorCodes.NETWORK_ERROR,
588
+ message: error instanceof Error ? error.message : "\u30CD\u30C3\u30C8\u30EF\u30FC\u30AF\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F"
619
589
  }
620
590
  };
621
591
  }
622
- if (authMode === "token") {
623
- const tokens = tokenStore.getTokens();
624
- const headers = {
625
- "Content-Type": "application/json",
626
- ...sdkHeaders(),
627
- ...options.headers
628
- };
629
- if (tokens) {
630
- headers["Authorization"] = `Bearer ${tokens.accessToken}`;
631
- }
592
+ let tokenResponse;
593
+ try {
594
+ tokenResponse = await response.json();
595
+ } catch (parseError) {
596
+ logger.error("Parse error during token refresh:", parseError);
632
597
  return {
633
- ...options,
634
- credentials: "omit",
635
- headers
598
+ error: {
599
+ code: errorCodes.PARSE_ERROR,
600
+ message: `\u30C8\u30FC\u30AF\u30F3\u30EC\u30B9\u30DD\u30F3\u30B9\u306E\u89E3\u6790\u306B\u5931\u6557\u3057\u307E\u3057\u305F (status: ${response.status})`
601
+ }
636
602
  };
637
603
  }
638
- return {
639
- ...options,
640
- credentials: "include",
641
- headers: {
642
- "Content-Type": "application/json",
643
- ...sdkHeaders(),
644
- ...options.headers
604
+ if (!response.ok) {
605
+ const errorBody = tokenResponse;
606
+ logger.error("Token refresh failed:", errorBody);
607
+ const irrecoverableErrors = ["token_revoked", "invalid_grant"];
608
+ if (errorBody.error && irrecoverableErrors.includes(errorBody.error)) {
609
+ tokenStore.clearTokens();
610
+ logger.debug("Cleared tokens due to irrecoverable refresh error:", errorBody.error);
645
611
  }
646
- };
612
+ return {
613
+ error: {
614
+ code: errorBody.error ?? errorCodes.TOKEN_REFRESH_ERROR,
615
+ message: errorBody.error_description ?? "\u30C8\u30FC\u30AF\u30F3\u30EA\u30D5\u30EC\u30C3\u30B7\u30E5\u306B\u5931\u6557\u3057\u307E\u3057\u305F"
616
+ }
617
+ };
618
+ }
619
+ tokenStore.setTokens({
620
+ accessToken: tokenResponse.access_token,
621
+ refreshToken: tokenResponse.refresh_token,
622
+ expiresAt: Date.now() + tokenResponse.expires_in * MS_PER_SECOND
623
+ });
624
+ logger.debug("Token refresh successful");
625
+ return { data: void 0 };
626
+ }
627
+ async function signOutToken() {
628
+ const tokens = tokenStore.getTokens();
629
+ tokenStore.clearTokens();
630
+ if (!tokens) {
631
+ logger.debug("No tokens to revoke");
632
+ return { data: void 0 };
633
+ }
634
+ const url = `${baseUrl}${OAUTH_REVOKE_ENDPOINT}`;
635
+ logger.debug("Revoking token:", url);
636
+ try {
637
+ const response = await fetch(url, {
638
+ method: "POST",
639
+ credentials: "omit",
640
+ headers: { "Content-Type": "application/x-www-form-urlencoded", ...sdkHeaders() },
641
+ body: new URLSearchParams({
642
+ token: tokens.accessToken,
643
+ client_id: clientId
644
+ }).toString()
645
+ });
646
+ if (!response.ok) {
647
+ logger.warn(
648
+ "\u30C8\u30FC\u30AF\u30F3\u7121\u52B9\u5316\u30EA\u30AF\u30A8\u30B9\u30C8\u304C\u30B5\u30FC\u30D0\u30FC\u3067\u5931\u6557\u3057\u307E\u3057\u305F:",
649
+ `status=${response.status}`
650
+ );
651
+ }
652
+ } catch (error) {
653
+ 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);
654
+ }
655
+ logger.debug("Token sign-out completed");
656
+ return { data: void 0 };
647
657
  }
658
+ return { exchangeCodeForTokens, refreshAccessToken, signOutToken };
659
+ }
660
+
661
+ // src/client/session.ts
662
+ var SESSION_ENDPOINT = "/api/v1/auth/session";
663
+ var LOGOUT_ENDPOINT = "/api/v1/auth/signout";
664
+ var OAUTH_USERINFO_ENDPOINT = "/api/v1/oauth/userinfo";
665
+ var NO_CONTENT_STATUS = 204;
666
+ var UNAUTHORIZED_STATUS = 401;
667
+ function normalizeFFIDUser(user) {
668
+ return {
669
+ ...user,
670
+ locale: user.locale ?? null,
671
+ timezone: user.timezone ?? null
672
+ };
673
+ }
674
+ function createSessionMethods(deps) {
675
+ const {
676
+ authMode,
677
+ baseUrl,
678
+ serviceCode,
679
+ tokenStore,
680
+ logger,
681
+ fetchWithAuth,
682
+ refreshAccessToken,
683
+ errorCodes
684
+ } = deps;
648
685
  async function getSession() {
649
686
  if (authMode === "token") {
650
687
  return getSessionFromUserinfo();
@@ -661,19 +698,12 @@ function createFFIDClient(config) {
661
698
  }
662
699
  return result;
663
700
  }
664
- function normalizeFFIDUser(user) {
665
- return {
666
- ...user,
667
- locale: user.locale ?? null,
668
- timezone: user.timezone ?? null
669
- };
670
- }
671
701
  async function getSessionFromUserinfo() {
672
702
  const tokens = tokenStore.getTokens();
673
703
  if (!tokens) {
674
704
  return {
675
705
  error: {
676
- code: FFID_ERROR_CODES.NO_TOKENS,
706
+ code: errorCodes.NO_TOKENS,
677
707
  message: "\u30C8\u30FC\u30AF\u30F3\u304C\u4FDD\u5B58\u3055\u308C\u3066\u3044\u307E\u305B\u3093"
678
708
  }
679
709
  };
@@ -694,7 +724,7 @@ function createFFIDClient(config) {
694
724
  logger.error("Network error:", error);
695
725
  return {
696
726
  error: {
697
- code: FFID_ERROR_CODES.NETWORK_ERROR,
727
+ code: errorCodes.NETWORK_ERROR,
698
728
  message: error instanceof Error ? error.message : "\u30CD\u30C3\u30C8\u30EF\u30FC\u30AF\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F"
699
729
  }
700
730
  };
@@ -717,7 +747,7 @@ function createFFIDClient(config) {
717
747
  logger.error("Network error on retry:", retryError);
718
748
  return {
719
749
  error: {
720
- code: FFID_ERROR_CODES.NETWORK_ERROR,
750
+ code: errorCodes.NETWORK_ERROR,
721
751
  message: retryError instanceof Error ? retryError.message : "\u30CD\u30C3\u30C8\u30EF\u30FC\u30AF\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F"
722
752
  }
723
753
  };
@@ -734,7 +764,7 @@ function createFFIDClient(config) {
734
764
  logger.error("Parse error:", parseError, "Status:", response.status);
735
765
  return {
736
766
  error: {
737
- code: FFID_ERROR_CODES.PARSE_ERROR,
767
+ code: errorCodes.PARSE_ERROR,
738
768
  message: `\u30B5\u30FC\u30D0\u30FC\u304B\u3089\u4E0D\u6B63\u306A\u30EC\u30B9\u30DD\u30F3\u30B9\u3092\u53D7\u4FE1\u3057\u307E\u3057\u305F (status: ${response.status})`
739
769
  }
740
770
  };
@@ -743,7 +773,7 @@ function createFFIDClient(config) {
743
773
  const errorBody = rawUserinfo;
744
774
  return {
745
775
  error: {
746
- code: errorBody.code ?? FFID_ERROR_CODES.UNKNOWN_ERROR,
776
+ code: errorBody.code ?? errorCodes.UNKNOWN_ERROR,
747
777
  message: errorBody.message ?? "\u4E0D\u660E\u306A\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F"
748
778
  }
749
779
  };
@@ -762,16 +792,10 @@ function createFFIDClient(config) {
762
792
  data: {
763
793
  user,
764
794
  organizations: [],
765
- subscriptions: mapUserinfoSubscriptionToSession(userinfo, config.serviceCode)
795
+ subscriptions: mapUserinfoSubscriptionToSession(userinfo, serviceCode)
766
796
  }
767
797
  };
768
798
  }
769
- async function signOut() {
770
- if (authMode === "token") {
771
- return signOutToken();
772
- }
773
- return signOutCookie();
774
- }
775
799
  async function signOutCookie() {
776
800
  const url = `${baseUrl}${LOGOUT_ENDPOINT}`;
777
801
  logger.debug("Fetching:", url);
@@ -786,7 +810,7 @@ function createFFIDClient(config) {
786
810
  logger.error("Network error:", error);
787
811
  return {
788
812
  error: {
789
- code: FFID_ERROR_CODES.NETWORK_ERROR,
813
+ code: errorCodes.NETWORK_ERROR,
790
814
  message: error instanceof Error ? error.message : "\u30CD\u30C3\u30C8\u30EF\u30FC\u30AF\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F"
791
815
  }
792
816
  };
@@ -800,14 +824,20 @@ function createFFIDClient(config) {
800
824
  const raw = await response.json();
801
825
  return {
802
826
  error: raw.error ?? {
803
- code: FFID_ERROR_CODES.UNKNOWN_ERROR,
827
+ code: errorCodes.UNKNOWN_ERROR,
804
828
  message: "\u4E0D\u660E\u306A\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F"
805
829
  }
806
830
  };
807
- } catch {
831
+ } catch (parseError) {
832
+ logger.error(
833
+ "\u30B5\u30A4\u30F3\u30A2\u30A6\u30C8\u30EC\u30B9\u30DD\u30F3\u30B9\u306E\u89E3\u6790\u306B\u5931\u6557\u3057\u307E\u3057\u305F:",
834
+ parseError,
835
+ "Status:",
836
+ response.status
837
+ );
808
838
  return {
809
839
  error: {
810
- code: FFID_ERROR_CODES.PARSE_ERROR,
840
+ code: errorCodes.PARSE_ERROR,
811
841
  message: `\u30B5\u30FC\u30D0\u30FC\u304B\u3089\u4E0D\u6B63\u306A\u30EC\u30B9\u30DD\u30F3\u30B9\u3092\u53D7\u4FE1\u3057\u307E\u3057\u305F (status: ${response.status})`
812
842
  }
813
843
  };
@@ -816,125 +846,221 @@ function createFFIDClient(config) {
816
846
  logger.debug("Response:", response.status);
817
847
  return { data: void 0 };
818
848
  }
819
- async function signOutToken() {
820
- const tokens = tokenStore.getTokens();
821
- tokenStore.clearTokens();
822
- if (!tokens) {
823
- logger.debug("No tokens to revoke");
824
- return { data: void 0 };
825
- }
826
- const url = `${baseUrl}${OAUTH_REVOKE_ENDPOINT}`;
827
- logger.debug("Revoking token:", url);
849
+ return { getSession, signOutCookie };
850
+ }
851
+
852
+ // src/auth/pkce.ts
853
+ var VERIFIER_STORAGE_KEY = "ffid_code_verifier";
854
+ var CODE_VERIFIER_MIN_LENGTH = 43;
855
+ var UNRESERVED_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~";
856
+ function generateCodeVerifier() {
857
+ const length = CODE_VERIFIER_MIN_LENGTH;
858
+ const randomValues = new Uint8Array(length);
859
+ crypto.getRandomValues(randomValues);
860
+ let verifier = "";
861
+ for (let i = 0; i < length; i++) {
862
+ verifier += UNRESERVED_CHARS[randomValues[i] % UNRESERVED_CHARS.length];
863
+ }
864
+ return verifier;
865
+ }
866
+ async function generateCodeChallenge(verifier) {
867
+ const encoder = new TextEncoder();
868
+ const data = encoder.encode(verifier);
869
+ const digest = await crypto.subtle.digest("SHA-256", data);
870
+ return base64UrlEncode(digest);
871
+ }
872
+ function storeCodeVerifier(verifier) {
873
+ try {
874
+ if (typeof window === "undefined") return;
875
+ window.sessionStorage.setItem(VERIFIER_STORAGE_KEY, verifier);
876
+ } catch {
877
+ }
878
+ }
879
+ function base64UrlEncode(buffer) {
880
+ const bytes = new Uint8Array(buffer);
881
+ let binary = "";
882
+ for (let i = 0; i < bytes.length; i++) {
883
+ binary += String.fromCharCode(bytes[i]);
884
+ }
885
+ return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
886
+ }
887
+
888
+ // src/client/redirect.ts
889
+ var OAUTH_AUTHORIZE_ENDPOINT = "/api/v1/oauth/authorize";
890
+ var STATE_RANDOM_BYTES = 16;
891
+ var HEX_BASE2 = 16;
892
+ function generateRandomState() {
893
+ const array = new Uint8Array(STATE_RANDOM_BYTES);
894
+ crypto.getRandomValues(array);
895
+ return Array.from(array, (byte) => byte.toString(HEX_BASE2).padStart(2, "0")).join("");
896
+ }
897
+ function createRedirectMethods(deps) {
898
+ const {
899
+ authMode,
900
+ baseUrl,
901
+ clientId,
902
+ serviceCode,
903
+ resolvedRedirectUri,
904
+ logger
905
+ } = deps;
906
+ async function redirectToAuthorize() {
907
+ const verifier = generateCodeVerifier();
908
+ storeCodeVerifier(verifier);
828
909
  try {
829
- await fetch(url, {
830
- method: "POST",
831
- credentials: "omit",
832
- headers: { "Content-Type": "application/x-www-form-urlencoded", ...sdkHeaders() },
833
- body: new URLSearchParams({
834
- token: tokens.accessToken,
835
- client_id: clientId
836
- }).toString()
910
+ const challenge = await generateCodeChallenge(verifier);
911
+ const state = generateRandomState();
912
+ const redirectUri = resolvedRedirectUri ?? window.location.origin + window.location.pathname;
913
+ const params = new URLSearchParams({
914
+ response_type: "code",
915
+ client_id: clientId,
916
+ redirect_uri: redirectUri,
917
+ state,
918
+ code_challenge: challenge,
919
+ code_challenge_method: "S256"
837
920
  });
921
+ const authorizeUrl = `${baseUrl}${OAUTH_AUTHORIZE_ENDPOINT}?${params.toString()}`;
922
+ logger.debug("Redirecting to authorize:", authorizeUrl);
923
+ window.location.href = authorizeUrl;
924
+ return true;
838
925
  } catch (error) {
839
- logger.warn("Token revocation failed:", error);
926
+ logger.error("PKCE \u30B3\u30FC\u30C9\u30C1\u30E3\u30EC\u30F3\u30B8\u306E\u751F\u6210\u306B\u5931\u6557\u3057\u307E\u3057\u305F:", error);
927
+ return false;
840
928
  }
841
- logger.debug("Token sign-out completed");
842
- return { data: void 0 };
843
929
  }
844
- async function exchangeCodeForTokens(code, codeVerifier) {
845
- const url = `${baseUrl}${OAUTH_TOKEN_ENDPOINT}`;
846
- logger.debug("Exchanging code for tokens:", url);
847
- const effectiveRedirectUri = resolvedRedirectUri ?? (typeof window !== "undefined" ? window.location.origin + window.location.pathname : null);
848
- if (!effectiveRedirectUri) {
849
- logger.error("redirectUri is required for token exchange in SSR environments. Set config.redirectUri explicitly.");
850
- return {
851
- error: {
852
- code: FFID_ERROR_CODES.TOKEN_EXCHANGE_ERROR,
853
- message: "redirectUri \u304C\u672A\u8A2D\u5B9A\u3067\u3059\u3002SSR\u74B0\u5883\u3067\u306F config.redirectUri \u3092\u660E\u793A\u7684\u306B\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044"
854
- }
855
- };
930
+ async function redirectToLogin() {
931
+ if (typeof window === "undefined") {
932
+ logger.debug("Cannot redirect in SSR context");
933
+ return false;
856
934
  }
857
- const body = {
858
- grant_type: "authorization_code",
859
- code,
860
- client_id: clientId,
861
- redirect_uri: effectiveRedirectUri
862
- };
863
- if (codeVerifier) {
864
- body.code_verifier = codeVerifier;
935
+ if (authMode === "token") {
936
+ return redirectToAuthorize();
865
937
  }
866
- let response;
867
- try {
868
- response = await fetch(url, {
869
- method: "POST",
870
- credentials: "omit",
871
- headers: { "Content-Type": "application/x-www-form-urlencoded", ...sdkHeaders() },
872
- body: new URLSearchParams(body).toString()
873
- });
874
- } catch (error) {
875
- logger.error("Network error during token exchange:", error);
938
+ const currentUrl = window.location.href;
939
+ const loginUrl = `${baseUrl}/login?redirect=${encodeURIComponent(currentUrl)}&service=${encodeURIComponent(serviceCode)}`;
940
+ logger.debug("Redirecting to login:", loginUrl);
941
+ window.location.href = loginUrl;
942
+ return true;
943
+ }
944
+ function getLoginUrl(redirectUrl) {
945
+ const redirect = redirectUrl ?? (typeof window !== "undefined" ? window.location.href : "");
946
+ return `${baseUrl}/login?redirect=${encodeURIComponent(redirect)}&service=${encodeURIComponent(serviceCode)}`;
947
+ }
948
+ function getSignupUrl(redirectUrl) {
949
+ const redirect = redirectUrl ?? (typeof window !== "undefined" ? window.location.href : "");
950
+ return `${baseUrl}/signup?redirect=${encodeURIComponent(redirect)}&service=${encodeURIComponent(serviceCode)}`;
951
+ }
952
+ return { redirectToLogin, redirectToAuthorize, getLoginUrl, getSignupUrl };
953
+ }
954
+
955
+ // src/client/ffid-client.ts
956
+ var UNAUTHORIZED_STATUS2 = 401;
957
+ var SDK_LOG_PREFIX = "[FFID SDK]";
958
+ var noopLogger = {
959
+ debug: () => {
960
+ },
961
+ info: () => {
962
+ },
963
+ warn: (...args) => console.warn(SDK_LOG_PREFIX, ...args),
964
+ error: (...args) => console.error(SDK_LOG_PREFIX, ...args)
965
+ };
966
+ var consoleLogger = {
967
+ debug: (...args) => console.debug(SDK_LOG_PREFIX, ...args),
968
+ info: (...args) => console.info(SDK_LOG_PREFIX, ...args),
969
+ warn: (...args) => console.warn(SDK_LOG_PREFIX, ...args),
970
+ error: (...args) => console.error(SDK_LOG_PREFIX, ...args)
971
+ };
972
+ var FFID_ERROR_CODES = {
973
+ NETWORK_ERROR: "NETWORK_ERROR",
974
+ PARSE_ERROR: "PARSE_ERROR",
975
+ UNKNOWN_ERROR: "UNKNOWN_ERROR",
976
+ TOKEN_EXCHANGE_ERROR: "TOKEN_EXCHANGE_ERROR",
977
+ TOKEN_REFRESH_ERROR: "TOKEN_REFRESH_ERROR",
978
+ NO_TOKENS: "NO_TOKENS",
979
+ TOKEN_VERIFICATION_ERROR: "TOKEN_VERIFICATION_ERROR"
980
+ };
981
+ var EXT_CHECK_ENDPOINT = "/api/v1/subscriptions/ext/check";
982
+ function createFFIDClient(config) {
983
+ if (!config.serviceCode || !config.serviceCode.trim()) {
984
+ throw new Error("FFID Client: serviceCode \u304C\u672A\u8A2D\u5B9A\u3067\u3059");
985
+ }
986
+ const baseUrl = config.apiBaseUrl ?? chunkYUIITYBE_cjs.DEFAULT_API_BASE_URL;
987
+ const authMode = config.authMode ?? "cookie";
988
+ const clientId = config.clientId ?? config.serviceCode;
989
+ const resolvedRedirectUri = config.redirectUri ?? null;
990
+ const serviceApiKey = config.serviceApiKey?.trim();
991
+ const verifyStrategy = config.verifyStrategy ?? "jwt";
992
+ const cache = config.cache;
993
+ const timeout = config.timeout;
994
+ if (authMode === "service-key" && !serviceApiKey) {
995
+ throw new Error("FFID Client: service-key \u30E2\u30FC\u30C9\u3067\u306F serviceApiKey \u304C\u5FC5\u9808\u3067\u3059");
996
+ }
997
+ if (cache && cache.ttl <= 0) {
998
+ throw new Error("FFID Client: cache.ttl \u306F\u6B63\u306E\u6570\u5024\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044");
999
+ }
1000
+ if (timeout !== void 0 && timeout <= 0) {
1001
+ throw new Error("FFID Client: timeout \u306F\u6B63\u306E\u6570\u5024\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044");
1002
+ }
1003
+ const logger = config.logger ?? (config.debug ? consoleLogger : noopLogger);
1004
+ const tokenStore = authMode === "token" ? createTokenStore() : createTokenStore("memory");
1005
+ function createError(code, message) {
1006
+ return { code, message };
1007
+ }
1008
+ function buildFetchOptions(options) {
1009
+ if (authMode === "service-key") {
876
1010
  return {
877
- error: {
878
- code: FFID_ERROR_CODES.NETWORK_ERROR,
879
- message: error instanceof Error ? error.message : "\u30CD\u30C3\u30C8\u30EF\u30FC\u30AF\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F"
1011
+ ...options,
1012
+ credentials: "omit",
1013
+ headers: {
1014
+ "Content-Type": "application/json",
1015
+ ...sdkHeaders(),
1016
+ "X-Service-Api-Key": serviceApiKey,
1017
+ ...options.headers
880
1018
  }
881
1019
  };
882
1020
  }
883
- let tokenResponse;
884
- try {
885
- tokenResponse = await response.json();
886
- } catch (parseError) {
887
- logger.error("Parse error during token exchange:", parseError);
888
- return {
889
- error: {
890
- code: FFID_ERROR_CODES.PARSE_ERROR,
891
- message: `\u30C8\u30FC\u30AF\u30F3\u30EC\u30B9\u30DD\u30F3\u30B9\u306E\u89E3\u6790\u306B\u5931\u6557\u3057\u307E\u3057\u305F (status: ${response.status})`
892
- }
1021
+ if (authMode === "token") {
1022
+ const tokens = tokenStore.getTokens();
1023
+ const headers = {
1024
+ "Content-Type": "application/json",
1025
+ ...sdkHeaders(),
1026
+ ...options.headers
893
1027
  };
894
- }
895
- if (!response.ok) {
896
- const errorBody = tokenResponse;
1028
+ if (tokens) {
1029
+ headers["Authorization"] = `Bearer ${tokens.accessToken}`;
1030
+ }
897
1031
  return {
898
- error: {
899
- code: errorBody.error ?? FFID_ERROR_CODES.TOKEN_EXCHANGE_ERROR,
900
- message: errorBody.error_description ?? "\u30C8\u30FC\u30AF\u30F3\u4EA4\u63DB\u306B\u5931\u6557\u3057\u307E\u3057\u305F"
901
- }
1032
+ ...options,
1033
+ credentials: "omit",
1034
+ headers
902
1035
  };
903
1036
  }
904
- tokenStore.setTokens({
905
- accessToken: tokenResponse.access_token,
906
- refreshToken: tokenResponse.refresh_token,
907
- expiresAt: Date.now() + tokenResponse.expires_in * MS_PER_SECOND
908
- });
909
- logger.debug("Token exchange successful");
910
- return { data: void 0 };
1037
+ return {
1038
+ ...options,
1039
+ credentials: "include",
1040
+ headers: {
1041
+ "Content-Type": "application/json",
1042
+ ...sdkHeaders(),
1043
+ ...options.headers
1044
+ }
1045
+ };
911
1046
  }
912
- async function refreshAccessToken() {
913
- const tokens = tokenStore.getTokens();
914
- if (!tokens) {
915
- return {
916
- error: {
917
- code: FFID_ERROR_CODES.NO_TOKENS,
918
- message: "\u30EA\u30D5\u30EC\u30C3\u30B7\u30E5\u30C8\u30FC\u30AF\u30F3\u304C\u3042\u308A\u307E\u305B\u3093"
919
- }
920
- };
921
- }
922
- const url = `${baseUrl}${OAUTH_TOKEN_ENDPOINT}`;
923
- logger.debug("Refreshing access token:", url);
1047
+ const { exchangeCodeForTokens, refreshAccessToken, signOutToken } = createOAuthTokenMethods({
1048
+ baseUrl,
1049
+ clientId,
1050
+ resolvedRedirectUri,
1051
+ tokenStore,
1052
+ logger,
1053
+ errorCodes: FFID_ERROR_CODES
1054
+ });
1055
+ async function fetchWithAuth(endpoint, options = {}) {
1056
+ const url = `${baseUrl}${endpoint}`;
1057
+ logger.debug("Fetching:", url);
1058
+ const fetchOptions = buildFetchOptions(options);
924
1059
  let response;
925
1060
  try {
926
- response = await fetch(url, {
927
- method: "POST",
928
- credentials: "omit",
929
- headers: { "Content-Type": "application/x-www-form-urlencoded", ...sdkHeaders() },
930
- body: new URLSearchParams({
931
- grant_type: "refresh_token",
932
- refresh_token: tokens.refreshToken,
933
- client_id: clientId
934
- }).toString()
935
- });
1061
+ response = await fetch(url, fetchOptions);
936
1062
  } catch (error) {
937
- logger.error("Network error during token refresh:", error);
1063
+ logger.error("Network error:", error);
938
1064
  return {
939
1065
  error: {
940
1066
  code: FFID_ERROR_CODES.NETWORK_ERROR,
@@ -942,88 +1068,80 @@ function createFFIDClient(config) {
942
1068
  }
943
1069
  };
944
1070
  }
945
- let tokenResponse;
1071
+ if (authMode === "token" && response.status === UNAUTHORIZED_STATUS2) {
1072
+ const refreshResult = await refreshAccessToken();
1073
+ if (!refreshResult.error) {
1074
+ logger.debug("Token refreshed, retrying request");
1075
+ const retryOptions = buildFetchOptions(options);
1076
+ try {
1077
+ response = await fetch(url, retryOptions);
1078
+ } catch (retryError) {
1079
+ logger.error("Network error on retry:", retryError);
1080
+ return {
1081
+ error: {
1082
+ code: FFID_ERROR_CODES.NETWORK_ERROR,
1083
+ message: retryError instanceof Error ? retryError.message : "\u30CD\u30C3\u30C8\u30EF\u30FC\u30AF\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F"
1084
+ }
1085
+ };
1086
+ }
1087
+ }
1088
+ }
1089
+ let raw;
946
1090
  try {
947
- tokenResponse = await response.json();
1091
+ raw = await response.json();
948
1092
  } catch (parseError) {
949
- logger.error("Parse error during token refresh:", parseError);
1093
+ logger.error("Parse error:", parseError, "Status:", response.status);
950
1094
  return {
951
1095
  error: {
952
1096
  code: FFID_ERROR_CODES.PARSE_ERROR,
953
- message: `\u30C8\u30FC\u30AF\u30F3\u30EC\u30B9\u30DD\u30F3\u30B9\u306E\u89E3\u6790\u306B\u5931\u6557\u3057\u307E\u3057\u305F (status: ${response.status})`
1097
+ message: `\u30B5\u30FC\u30D0\u30FC\u304B\u3089\u4E0D\u6B63\u306A\u30EC\u30B9\u30DD\u30F3\u30B9\u3092\u53D7\u4FE1\u3057\u307E\u3057\u305F (status: ${response.status})`
954
1098
  }
955
1099
  };
956
1100
  }
1101
+ logger.debug("Response:", response.status, raw);
1102
+ checkVersionHeader(response, logger);
957
1103
  if (!response.ok) {
958
- const errorBody = tokenResponse;
959
- logger.error("Token refresh failed:", errorBody);
960
- const irrecoverableErrors = ["token_revoked", "invalid_grant"];
961
- if (errorBody.error && irrecoverableErrors.includes(errorBody.error)) {
962
- tokenStore.clearTokens();
963
- logger.debug("Cleared tokens due to irrecoverable refresh error:", errorBody.error);
964
- }
1104
+ return {
1105
+ error: raw.error ?? {
1106
+ code: FFID_ERROR_CODES.UNKNOWN_ERROR,
1107
+ message: "\u4E0D\u660E\u306A\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F"
1108
+ }
1109
+ };
1110
+ }
1111
+ if (raw.data === void 0) {
965
1112
  return {
966
1113
  error: {
967
- code: errorBody.error ?? FFID_ERROR_CODES.TOKEN_REFRESH_ERROR,
968
- message: errorBody.error_description ?? "\u30C8\u30FC\u30AF\u30F3\u30EA\u30D5\u30EC\u30C3\u30B7\u30E5\u306B\u5931\u6557\u3057\u307E\u3057\u305F"
1114
+ code: FFID_ERROR_CODES.UNKNOWN_ERROR,
1115
+ message: "\u30B5\u30FC\u30D0\u30FC\u304B\u3089\u30C7\u30FC\u30BF\u304C\u8FD4\u3055\u308C\u307E\u305B\u3093\u3067\u3057\u305F"
969
1116
  }
970
1117
  };
971
1118
  }
972
- tokenStore.setTokens({
973
- accessToken: tokenResponse.access_token,
974
- refreshToken: tokenResponse.refresh_token,
975
- expiresAt: Date.now() + tokenResponse.expires_in * MS_PER_SECOND
976
- });
977
- logger.debug("Token refresh successful");
978
- return { data: void 0 };
1119
+ return { data: raw.data };
979
1120
  }
980
- function redirectToLogin() {
981
- if (typeof window === "undefined") {
982
- logger.debug("Cannot redirect in SSR context");
983
- return false;
984
- }
1121
+ const { getSession, signOutCookie } = createSessionMethods({
1122
+ authMode,
1123
+ baseUrl,
1124
+ serviceCode: config.serviceCode,
1125
+ tokenStore,
1126
+ logger,
1127
+ fetchWithAuth,
1128
+ refreshAccessToken,
1129
+ errorCodes: FFID_ERROR_CODES
1130
+ });
1131
+ async function signOut() {
985
1132
  if (authMode === "token") {
986
- return redirectToAuthorize();
1133
+ return signOutToken();
987
1134
  }
988
- const currentUrl = window.location.href;
989
- const loginUrl = `${baseUrl}/login?redirect=${encodeURIComponent(currentUrl)}&service=${encodeURIComponent(config.serviceCode)}`;
990
- logger.debug("Redirecting to login:", loginUrl);
991
- window.location.href = loginUrl;
992
- return true;
993
- }
994
- function redirectToAuthorize() {
995
- const verifier = generateCodeVerifier();
996
- storeCodeVerifier(verifier);
997
- generateCodeChallenge(verifier).then((challenge) => {
998
- const state = generateRandomState();
999
- const redirectUri = resolvedRedirectUri ?? window.location.origin + window.location.pathname;
1000
- const params = new URLSearchParams({
1001
- response_type: "code",
1002
- client_id: clientId,
1003
- redirect_uri: redirectUri,
1004
- state,
1005
- code_challenge: challenge,
1006
- code_challenge_method: "S256"
1007
- });
1008
- const authorizeUrl = `${baseUrl}${OAUTH_AUTHORIZE_ENDPOINT}?${params.toString()}`;
1009
- logger.debug("Redirecting to authorize:", authorizeUrl);
1010
- window.location.href = authorizeUrl;
1011
- }).catch((error) => {
1012
- logger.error("Failed to generate code challenge:", error);
1013
- });
1014
- return true;
1015
- }
1016
- function getLoginUrl(redirectUrl) {
1017
- const redirect = redirectUrl ?? (typeof window !== "undefined" ? window.location.href : "");
1018
- return `${baseUrl}/login?redirect=${encodeURIComponent(redirect)}&service=${encodeURIComponent(config.serviceCode)}`;
1019
- }
1020
- function getSignupUrl(redirectUrl) {
1021
- const redirect = redirectUrl ?? (typeof window !== "undefined" ? window.location.href : "");
1022
- return `${baseUrl}/signup?redirect=${encodeURIComponent(redirect)}&service=${encodeURIComponent(config.serviceCode)}`;
1023
- }
1024
- function createError(code, message) {
1025
- return { code, message };
1135
+ return signOutCookie();
1026
1136
  }
1137
+ const { redirectToLogin, getLoginUrl, getSignupUrl } = createRedirectMethods({
1138
+ authMode,
1139
+ baseUrl,
1140
+ clientId,
1141
+ serviceCode: config.serviceCode,
1142
+ resolvedRedirectUri,
1143
+ logger
1144
+ });
1027
1145
  async function checkSubscription(params) {
1028
1146
  if (!params.userId || !params.organizationId) {
1029
1147
  return {
@@ -1080,11 +1198,6 @@ function createFFIDClient(config) {
1080
1198
  redirectUri: resolvedRedirectUri
1081
1199
  };
1082
1200
  }
1083
- function generateRandomState() {
1084
- const array = new Uint8Array(STATE_RANDOM_BYTES);
1085
- crypto.getRandomValues(array);
1086
- return Array.from(array, (byte) => byte.toString(HEX_BASE2).padStart(2, "0")).join("");
1087
- }
1088
1201
 
1089
1202
  // src/client/cache/memory-cache-adapter.ts
1090
1203
  var MS_PER_SECOND2 = 1e3;