connectbase-client 3.7.2 → 3.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -35,15 +35,23 @@ interface HttpClientConfig {
35
35
  refreshToken?: string;
36
36
  /**
37
37
  * 토큰 저장 방식. **기본값은 'none' (메모리 저장)**.
38
- * XSS 취약점이 하나라도 있을 경우 세션이 탈취되므로, 영구 저장 옵션은
39
- * 위험을 이해하고 명시적으로 선택한 경우에만 사용한다.
38
+ * XSS 취약점이 하나라도 있을 경우 영구 저장은 즉시 세션 탈취로 이어지므로,
39
+ * 영구 저장 옵션은 위험을 이해하고 명시적으로 선택한 경우에만 사용한다.
40
40
  *
41
- * - 'none' (권장·기본값): 메모리에만 저장. 새로고침 재로그인 필요
42
- * 서버가 refresh token HttpOnly;Secure;SameSite 쿠키로 발급하는 구성에서만 사용
43
- * - 'sessionStorage': 종료 삭제. JS 접근 가능 → XSS 로 탈취 가능
41
+ * - 'none' (권장·기본값): access token 만 메모리 저장. refresh token 서버가 발급한
42
+ * HttpOnly cookie 로만 보관되어 JS 접근할 없다 (XSS 시 탈취 불가). 새로고침 후에는
43
+ * `autoRestoreSession`(기본 true) 으로 cookie 만으로 자동 복구된다.
44
+ * - 'sessionStorage': 탭 종료 시 삭제. JS 접근 가능 → XSS 로 탭 세션 탈취 가능
44
45
  * - 'localStorage': 브라우저 종료 후에도 유지. JS 접근 가능 → XSS 로 영구 탈취 가능
45
46
  */
46
47
  persistence?: TokenPersistence;
48
+ /**
49
+ * 새로고침/탭 재개 시 HttpOnly cookie 로부터 access token 을 자동 복구할지 여부.
50
+ * 기본값은 브라우저 환경에서 true. 비-브라우저(Node.js, RN) 에서는 무시된다.
51
+ *
52
+ * cookie 가 없는 경우(미로그인) 조용히 실패하며 콘솔 에러를 발생시키지 않는다.
53
+ */
54
+ autoRestoreSession?: boolean;
47
55
  /**
48
56
  * 요청별 기본 타임아웃(ms). 개별 호출의 `timeout` 이 우선.
49
57
  * 기본값 30000ms. 0 또는 음수 지정 시 타임아웃 비활성화.
@@ -121,6 +129,17 @@ declare class HttpClient {
121
129
  */
122
130
  getBaseUrl(): string;
123
131
  private refreshAccessToken;
132
+ /**
133
+ * 새로고침/탭 재개 시 HttpOnly cookie 만으로 access token 을 복구한다.
134
+ *
135
+ * 동작:
136
+ * - 메모리에 access token 이 이미 있으면 그대로 반환 (true).
137
+ * - 없으면 `/v1/auth/re-issue` 를 cookie 만으로 호출. cookie 가 있으면 access token 회복.
138
+ * - cookie 가 없거나(미로그인) 만료된 경우 조용히 false 반환 (콘솔 에러 없음).
139
+ *
140
+ * 비-브라우저 환경에서는 cookie 흐름이 없으므로 즉시 false 반환.
141
+ */
142
+ tryRestoreSessionFromCookie(): Promise<boolean>;
124
143
  private emitError;
125
144
  private isTokenExpired;
126
145
  private prepareHeaders;
@@ -7420,12 +7439,23 @@ interface ConnectBaseConfig {
7420
7439
  onTokenExpired?: () => void;
7421
7440
  /**
7422
7441
  * 토큰 저장 방식.
7423
- * - 'none' (기본·권장): 메모리에만 저장. 새로고침 재로그인 필요. XSS 노출 최소화
7424
- * - 'sessionStorage': 종료 삭제 (XSS 시 현재 세션 탈취 가능)
7425
- * - 'localStorage': 브라우저 종료 후에도 유지 (XSS 토큰 영구 탈취 가능 — 콘솔 경고 출력)
7442
+ * - 'none' (기본·권장): access token 메모리. refresh token 서버 HttpOnly cookie 로
7443
+ * 보관되어 JS 접근할 없음 (XSS 시 탈취 불가). 새로고침 후에는 `autoRestoreSession`
7444
+ * (기본 true) 으로 cookie 만으로 자동 복구.
7445
+ * - 'sessionStorage': 탭 종료 시 삭제 (XSS 시 탭 세션 탈취 가능 — 콘솔 경고 출력)
7446
+ * - 'localStorage': 브라우저 종료 후에도 유지 (XSS 시 영구 탈취 가능 — 콘솔 경고 출력)
7426
7447
  * @default 'none'
7427
7448
  */
7428
7449
  persistence?: TokenPersistence;
7450
+ /**
7451
+ * 새로고침/탭 재개 시 HttpOnly cookie 만으로 access token 을 자동 복구할지 여부.
7452
+ * 브라우저 환경에서 기본 true. cookie 가 없거나 만료되었으면 조용히 실패.
7453
+ *
7454
+ * cookie 가 없는 미로그인 상태에서 콘솔에 401 노이즈가 찍히지 않도록 silent 처리한다.
7455
+ *
7456
+ * @default true (브라우저), false (Node.js / RN)
7457
+ */
7458
+ autoRestoreSession?: boolean;
7429
7459
  /**
7430
7460
  * 에러 트래커 설정
7431
7461
  */
@@ -7578,6 +7608,15 @@ declare class ConnectBase {
7578
7608
  */
7579
7609
  readonly endpoint: EndpointAPI;
7580
7610
  constructor(config?: ConnectBaseConfig);
7611
+ /**
7612
+ * 새로고침/탭 재개 후 cookie 만으로 세션을 복원한다 (브라우저 전용).
7613
+ *
7614
+ * 일반적으로는 ConnectBase 생성 시 `autoRestoreSession: true` (기본값) 으로 자동 호출되지만,
7615
+ * 호출 결과를 await 해서 로그인 상태에 따라 다른 UI 를 그리고 싶다면 명시적으로 호출한다.
7616
+ *
7617
+ * @returns access token 복원 성공 시 true, 미로그인/cookie 만료 시 false
7618
+ */
7619
+ restoreSession(): Promise<boolean>;
7581
7620
  /**
7582
7621
  * 수동으로 토큰 설정 (기존 토큰으로 세션 복원 시)
7583
7622
  */
package/dist/index.d.ts CHANGED
@@ -35,15 +35,23 @@ interface HttpClientConfig {
35
35
  refreshToken?: string;
36
36
  /**
37
37
  * 토큰 저장 방식. **기본값은 'none' (메모리 저장)**.
38
- * XSS 취약점이 하나라도 있을 경우 세션이 탈취되므로, 영구 저장 옵션은
39
- * 위험을 이해하고 명시적으로 선택한 경우에만 사용한다.
38
+ * XSS 취약점이 하나라도 있을 경우 영구 저장은 즉시 세션 탈취로 이어지므로,
39
+ * 영구 저장 옵션은 위험을 이해하고 명시적으로 선택한 경우에만 사용한다.
40
40
  *
41
- * - 'none' (권장·기본값): 메모리에만 저장. 새로고침 재로그인 필요
42
- * 서버가 refresh token HttpOnly;Secure;SameSite 쿠키로 발급하는 구성에서만 사용
43
- * - 'sessionStorage': 종료 삭제. JS 접근 가능 → XSS 로 탈취 가능
41
+ * - 'none' (권장·기본값): access token 만 메모리 저장. refresh token 서버가 발급한
42
+ * HttpOnly cookie 로만 보관되어 JS 접근할 없다 (XSS 시 탈취 불가). 새로고침 후에는
43
+ * `autoRestoreSession`(기본 true) 으로 cookie 만으로 자동 복구된다.
44
+ * - 'sessionStorage': 탭 종료 시 삭제. JS 접근 가능 → XSS 로 탭 세션 탈취 가능
44
45
  * - 'localStorage': 브라우저 종료 후에도 유지. JS 접근 가능 → XSS 로 영구 탈취 가능
45
46
  */
46
47
  persistence?: TokenPersistence;
48
+ /**
49
+ * 새로고침/탭 재개 시 HttpOnly cookie 로부터 access token 을 자동 복구할지 여부.
50
+ * 기본값은 브라우저 환경에서 true. 비-브라우저(Node.js, RN) 에서는 무시된다.
51
+ *
52
+ * cookie 가 없는 경우(미로그인) 조용히 실패하며 콘솔 에러를 발생시키지 않는다.
53
+ */
54
+ autoRestoreSession?: boolean;
47
55
  /**
48
56
  * 요청별 기본 타임아웃(ms). 개별 호출의 `timeout` 이 우선.
49
57
  * 기본값 30000ms. 0 또는 음수 지정 시 타임아웃 비활성화.
@@ -121,6 +129,17 @@ declare class HttpClient {
121
129
  */
122
130
  getBaseUrl(): string;
123
131
  private refreshAccessToken;
132
+ /**
133
+ * 새로고침/탭 재개 시 HttpOnly cookie 만으로 access token 을 복구한다.
134
+ *
135
+ * 동작:
136
+ * - 메모리에 access token 이 이미 있으면 그대로 반환 (true).
137
+ * - 없으면 `/v1/auth/re-issue` 를 cookie 만으로 호출. cookie 가 있으면 access token 회복.
138
+ * - cookie 가 없거나(미로그인) 만료된 경우 조용히 false 반환 (콘솔 에러 없음).
139
+ *
140
+ * 비-브라우저 환경에서는 cookie 흐름이 없으므로 즉시 false 반환.
141
+ */
142
+ tryRestoreSessionFromCookie(): Promise<boolean>;
124
143
  private emitError;
125
144
  private isTokenExpired;
126
145
  private prepareHeaders;
@@ -7420,12 +7439,23 @@ interface ConnectBaseConfig {
7420
7439
  onTokenExpired?: () => void;
7421
7440
  /**
7422
7441
  * 토큰 저장 방식.
7423
- * - 'none' (기본·권장): 메모리에만 저장. 새로고침 재로그인 필요. XSS 노출 최소화
7424
- * - 'sessionStorage': 종료 삭제 (XSS 시 현재 세션 탈취 가능)
7425
- * - 'localStorage': 브라우저 종료 후에도 유지 (XSS 토큰 영구 탈취 가능 — 콘솔 경고 출력)
7442
+ * - 'none' (기본·권장): access token 메모리. refresh token 서버 HttpOnly cookie 로
7443
+ * 보관되어 JS 접근할 없음 (XSS 시 탈취 불가). 새로고침 후에는 `autoRestoreSession`
7444
+ * (기본 true) 으로 cookie 만으로 자동 복구.
7445
+ * - 'sessionStorage': 탭 종료 시 삭제 (XSS 시 탭 세션 탈취 가능 — 콘솔 경고 출력)
7446
+ * - 'localStorage': 브라우저 종료 후에도 유지 (XSS 시 영구 탈취 가능 — 콘솔 경고 출력)
7426
7447
  * @default 'none'
7427
7448
  */
7428
7449
  persistence?: TokenPersistence;
7450
+ /**
7451
+ * 새로고침/탭 재개 시 HttpOnly cookie 만으로 access token 을 자동 복구할지 여부.
7452
+ * 브라우저 환경에서 기본 true. cookie 가 없거나 만료되었으면 조용히 실패.
7453
+ *
7454
+ * cookie 가 없는 미로그인 상태에서 콘솔에 401 노이즈가 찍히지 않도록 silent 처리한다.
7455
+ *
7456
+ * @default true (브라우저), false (Node.js / RN)
7457
+ */
7458
+ autoRestoreSession?: boolean;
7429
7459
  /**
7430
7460
  * 에러 트래커 설정
7431
7461
  */
@@ -7578,6 +7608,15 @@ declare class ConnectBase {
7578
7608
  */
7579
7609
  readonly endpoint: EndpointAPI;
7580
7610
  constructor(config?: ConnectBaseConfig);
7611
+ /**
7612
+ * 새로고침/탭 재개 후 cookie 만으로 세션을 복원한다 (브라우저 전용).
7613
+ *
7614
+ * 일반적으로는 ConnectBase 생성 시 `autoRestoreSession: true` (기본값) 으로 자동 호출되지만,
7615
+ * 호출 결과를 await 해서 로그인 상태에 따라 다른 UI 를 그리고 싶다면 명시적으로 호출한다.
7616
+ *
7617
+ * @returns access token 복원 성공 시 true, 미로그인/cookie 만료 시 false
7618
+ */
7619
+ restoreSession(): Promise<boolean>;
7581
7620
  /**
7582
7621
  * 수동으로 토큰 설정 (기존 토큰으로 세션 복원 시)
7583
7622
  */
package/dist/index.js CHANGED
@@ -109,11 +109,11 @@ var HttpClient = class {
109
109
  if (typeof window === "undefined") return;
110
110
  if (this.config.persistence === "localStorage") {
111
111
  console.warn(
112
- `[connect-base-client] persistence="localStorage" \uB294 XSS \uC2DC \uD1A0\uD070 \uC601\uAD6C \uD0C8\uCDE8 \uC704\uD5D8\uC774 \uC788\uC2B5\uB2C8\uB2E4. refresh token \uC740 \uC11C\uBC84 HttpOnly \uCFE0\uD0A4\uB85C \uBC1C\uAE09\uBC1B\uACE0 \uAE30\uBCF8\uAC12('none')\uC744 \uC0AC\uC6A9\uD558\uC138\uC694.`
112
+ `[connect-base-client] persistence="localStorage" \uB294 XSS \uC2DC refresh token \uC601\uAD6C \uD0C8\uCDE8 \uC704\uD5D8\uC774 \uC788\uC2B5\uB2C8\uB2E4. \uAE30\uBCF8\uAC12('none') \uC744 \uC0AC\uC6A9\uD558\uBA74 \uC11C\uBC84\uAC00 \uBC1C\uAE09\uD55C HttpOnly cookie \uB85C\uB9CC refresh token \uC774 \uBCF4\uAD00\uB418\uC5B4 JS \uAC00 \uC811\uADFC\uD560 \uC218 \uC5C6\uACE0, \uC0C8\uB85C\uACE0\uCE68 \uD6C4\uC5D0\uB3C4 autoRestoreSession \uC73C\uB85C \uC790\uB3D9 \uBCF5\uAD6C\uB429\uB2C8\uB2E4.`
113
113
  );
114
114
  } else if (this.config.persistence === "sessionStorage") {
115
115
  console.warn(
116
- '[connect-base-client] persistence="sessionStorage" \uB294 XSS \uC2DC \uD604\uC7AC \uD0ED \uC138\uC158 \uD0C8\uCDE8 \uC704\uD5D8\uC774 \uC788\uC2B5\uB2C8\uB2E4.'
116
+ `[connect-base-client] persistence="sessionStorage" \uB294 XSS \uC2DC \uD0ED \uC138\uC158 \uD0C8\uCDE8 \uC704\uD5D8\uC774 \uC788\uC2B5\uB2C8\uB2E4. \uAE30\uBCF8\uAC12('none') + HttpOnly cookie \uD750\uB984\uC744 \uAD8C\uC7A5\uD569\uB2C8\uB2E4.`
117
117
  );
118
118
  }
119
119
  }
@@ -249,7 +249,7 @@ var HttpClient = class {
249
249
  throw error;
250
250
  }
251
251
  this.isRefreshing = true;
252
- if (!this.config.refreshToken) {
252
+ if (!this.config.refreshToken && typeof window === "undefined") {
253
253
  this.isRefreshing = false;
254
254
  this.config.onTokenExpired?.();
255
255
  const error = new AuthError("Refresh token is missing. Please login again.");
@@ -262,22 +262,39 @@ var HttpClient = class {
262
262
  timeout: this.config.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS
263
263
  });
264
264
  try {
265
+ const headers = {
266
+ "Content-Type": "application/json"
267
+ };
268
+ if (this.config.refreshToken) {
269
+ headers.Authorization = `Bearer ${this.config.refreshToken}`;
270
+ }
271
+ const credential = this.getCredential();
272
+ if (credential) {
273
+ headers["X-Public-Key"] = credential;
274
+ }
265
275
  const response = await fetch(`${this.config.baseUrl}/v1/auth/re-issue`, {
266
276
  method: "POST",
267
- headers: {
268
- "Content-Type": "application/json",
269
- "Authorization": `Bearer ${this.config.refreshToken}`
270
- },
277
+ headers,
278
+ credentials: "include",
271
279
  signal
272
280
  });
273
281
  if (!response.ok) {
274
282
  throw new Error("Token refresh failed");
275
283
  }
276
284
  const data = await response.json();
277
- this.setTokens(data.access_token, data.refresh_token);
285
+ if (!data || typeof data.access_token !== "string") {
286
+ throw new Error("Token refresh response missing access_token");
287
+ }
288
+ const nextRefreshToken = typeof data.refresh_token === "string" && data.refresh_token.length > 0 ? data.refresh_token : this.config.refreshToken ?? "";
289
+ if (nextRefreshToken) {
290
+ this.setTokens(data.access_token, nextRefreshToken);
291
+ } else {
292
+ this.config.accessToken = data.access_token;
293
+ this.persistTokens();
294
+ }
278
295
  this.config.onTokenRefresh?.({
279
296
  accessToken: data.access_token,
280
- refreshToken: data.refresh_token
297
+ refreshToken: nextRefreshToken
281
298
  });
282
299
  this.refreshFailureCount = 0;
283
300
  this.refreshLockedUntil = 0;
@@ -303,6 +320,28 @@ var HttpClient = class {
303
320
  })();
304
321
  return this.refreshPromise;
305
322
  }
323
+ /**
324
+ * 새로고침/탭 재개 시 HttpOnly cookie 만으로 access token 을 복구한다.
325
+ *
326
+ * 동작:
327
+ * - 메모리에 access token 이 이미 있으면 그대로 반환 (true).
328
+ * - 없으면 `/v1/auth/re-issue` 를 cookie 만으로 호출. cookie 가 있으면 access token 회복.
329
+ * - cookie 가 없거나(미로그인) 만료된 경우 조용히 false 반환 (콘솔 에러 없음).
330
+ *
331
+ * 비-브라우저 환경에서는 cookie 흐름이 없으므로 즉시 false 반환.
332
+ */
333
+ async tryRestoreSessionFromCookie() {
334
+ if (typeof window === "undefined") return false;
335
+ if (this.config.accessToken && !this.isTokenExpired(this.config.accessToken)) {
336
+ return true;
337
+ }
338
+ try {
339
+ const newAccessToken = await this.refreshAccessToken();
340
+ return !!newAccessToken;
341
+ } catch {
342
+ return false;
343
+ }
344
+ }
306
345
  emitError(error) {
307
346
  try {
308
347
  this.config.onError?.(error);
@@ -402,6 +441,7 @@ var HttpClient = class {
402
441
  try {
403
442
  const response = await fetch(`${this.config.baseUrl}${url}`, {
404
443
  ...init,
444
+ credentials: "include",
405
445
  signal
406
446
  });
407
447
  return await this.handleResponse(response);
@@ -470,6 +510,7 @@ var HttpClient = class {
470
510
  }
471
511
  return fetch(`${this.config.baseUrl}${url}`, {
472
512
  ...init,
513
+ credentials: "include",
473
514
  headers: mergedHeaders
474
515
  });
475
516
  }
@@ -9600,6 +9641,7 @@ var ConnectBase = class {
9600
9641
  publicKey: config.publicKey,
9601
9642
  secretKey: config.secretKey,
9602
9643
  persistence: config.persistence,
9644
+ autoRestoreSession: config.autoRestoreSession,
9603
9645
  requestTimeoutMs: config.requestTimeoutMs,
9604
9646
  onError: config.onError,
9605
9647
  onTokenRefresh: config.onTokenRefresh,
@@ -9629,6 +9671,21 @@ var ConnectBase = class {
9629
9671
  this.analytics = new AnalyticsAPI(this.http);
9630
9672
  this.endpoint = new EndpointAPI(this.http);
9631
9673
  this.auth._attachAnalytics(this.analytics);
9674
+ const shouldAutoRestore = config.autoRestoreSession ?? true;
9675
+ if (shouldAutoRestore && typeof window !== "undefined") {
9676
+ void this.http.tryRestoreSessionFromCookie();
9677
+ }
9678
+ }
9679
+ /**
9680
+ * 새로고침/탭 재개 후 cookie 만으로 세션을 복원한다 (브라우저 전용).
9681
+ *
9682
+ * 일반적으로는 ConnectBase 생성 시 `autoRestoreSession: true` (기본값) 으로 자동 호출되지만,
9683
+ * 호출 결과를 await 해서 로그인 상태에 따라 다른 UI 를 그리고 싶다면 명시적으로 호출한다.
9684
+ *
9685
+ * @returns access token 복원 성공 시 true, 미로그인/cookie 만료 시 false
9686
+ */
9687
+ async restoreSession() {
9688
+ return this.http.tryRestoreSessionFromCookie();
9632
9689
  }
9633
9690
  /**
9634
9691
  * 수동으로 토큰 설정 (기존 토큰으로 세션 복원 시)
package/dist/index.mjs CHANGED
@@ -68,11 +68,11 @@ var HttpClient = class {
68
68
  if (typeof window === "undefined") return;
69
69
  if (this.config.persistence === "localStorage") {
70
70
  console.warn(
71
- `[connect-base-client] persistence="localStorage" \uB294 XSS \uC2DC \uD1A0\uD070 \uC601\uAD6C \uD0C8\uCDE8 \uC704\uD5D8\uC774 \uC788\uC2B5\uB2C8\uB2E4. refresh token \uC740 \uC11C\uBC84 HttpOnly \uCFE0\uD0A4\uB85C \uBC1C\uAE09\uBC1B\uACE0 \uAE30\uBCF8\uAC12('none')\uC744 \uC0AC\uC6A9\uD558\uC138\uC694.`
71
+ `[connect-base-client] persistence="localStorage" \uB294 XSS \uC2DC refresh token \uC601\uAD6C \uD0C8\uCDE8 \uC704\uD5D8\uC774 \uC788\uC2B5\uB2C8\uB2E4. \uAE30\uBCF8\uAC12('none') \uC744 \uC0AC\uC6A9\uD558\uBA74 \uC11C\uBC84\uAC00 \uBC1C\uAE09\uD55C HttpOnly cookie \uB85C\uB9CC refresh token \uC774 \uBCF4\uAD00\uB418\uC5B4 JS \uAC00 \uC811\uADFC\uD560 \uC218 \uC5C6\uACE0, \uC0C8\uB85C\uACE0\uCE68 \uD6C4\uC5D0\uB3C4 autoRestoreSession \uC73C\uB85C \uC790\uB3D9 \uBCF5\uAD6C\uB429\uB2C8\uB2E4.`
72
72
  );
73
73
  } else if (this.config.persistence === "sessionStorage") {
74
74
  console.warn(
75
- '[connect-base-client] persistence="sessionStorage" \uB294 XSS \uC2DC \uD604\uC7AC \uD0ED \uC138\uC158 \uD0C8\uCDE8 \uC704\uD5D8\uC774 \uC788\uC2B5\uB2C8\uB2E4.'
75
+ `[connect-base-client] persistence="sessionStorage" \uB294 XSS \uC2DC \uD0ED \uC138\uC158 \uD0C8\uCDE8 \uC704\uD5D8\uC774 \uC788\uC2B5\uB2C8\uB2E4. \uAE30\uBCF8\uAC12('none') + HttpOnly cookie \uD750\uB984\uC744 \uAD8C\uC7A5\uD569\uB2C8\uB2E4.`
76
76
  );
77
77
  }
78
78
  }
@@ -208,7 +208,7 @@ var HttpClient = class {
208
208
  throw error;
209
209
  }
210
210
  this.isRefreshing = true;
211
- if (!this.config.refreshToken) {
211
+ if (!this.config.refreshToken && typeof window === "undefined") {
212
212
  this.isRefreshing = false;
213
213
  this.config.onTokenExpired?.();
214
214
  const error = new AuthError("Refresh token is missing. Please login again.");
@@ -221,22 +221,39 @@ var HttpClient = class {
221
221
  timeout: this.config.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS
222
222
  });
223
223
  try {
224
+ const headers = {
225
+ "Content-Type": "application/json"
226
+ };
227
+ if (this.config.refreshToken) {
228
+ headers.Authorization = `Bearer ${this.config.refreshToken}`;
229
+ }
230
+ const credential = this.getCredential();
231
+ if (credential) {
232
+ headers["X-Public-Key"] = credential;
233
+ }
224
234
  const response = await fetch(`${this.config.baseUrl}/v1/auth/re-issue`, {
225
235
  method: "POST",
226
- headers: {
227
- "Content-Type": "application/json",
228
- "Authorization": `Bearer ${this.config.refreshToken}`
229
- },
236
+ headers,
237
+ credentials: "include",
230
238
  signal
231
239
  });
232
240
  if (!response.ok) {
233
241
  throw new Error("Token refresh failed");
234
242
  }
235
243
  const data = await response.json();
236
- this.setTokens(data.access_token, data.refresh_token);
244
+ if (!data || typeof data.access_token !== "string") {
245
+ throw new Error("Token refresh response missing access_token");
246
+ }
247
+ const nextRefreshToken = typeof data.refresh_token === "string" && data.refresh_token.length > 0 ? data.refresh_token : this.config.refreshToken ?? "";
248
+ if (nextRefreshToken) {
249
+ this.setTokens(data.access_token, nextRefreshToken);
250
+ } else {
251
+ this.config.accessToken = data.access_token;
252
+ this.persistTokens();
253
+ }
237
254
  this.config.onTokenRefresh?.({
238
255
  accessToken: data.access_token,
239
- refreshToken: data.refresh_token
256
+ refreshToken: nextRefreshToken
240
257
  });
241
258
  this.refreshFailureCount = 0;
242
259
  this.refreshLockedUntil = 0;
@@ -262,6 +279,28 @@ var HttpClient = class {
262
279
  })();
263
280
  return this.refreshPromise;
264
281
  }
282
+ /**
283
+ * 새로고침/탭 재개 시 HttpOnly cookie 만으로 access token 을 복구한다.
284
+ *
285
+ * 동작:
286
+ * - 메모리에 access token 이 이미 있으면 그대로 반환 (true).
287
+ * - 없으면 `/v1/auth/re-issue` 를 cookie 만으로 호출. cookie 가 있으면 access token 회복.
288
+ * - cookie 가 없거나(미로그인) 만료된 경우 조용히 false 반환 (콘솔 에러 없음).
289
+ *
290
+ * 비-브라우저 환경에서는 cookie 흐름이 없으므로 즉시 false 반환.
291
+ */
292
+ async tryRestoreSessionFromCookie() {
293
+ if (typeof window === "undefined") return false;
294
+ if (this.config.accessToken && !this.isTokenExpired(this.config.accessToken)) {
295
+ return true;
296
+ }
297
+ try {
298
+ const newAccessToken = await this.refreshAccessToken();
299
+ return !!newAccessToken;
300
+ } catch {
301
+ return false;
302
+ }
303
+ }
265
304
  emitError(error) {
266
305
  try {
267
306
  this.config.onError?.(error);
@@ -361,6 +400,7 @@ var HttpClient = class {
361
400
  try {
362
401
  const response = await fetch(`${this.config.baseUrl}${url}`, {
363
402
  ...init,
403
+ credentials: "include",
364
404
  signal
365
405
  });
366
406
  return await this.handleResponse(response);
@@ -429,6 +469,7 @@ var HttpClient = class {
429
469
  }
430
470
  return fetch(`${this.config.baseUrl}${url}`, {
431
471
  ...init,
472
+ credentials: "include",
432
473
  headers: mergedHeaders
433
474
  });
434
475
  }
@@ -9559,6 +9600,7 @@ var ConnectBase = class {
9559
9600
  publicKey: config.publicKey,
9560
9601
  secretKey: config.secretKey,
9561
9602
  persistence: config.persistence,
9603
+ autoRestoreSession: config.autoRestoreSession,
9562
9604
  requestTimeoutMs: config.requestTimeoutMs,
9563
9605
  onError: config.onError,
9564
9606
  onTokenRefresh: config.onTokenRefresh,
@@ -9588,6 +9630,21 @@ var ConnectBase = class {
9588
9630
  this.analytics = new AnalyticsAPI(this.http);
9589
9631
  this.endpoint = new EndpointAPI(this.http);
9590
9632
  this.auth._attachAnalytics(this.analytics);
9633
+ const shouldAutoRestore = config.autoRestoreSession ?? true;
9634
+ if (shouldAutoRestore && typeof window !== "undefined") {
9635
+ void this.http.tryRestoreSessionFromCookie();
9636
+ }
9637
+ }
9638
+ /**
9639
+ * 새로고침/탭 재개 후 cookie 만으로 세션을 복원한다 (브라우저 전용).
9640
+ *
9641
+ * 일반적으로는 ConnectBase 생성 시 `autoRestoreSession: true` (기본값) 으로 자동 호출되지만,
9642
+ * 호출 결과를 await 해서 로그인 상태에 따라 다른 UI 를 그리고 싶다면 명시적으로 호출한다.
9643
+ *
9644
+ * @returns access token 복원 성공 시 true, 미로그인/cookie 만료 시 false
9645
+ */
9646
+ async restoreSession() {
9647
+ return this.http.tryRestoreSessionFromCookie();
9591
9648
  }
9592
9649
  /**
9593
9650
  * 수동으로 토큰 설정 (기존 토큰으로 세션 복원 시)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "connectbase-client",
3
- "version": "3.7.2",
3
+ "version": "3.8.1",
4
4
  "description": "Connect Base JavaScript/TypeScript SDK for browser and Node.js",
5
5
  "repository": {
6
6
  "type": "git",