@feelflow/ffid-sdk 1.10.0 → 1.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -9,6 +9,7 @@ var DEFAULT_API_BASE_URL = "https://id.feelflow.net";
9
9
 
10
10
  // src/auth/token-store.ts
11
11
  var STORAGE_KEY = "ffid_tokens";
12
+ var TOKEN_STORE_LOG_PREFIX = "[FFID SDK] TokenStore:";
12
13
  var EXPIRY_BUFFER_SECONDS = 30;
13
14
  var EXPIRY_BUFFER_MS = EXPIRY_BUFFER_SECONDS * 1e3;
14
15
  function isLocalStorageAvailable() {
@@ -32,22 +33,33 @@ function createLocalStorageStore() {
32
33
  const raw = storage.getItem(STORAGE_KEY);
33
34
  if (!raw) return null;
34
35
  const parsed = JSON.parse(raw);
35
- if (!isTokenData(parsed)) return null;
36
+ if (!isTokenData(parsed)) {
37
+ 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");
38
+ storage.removeItem(STORAGE_KEY);
39
+ return null;
40
+ }
36
41
  return parsed;
37
- } catch {
42
+ } catch (error) {
43
+ 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);
44
+ try {
45
+ storage.removeItem(STORAGE_KEY);
46
+ } catch {
47
+ }
38
48
  return null;
39
49
  }
40
50
  },
41
51
  setTokens(tokens) {
42
52
  try {
43
53
  storage.setItem(STORAGE_KEY, JSON.stringify(tokens));
44
- } catch {
54
+ } catch (error) {
55
+ 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);
45
56
  }
46
57
  },
47
58
  clearTokens() {
48
59
  try {
49
60
  storage.removeItem(STORAGE_KEY);
50
- } catch {
61
+ } catch (error) {
62
+ console.warn(TOKEN_STORE_LOG_PREFIX, "\u30C8\u30FC\u30AF\u30F3\u524A\u9664\u306B\u5931\u6557\u3057\u307E\u3057\u305F", error);
51
63
  }
52
64
  },
