connectbase-client 3.7.1 → 3.8.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.
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,35 @@ 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
+ }
265
271
  const response = await fetch(`${this.config.baseUrl}/v1/auth/re-issue`, {
266
272
  method: "POST",
267
- headers: {
268
- "Content-Type": "application/json",
269
- "Authorization": `Bearer ${this.config.refreshToken}`
270
- },
273
+ headers,
274
+ credentials: "include",
271
275
  signal
272
276
  });
273
277
  if (!response.ok) {
274
278
  throw new Error("Token refresh failed");
275
279
  }
276
280
  const data = await response.json();
277
- this.setTokens(data.access_token, data.refresh_token);
281
+ if (!data || typeof data.access_token !== "string") {
282
+ throw new Error("Token refresh response missing access_token");
283
+ }
284
+ const nextRefreshToken = typeof data.refresh_token === "string" && data.refresh_token.length > 0 ? data.refresh_token : this.config.refreshToken ?? "";
285
+ if (nextRefreshToken) {
286
+ this.setTokens(data.access_token, nextRefreshToken);
287
+ } else {
288
+ this.config.accessToken = data.access_token;
289
+ this.persistTokens();
290
+ }
278
291
  this.config.onTokenRefresh?.({
279
292
  accessToken: data.access_token,
280
- refreshToken: data.refresh_token
293
+ refreshToken: nextRefreshToken
281
294
  });
282
295
  this.refreshFailureCount = 0;
283
296
  this.refreshLockedUntil = 0;
@@ -303,6 +316,28 @@ var HttpClient = class {
303
316
  })();
304
317
  return this.refreshPromise;
305
318
  }
