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