53
65
  isAccessTokenExpired() {
@@ -90,54 +102,6 @@ function createTokenStore(storageType) {
90
102
  return createMemoryStore();
91
103
  }
92
104
 
93
- // src/auth/pkce.ts
94
- var VERIFIER_STORAGE_KEY = "ffid_code_verifier";
95
- var CODE_VERIFIER_MIN_LENGTH = 43;
96
- var UNRESERVED_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~";
97
- function generateCodeVerifier() {
98
- const length = CODE_VERIFIER_MIN_LENGTH;
99
- const randomValues = new Uint8Array(length);
100
- crypto.getRandomValues(randomValues);
101
- let verifier = "";
102
- for (let i = 0; i < length; i++) {
103
- verifier += UNRESERVED_CHARS[randomValues[i] % UNRESERVED_CHARS.length];
104
- }
105
- return verifier;
106
- }
107
- async function generateCodeChallenge(verifier) {
108
- const encoder = new TextEncoder();
109
- const data = encoder.encode(verifier);
110
- const digest = await crypto.subtle.digest("SHA-256", data);
111
- return base64UrlEncode(digest);
112
- }
113
- function storeCodeVerifier(verifier) {
114
- try {
115
- if (typeof window === "undefined") return;
116
- window.sessionStorage.setItem(VERIFIER_STORAGE_KEY, verifier);
117
- } catch {
118
- }
119
- }
120
- function retrieveCodeVerifier() {
121
- try {
122
- if (typeof window === "undefined") return null;
123
- const verifier = window.sessionStorage.getItem(VERIFIER_STORAGE_KEY);
124
- if (verifier) {
125
- window.sessionStorage.removeItem(VERIFIER_STORAGE_KEY);
126
- }
127
- return verifier;
128
- } catch {
129
- return null;
130
- }
131
- }
132
- function base64UrlEncode(buffer) {
133
- const bytes = new Uint8Array(buffer);
134
- let binary = "";
135
- for (let i = 0; i < bytes.length; i++) {
136
- binary += String.fromCharCode(bytes[i]);
137
- }
138
- return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
139
- }
140
-
141
105
  // src/client/oauth-userinfo.ts
142
106
  var VALID_SUBSCRIPTION_STATUSES = ["trialing", "active", "past_due", "canceled", "paused"];
143
107
  function isValidSubscriptionStatus(value) {
@@ -241,6 +205,7 @@ var OAUTH_INTROSPECT_ENDPOINT = "/api/v1/oauth/introspect";
241
205
  var CACHE_KEY_PREFIX = "ffid:introspect:";
242
206
  var HEX_BASE = 16;
243
207
  var HEX_BYTE_WIDTH = 2;
208
+ var TOKEN_LOG_PREFIX_LENGTH = 8;
244
209
  function createVerifyAccessToken(deps) {
245
210
  const { authMode, baseUrl, serviceCode, serviceApiKey, verifyStrategy, logger, createError, errorCodes, cache, timeout } = deps;
246
211
  let jwtVerify2 = null;
@@ -256,7 +221,7 @@ function createVerifyAccessToken(deps) {
256
221
  }
257
222
  return jwtVerify2;
258
223
  }
259
- async function verifyAccessToken(accessToken) {
224
+ async function verifyAccessToken(accessToken, options) {
260
225
  if (authMode !== "service-key") {
261
226
  return {
262
227
  error: createError(
@@ -273,12 +238,24 @@ function createVerifyAccessToken(deps) {
273
238
  )
274
239
  };
275
240
  }
241
+ if (options?.includeProfile && verifyStrategy === "jwt" && !serviceApiKey) {
242
+ return {
243
+ error: createError(
244
+ errorCodes.TOKEN_VERIFICATION_ERROR,
245
+ "includeProfile: true \u3092\u4F7F\u7528\u3059\u308B\u306B\u306F serviceApiKey \u306E\u8A2D\u5B9A\u304C\u5FC5\u8981\u3067\u3059"
246
+ )
247
+ };
248
+ }
276
249
  if (verifyStrategy === "jwt") {
277
- return getJwtVerifier()(accessToken);
250
+ const jwtResult = await getJwtVerifier()(accessToken);
251
+ if (!options?.includeProfile || jwtResult.error) {
252
+ return jwtResult;
253
+ }
254
+ return verifyViaIntrospect(accessToken, "profile");
278
255
  }
279
256
  return verifyViaIntrospect(accessToken);
280
257
  }
281
- async function verifyViaIntrospect(accessToken) {
258
+ async function verifyViaIntrospect(accessToken, cacheKeySuffix) {
282
259
  if (!serviceApiKey) {
283
260
  return {
284
261
  error: createError(
@@ -289,7 +266,7 @@ function createVerifyAccessToken(deps) {
289
266
  }
290
267
  const url = `${baseUrl}${OAUTH_INTROSPECT_ENDPOINT}`;
291
268
  logger.debug("Verifying access token:", url);
292
- const cacheKey = await buildCacheKey(accessToken);
269
+ const cacheKey = cache ? await buildCacheKey(accessToken, cacheKeySuffix) : "";
293
270
  if (cache) {
294
271
  try {
295
272
  const cached = await cache.adapter.get(cacheKey);
@@ -391,16 +368,23 @@ function createVerifyAccessToken(deps) {
391
368
  }
392
369
  return { data: userinfo };
393
370
  }
394
- async function buildCacheKey(token) {
371
+ async function buildCacheKey(token, suffix) {
372
+ let key;
395
373
  if (typeof globalThis.crypto?.subtle?.digest === "function") {
396
374
  const encoder = new TextEncoder();
397
375
  const data = encoder.encode(token);
398
376
  const hashBuffer = await crypto.subtle.digest("SHA-256", data);
399
377
  const hashArray = Array.from(new Uint8Array(hashBuffer));
400
378
  const hashHex = hashArray.map((b) => b.toString(HEX_BASE).padStart(HEX_BYTE_WIDTH, "0")).join("");
401
- return CACHE_KEY_PREFIX + hashHex;
379
+ key = CACHE_KEY_PREFIX + hashHex;
380
+ } else {
381
+ const tokenPrefix = token.length > TOKEN_LOG_PREFIX_LENGTH ? token.substring(0, TOKEN_LOG_PREFIX_LENGTH) + "..." : "***";
382
+ logger.warn(
383
+ `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})`
384
+ );
385
+ key = CACHE_KEY_PREFIX + token;
402
386
  }
403
- return CACHE_KEY_PREFIX + token;
387
+ return suffix ? `${key}:${suffix}` : key;
404
388
  }
405
389
  return verifyAccessToken;
406
390
  }
@@ -453,9 +437,15 @@ function createBillingMethods(deps) {
453
437
  }
454
438
 
455
439
  // src/client/version-check.ts
456
- var SDK_VERSION = "1.10.0";
440
+ var SDK_VERSION = "1.13.0";
457
441
  var SDK_USER_AGENT = `FFID-SDK/${SDK_VERSION} (TypeScript)`;
458
442
  var SDK_VERSION_HEADER = "X-FFID-SDK-Version";
443
+ function sdkHeaders() {
444
+ return {
445
+ "User-Agent": SDK_USER_AGENT,
446
+ [SDK_VERSION_HEADER]: SDK_VERSION
447
+ };
448
+ }
459
449
  var LATEST_VERSION_HEADER = "X-FFID-SDK-Latest-Version";
460
450
  var SEMVER_PATTERN = /^\d+(\.\d+)*$/;
461
451
  var _versionWarningShown = false;
@@ -489,178 +479,249 @@ npm install @feelflow/ffid-sdk@latest \u3067\u30A2\u30C3\u30D7\u30C7\u30FC\u30C8
489
479
  }
490
480
  }
491
481
 
492
- // src/client/ffid-client.ts
493
- var NO_CONTENT_STATUS = 204;
494
- var SESSION_ENDPOINT = "/api/v1/auth/session";
495
- var LOGOUT_ENDPOINT = "/api/v1/auth/signout";
482
+ // src/client/oauth-token.ts
496
483
  var OAUTH_TOKEN_ENDPOINT = "/api/v1/oauth/token";
497
- var OAUTH_USERINFO_ENDPOINT = "/api/v1/oauth/userinfo";
498
- var OAUTH_AUTHORIZE_ENDPOINT = "/api/v1/oauth/authorize";
499
484
  var OAUTH_REVOKE_ENDPOINT = "/api/v1/oauth/revoke";
500
- var EXT_CHECK_ENDPOINT = "/api/v1/subscriptions/ext/check";
501
- var SDK_LOG_PREFIX = "[FFID SDK]";
502
485
  var MS_PER_SECOND = 1e3;
503
- var UNAUTHORIZED_STATUS = 401;
504
- var STATE_RANDOM_BYTES = 16;
505
- var HEX_BASE2 = 16;
506
- var noopLogger = {
507
- debug: () => {
508
- },
509
- info: () => {
510
- },
511
- warn: () => {
512
- },
513
- error: (...args) => console.error(SDK_LOG_PREFIX, ...args)
514
- };
515
- var consoleLogger = {
516
- debug: (...args) => console.debug(SDK_LOG_PREFIX, ...args),
517
- info: (...args) => console.info(SDK_LOG_PREFIX, ...args),
518
- warn: (...args) => console.warn(SDK_LOG_PREFIX, ...args),
519
- error: (...args) => console.error(SDK_LOG_PREFIX, ...args)
520
- };
521
- var FFID_ERROR_CODES = {
522
- NETWORK_ERROR: "NETWORK_ERROR",
523
- PARSE_ERROR: "PARSE_ERROR",
524
- UNKNOWN_ERROR: "UNKNOWN_ERROR",
525
- TOKEN_EXCHANGE_ERROR: "TOKEN_EXCHANGE_ERROR",
526
- TOKEN_REFRESH_ERROR: "TOKEN_REFRESH_ERROR",
527
- NO_TOKENS: "NO_TOKENS",
528
- TOKEN_VERIFICATION_ERROR: "TOKEN_VERIFICATION_ERROR"
529
- };
530
- function createFFIDClient(config) {
531
- if (!config.serviceCode || !config.serviceCode.trim()) {
532
- throw new Error("FFID Client: serviceCode \u304C\u672A\u8A2D\u5B9A\u3067\u3059");
486
+ function validateTokenResponse(tokenResponse) {
487
+ const invalid = [];
488
+ if (!tokenResponse.access_token) {
489
+ invalid.push("access_token");
533
490
  }
534
- const baseUrl = config.apiBaseUrl ?? DEFAULT_API_BASE_URL;
535
- const authMode = config.authMode ?? "cookie";
536
- const clientId = config.clientId ?? config.serviceCode;
537
- const resolvedRedirectUri = config.redirectUri ?? null;
538
- const serviceApiKey = config.serviceApiKey?.trim();
539
- const verifyStrategy = config.verifyStrategy ?? "jwt";
540
- const cache = config.cache;
541
- const timeout = config.timeout;
542
- if (authMode === "service-key" && !serviceApiKey) {
543
- throw new Error("FFID Client: service-key \u30E2\u30FC\u30C9\u3067\u306F serviceApiKey \u304C\u5FC5\u9808\u3067\u3059");
491
+ if (!tokenResponse.refresh_token) {
492
+ invalid.push("refresh_token");
544
493
  }
545
- if (cache && cache.ttl <= 0) {
546
- throw new Error("FFID Client: cache.ttl \u306F\u6B63\u306E\u6570\u5024\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044");
494
+ if (typeof tokenResponse.expires_in !== "number" || tokenResponse.expires_in <= 0) {
495
+ invalid.push("expires_in");
547
496
  }
548
- if (timeout !== void 0 && timeout <= 0) {
549
- throw new Error("FFID Client: timeout \u306F\u6B63\u306E\u6570\u5024\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044");
497
+ if (invalid.length > 0) {
498
+ return `\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(", ")}`;
550
499
  }
551
- const logger = config.logger ?? (config.debug ? consoleLogger : noopLogger);
552
- const tokenStore = authMode === "token" ? createTokenStore() : createTokenStore("memory");
553
- async function fetchWithAuth(endpoint, options = {}) {
554
- const url = `${baseUrl}${endpoint}`;
555
- logger.debug("Fetching:", url);
556
- const fetchOptions = buildFetchOptions(options);
500
+ return null;
501
+ }
502
+ function createOAuthTokenMethods(deps) {
503
+ const {
504
+ baseUrl,
505
+ clientId,
506
+ resolvedRedirectUri,
507
+ tokenStore,
508
+ logger,
509
+ errorCodes
510
+ } = deps;
511
+ async function exchangeCodeForTokens(code, codeVerifier) {
512
+ const url = `${baseUrl}${OAUTH_TOKEN_ENDPOINT}`;
513
+ logger.debug("Exchanging code for tokens:", url);
514
+ const effectiveRedirectUri = resolvedRedirectUri ?? (typeof window !== "undefined" ? window.location.origin + window.location.pathname : null);
515
+ if (!effectiveRedirectUri) {
516
+ logger.error("redirectUri is required for token exchange in SSR environments. Set config.redirectUri explicitly.");
517
+ return {
518
+ error: {
519
+ code: errorCodes.TOKEN_EXCHANGE_ERROR,
520
+ 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"
521
+ }
522
+ };
523
+ }
524
+ const body = {
525
+ grant_type: "authorization_code",
526
+ code,
527
+ client_id: clientId,
528
+ redirect_uri: effectiveRedirectUri
529
+ };
530
+ if (codeVerifier) {
531
+ body.code_verifier = codeVerifier;
532
+ }
557
533
  let response;
558
534
  try {
559
- response = await fetch(url, fetchOptions);
535
+ response = await fetch(url, {
536
+ method: "POST",
537
+ credentials: "omit",
538
+ headers: { "Content-Type": "application/x-www-form-urlencoded", ...sdkHeaders() },
539
+ body: new URLSearchParams(body).toString()
540
+ });
560
541
  } catch (error) {
561
- logger.error("Network error:", error);
542
+ logger.error("Network error during token exchange:", error);
562
543
  return {
563
544
  error: {
564
- code: FFID_ERROR_CODES.NETWORK_ERROR,
545
+ code: errorCodes.NETWORK_ERROR,
565
546
  message: error instanceof Error ? error.message : "\u30CD\u30C3\u30C8\u30EF\u30FC\u30AF\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F"
566
547
  }
567
548
  };
568
549
  }
569
- if (authMode === "token" && response.status === UNAUTHORIZED_STATUS) {
570
- const refreshResult = await refreshAccessToken();
571
- if (!refreshResult.error) {
572
- logger.debug("Token refreshed, retrying request");
573
- const retryOptions = buildFetchOptions(options);
574
- try {
575
- response = await fetch(url, retryOptions);
576
- } catch (retryError) {
577
- logger.error("Network error on retry:", retryError);
578
- return {
579
- error: {
580
- code: FFID_ERROR_CODES.NETWORK_ERROR,
581
- message: retryError instanceof Error ? retryError.message : "\u30CD\u30C3\u30C8\u30EF\u30FC\u30AF\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F"
582
- }
583
- };
584
- }
585
- }
586
- }
587
- let raw;
550
+ let tokenResponse;
588
551
  try {
589
- raw = await response.json();
552
+ tokenResponse = await response.json();
590
553
  } catch (parseError) {
591
- logger.error("Parse error:", parseError, "Status:", response.status);
554
+ logger.error("Parse error during token exchange:", parseError);
592
555
  return {
593
556
  error: {
594
- code: FFID_ERROR_CODES.PARSE_ERROR,
595
- 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})`
557
+ code: errorCodes.PARSE_ERROR,
558
+ message: `\u30C8\u30FC\u30AF\u30F3\u30EC\u30B9\u30DD\u30F3\u30B9\u306E\u89E3\u6790\u306B\u5931\u6557\u3057\u307E\u3057\u305F (status: ${response.status})`
596
559
  }
597
560
  };
598
561
  }
599
- logger.debug("Response:", response.status, raw);
600
- checkVersionHeader(response, logger);
601
562
  if (!response.ok) {
563
+ const errorBody = tokenResponse;
602
564
  return {
603
- error: raw.error ?? {
604
- code: FFID_ERROR_CODES.UNKNOWN_ERROR,
605
- message: "\u4E0D\u660E\u306A\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F"
565
+ error: {
566
+ code: errorBody.error ?? errorCodes.TOKEN_EXCHANGE_ERROR,
567
+ message: errorBody.error_description ?? "\u30C8\u30FC\u30AF\u30F3\u4EA4\u63DB\u306B\u5931\u6557\u3057\u307E\u3057\u305F"
606
568
  }
607
569
  };
608
570
  }
609
- if (raw.data === void 0) {
571
+ const validationError = validateTokenResponse(tokenResponse);
572
+ if (validationError) {
573
+ logger.error("Token exchange validation failed:", validationError);
610
574
  return {
611
575
  error: {
612
- code: FFID_ERROR_CODES.UNKNOWN_ERROR,
613
- message: "\u30B5\u30FC\u30D0\u30FC\u304B\u3089\u30C7\u30FC\u30BF\u304C\u8FD4\u3055\u308C\u307E\u305B\u3093\u3067\u3057\u305F"
576
+ code: errorCodes.TOKEN_EXCHANGE_ERROR,
577
+ message: validationError
614
578
  }
615
579
  };
616
580
  }
617
- return { data: raw.data };
618
- }
619
- function sdkHeaders() {
620
- return {
621
- "User-Agent": SDK_USER_AGENT,
622
- [SDK_VERSION_HEADER]: SDK_VERSION
623
- };
581
+ tokenStore.setTokens({
582
+ accessToken: tokenResponse.access_token,
583
+ refreshToken: tokenResponse.refresh_token,
584
+ expiresAt: Date.now() + tokenResponse.expires_in * MS_PER_SECOND
585
+ });
586
+ logger.debug("Token exchange successful");
587
+ return { data: void 0 };
624
588
  }
625
- function buildFetchOptions(options) {
626
- if (authMode === "service-key") {
589
+ async function refreshAccessToken() {
590
+ const tokens = tokenStore.getTokens();
591
+ if (!tokens) {
627
592
  return {
628
- ...options,
629
- credentials: "omit",
630
- headers: {
631
- "Content-Type": "application/json",
632
- ...sdkHeaders(),
633
- "X-Service-Api-Key": serviceApiKey,
634
- ...options.headers
593
+ error: {
594
+ code: errorCodes.NO_TOKENS,
595
+ message: "\u30EA\u30D5\u30EC\u30C3\u30B7\u30E5\u30C8\u30FC\u30AF\u30F3\u304C\u3042\u308A\u307E\u305B\u3093"
635
596
  }
636
597
  };
637
598
  }
638
- if (authMode === "token") {
639
- const tokens = tokenStore.getTokens();
640
- const headers = {
641
- "Content-Type": "application/json",
642
- ...sdkHeaders(),
643
- ...options.headers
644
- };
645
- if (tokens) {
646
- headers["Authorization"] = `Bearer ${tokens.accessToken}`;
647
- }
648
- return {
649
- ...options,
599
+ const url = `${baseUrl}${OAUTH_TOKEN_ENDPOINT}`;
600
+ logger.debug("Refreshing access token:", url);
601
+ let response;
602
+ try {
603
+ response = await fetch(url, {
604
+ method: "POST",
650
605
  credentials: "omit",
651
- headers
606
+ headers: { "Content-Type": "application/x-www-form-urlencoded", ...sdkHeaders() },
607
+ body: new URLSearchParams({
608
+ grant_type: "refresh_token",
609
+ refresh_token: tokens.refreshToken,
610
+ client_id: clientId
611
+ }).toString()
612
+ });
613
+ } catch (error) {
614
+ logger.error("Network error during token refresh:", error);
615
+ return {
616
+ error: {
617
+ code: errorCodes.NETWORK_ERROR,
618
+ message: error instanceof Error ? error.message : "\u30CD\u30C3\u30C8\u30EF\u30FC\u30AF\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F"
619
+ }
652
620
  };
653
621
  }
654
- return {
655
- ...options,
656
- credentials: "include",
657
- headers: {
658
- "Content-Type": "application/json",
659
- ...sdkHeaders(),
660
- ...options.headers
622
+ let tokenResponse;
623
+ try {
624
+ tokenResponse = await response.json();
625
+ } catch (parseError) {
626
+ logger.error("Parse error during token refresh:", parseError);
627
+ return {
628
+ error: {
629
+ code: errorCodes.PARSE_ERROR,
630
+ message: `\u30C8\u30FC\u30AF\u30F3\u30EC\u30B9\u30DD\u30F3\u30B9\u306E\u89E3\u6790\u306B\u5931\u6557\u3057\u307E\u3057\u305F (status: ${response.status})`
631
+ }
632
+ };
633
+ }
634
+ if (!response.ok) {
635
+ const errorBody = tokenResponse;
636
+ logger.error("Token refresh failed:", errorBody);
637
+ const irrecoverableErrors = ["token_revoked", "invalid_grant"];
638
+ if (errorBody.error && irrecoverableErrors.includes(errorBody.error)) {
639
+ tokenStore.clearTokens();
640
+ logger.debug("Cleared tokens due to irrecoverable refresh error:", errorBody.error);
661
641
  }
662
- };
642
+ return {
643
+ error: {
644
+ code: errorBody.error ?? errorCodes.TOKEN_REFRESH_ERROR,
645
+ message: errorBody.error_description ?? "\u30C8\u30FC\u30AF\u30F3\u30EA\u30D5\u30EC\u30C3\u30B7\u30E5\u306B\u5931\u6557\u3057\u307E\u3057\u305F"
646
+ }
647
+ };
648
+ }
649
+ const validationError = validateTokenResponse(tokenResponse);
650
+ if (validationError) {
651
+ logger.error("Token refresh validation failed:", validationError);
652
+ return {
653
+ error: {
654
+ code: errorCodes.TOKEN_REFRESH_ERROR,
655
+ message: validationError
656
+ }
657
+ };
658
+ }
659
+ tokenStore.setTokens({
660
+ accessToken: tokenResponse.access_token,
661
+ refreshToken: tokenResponse.refresh_token,
662
+ expiresAt: Date.now() + tokenResponse.expires_in * MS_PER_SECOND
663
+ });
664
+ logger.debug("Token refresh successful");
665
+ return { data: void 0 };
663
666
  }
667
+ async function signOutToken() {
668
+ const tokens = tokenStore.getTokens();
669
+ tokenStore.clearTokens();
670
+ if (!tokens) {
671
+ logger.debug("No tokens to revoke");
672
+ return { data: void 0 };
673
+ }
674
+ const url = `${baseUrl}${OAUTH_REVOKE_ENDPOINT}`;
675
+ logger.debug("Revoking token:", url);
676
+ try {
677
+ const response = await fetch(url, {
678
+ method: "POST",
679
+ credentials: "omit",
680
+ headers: { "Content-Type": "application/x-www-form-urlencoded", ...sdkHeaders() },
681
+ body: new URLSearchParams({
682
+ token: tokens.accessToken,
683
+ client_id: clientId
684
+ }).toString()
685
+ });
686
+ if (!response.ok) {
687
+ logger.warn(
688
+ "\u30C8\u30FC\u30AF\u30F3\u7121\u52B9\u5316\u30EA\u30AF\u30A8\u30B9\u30C8\u304C\u30B5\u30FC\u30D0\u30FC\u3067\u5931\u6557\u3057\u307E\u3057\u305F:",
689
+ `status=${response.status}`
690
+ );
691
+ }
692
+ } catch (error) {
693
+ 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);
694
+ }
695
+ logger.debug("Token sign-out completed");
696
+ return { data: void 0 };
697
+ }
698
+ return { exchangeCodeForTokens, refreshAccessToken, signOutToken };
699
+ }
700
+
701
+ // src/client/session.ts
702
+ var SESSION_ENDPOINT = "/api/v1/auth/session";
703
+ var LOGOUT_ENDPOINT = "/api/v1/auth/signout";
704
+ var OAUTH_USERINFO_ENDPOINT = "/api/v1/oauth/userinfo";
705
+ var NO_CONTENT_STATUS = 204;
706
+ var UNAUTHORIZED_STATUS = 401;
707
+ function normalizeFFIDUser(user) {
708
+ return {
709
+ ...user,
710
+ locale: user.locale ?? null,
711
+ timezone: user.timezone ?? null
712
+ };
713
+ }
714
+ function createSessionMethods(deps) {
715
+ const {
716
+ authMode,
717
+ baseUrl,
718
+ serviceCode,
719
+ tokenStore,
720
+ logger,
721
+ fetchWithAuth,
722
+ refreshAccessToken,
723
+ errorCodes
724
+ } = deps;
664
725
  async function getSession() {
665
726
  if (authMode === "token") {
666
727
  return getSessionFromUserinfo();
@@ -677,19 +738,12 @@ function createFFIDClient(config) {
677
738
  }
678
739
  return result;
679
740
  }
680
- function normalizeFFIDUser(user) {
681
- return {
682
- ...user,
683
- locale: user.locale ?? null,
684
- timezone: user.timezone ?? null
685
- };
686
- }
687
741
  async function getSessionFromUserinfo() {
688
742
  const tokens = tokenStore.getTokens();
689
743
  if (!tokens) {
690
744
  return {
691
745
  error: {
692
- code: FFID_ERROR_CODES.NO_TOKENS,
746
+ code: errorCodes.NO_TOKENS,
693
747
  message: "\u30C8\u30FC\u30AF\u30F3\u304C\u4FDD\u5B58\u3055\u308C\u3066\u3044\u307E\u305B\u3093"
694
748
  }
695
749
  };
@@ -710,7 +764,7 @@ function createFFIDClient(config) {
710
764
  logger.error("Network error:", error);
711
765
  return {
712
766
  error: {
713
- code: FFID_ERROR_CODES.NETWORK_ERROR,
767
+ code: errorCodes.NETWORK_ERROR,
714
768
  message: error instanceof Error ? error.message : "\u30CD\u30C3\u30C8\u30EF\u30FC\u30AF\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F"
715
769
  }
716
770
  };
@@ -733,7 +787,7 @@ function createFFIDClient(config) {
733
787
  logger.error("Network error on retry:", retryError);
734
788
  return {
735
789
  error: {
736
- code: FFID_ERROR_CODES.NETWORK_ERROR,
790
+ code: errorCodes.NETWORK_ERROR,
737
791
  message: retryError instanceof Error ? retryError.message : "\u30CD\u30C3\u30C8\u30EF\u30FC\u30AF\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F"
738
792
  }
739
793
  };
@@ -750,7 +804,7 @@ function createFFIDClient(config) {
750
804
  logger.error("Parse error:", parseError, "Status:", response.status);
751
805
  return {
752
806
  error: {
753
- code: FFID_ERROR_CODES.PARSE_ERROR,
807
+ code: errorCodes.PARSE_ERROR,
754
808
  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})`
755
809
  }
756
810
  };
@@ -759,7 +813,7 @@ function createFFIDClient(config) {
759
813
  const errorBody = rawUserinfo;
760
814
  return {
761
815
  error: {
762
- code: errorBody.code ?? FFID_ERROR_CODES.UNKNOWN_ERROR,
816
+ code: errorBody.code ?? errorCodes.UNKNOWN_ERROR,
763
817
  message: errorBody.message ?? "\u4E0D\u660E\u306A\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F"
764
818
  }
765
819
  };
@@ -778,16 +832,10 @@ function createFFIDClient(config) {
778
832
  data: {
779
833
  user,
780
834
  organizations: [],
781
- subscriptions: mapUserinfoSubscriptionToSession(userinfo, config.serviceCode)
835
+ subscriptions: mapUserinfoSubscriptionToSession(userinfo, serviceCode)
782
836
  }
783
837
  };
784
838
  }
785
- async function signOut() {
786
- if (authMode === "token") {
787
- return signOutToken();
788
- }
789
- return signOutCookie();
790
- }
791
839
  async function signOutCookie() {
792
840
  const url = `${baseUrl}${LOGOUT_ENDPOINT}`;
793
841
  logger.debug("Fetching:", url);
@@ -802,7 +850,7 @@ function createFFIDClient(config) {
802
850
  logger.error("Network error:", error);
803
851
  return {
804
852
  error: {
805
- code: FFID_ERROR_CODES.NETWORK_ERROR,
853
+ code: errorCodes.NETWORK_ERROR,
806
854
  message: error instanceof Error ? error.message : "\u30CD\u30C3\u30C8\u30EF\u30FC\u30AF\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F"
807
855
  }
808
856
  };
@@ -816,14 +864,20 @@ function createFFIDClient(config) {
816
864
  const raw = await response.json();
817
865
  return {
818
866
  error: raw.error ?? {
819
- code: FFID_ERROR_CODES.UNKNOWN_ERROR,
867
+ code: errorCodes.UNKNOWN_ERROR,
820
868
  message: "\u4E0D\u660E\u306A\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F"
821
869
  }
822
870
  };
823
- } catch {
871
+ } catch (parseError) {
872
+ logger.error(
873
+ "\u30B5\u30A4\u30F3\u30A2\u30A6\u30C8\u30EC\u30B9\u30DD\u30F3\u30B9\u306E\u89E3\u6790\u306B\u5931\u6557\u3057\u307E\u3057\u305F:",
874
+ parseError,
875
+ "Status:",
876
+ response.status
877
+ );
824
878
  return {
825
879
  error: {
826
- code: FFID_ERROR_CODES.PARSE_ERROR,
880
+ code: errorCodes.PARSE_ERROR,
827
881
  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})`
828
882
  }
829
883
  };
@@ -832,125 +886,496 @@ function createFFIDClient(config) {
832
886
  logger.debug("Response:", response.status);
833
887
  return { data: void 0 };
834
888
  }
835
- async function signOutToken() {
836
- const tokens = tokenStore.getTokens();
837
- tokenStore.clearTokens();
838
- if (!tokens) {
839
- logger.debug("No tokens to revoke");
840
- return { data: void 0 };
889
+ return { getSession, signOutCookie };
890
+ }
891
+
892
+ // src/auth/pkce.ts
893
+ var VERIFIER_STORAGE_KEY = "ffid_code_verifier";
894
+ var CODE_VERIFIER_MIN_LENGTH = 43;
895
+ var UNRESERVED_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~";
896
+ function generateCodeVerifier() {
897
+ const length = CODE_VERIFIER_MIN_LENGTH;
898
+ const randomValues = new Uint8Array(length);
899
+ crypto.getRandomValues(randomValues);
900
+ let verifier = "";
901
+ for (let i = 0; i < length; i++) {
902
+ verifier += UNRESERVED_CHARS[randomValues[i] % UNRESERVED_CHARS.length];
903
+ }
904
+ return verifier;
905
+ }
906
+ async function generateCodeChallenge(verifier) {
907
+ const encoder = new TextEncoder();
908
+ const data = encoder.encode(verifier);
909
+ const digest = await crypto.subtle.digest("SHA-256", data);
910
+ return base64UrlEncode(digest);
911
+ }
912
+ function storeCodeVerifier(verifier, logger) {
913
+ try {
914
+ if (typeof window === "undefined") {
915
+ logger?.warn("storeCodeVerifier: sessionStorage is not available in SSR context");
916
+ return false;
841
917
  }
842
- const url = `${baseUrl}${OAUTH_REVOKE_ENDPOINT}`;
843
- logger.debug("Revoking token:", url);
918
+ window.sessionStorage.setItem(VERIFIER_STORAGE_KEY, verifier);
919
+ return true;
920
+ } catch (error) {
921
+ logger?.warn("storeCodeVerifier: sessionStorage \u3078\u306E\u4FDD\u5B58\u306B\u5931\u6557\u3057\u307E\u3057\u305F:", error);
922
+ return false;
923
+ }
924
+ }
925
+ function retrieveCodeVerifier(logger) {
926
+ try {
927
+ if (typeof window === "undefined") return null;
928
+ const verifier = window.sessionStorage.getItem(VERIFIER_STORAGE_KEY);
929
+ if (verifier) {
930
+ window.sessionStorage.removeItem(VERIFIER_STORAGE_KEY);
931
+ }
932
+ return verifier;
933
+ } catch (error) {
934
+ logger?.warn("retrieveCodeVerifier: sessionStorage \u304B\u3089\u306E\u53D6\u5F97\u306B\u5931\u6557\u3057\u307E\u3057\u305F:", error);
935
+ return null;
936
+ }
937
+ }
938
+ function base64UrlEncode(buffer) {
939
+ const bytes = new Uint8Array(buffer);
940
+ let binary = "";
941
+ for (let i = 0; i < bytes.length; i++) {
942
+ binary += String.fromCharCode(bytes[i]);
943
+ }
944
+ return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
945
+ }
946
+
947
+ // src/client/redirect.ts
948
+ var OAUTH_AUTHORIZE_ENDPOINT = "/api/v1/oauth/authorize";
949
+ var STATE_RANDOM_BYTES = 16;
950
+ var HEX_BASE2 = 16;
951
+ function generateRandomState() {
952
+ const array = new Uint8Array(STATE_RANDOM_BYTES);
953
+ crypto.getRandomValues(array);
954
+ return Array.from(array, (byte) => byte.toString(HEX_BASE2).padStart(2, "0")).join("");
955
+ }
956
+ function createRedirectMethods(deps) {
957
+ const {
958
+ authMode,
959
+ baseUrl,
960
+ clientId,
961
+ serviceCode,
962
+ resolvedRedirectUri,
963
+ logger
964
+ } = deps;
965
+ async function redirectToAuthorize() {
966
+ const verifier = generateCodeVerifier();
967
+ storeCodeVerifier(verifier, logger);
968
+ let challenge;
844
969
  try {
845
- await fetch(url, {
846
- method: "POST",
847
- credentials: "omit",
848
- headers: { "Content-Type": "application/x-www-form-urlencoded", ...sdkHeaders() },
849
- body: new URLSearchParams({
850
- token: tokens.accessToken,
851
- client_id: clientId
852
- }).toString()
853
- });
970
+ challenge = await generateCodeChallenge(verifier);
854
971
  } catch (error) {
855
- logger.warn("Token revocation failed:", error);
972
+ const errorMessage = error instanceof Error ? error.message : "PKCE \u30B3\u30FC\u30C9\u30C1\u30E3\u30EC\u30F3\u30B8\u306E\u751F\u6210\u306B\u5931\u6557\u3057\u307E\u3057\u305F";
973
+ logger.error("PKCE \u30B3\u30FC\u30C9\u30C1\u30E3\u30EC\u30F3\u30B8\u306E\u751F\u6210\u306B\u5931\u6557\u3057\u307E\u3057\u305F:", error);
974
+ return { success: false, error: errorMessage };
975
+ }
976
+ const state = generateRandomState();
977
+ const redirectUri = resolvedRedirectUri ?? window.location.origin + window.location.pathname;
978
+ const params = new URLSearchParams({
979
+ response_type: "code",
980
+ client_id: clientId,
981
+ redirect_uri: redirectUri,
982
+ state,
983
+ code_challenge: challenge,
984
+ code_challenge_method: "S256"
985
+ });
986
+ const authorizeUrl = `${baseUrl}${OAUTH_AUTHORIZE_ENDPOINT}?${params.toString()}`;
987
+ logger.debug("Redirecting to authorize:", authorizeUrl);
988
+ window.location.href = authorizeUrl;
989
+ return { success: true };
990
+ }
991
+ async function redirectToLogin() {
992
+ if (typeof window === "undefined") {
993
+ logger.warn("SSR \u74B0\u5883\u3067\u306F\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3067\u304D\u307E\u305B\u3093");
994
+ return { success: false, error: "SSR \u74B0\u5883\u3067\u306F\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u3067\u304D\u307E\u305B\u3093" };
856
995
  }
857
- logger.debug("Token sign-out completed");
858
- return { data: void 0 };
996
+ if (authMode === "token") {
997
+ return redirectToAuthorize();
998
+ }
999
+ const currentUrl = window.location.href;
1000
+ const loginUrl = `${baseUrl}/login?redirect=${encodeURIComponent(currentUrl)}&service=${encodeURIComponent(serviceCode)}`;
1001
+ logger.debug("Redirecting to login:", loginUrl);
1002
+ window.location.href = loginUrl;
1003
+ return { success: true };
859
1004
  }
860
- async function exchangeCodeForTokens(code, codeVerifier) {
861
- const url = `${baseUrl}${OAUTH_TOKEN_ENDPOINT}`;
862
- logger.debug("Exchanging code for tokens:", url);
863
- const effectiveRedirectUri = resolvedRedirectUri ?? (typeof window !== "undefined" ? window.location.origin + window.location.pathname : null);
864
- if (!effectiveRedirectUri) {
865
- logger.error("redirectUri is required for token exchange in SSR environments. Set config.redirectUri explicitly.");
1005
+ function getLoginUrl(redirectUrl) {
1006
+ let redirect;
1007
+ if (redirectUrl != null) {
1008
+ redirect = redirectUrl;
1009
+ } else if (typeof window !== "undefined") {
1010
+ redirect = window.location.href;
1011
+ } else {
1012
+ logger.warn("getLoginUrl: SSR \u74B0\u5883\u3067 redirectUrl \u304C\u672A\u6307\u5B9A\u306E\u305F\u3081\u7A7A\u6587\u5B57\u306B\u30D5\u30A9\u30FC\u30EB\u30D0\u30C3\u30AF\u3057\u307E\u3059");
1013
+ redirect = "";
1014
+ }
1015
+ return `${baseUrl}/login?redirect=${encodeURIComponent(redirect)}&service=${encodeURIComponent(serviceCode)}`;
1016
+ }
1017
+ function getSignupUrl(redirectUrl) {
1018
+ let redirect;
1019
+ if (redirectUrl != null) {
1020
+ redirect = redirectUrl;
1021
+ } else if (typeof window !== "undefined") {
1022
+ redirect = window.location.href;
1023
+ } else {
1024
+ logger.warn("getSignupUrl: SSR \u74B0\u5883\u3067 redirectUrl \u304C\u672A\u6307\u5B9A\u306E\u305F\u3081\u7A7A\u6587\u5B57\u306B\u30D5\u30A9\u30FC\u30EB\u30D0\u30C3\u30AF\u3057\u307E\u3059");
1025
+ redirect = "";
1026
+ }
1027
+ return `${baseUrl}/signup?redirect=${encodeURIComponent(redirect)}&service=${encodeURIComponent(serviceCode)}`;
1028
+ }
1029
+ return { redirectToLogin, redirectToAuthorize, getLoginUrl, getSignupUrl };
1030
+ }
1031
+
1032
+ // src/client/password-reset.ts
1033
+ var RESET_PASSWORD_BASE = "/api/v1/auth/reset-password";
1034
+ function isBlank(value) {
1035
+ return !value || !value.trim();
1036
+ }
1037
+ function createPasswordResetMethods(deps) {
1038
+ const { baseUrl, logger, createError, fetchWithAuth, errorCodes } = deps;
1039
+ async function fetchPublic(endpoint, options = {}) {
1040
+ const url = `${baseUrl}${endpoint}`;
1041
+ logger.debug("Fetching (public):", url);
1042
+ let response;
1043
+ try {
1044
+ response = await fetch(url, {
1045
+ ...options,
1046
+ credentials: "include",
1047
+ headers: {
1048
+ "Content-Type": "application/json",
1049
+ ...sdkHeaders(),
1050
+ ...options.headers
1051
+ }
1052
+ });
1053
+ } catch (error) {
1054
+ logger.error("Network error:", error);
866
1055
  return {
867
1056
  error: {
868
- code: FFID_ERROR_CODES.TOKEN_EXCHANGE_ERROR,
869
- 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"
1057
+ code: errorCodes.NETWORK_ERROR,
1058
+ message: error instanceof Error ? error.message : "\u30CD\u30C3\u30C8\u30EF\u30FC\u30AF\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F"
870
1059
  }
871
1060
  };
872
1061
  }
873
- const body = {
874
- grant_type: "authorization_code",
875
- code,
876
- client_id: clientId,
877
- redirect_uri: effectiveRedirectUri
878
- };
879
- if (codeVerifier) {
880
- body.code_verifier = codeVerifier;
1062
+ let raw;
1063
+ try {
1064
+ raw = await response.json();
1065
+ } catch (parseError) {
1066
+ logger.error("Parse error:", parseError, "Status:", response.status);
1067
+ return {
1068
+ error: {
1069
+ code: errorCodes.PARSE_ERROR,
1070
+ 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})`
1071
+ }
1072
+ };
1073
+ }
1074
+ logger.debug("Response (public):", response.status, raw);
1075
+ checkVersionHeader(response, logger);
1076
+ if (!response.ok) {
1077
+ return {
1078
+ error: raw.error ?? {
1079
+ code: errorCodes.UNKNOWN_ERROR,
1080
+ message: "\u30D1\u30B9\u30EF\u30FC\u30C9\u30EA\u30BB\u30C3\u30C8\u306B\u5931\u6557\u3057\u307E\u3057\u305F"
1081
+ }
1082
+ };
1083
+ }
1084
+ if (raw.data === void 0) {
1085
+ return {
1086
+ error: {
1087
+ code: errorCodes.UNKNOWN_ERROR,
1088
+ message: "\u30B5\u30FC\u30D0\u30FC\u304B\u3089\u30C7\u30FC\u30BF\u304C\u8FD4\u3055\u308C\u307E\u305B\u3093\u3067\u3057\u305F"
1089
+ }
1090
+ };
1091
+ }
1092
+ return { data: raw.data };
1093
+ }
1094
+ async function requestPasswordReset(email) {
1095
+ if (isBlank(email)) {
1096
+ return {
1097
+ error: createError("VALIDATION_ERROR", "\u30E1\u30FC\u30EB\u30A2\u30C9\u30EC\u30B9\u306F\u5FC5\u9808\u3067\u3059")
1098
+ };
1099
+ }
1100
+ return fetchPublic(
1101
+ RESET_PASSWORD_BASE,
1102
+ {
1103
+ method: "POST",
1104
+ body: JSON.stringify({ email })
1105
+ }
1106
+ );
1107
+ }
1108
+ async function verifyPasswordResetToken(accessToken) {
1109
+ if (isBlank(accessToken)) {
1110
+ return {
1111
+ error: createError("VALIDATION_ERROR", "\u30A2\u30AF\u30BB\u30B9\u30C8\u30FC\u30AF\u30F3\u306F\u5FC5\u9808\u3067\u3059")
1112
+ };
881
1113
  }
1114
+ const query = new URLSearchParams({
1115
+ access_token: accessToken,
1116
+ type: "recovery"
1117
+ });
1118
+ return fetchPublic(
1119
+ `${RESET_PASSWORD_BASE}/verify?${query.toString()}`
1120
+ );
1121
+ }
1122
+ async function establishResetSession(accessToken, refreshToken) {
1123
+ if (isBlank(accessToken)) {
1124
+ return {
1125
+ error: createError("VALIDATION_ERROR", "\u30A2\u30AF\u30BB\u30B9\u30C8\u30FC\u30AF\u30F3\u306F\u5FC5\u9808\u3067\u3059")
1126
+ };
1127
+ }
1128
+ if (isBlank(refreshToken)) {
1129
+ return {
1130
+ error: createError("VALIDATION_ERROR", "\u30EA\u30D5\u30EC\u30C3\u30B7\u30E5\u30C8\u30FC\u30AF\u30F3\u306F\u5FC5\u9808\u3067\u3059")
1131
+ };
1132
+ }
1133
+ return fetchPublic(
1134
+ `${RESET_PASSWORD_BASE}/session`,
1135
+ {
1136
+ method: "POST",
1137
+ body: JSON.stringify({ accessToken, refreshToken })
1138
+ }
1139
+ );
1140
+ }
1141
+ async function confirmPasswordReset(password) {
1142
+ if (isBlank(password)) {
1143
+ return {
1144
+ error: createError("VALIDATION_ERROR", "\u30D1\u30B9\u30EF\u30FC\u30C9\u306F\u5FC5\u9808\u3067\u3059")
1145
+ };
1146
+ }
1147
+ return fetchWithAuth(
1148
+ `${RESET_PASSWORD_BASE}/confirm`,
1149
+ {
1150
+ method: "POST",
1151
+ body: JSON.stringify({ password })
1152
+ }
1153
+ );
1154
+ }
1155
+ return {
1156
+ requestPasswordReset,
1157
+ verifyPasswordResetToken,
1158
+ establishResetSession,
1159
+ confirmPasswordReset
1160
+ };
1161
+ }
1162
+
1163
+ // src/client/otp.ts
1164
+ var OTP_BASE = "/api/v1/auth/otp";
1165
+ function isBlank2(value) {
1166
+ return !value || !value.trim();
1167
+ }
1168
+ function createOtpMethods(deps) {
1169
+ const { baseUrl, logger, createError, errorCodes } = deps;
1170
+ async function fetchPublic(endpoint, options = {}) {
1171
+ const url = `${baseUrl}${endpoint}`;
1172
+ logger.debug("Fetching (public):", url);
882
1173
  let response;
883
1174
  try {
884
1175
  response = await fetch(url, {
885
- method: "POST",
886
- credentials: "omit",
887
- headers: { "Content-Type": "application/x-www-form-urlencoded", ...sdkHeaders() },
888
- body: new URLSearchParams(body).toString()
1176
+ ...options,
1177
+ credentials: "include",
1178
+ headers: {
1179
+ "Content-Type": "application/json",
1180
+ ...sdkHeaders(),
1181
+ ...options.headers
1182
+ }
889
1183
  });
890
1184
  } catch (error) {
891
- logger.error("Network error during token exchange:", error);
1185
+ logger.error("Network error:", error);
1186
+ return {
1187
+ error: {
1188
+ code: errorCodes.NETWORK_ERROR,
1189
+ message: error instanceof Error ? error.message : "\u30CD\u30C3\u30C8\u30EF\u30FC\u30AF\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F"
1190
+ }
1191
+ };
1192
+ }
1193
+ let raw;
1194
+ try {
1195
+ raw = await response.json();
1196
+ } catch (parseError) {
1197
+ logger.error("Parse error:", parseError, "Status:", response.status);
1198
+ return {
1199
+ error: {
1200
+ code: errorCodes.PARSE_ERROR,
1201
+ 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})`
1202
+ }
1203
+ };
1204
+ }
1205
+ logger.debug("Response (public):", response.status, raw);
1206
+ checkVersionHeader(response, logger);
1207
+ if (!response.ok) {
1208
+ return {
1209
+ error: raw.error ?? {
1210
+ code: errorCodes.UNKNOWN_ERROR,
1211
+ message: "OTP\u8A8D\u8A3C\u306B\u5931\u6557\u3057\u307E\u3057\u305F"
1212
+ }
1213
+ };
1214
+ }
1215
+ if (raw.data === void 0) {
1216
+ return {
1217
+ error: {
1218
+ code: errorCodes.UNKNOWN_ERROR,
1219
+ message: "\u30B5\u30FC\u30D0\u30FC\u304B\u3089\u30C7\u30FC\u30BF\u304C\u8FD4\u3055\u308C\u307E\u305B\u3093\u3067\u3057\u305F"
1220
+ }
1221
+ };
1222
+ }
1223
+ return { data: raw.data };
1224
+ }
1225
+ async function sendOtp(email, options) {
1226
+ if (isBlank2(email)) {
1227
+ return {
1228
+ error: createError("VALIDATION_ERROR", "\u30E1\u30FC\u30EB\u30A2\u30C9\u30EC\u30B9\u306F\u5FC5\u9808\u3067\u3059")
1229
+ };
1230
+ }
1231
+ return fetchPublic(
1232
+ `${OTP_BASE}/send`,
1233
+ {
1234
+ method: "POST",
1235
+ body: JSON.stringify({
1236
+ email,
1237
+ ...options?.redirectUrl ? { redirectUrl: options.redirectUrl } : {}
1238
+ })
1239
+ }
1240
+ );
1241
+ }
1242
+ async function verifyOtp(params) {
1243
+ if (isBlank2(params.accessToken)) {
892
1244
  return {
893
- error: {
894
- code: FFID_ERROR_CODES.NETWORK_ERROR,
895
- message: error instanceof Error ? error.message : "\u30CD\u30C3\u30C8\u30EF\u30FC\u30AF\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F"
896
- }
1245
+ error: createError("VALIDATION_ERROR", "\u30A2\u30AF\u30BB\u30B9\u30C8\u30FC\u30AF\u30F3\u306F\u5FC5\u9808\u3067\u3059")
897
1246
  };
898
1247
  }
899
- let tokenResponse;
900
- try {
901
- tokenResponse = await response.json();
902
- } catch (parseError) {
903
- logger.error("Parse error during token exchange:", parseError);
1248
+ if (isBlank2(params.refreshToken)) {
904
1249
  return {
905
- error: {
906
- code: FFID_ERROR_CODES.PARSE_ERROR,
907
- message: `\u30C8\u30FC\u30AF\u30F3\u30EC\u30B9\u30DD\u30F3\u30B9\u306E\u89E3\u6790\u306B\u5931\u6557\u3057\u307E\u3057\u305F (status: ${response.status})`
908
- }
1250
+ error: createError("VALIDATION_ERROR", "\u30EA\u30D5\u30EC\u30C3\u30B7\u30E5\u30C8\u30FC\u30AF\u30F3\u306F\u5FC5\u9808\u3067\u3059")
909
1251
  };
910
1252
  }
911
- if (!response.ok) {
912
- const errorBody = tokenResponse;
1253
+ return fetchPublic(
1254
+ `${OTP_BASE}/verify`,
1255
+ {
1256
+ method: "POST",
1257
+ body: JSON.stringify({
1258
+ accessToken: params.accessToken,
1259
+ refreshToken: params.refreshToken
1260
+ })
1261
+ }
1262
+ );
1263
+ }
1264
+ return {
1265
+ sendOtp,
1266
+ verifyOtp
1267
+ };
1268
+ }
1269
+
1270
+ // src/client/ffid-client.ts
1271
+ var UNAUTHORIZED_STATUS2 = 401;
1272
+ var SDK_LOG_PREFIX = "[FFID SDK]";
1273
+ var noopLogger = {
1274
+ debug: () => {
1275
+ },
1276
+ info: () => {
1277
+ },
1278
+ warn: (...args) => console.warn(SDK_LOG_PREFIX, ...args),
1279
+ error: (...args) => console.error(SDK_LOG_PREFIX, ...args)
1280
+ };
1281
+ var consoleLogger = {
1282
+ debug: (...args) => console.debug(SDK_LOG_PREFIX, ...args),
1283
+ info: (...args) => console.info(SDK_LOG_PREFIX, ...args),
1284
+ warn: (...args) => console.warn(SDK_LOG_PREFIX, ...args),
1285
+ error: (...args) => console.error(SDK_LOG_PREFIX, ...args)
1286
+ };
1287
+ var FFID_ERROR_CODES = {
1288
+ NETWORK_ERROR: "NETWORK_ERROR",
1289
+ PARSE_ERROR: "PARSE_ERROR",
1290
+ UNKNOWN_ERROR: "UNKNOWN_ERROR",
1291
+ TOKEN_EXCHANGE_ERROR: "TOKEN_EXCHANGE_ERROR",
1292
+ TOKEN_REFRESH_ERROR: "TOKEN_REFRESH_ERROR",
1293
+ NO_TOKENS: "NO_TOKENS",
1294
+ TOKEN_VERIFICATION_ERROR: "TOKEN_VERIFICATION_ERROR"
1295
+ };
1296
+ var EXT_CHECK_ENDPOINT = "/api/v1/subscriptions/ext/check";
1297
+ function createFFIDClient(config) {
1298
+ if (!config.serviceCode || !config.serviceCode.trim()) {
1299
+ throw new Error("FFID Client: serviceCode \u304C\u672A\u8A2D\u5B9A\u3067\u3059");
1300
+ }
1301
+ const baseUrl = config.apiBaseUrl ?? DEFAULT_API_BASE_URL;
1302
+ const authMode = config.authMode ?? "cookie";
1303
+ const clientId = config.clientId ?? config.serviceCode;
1304
+ const resolvedRedirectUri = config.redirectUri ?? null;
1305
+ const serviceApiKey = config.serviceApiKey?.trim();
1306
+ const verifyStrategy = config.verifyStrategy ?? "jwt";
1307
+ const cache = config.cache;
1308
+ const timeout = config.timeout;
1309
+ if (authMode === "service-key" && !serviceApiKey) {
1310
+ throw new Error("FFID Client: service-key \u30E2\u30FC\u30C9\u3067\u306F serviceApiKey \u304C\u5FC5\u9808\u3067\u3059");
1311
+ }
1312
+ if (cache && cache.ttl <= 0) {
1313
+ throw new Error("FFID Client: cache.ttl \u306F\u6B63\u306E\u6570\u5024\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044");
1314
+ }
1315
+ if (timeout !== void 0 && timeout <= 0) {
1316
+ throw new Error("FFID Client: timeout \u306F\u6B63\u306E\u6570\u5024\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044");
1317
+ }
1318
+ const logger = config.logger ?? (config.debug ? consoleLogger : noopLogger);
1319
+ const tokenStore = authMode === "token" ? createTokenStore() : createTokenStore("memory");
1320
+ function createError(code, message) {
1321
+ return { code, message };
1322
+ }
1323
+ function buildFetchOptions(options) {
1324
+ if (authMode === "service-key") {
913
1325
  return {
914
- error: {
915
- code: errorBody.error ?? FFID_ERROR_CODES.TOKEN_EXCHANGE_ERROR,
916
- message: errorBody.error_description ?? "\u30C8\u30FC\u30AF\u30F3\u4EA4\u63DB\u306B\u5931\u6557\u3057\u307E\u3057\u305F"
1326
+ ...options,
1327
+ credentials: "omit",
1328
+ headers: {
1329
+ "Content-Type": "application/json",
1330
+ ...sdkHeaders(),
1331
+ "X-Service-Api-Key": serviceApiKey,
1332
+ ...options.headers
917
1333
  }
918
1334
  };
919
1335
  }
920
- tokenStore.setTokens({
921
- accessToken: tokenResponse.access_token,
922
- refreshToken: tokenResponse.refresh_token,
923
- expiresAt: Date.now() + tokenResponse.expires_in * MS_PER_SECOND
924
- });
925
- logger.debug("Token exchange successful");
926
- return { data: void 0 };
927
- }
928
- async function refreshAccessToken() {
929
- const tokens = tokenStore.getTokens();
930
- if (!tokens) {
1336
+ if (authMode === "token") {
1337
+ const tokens = tokenStore.getTokens();
1338
+ const headers = {
1339
+ "Content-Type": "application/json",
1340
+ ...sdkHeaders(),
1341
+ ...options.headers
1342
+ };
1343
+ if (tokens) {
1344
+ headers["Authorization"] = `Bearer ${tokens.accessToken}`;
1345
+ }
931
1346
  return {
932
- error: {
933
- code: FFID_ERROR_CODES.NO_TOKENS,
934
- message: "\u30EA\u30D5\u30EC\u30C3\u30B7\u30E5\u30C8\u30FC\u30AF\u30F3\u304C\u3042\u308A\u307E\u305B\u3093"
935
- }
1347
+ ...options,
1348
+ credentials: "omit",
1349
+ headers
936
1350
  };
937
1351
  }
938
- const url = `${baseUrl}${OAUTH_TOKEN_ENDPOINT}`;
939
- logger.debug("Refreshing access token:", url);
1352
+ return {
1353
+ ...options,
1354
+ credentials: "include",
1355
+ headers: {
1356
+ "Content-Type": "application/json",
1357
+ ...sdkHeaders(),
1358
+ ...options.headers
1359
+ }
1360
+ };
1361
+ }
1362
+ const { exchangeCodeForTokens, refreshAccessToken, signOutToken } = createOAuthTokenMethods({
1363
+ baseUrl,
1364
+ clientId,
1365
+ resolvedRedirectUri,
1366
+ tokenStore,
1367
+ logger,
1368
+ errorCodes: FFID_ERROR_CODES
1369
+ });
1370
+ async function fetchWithAuth(endpoint, options = {}) {
1371
+ const url = `${baseUrl}${endpoint}`;
1372
+ logger.debug("Fetching:", url);
1373
+ const fetchOptions = buildFetchOptions(options);
940
1374
  let response;
941
1375
  try {
942
- response = await fetch(url, {
943
- method: "POST",
944
- credentials: "omit",
945
- headers: { "Content-Type": "application/x-www-form-urlencoded", ...sdkHeaders() },
946
- body: new URLSearchParams({
947
- grant_type: "refresh_token",
948
- refresh_token: tokens.refreshToken,
949
- client_id: clientId
950
- }).toString()
951
- });
1376
+ response = await fetch(url, fetchOptions);
952
1377
  } catch (error) {
953
- logger.error("Network error during token refresh:", error);
1378
+ logger.error("Network error:", error);
954
1379
  return {
955
1380
  error: {
956
1381
  code: FFID_ERROR_CODES.NETWORK_ERROR,
@@ -958,88 +1383,83 @@ function createFFIDClient(config) {
958
1383
  }
959
1384
  };
960
1385
  }
961
- let tokenResponse;
1386
+ if (authMode === "token" && response.status === UNAUTHORIZED_STATUS2) {
1387
+ const refreshResult = await refreshAccessToken();
1388
+ if (!refreshResult.error) {
1389
+ logger.debug("Token refreshed, retrying request");
1390
+ const retryOptions = buildFetchOptions(options);
1391
+ try {
1392
+ response = await fetch(url, retryOptions);
1393
+ } catch (retryError) {
1394
+ logger.error("Network error on retry:", retryError);
1395
+ return {
1396
+ error: {
1397
+ code: FFID_ERROR_CODES.NETWORK_ERROR,
1398
+ message: retryError instanceof Error ? retryError.message : "\u30CD\u30C3\u30C8\u30EF\u30FC\u30AF\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F"
1399
+ }
1400
+ };
1401
+ }
1402
+ } else {
1403
+ logger.warn("Token refresh failed, returning refresh error:", refreshResult.error);
1404
+ return { error: refreshResult.error };
1405
+ }
1406
+ }
1407
+ let raw;
962
1408
  try {
963
- tokenResponse = await response.json();
1409
+ raw = await response.json();
964
1410
  } catch (parseError) {
965
- logger.error("Parse error during token refresh:", parseError);
1411
+ logger.error("Parse error:", parseError, "Status:", response.status);
966
1412
  return {
967
1413
  error: {
968
1414
  code: FFID_ERROR_CODES.PARSE_ERROR,
969
- message: `\u30C8\u30FC\u30AF\u30F3\u30EC\u30B9\u30DD\u30F3\u30B9\u306E\u89E3\u6790\u306B\u5931\u6557\u3057\u307E\u3057\u305F (status: ${response.status})`
1415
+ 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})`
970
1416
  }
971
1417
  };
972
1418
  }
1419
+ logger.debug("Response:", response.status, raw);
1420
+ checkVersionHeader(response, logger);
973
1421
  if (!response.ok) {
974
- const errorBody = tokenResponse;
975
- logger.error("Token refresh failed:", errorBody);
976
- const irrecoverableErrors = ["token_revoked", "invalid_grant"];
977
- if (errorBody.error && irrecoverableErrors.includes(errorBody.error)) {
978
- tokenStore.clearTokens();
979
- logger.debug("Cleared tokens due to irrecoverable refresh error:", errorBody.error);
980
- }
1422
+ return {
1423
+ error: raw.error ?? {
1424
+ code: FFID_ERROR_CODES.UNKNOWN_ERROR,
1425
+ message: "\u4E0D\u660E\u306A\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F"
1426
+ }
1427
+ };
1428
+ }
1429
+ if (raw.data === void 0) {
981
1430
  return {
982
1431
  error: {
983
- code: errorBody.error ?? FFID_ERROR_CODES.TOKEN_REFRESH_ERROR,
984
- message: errorBody.error_description ?? "\u30C8\u30FC\u30AF\u30F3\u30EA\u30D5\u30EC\u30C3\u30B7\u30E5\u306B\u5931\u6557\u3057\u307E\u3057\u305F"
1432
+ code: FFID_ERROR_CODES.UNKNOWN_ERROR,
1433
+ message: "\u30B5\u30FC\u30D0\u30FC\u304B\u3089\u30C7\u30FC\u30BF\u304C\u8FD4\u3055\u308C\u307E\u305B\u3093\u3067\u3057\u305F"
985
1434
  }
986
1435
  };
987
1436
  }
988
- tokenStore.setTokens({
989
- accessToken: tokenResponse.access_token,
990
- refreshToken: tokenResponse.refresh_token,
991
- expiresAt: Date.now() + tokenResponse.expires_in * MS_PER_SECOND
992
- });
993
- logger.debug("Token refresh successful");
994
- return { data: void 0 };
1437
+ return { data: raw.data };
995
1438
  }
996
- function redirectToLogin() {
997
- if (typeof window === "undefined") {
998
- logger.debug("Cannot redirect in SSR context");
999
- return false;
1000
- }
1439
+ const { getSession, signOutCookie } = createSessionMethods({
1440
+ authMode,
1441
+ baseUrl,
1442
+ serviceCode: config.serviceCode,
1443
+ tokenStore,
1444
+ logger,
1445
+ fetchWithAuth,
1446
+ refreshAccessToken,
1447
+ errorCodes: FFID_ERROR_CODES
1448
+ });
1449
+ async function signOut() {
1001
1450
  if (authMode === "token") {
1002
- return redirectToAuthorize();
1451
+ return signOutToken();
1003
1452
  }
1004
- const currentUrl = window.location.href;
1005
- const loginUrl = `${baseUrl}/login?redirect=${encodeURIComponent(currentUrl)}&service=${encodeURIComponent(config.serviceCode)}`;
1006
- logger.debug("Redirecting to login:", loginUrl);
1007
- window.location.href = loginUrl;
1008
- return true;
1009
- }
1010
- function redirectToAuthorize() {
1011
- const verifier = generateCodeVerifier();
1012
- storeCodeVerifier(verifier);
1013
- generateCodeChallenge(verifier).then((challenge) => {
1014
- const state = generateRandomState();
1015
- const redirectUri = resolvedRedirectUri ?? window.location.origin + window.location.pathname;
1016
- const params = new URLSearchParams({
1017
- response_type: "code",
1018
- client_id: clientId,
1019
- redirect_uri: redirectUri,
1020
- state,
1021
- code_challenge: challenge,
1022
- code_challenge_method: "S256"
1023
- });
1024
- const authorizeUrl = `${baseUrl}${OAUTH_AUTHORIZE_ENDPOINT}?${params.toString()}`;
1025
- logger.debug("Redirecting to authorize:", authorizeUrl);
1026
- window.location.href = authorizeUrl;
1027
- }).catch((error) => {
1028
- logger.error("Failed to generate code challenge:", error);
1029
- });
1030
- return true;
1031
- }
1032
- function getLoginUrl(redirectUrl) {
1033
- const redirect = redirectUrl ?? (typeof window !== "undefined" ? window.location.href : "");
1034
- return `${baseUrl}/login?redirect=${encodeURIComponent(redirect)}&service=${encodeURIComponent(config.serviceCode)}`;
1035
- }
1036
- function getSignupUrl(redirectUrl) {
1037
- const redirect = redirectUrl ?? (typeof window !== "undefined" ? window.location.href : "");
1038
- return `${baseUrl}/signup?redirect=${encodeURIComponent(redirect)}&service=${encodeURIComponent(config.serviceCode)}`;
1039
- }
1040
- function createError(code, message) {
1041
- return { code, message };
1453
+ return signOutCookie();
1042
1454
  }
1455
+ const { redirectToLogin, getLoginUrl, getSignupUrl } = createRedirectMethods({
1456
+ authMode,
1457
+ baseUrl,
1458
+ clientId,
1459
+ serviceCode: config.serviceCode,
1460
+ resolvedRedirectUri,
1461
+ logger
1462
+ });
1043
1463
  async function checkSubscription(params) {
1044
1464
  if (!params.userId || !params.organizationId) {
1045
1465
  return {
@@ -1059,6 +1479,24 @@ function createFFIDClient(config) {
1059
1479
  fetchWithAuth,
1060
1480
  createError
1061
1481
  });
1482
+ const {
1483
+ requestPasswordReset,
1484
+ verifyPasswordResetToken,
1485
+ establishResetSession,
1486
+ confirmPasswordReset
1487
+ } = createPasswordResetMethods({
1488
+ baseUrl,
1489
+ logger,
1490
+ createError,
1491
+ fetchWithAuth,
1492
+ errorCodes: FFID_ERROR_CODES
1493
+ });
1494
+ const { sendOtp, verifyOtp } = createOtpMethods({
1495
+ baseUrl,
1496
+ logger,
1497
+ createError,
1498
+ errorCodes: FFID_ERROR_CODES
1499
+ });
1062
1500
  const verifyAccessToken = createVerifyAccessToken({
1063
1501
  authMode,
1064
1502
  baseUrl,
@@ -1084,6 +1522,12 @@ function createFFIDClient(config) {
1084
1522
  createCheckoutSession,
1085
1523
  createPortalSession,
1086
1524
  verifyAccessToken,
1525
+ requestPasswordReset,
1526
+ verifyPasswordResetToken,
1527
+ establishResetSession,
1528
+ confirmPasswordReset,
1529
+ sendOtp,
1530
+ verifyOtp,
1087
1531
  /** Token store (token mode only) */
1088
1532
  tokenStore,
1089
1533
  /** Resolved auth mode */
@@ -1096,11 +1540,6 @@ function createFFIDClient(config) {
1096
1540
  redirectUri: resolvedRedirectUri
1097
1541
  };
1098
1542
  }
1099
- function generateRandomState() {
1100
- const array = new Uint8Array(STATE_RANDOM_BYTES);
1101
- crypto.getRandomValues(array);
1102
- return Array.from(array, (byte) => byte.toString(HEX_BASE2).padStart(2, "0")).join("");
1103
- }
1104
1543
  var DEFAULT_REFRESH_INTERVAL_MS = 5 * 60 * 1e3;
1105
1544
  var TOKEN_REFRESH_RATIO = 0.8;
1106
1545
  var FFIDContext = react.createContext(null);
@@ -1186,7 +1625,9 @@ function FFIDProvider({
1186
1625
  }
1187
1626
  }, [client]);
1188
1627
  const login = react.useCallback(() => {
1189
- client.redirectToLogin();
1628
+ client.redirectToLogin().catch((err) => {
1629
+ client.logger.error("\u30ED\u30B0\u30A4\u30F3\u30EA\u30C0\u30A4\u30EC\u30AF\u30C8\u306B\u5931\u6557\u3057\u307E\u3057\u305F:", err);
1630
+ });
1190
1631
  }, [client]);
1191
1632
  const logout = react.useCallback(async () => {
1192
1633
  client.logger.debug("Signing out...");