319
+ /**
320
+ * 새로고침/탭 재개 시 HttpOnly cookie 만으로 access token 을 복구한다.
321
+ *
322
+ * 동작:
323
+ * - 메모리에 access token 이 이미 있으면 그대로 반환 (true).
324
+ * - 없으면 `/v1/auth/re-issue` 를 cookie 만으로 호출. cookie 가 있으면 access token 회복.
325
+ * - cookie 가 없거나(미로그인) 만료된 경우 조용히 false 반환 (콘솔 에러 없음).
326
+ *
327
+ * 비-브라우저 환경에서는 cookie 흐름이 없으므로 즉시 false 반환.
328
+ */
329
+ async tryRestoreSessionFromCookie() {
330
+ if (typeof window === "undefined") return false;
331
+ if (this.config.accessToken && !this.isTokenExpired(this.config.accessToken)) {
332
+ return true;
333
+ }
334
+ try {
335
+ const newAccessToken = await this.refreshAccessToken();
336
+ return !!newAccessToken;
337
+ } catch {
338
+ return false;
339
+ }
340
+ }
306
341
  emitError(error) {
307
342
  try {
308
343
  this.config.onError?.(error);
@@ -402,6 +437,7 @@ var HttpClient = class {
402
437
  try {
403
438
  const response = await fetch(`${this.config.baseUrl}${url}`, {
404
439
  ...init,
440
+ credentials: "include",
405
441
  signal
406
442
  });
407
443
  return await this.handleResponse(response);
@@ -470,6 +506,7 @@ var HttpClient = class {
470
506
  }
471
507
  return fetch(`${this.config.baseUrl}${url}`, {
472
508
  ...init,
509
+ credentials: "include",
473
510
  headers: mergedHeaders
474
511
  });
475
512
  }
@@ -9600,6 +9637,7 @@ var ConnectBase = class {
9600
9637
  publicKey: config.publicKey,
9601
9638
  secretKey: config.secretKey,
9602
9639
  persistence: config.persistence,
9640
+ autoRestoreSession: config.autoRestoreSession,
9603
9641
  requestTimeoutMs: config.requestTimeoutMs,
9604
9642
  onError: config.onError,
9605
9643
  onTokenRefresh: config.onTokenRefresh,
@@ -9629,6 +9667,21 @@ var ConnectBase = class {
9629
9667
  this.analytics = new AnalyticsAPI(this.http);
9630
9668
  this.endpoint = new EndpointAPI(this.http);
9631
9669
  this.auth._attachAnalytics(this.analytics);
9670
+ const shouldAutoRestore = config.autoRestoreSession ?? true;
9671
+ if (shouldAutoRestore && typeof window !== "undefined") {
9672
+ void this.http.tryRestoreSessionFromCookie();
9673
+ }
9674
+ }
9675
+ /**
9676
+ * 새로고침/탭 재개 후 cookie 만으로 세션을 복원한다 (브라우저 전용).
9677
+ *
9678
+ * 일반적으로는 ConnectBase 생성 시 `autoRestoreSession: true` (기본값) 으로 자동 호출되지만,
9679
+ * 호출 결과를 await 해서 로그인 상태에 따라 다른 UI 를 그리고 싶다면 명시적으로 호출한다.
9680
+ *
9681
+ * @returns access token 복원 성공 시 true, 미로그인/cookie 만료 시 false
9682
+ */
9683
+ async restoreSession() {
9684
+ return this.http.tryRestoreSessionFromCookie();
9632
9685
  }
9633
9686
  /**
9634
9687
  * 수동으로 토큰 설정 (기존 토큰으로 세션 복원 시)
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,35 @@ 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
+ }
224
230
  const response = await fetch(`${this.config.baseUrl}/v1/auth/re-issue`, {
225
231
  method: "POST",
226
- headers: {
227
- "Content-Type": "application/json",
228
- "Authorization": `Bearer ${this.config.refreshToken}`
229
- },
232
+ headers,
233
+ credentials: "include",
230
234
  signal
231
235
  });
232
236
  if (!response.ok) {
233
237
  throw new Error("Token refresh failed");
234
238
  }
235
239
  const data = await response.json();
236
- this.setTokens(data.access_token, data.refresh_token);
240
+ if (!data || typeof data.access_token !== "string") {
241
+ throw new Error("Token refresh response missing access_token");
242
+ }
243
+ const nextRefreshToken = typeof data.refresh_token === "string" && data.refresh_token.length > 0 ? data.refresh_token : this.config.refreshToken ?? "";
244
+ if (nextRefreshToken) {
245
+ this.setTokens(data.access_token, nextRefreshToken);
246
+ } else {
247
+ this.config.accessToken = data.access_token;
248
+ this.persistTokens();
249
+ }
237
250
  this.config.onTokenRefresh?.({
238
251
  accessToken: data.access_token,
239
- refreshToken: data.refresh_token
252
+ refreshToken: nextRefreshToken
240
253
  });
241
254
  this.refreshFailureCount = 0;
242
255
  this.refreshLockedUntil = 0;
@@ -262,6 +275,28 @@ var HttpClient = class {
262
275
  })();
263
276
  return this.refreshPromise;
264
277
  }
278
+ /**
279
+ * 새로고침/탭 재개 시 HttpOnly cookie 만으로 access token 을 복구한다.
280
+ *
281
+ * 동작:
282
+ * - 메모리에 access token 이 이미 있으면 그대로 반환 (true).
283
+ * - 없으면 `/v1/auth/re-issue` 를 cookie 만으로 호출. cookie 가 있으면 access token 회복.
284
+ * - cookie 가 없거나(미로그인) 만료된 경우 조용히 false 반환 (콘솔 에러 없음).
285
+ *
286
+ * 비-브라우저 환경에서는 cookie 흐름이 없으므로 즉시 false 반환.
287
+ */
288
+ async tryRestoreSessionFromCookie() {
289
+ if (typeof window === "undefined") return false;
290
+ if (this.config.accessToken && !this.isTokenExpired(this.config.accessToken)) {
291
+ return true;
292
+ }
293
+ try {
294
+ const newAccessToken = await this.refreshAccessToken();
295
+ return !!newAccessToken;
296
+ } catch {
297
+ return false;
298
+ }
299
+ }
265
300
  emitError(error) {
266
301
  try {
267
302
  this.config.onError?.(error);
@@ -361,6 +396,7 @@ var HttpClient = class {
361
396
  try {
362
397
  const response = await fetch(`${this.config.baseUrl}${url}`, {
363
398
  ...init,
399
+ credentials: "include",
364
400
  signal
365
401
  });
366
402
  return await this.handleResponse(response);
@@ -429,6 +465,7 @@ var HttpClient = class {
429
465
  }
430
466
  return fetch(`${this.config.baseUrl}${url}`, {
431
467
  ...init,
468
+ credentials: "include",
432
469
  headers: mergedHeaders
433
470
  });
434
471
  }
@@ -9559,6 +9596,7 @@ var ConnectBase = class {
9559
9596
  publicKey: config.publicKey,
9560
9597
  secretKey: config.secretKey,
9561
9598
  persistence: config.persistence,
9599
+ autoRestoreSession: config.autoRestoreSession,
9562
9600
  requestTimeoutMs: config.requestTimeoutMs,
9563
9601
  onError: config.onError,
9564
9602
  onTokenRefresh: config.onTokenRefresh,
@@ -9588,6 +9626,21 @@ var ConnectBase = class {
9588
9626
  this.analytics = new AnalyticsAPI(this.http);
9589
9627
  this.endpoint = new EndpointAPI(this.http);
9590
9628
  this.auth._attachAnalytics(this.analytics);
9629
+ const shouldAutoRestore = config.autoRestoreSession ?? true;
9630
+ if (shouldAutoRestore && typeof window !== "undefined") {
9631
+ void this.http.tryRestoreSessionFromCookie();
9632
+ }
9633
+ }
9634
+ /**
9635
+ * 새로고침/탭 재개 후 cookie 만으로 세션을 복원한다 (브라우저 전용).
9636
+ *
9637
+ * 일반적으로는 ConnectBase 생성 시 `autoRestoreSession: true` (기본값) 으로 자동 호출되지만,
9638
+ * 호출 결과를 await 해서 로그인 상태에 따라 다른 UI 를 그리고 싶다면 명시적으로 호출한다.
9639
+ *
9640
+ * @returns access token 복원 성공 시 true, 미로그인/cookie 만료 시 false
9641
+ */
9642
+ async restoreSession() {
9643
+ return this.http.tryRestoreSessionFromCookie();
9591
9644
  }
9592
9645
  /**
9593
9646
  * 수동으로 토큰 설정 (기존 토큰으로 세션 복원 시)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "connectbase-client",
3
- "version": "3.7.1",
3
+ "version": "3.8.0",
4
4
  "description": "Connect Base JavaScript/TypeScript SDK for browser and Node.js",
5
5
  "repository": {
6
6
  "type": "git",