connectbase-client 3.30.0 → 3.32.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/CHANGELOG.md +54 -0
- package/README.md +6 -3
- package/dist/connect-base.umd.js +4 -4
- package/dist/index.d.mts +53 -1
- package/dist/index.d.ts +53 -1
- package/dist/index.js +81 -5
- package/dist/index.mjs +81 -5
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -3847,6 +3847,12 @@ declare function escapeToExternalBrowser(currentUrl?: string): boolean;
|
|
|
3847
3847
|
*/
|
|
3848
3848
|
declare class OAuthAPI {
|
|
3849
3849
|
private http;
|
|
3850
|
+
/**
|
|
3851
|
+
* 부팅 안전망(consumeRedirectCallbackOnBoot)이 리다이렉트 콜백을 자동 소비했을 때의 promise.
|
|
3852
|
+
* 앱이 그 후 `getCallbackResult()` 를 호출해도 중복으로 `bootstrapRefreshCookie` 를
|
|
3853
|
+
* 발화하지 않도록 이 promise 를 공유한다 (이중 re-issue/rotation 방지).
|
|
3854
|
+
*/
|
|
3855
|
+
private bootConsumePromise;
|
|
3850
3856
|
constructor(http: HttpClient);
|
|
3851
3857
|
/**
|
|
3852
3858
|
* 활성화된 OAuth 프로바이더 목록 조회
|
|
@@ -3971,6 +3977,28 @@ declare class OAuthAPI {
|
|
|
3971
3977
|
state?: string;
|
|
3972
3978
|
error?: string;
|
|
3973
3979
|
} | null>;
|
|
3980
|
+
/**
|
|
3981
|
+
* (SDK 내부용 — `ConnectBase` 생성자가 호출) 부팅 안전망.
|
|
3982
|
+
*
|
|
3983
|
+
* OAuth 리다이렉트 콜백 페이지에서 앱이 `getCallbackResult()` 를 호출하지 않아도,
|
|
3984
|
+
* `new ConnectBase()` 만으로 URL 의 토큰(`?access_token=&refresh_token=&member_id=`)을
|
|
3985
|
+
* 적재해 세션을 확립한다.
|
|
3986
|
+
*
|
|
3987
|
+
* **왜 필요한가:** 정적 호스팅(웹 스토리지)은 SPA fallback 으로 `/auth/callback` 같은 콜백
|
|
3988
|
+
* 경로에 홈 `index.html` 을 서빙한다. 그 페이지가 콜백을 처리하지 않으면 URL 토큰이 영영
|
|
3989
|
+
* 소비되지 않아 "로그인 → 다시 로그인 페이지" 무한 루프가 발생한다 (여러 사용자 동시 보고).
|
|
3990
|
+
* 이 안전망은 콜백 처리 코드를 빠뜨린 앱도 동작하게 한다.
|
|
3991
|
+
*
|
|
3992
|
+
* **token rotation race(2026-05-16) 가 없는 이유:** 사전 `re-issue`(cookie 복구) 없이 URL
|
|
3993
|
+
* 토큰을 *직접* 소비한다 — `getCallbackResult()` 의 리다이렉트 경로와 동일한 동작이다. 팝업
|
|
3994
|
+
* (`window.opener` 존재)은 `ConnectBase` 생성자에서 제외되어 호출되지 않는다 (팝업 콜백은
|
|
3995
|
+
* `getCallbackResult()` 의 postMessage 경로 전담). code-only(`?code=`)·에러(`?error=`)
|
|
3996
|
+
* 콜백도 생성자에서 제외된다 (네트워크 교환/에러 분기는 앱이 명시 호출).
|
|
3997
|
+
*
|
|
3998
|
+
* @returns 토큰 적재 성공 시 true, no-op(토큰 없음 등)이면 false.
|
|
3999
|
+
*/
|
|
4000
|
+
consumeRedirectCallbackOnBoot(): Promise<boolean>;
|
|
4001
|
+
private doConsumeRedirectOnBoot;
|
|
3974
4002
|
/**
|
|
3975
4003
|
* 콜백 URL 에서 1회용 `code` 를 토큰으로 교환합니다 (`OAUTH_CODE_ONLY` 서버 모드용).
|
|
3976
4004
|
*
|
|
@@ -7515,6 +7543,21 @@ interface AIChatRequest {
|
|
|
7515
7543
|
topK?: number;
|
|
7516
7544
|
agentic?: boolean;
|
|
7517
7545
|
toolGroupId?: string;
|
|
7546
|
+
/**
|
|
7547
|
+
* 0 보다 크면, 서버측 도구 그룹 실행(`toolGroupId` 사용 시)에서 각 도구 결과를 모델에
|
|
7548
|
+
* **재투입하기 전** N 자(rune 기준)로 잘라낸다(말미에 잘림 표시). SSE `onToolEvent` 의
|
|
7549
|
+
* `tool_end.result` 로 표시되는 값은 원문을 유지한다.
|
|
7550
|
+
*
|
|
7551
|
+
* 거대한 도구 결과(예: 복잡한 페이지의 `browser_snapshot`)가 누적 컨텍스트를 부풀려
|
|
7552
|
+
* 매 턴 prefill 을 무겁게 만들 때, 표시는 원문으로 두고 모델 재투입분만 압축해
|
|
7553
|
+
* 지연을 줄이는 용도. 미지정/0 이면 자르지 않는다.
|
|
7554
|
+
*/
|
|
7555
|
+
toolResultMaxChars?: number;
|
|
7556
|
+
/**
|
|
7557
|
+
* 요청에 포함한 MCP 도구(`tools`)를 호출할 외부 MCP 서버의 Public Key (cb_pk_* 형식).
|
|
7558
|
+
* MCP 도구를 직접 넘기지 않으면 불필요하다.
|
|
7559
|
+
*/
|
|
7560
|
+
mcpPublicKey?: string;
|
|
7518
7561
|
}
|
|
7519
7562
|
/**
|
|
7520
7563
|
* `chatStream` 콜백.
|
|
@@ -8812,9 +8855,18 @@ declare class ConnectBase {
|
|
|
8812
8855
|
*
|
|
8813
8856
|
* `?access_token=...&refresh_token=...` (legacy 토큰-in-URL 흐름) 또는
|
|
8814
8857
|
* `?code=...` (OAUTH_CODE_ONLY 흐름) 또는 OAuth 에러 응답 (`?error=...&state=...`)
|
|
8815
|
-
* 패턴이면 true. 콜백 페이지에서 `autoRestoreSession` 을 건너뛰어 rotation race 를 피한다.
|
|
8858
|
+
* 패턴이면 true. 콜백 페이지에서 일반 `autoRestoreSession` 을 건너뛰어 rotation race 를 피한다.
|
|
8816
8859
|
*/
|
|
8817
8860
|
private isOAuthCallbackUrl;
|
|
8861
|
+
/**
|
|
8862
|
+
* 현재 페이지가 토큰-in-URL OAuth 리다이렉트 콜백(팝업 아님)인지 판별.
|
|
8863
|
+
*
|
|
8864
|
+
* `?access_token=&refresh_token=` 이 있고 `window.opener` 가 없으면 true — 부팅 안전망
|
|
8865
|
+
* (`oauth.consumeRedirectCallbackOnBoot`)으로 토큰을 자동 소비할 대상이다. 팝업 콜백
|
|
8866
|
+
* (`window.opener` 존재)은 `getCallbackResult()` 의 postMessage 경로가 전담하므로 제외한다.
|
|
8867
|
+
* code-only(`?code=`)·에러(`?error=`) 콜백도 제외(네트워크 교환/에러 분기는 앱이 명시 호출).
|
|
8868
|
+
*/
|
|
8869
|
+
private isOAuthRedirectCallbackUrl;
|
|
8818
8870
|
/**
|
|
8819
8871
|
* 새로고침/탭 재개 후 cookie 만으로 세션을 복원한다 (브라우저 전용).
|
|
8820
8872
|
*
|
package/dist/index.d.ts
CHANGED
|
@@ -3847,6 +3847,12 @@ declare function escapeToExternalBrowser(currentUrl?: string): boolean;
|
|
|
3847
3847
|
*/
|
|
3848
3848
|
declare class OAuthAPI {
|
|
3849
3849
|
private http;
|
|
3850
|
+
/**
|
|
3851
|
+
* 부팅 안전망(consumeRedirectCallbackOnBoot)이 리다이렉트 콜백을 자동 소비했을 때의 promise.
|
|
3852
|
+
* 앱이 그 후 `getCallbackResult()` 를 호출해도 중복으로 `bootstrapRefreshCookie` 를
|
|
3853
|
+
* 발화하지 않도록 이 promise 를 공유한다 (이중 re-issue/rotation 방지).
|
|
3854
|
+
*/
|
|
3855
|
+
private bootConsumePromise;
|
|
3850
3856
|
constructor(http: HttpClient);
|
|
3851
3857
|
/**
|
|
3852
3858
|
* 활성화된 OAuth 프로바이더 목록 조회
|
|
@@ -3971,6 +3977,28 @@ declare class OAuthAPI {
|
|
|
3971
3977
|
state?: string;
|
|
3972
3978
|
error?: string;
|
|
3973
3979
|
} | null>;
|
|
3980
|
+
/**
|
|
3981
|
+
* (SDK 내부용 — `ConnectBase` 생성자가 호출) 부팅 안전망.
|
|
3982
|
+
*
|
|
3983
|
+
* OAuth 리다이렉트 콜백 페이지에서 앱이 `getCallbackResult()` 를 호출하지 않아도,
|
|
3984
|
+
* `new ConnectBase()` 만으로 URL 의 토큰(`?access_token=&refresh_token=&member_id=`)을
|
|
3985
|
+
* 적재해 세션을 확립한다.
|
|
3986
|
+
*
|
|
3987
|
+
* **왜 필요한가:** 정적 호스팅(웹 스토리지)은 SPA fallback 으로 `/auth/callback` 같은 콜백
|
|
3988
|
+
* 경로에 홈 `index.html` 을 서빙한다. 그 페이지가 콜백을 처리하지 않으면 URL 토큰이 영영
|
|
3989
|
+
* 소비되지 않아 "로그인 → 다시 로그인 페이지" 무한 루프가 발생한다 (여러 사용자 동시 보고).
|
|
3990
|
+
* 이 안전망은 콜백 처리 코드를 빠뜨린 앱도 동작하게 한다.
|
|
3991
|
+
*
|
|
3992
|
+
* **token rotation race(2026-05-16) 가 없는 이유:** 사전 `re-issue`(cookie 복구) 없이 URL
|
|
3993
|
+
* 토큰을 *직접* 소비한다 — `getCallbackResult()` 의 리다이렉트 경로와 동일한 동작이다. 팝업
|
|
3994
|
+
* (`window.opener` 존재)은 `ConnectBase` 생성자에서 제외되어 호출되지 않는다 (팝업 콜백은
|
|
3995
|
+
* `getCallbackResult()` 의 postMessage 경로 전담). code-only(`?code=`)·에러(`?error=`)
|
|
3996
|
+
* 콜백도 생성자에서 제외된다 (네트워크 교환/에러 분기는 앱이 명시 호출).
|
|
3997
|
+
*
|
|
3998
|
+
* @returns 토큰 적재 성공 시 true, no-op(토큰 없음 등)이면 false.
|
|
3999
|
+
*/
|
|
4000
|
+
consumeRedirectCallbackOnBoot(): Promise<boolean>;
|
|
4001
|
+
private doConsumeRedirectOnBoot;
|
|
3974
4002
|
/**
|
|
3975
4003
|
* 콜백 URL 에서 1회용 `code` 를 토큰으로 교환합니다 (`OAUTH_CODE_ONLY` 서버 모드용).
|
|
3976
4004
|
*
|
|
@@ -7515,6 +7543,21 @@ interface AIChatRequest {
|
|
|
7515
7543
|
topK?: number;
|
|
7516
7544
|
agentic?: boolean;
|
|
7517
7545
|
toolGroupId?: string;
|
|
7546
|
+
/**
|
|
7547
|
+
* 0 보다 크면, 서버측 도구 그룹 실행(`toolGroupId` 사용 시)에서 각 도구 결과를 모델에
|
|
7548
|
+
* **재투입하기 전** N 자(rune 기준)로 잘라낸다(말미에 잘림 표시). SSE `onToolEvent` 의
|
|
7549
|
+
* `tool_end.result` 로 표시되는 값은 원문을 유지한다.
|
|
7550
|
+
*
|
|
7551
|
+
* 거대한 도구 결과(예: 복잡한 페이지의 `browser_snapshot`)가 누적 컨텍스트를 부풀려
|
|
7552
|
+
* 매 턴 prefill 을 무겁게 만들 때, 표시는 원문으로 두고 모델 재투입분만 압축해
|
|
7553
|
+
* 지연을 줄이는 용도. 미지정/0 이면 자르지 않는다.
|
|
7554
|
+
*/
|
|
7555
|
+
toolResultMaxChars?: number;
|
|
7556
|
+
/**
|
|
7557
|
+
* 요청에 포함한 MCP 도구(`tools`)를 호출할 외부 MCP 서버의 Public Key (cb_pk_* 형식).
|
|
7558
|
+
* MCP 도구를 직접 넘기지 않으면 불필요하다.
|
|
7559
|
+
*/
|
|
7560
|
+
mcpPublicKey?: string;
|
|
7518
7561
|
}
|
|
7519
7562
|
/**
|
|
7520
7563
|
* `chatStream` 콜백.
|
|
@@ -8812,9 +8855,18 @@ declare class ConnectBase {
|
|
|
8812
8855
|
*
|
|
8813
8856
|
* `?access_token=...&refresh_token=...` (legacy 토큰-in-URL 흐름) 또는
|
|
8814
8857
|
* `?code=...` (OAUTH_CODE_ONLY 흐름) 또는 OAuth 에러 응답 (`?error=...&state=...`)
|
|
8815
|
-
* 패턴이면 true. 콜백 페이지에서 `autoRestoreSession` 을 건너뛰어 rotation race 를 피한다.
|
|
8858
|
+
* 패턴이면 true. 콜백 페이지에서 일반 `autoRestoreSession` 을 건너뛰어 rotation race 를 피한다.
|
|
8816
8859
|
*/
|
|
8817
8860
|
private isOAuthCallbackUrl;
|
|
8861
|
+
/**
|
|
8862
|
+
* 현재 페이지가 토큰-in-URL OAuth 리다이렉트 콜백(팝업 아님)인지 판별.
|
|
8863
|
+
*
|
|
8864
|
+
* `?access_token=&refresh_token=` 이 있고 `window.opener` 가 없으면 true — 부팅 안전망
|
|
8865
|
+
* (`oauth.consumeRedirectCallbackOnBoot`)으로 토큰을 자동 소비할 대상이다. 팝업 콜백
|
|
8866
|
+
* (`window.opener` 존재)은 `getCallbackResult()` 의 postMessage 경로가 전담하므로 제외한다.
|
|
8867
|
+
* code-only(`?code=`)·에러(`?error=`) 콜백도 제외(네트워크 교환/에러 분기는 앱이 명시 호출).
|
|
8868
|
+
*/
|
|
8869
|
+
private isOAuthRedirectCallbackUrl;
|
|
8818
8870
|
/**
|
|
8819
8871
|
* 새로고침/탭 재개 후 cookie 만으로 세션을 복원한다 (브라우저 전용).
|
|
8820
8872
|
*
|
package/dist/index.js
CHANGED
|
@@ -153,6 +153,10 @@ function sanitizePathForBreadcrumb(rawUrl) {
|
|
|
153
153
|
}
|
|
154
154
|
|
|
155
155
|
// src/core/http.ts
|
|
156
|
+
function fetchCredentialsForPath(url) {
|
|
157
|
+
const path = url.split("?")[0];
|
|
158
|
+
return path.startsWith("/v1/public/") ? "omit" : "include";
|
|
159
|
+
}
|
|
156
160
|
var TOKEN_STORAGE_KEY = "cb_auth_tokens";
|
|
157
161
|
function decodeJwtPayload(token) {
|
|
158
162
|
try {
|
|
@@ -679,7 +683,7 @@ var HttpClient = class {
|
|
|
679
683
|
try {
|
|
680
684
|
const response = await fetch(`${this.config.baseUrl}${url}`, {
|
|
681
685
|
...init,
|
|
682
|
-
credentials:
|
|
686
|
+
credentials: fetchCredentialsForPath(url),
|
|
683
687
|
signal
|
|
684
688
|
});
|
|
685
689
|
return { response, ok: response.ok, status: response.status };
|
|
@@ -748,7 +752,7 @@ var HttpClient = class {
|
|
|
748
752
|
}
|
|
749
753
|
return fetch(`${this.config.baseUrl}${url}`, {
|
|
750
754
|
...init,
|
|
751
|
-
credentials:
|
|
755
|
+
credentials: fetchCredentialsForPath(url),
|
|
752
756
|
headers: mergedHeaders
|
|
753
757
|
});
|
|
754
758
|
}
|
|
@@ -4810,6 +4814,12 @@ function escapeToExternalBrowser(currentUrl) {
|
|
|
4810
4814
|
var OAuthAPI = class {
|
|
4811
4815
|
constructor(http) {
|
|
4812
4816
|
this.http = http;
|
|
4817
|
+
/**
|
|
4818
|
+
* 부팅 안전망(consumeRedirectCallbackOnBoot)이 리다이렉트 콜백을 자동 소비했을 때의 promise.
|
|
4819
|
+
* 앱이 그 후 `getCallbackResult()` 를 호출해도 중복으로 `bootstrapRefreshCookie` 를
|
|
4820
|
+
* 발화하지 않도록 이 promise 를 공유한다 (이중 re-issue/rotation 방지).
|
|
4821
|
+
*/
|
|
4822
|
+
this.bootConsumePromise = null;
|
|
4813
4823
|
}
|
|
4814
4824
|
/**
|
|
4815
4825
|
* 활성화된 OAuth 프로바이더 목록 조회
|
|
@@ -5104,10 +5114,58 @@ var OAuthAPI = class {
|
|
|
5104
5114
|
window.close();
|
|
5105
5115
|
return result;
|
|
5106
5116
|
}
|
|
5117
|
+
if (this.bootConsumePromise) {
|
|
5118
|
+
const consumed = await this.bootConsumePromise;
|
|
5119
|
+
if (consumed) {
|
|
5120
|
+
return result;
|
|
5121
|
+
}
|
|
5122
|
+
}
|
|
5107
5123
|
this.http.setTokens(accessToken, refreshToken);
|
|
5108
5124
|
await this.http.bootstrapRefreshCookie();
|
|
5109
5125
|
return result;
|
|
5110
5126
|
}
|
|
5127
|
+
/**
|
|
5128
|
+
* (SDK 내부용 — `ConnectBase` 생성자가 호출) 부팅 안전망.
|
|
5129
|
+
*
|
|
5130
|
+
* OAuth 리다이렉트 콜백 페이지에서 앱이 `getCallbackResult()` 를 호출하지 않아도,
|
|
5131
|
+
* `new ConnectBase()` 만으로 URL 의 토큰(`?access_token=&refresh_token=&member_id=`)을
|
|
5132
|
+
* 적재해 세션을 확립한다.
|
|
5133
|
+
*
|
|
5134
|
+
* **왜 필요한가:** 정적 호스팅(웹 스토리지)은 SPA fallback 으로 `/auth/callback` 같은 콜백
|
|
5135
|
+
* 경로에 홈 `index.html` 을 서빙한다. 그 페이지가 콜백을 처리하지 않으면 URL 토큰이 영영
|
|
5136
|
+
* 소비되지 않아 "로그인 → 다시 로그인 페이지" 무한 루프가 발생한다 (여러 사용자 동시 보고).
|
|
5137
|
+
* 이 안전망은 콜백 처리 코드를 빠뜨린 앱도 동작하게 한다.
|
|
5138
|
+
*
|
|
5139
|
+
* **token rotation race(2026-05-16) 가 없는 이유:** 사전 `re-issue`(cookie 복구) 없이 URL
|
|
5140
|
+
* 토큰을 *직접* 소비한다 — `getCallbackResult()` 의 리다이렉트 경로와 동일한 동작이다. 팝업
|
|
5141
|
+
* (`window.opener` 존재)은 `ConnectBase` 생성자에서 제외되어 호출되지 않는다 (팝업 콜백은
|
|
5142
|
+
* `getCallbackResult()` 의 postMessage 경로 전담). code-only(`?code=`)·에러(`?error=`)
|
|
5143
|
+
* 콜백도 생성자에서 제외된다 (네트워크 교환/에러 분기는 앱이 명시 호출).
|
|
5144
|
+
*
|
|
5145
|
+
* @returns 토큰 적재 성공 시 true, no-op(토큰 없음 등)이면 false.
|
|
5146
|
+
*/
|
|
5147
|
+
consumeRedirectCallbackOnBoot() {
|
|
5148
|
+
if (!this.bootConsumePromise) {
|
|
5149
|
+
this.bootConsumePromise = this.doConsumeRedirectOnBoot();
|
|
5150
|
+
}
|
|
5151
|
+
return this.bootConsumePromise;
|
|
5152
|
+
}
|
|
5153
|
+
async doConsumeRedirectOnBoot() {
|
|
5154
|
+
try {
|
|
5155
|
+
if (typeof window === "undefined" || !window.location) return false;
|
|
5156
|
+
const params = new URLSearchParams(window.location.search);
|
|
5157
|
+
if (params.get("error")) return false;
|
|
5158
|
+
const accessToken = params.get("access_token");
|
|
5159
|
+
const refreshToken = params.get("refresh_token");
|
|
5160
|
+
const memberId = params.get("member_id");
|
|
5161
|
+
if (!accessToken || !refreshToken || !memberId) return false;
|
|
5162
|
+
this.http.setTokens(accessToken, refreshToken);
|
|
5163
|
+
await this.http.bootstrapRefreshCookie();
|
|
5164
|
+
return true;
|
|
5165
|
+
} catch {
|
|
5166
|
+
return false;
|
|
5167
|
+
}
|
|
5168
|
+
}
|
|
5111
5169
|
/**
|
|
5112
5170
|
* 콜백 URL 에서 1회용 `code` 를 토큰으로 교환합니다 (`OAUTH_CODE_ONLY` 서버 모드용).
|
|
5113
5171
|
*
|
|
@@ -10870,8 +10928,12 @@ var ConnectBase = class {
|
|
|
10870
10928
|
this.support = new SupportAPI(this.http);
|
|
10871
10929
|
this.auth._attachAnalytics(this.analytics);
|
|
10872
10930
|
const shouldAutoRestore = config.autoRestoreSession ?? true;
|
|
10873
|
-
if (shouldAutoRestore && typeof window !== "undefined"
|
|
10874
|
-
|
|
10931
|
+
if (shouldAutoRestore && typeof window !== "undefined") {
|
|
10932
|
+
if (this.isOAuthRedirectCallbackUrl()) {
|
|
10933
|
+
this.http.setBootRestorePromise(this.oauth.consumeRedirectCallbackOnBoot());
|
|
10934
|
+
} else if (!this.isOAuthCallbackUrl()) {
|
|
10935
|
+
this.http.setBootRestorePromise(this.http.tryRestoreSessionFromCookie());
|
|
10936
|
+
}
|
|
10875
10937
|
}
|
|
10876
10938
|
}
|
|
10877
10939
|
/**
|
|
@@ -10879,7 +10941,7 @@ var ConnectBase = class {
|
|
|
10879
10941
|
*
|
|
10880
10942
|
* `?access_token=...&refresh_token=...` (legacy 토큰-in-URL 흐름) 또는
|
|
10881
10943
|
* `?code=...` (OAUTH_CODE_ONLY 흐름) 또는 OAuth 에러 응답 (`?error=...&state=...`)
|
|
10882
|
-
* 패턴이면 true. 콜백 페이지에서 `autoRestoreSession` 을 건너뛰어 rotation race 를 피한다.
|
|
10944
|
+
* 패턴이면 true. 콜백 페이지에서 일반 `autoRestoreSession` 을 건너뛰어 rotation race 를 피한다.
|
|
10883
10945
|
*/
|
|
10884
10946
|
isOAuthCallbackUrl() {
|
|
10885
10947
|
if (typeof window === "undefined" || !window.location) return false;
|
|
@@ -10889,6 +10951,20 @@ var ConnectBase = class {
|
|
|
10889
10951
|
if (params.has("error") && params.has("state")) return true;
|
|
10890
10952
|
return false;
|
|
10891
10953
|
}
|
|
10954
|
+
/**
|
|
10955
|
+
* 현재 페이지가 토큰-in-URL OAuth 리다이렉트 콜백(팝업 아님)인지 판별.
|
|
10956
|
+
*
|
|
10957
|
+
* `?access_token=&refresh_token=` 이 있고 `window.opener` 가 없으면 true — 부팅 안전망
|
|
10958
|
+
* (`oauth.consumeRedirectCallbackOnBoot`)으로 토큰을 자동 소비할 대상이다. 팝업 콜백
|
|
10959
|
+
* (`window.opener` 존재)은 `getCallbackResult()` 의 postMessage 경로가 전담하므로 제외한다.
|
|
10960
|
+
* code-only(`?code=`)·에러(`?error=`) 콜백도 제외(네트워크 교환/에러 분기는 앱이 명시 호출).
|
|
10961
|
+
*/
|
|
10962
|
+
isOAuthRedirectCallbackUrl() {
|
|
10963
|
+
if (typeof window === "undefined" || !window.location) return false;
|
|
10964
|
+
if (window.opener) return false;
|
|
10965
|
+
const params = new URLSearchParams(window.location.search);
|
|
10966
|
+
return params.has("access_token") && params.has("refresh_token");
|
|
10967
|
+
}
|
|
10892
10968
|
/**
|
|
10893
10969
|
* 새로고침/탭 재개 후 cookie 만으로 세션을 복원한다 (브라우저 전용).
|
|
10894
10970
|
*
|
package/dist/index.mjs
CHANGED
|
@@ -107,6 +107,10 @@ function sanitizePathForBreadcrumb(rawUrl) {
|
|
|
107
107
|
}
|
|
108
108
|
|
|
109
109
|
// src/core/http.ts
|
|
110
|
+
function fetchCredentialsForPath(url) {
|
|
111
|
+
const path = url.split("?")[0];
|
|
112
|
+
return path.startsWith("/v1/public/") ? "omit" : "include";
|
|
113
|
+
}
|
|
110
114
|
var TOKEN_STORAGE_KEY = "cb_auth_tokens";
|
|
111
115
|
function decodeJwtPayload(token) {
|
|
112
116
|
try {
|
|
@@ -633,7 +637,7 @@ var HttpClient = class {
|
|
|
633
637
|
try {
|
|
634
638
|
const response = await fetch(`${this.config.baseUrl}${url}`, {
|
|
635
639
|
...init,
|
|
636
|
-
credentials:
|
|
640
|
+
credentials: fetchCredentialsForPath(url),
|
|
637
641
|
signal
|
|
638
642
|
});
|
|
639
643
|
return { response, ok: response.ok, status: response.status };
|
|
@@ -702,7 +706,7 @@ var HttpClient = class {
|
|
|
702
706
|
}
|
|
703
707
|
return fetch(`${this.config.baseUrl}${url}`, {
|
|
704
708
|
...init,
|
|
705
|
-
credentials:
|
|
709
|
+
credentials: fetchCredentialsForPath(url),
|
|
706
710
|
headers: mergedHeaders
|
|
707
711
|
});
|
|
708
712
|
}
|
|
@@ -4764,6 +4768,12 @@ function escapeToExternalBrowser(currentUrl) {
|
|
|
4764
4768
|
var OAuthAPI = class {
|
|
4765
4769
|
constructor(http) {
|
|
4766
4770
|
this.http = http;
|
|
4771
|
+
/**
|
|
4772
|
+
* 부팅 안전망(consumeRedirectCallbackOnBoot)이 리다이렉트 콜백을 자동 소비했을 때의 promise.
|
|
4773
|
+
* 앱이 그 후 `getCallbackResult()` 를 호출해도 중복으로 `bootstrapRefreshCookie` 를
|
|
4774
|
+
* 발화하지 않도록 이 promise 를 공유한다 (이중 re-issue/rotation 방지).
|
|
4775
|
+
*/
|
|
4776
|
+
this.bootConsumePromise = null;
|
|
4767
4777
|
}
|
|
4768
4778
|
/**
|
|
4769
4779
|
* 활성화된 OAuth 프로바이더 목록 조회
|
|
@@ -5058,10 +5068,58 @@ var OAuthAPI = class {
|
|
|
5058
5068
|
window.close();
|
|
5059
5069
|
return result;
|
|
5060
5070
|
}
|
|
5071
|
+
if (this.bootConsumePromise) {
|
|
5072
|
+
const consumed = await this.bootConsumePromise;
|
|
5073
|
+
if (consumed) {
|
|
5074
|
+
return result;
|
|
5075
|
+
}
|
|
5076
|
+
}
|
|
5061
5077
|
this.http.setTokens(accessToken, refreshToken);
|
|
5062
5078
|
await this.http.bootstrapRefreshCookie();
|
|
5063
5079
|
return result;
|
|
5064
5080
|
}
|
|
5081
|
+
/**
|
|
5082
|
+
* (SDK 내부용 — `ConnectBase` 생성자가 호출) 부팅 안전망.
|
|
5083
|
+
*
|
|
5084
|
+
* OAuth 리다이렉트 콜백 페이지에서 앱이 `getCallbackResult()` 를 호출하지 않아도,
|
|
5085
|
+
* `new ConnectBase()` 만으로 URL 의 토큰(`?access_token=&refresh_token=&member_id=`)을
|
|
5086
|
+
* 적재해 세션을 확립한다.
|
|
5087
|
+
*
|
|
5088
|
+
* **왜 필요한가:** 정적 호스팅(웹 스토리지)은 SPA fallback 으로 `/auth/callback` 같은 콜백
|
|
5089
|
+
* 경로에 홈 `index.html` 을 서빙한다. 그 페이지가 콜백을 처리하지 않으면 URL 토큰이 영영
|
|
5090
|
+
* 소비되지 않아 "로그인 → 다시 로그인 페이지" 무한 루프가 발생한다 (여러 사용자 동시 보고).
|
|
5091
|
+
* 이 안전망은 콜백 처리 코드를 빠뜨린 앱도 동작하게 한다.
|
|
5092
|
+
*
|
|
5093
|
+
* **token rotation race(2026-05-16) 가 없는 이유:** 사전 `re-issue`(cookie 복구) 없이 URL
|
|
5094
|
+
* 토큰을 *직접* 소비한다 — `getCallbackResult()` 의 리다이렉트 경로와 동일한 동작이다. 팝업
|
|
5095
|
+
* (`window.opener` 존재)은 `ConnectBase` 생성자에서 제외되어 호출되지 않는다 (팝업 콜백은
|
|
5096
|
+
* `getCallbackResult()` 의 postMessage 경로 전담). code-only(`?code=`)·에러(`?error=`)
|
|
5097
|
+
* 콜백도 생성자에서 제외된다 (네트워크 교환/에러 분기는 앱이 명시 호출).
|
|
5098
|
+
*
|
|
5099
|
+
* @returns 토큰 적재 성공 시 true, no-op(토큰 없음 등)이면 false.
|
|
5100
|
+
*/
|
|
5101
|
+
consumeRedirectCallbackOnBoot() {
|
|
5102
|
+
if (!this.bootConsumePromise) {
|
|
5103
|
+
this.bootConsumePromise = this.doConsumeRedirectOnBoot();
|
|
5104
|
+
}
|
|
5105
|
+
return this.bootConsumePromise;
|
|
5106
|
+
}
|
|
5107
|
+
async doConsumeRedirectOnBoot() {
|
|
5108
|
+
try {
|
|
5109
|
+
if (typeof window === "undefined" || !window.location) return false;
|
|
5110
|
+
const params = new URLSearchParams(window.location.search);
|
|
5111
|
+
if (params.get("error")) return false;
|
|
5112
|
+
const accessToken = params.get("access_token");
|
|
5113
|
+
const refreshToken = params.get("refresh_token");
|
|
5114
|
+
const memberId = params.get("member_id");
|
|
5115
|
+
if (!accessToken || !refreshToken || !memberId) return false;
|
|
5116
|
+
this.http.setTokens(accessToken, refreshToken);
|
|
5117
|
+
await this.http.bootstrapRefreshCookie();
|
|
5118
|
+
return true;
|
|
5119
|
+
} catch {
|
|
5120
|
+
return false;
|
|
5121
|
+
}
|
|
5122
|
+
}
|
|
5065
5123
|
/**
|
|
5066
5124
|
* 콜백 URL 에서 1회용 `code` 를 토큰으로 교환합니다 (`OAUTH_CODE_ONLY` 서버 모드용).
|
|
5067
5125
|
*
|
|
@@ -10824,8 +10882,12 @@ var ConnectBase = class {
|
|
|
10824
10882
|
this.support = new SupportAPI(this.http);
|
|
10825
10883
|
this.auth._attachAnalytics(this.analytics);
|
|
10826
10884
|
const shouldAutoRestore = config.autoRestoreSession ?? true;
|
|
10827
|
-
if (shouldAutoRestore && typeof window !== "undefined"
|
|
10828
|
-
|
|
10885
|
+
if (shouldAutoRestore && typeof window !== "undefined") {
|
|
10886
|
+
if (this.isOAuthRedirectCallbackUrl()) {
|
|
10887
|
+
this.http.setBootRestorePromise(this.oauth.consumeRedirectCallbackOnBoot());
|
|
10888
|
+
} else if (!this.isOAuthCallbackUrl()) {
|
|
10889
|
+
this.http.setBootRestorePromise(this.http.tryRestoreSessionFromCookie());
|
|
10890
|
+
}
|
|
10829
10891
|
}
|
|
10830
10892
|
}
|
|
10831
10893
|
/**
|
|
@@ -10833,7 +10895,7 @@ var ConnectBase = class {
|
|
|
10833
10895
|
*
|
|
10834
10896
|
* `?access_token=...&refresh_token=...` (legacy 토큰-in-URL 흐름) 또는
|
|
10835
10897
|
* `?code=...` (OAUTH_CODE_ONLY 흐름) 또는 OAuth 에러 응답 (`?error=...&state=...`)
|
|
10836
|
-
* 패턴이면 true. 콜백 페이지에서 `autoRestoreSession` 을 건너뛰어 rotation race 를 피한다.
|
|
10898
|
+
* 패턴이면 true. 콜백 페이지에서 일반 `autoRestoreSession` 을 건너뛰어 rotation race 를 피한다.
|
|
10837
10899
|
*/
|
|
10838
10900
|
isOAuthCallbackUrl() {
|
|
10839
10901
|
if (typeof window === "undefined" || !window.location) return false;
|
|
@@ -10843,6 +10905,20 @@ var ConnectBase = class {
|
|
|
10843
10905
|
if (params.has("error") && params.has("state")) return true;
|
|
10844
10906
|
return false;
|
|
10845
10907
|
}
|
|
10908
|
+
/**
|
|
10909
|
+
* 현재 페이지가 토큰-in-URL OAuth 리다이렉트 콜백(팝업 아님)인지 판별.
|
|
10910
|
+
*
|
|
10911
|
+
* `?access_token=&refresh_token=` 이 있고 `window.opener` 가 없으면 true — 부팅 안전망
|
|
10912
|
+
* (`oauth.consumeRedirectCallbackOnBoot`)으로 토큰을 자동 소비할 대상이다. 팝업 콜백
|
|
10913
|
+
* (`window.opener` 존재)은 `getCallbackResult()` 의 postMessage 경로가 전담하므로 제외한다.
|
|
10914
|
+
* code-only(`?code=`)·에러(`?error=`) 콜백도 제외(네트워크 교환/에러 분기는 앱이 명시 호출).
|
|
10915
|
+
*/
|
|
10916
|
+
isOAuthRedirectCallbackUrl() {
|
|
10917
|
+
if (typeof window === "undefined" || !window.location) return false;
|
|
10918
|
+
if (window.opener) return false;
|
|
10919
|
+
const params = new URLSearchParams(window.location.search);
|
|
10920
|
+
return params.has("access_token") && params.has("refresh_token");
|
|
10921
|
+
}
|
|
10846
10922
|
/**
|
|
10847
10923
|
* 새로고침/탭 재개 후 cookie 만으로 세션을 복원한다 (브라우저 전용).
|
|
10848
10924
|
*
|