Learning Docs 0.1.0 버전

Auto-refresh 동작

SDK 의 setupLearningSdk() 가 자동으로 처리. Frontend 가 명시 호출할 일 없음.

작동 방식

1. 일반 호출 — Request interceptor 가 Authorization 자동 주입

[SDK 호출]
  → Request interceptor: hooks.getAccessToken() 의 token 을 Authorization 헤더에 자동
  → fetch
  → 200 응답

2. 만료 — Response interceptor 가 자동 refresh + retry

[SDK 호출]
  → 401 응답 (access token 만료)
  → Response interceptor: refresh promise 시작 (singleton — 동시 401 N개 = 1 refresh)
  → POST /client/auth/refresh-token (cookie 자동 동봉, credentials: 'include')
  → 새 accessToken 받음 → hooks.setAccessToken 호출
  → 원 request 재시도 (1회)
  → 200 응답

동시에 401 받은 N 개 호출이 있어도 refresh 는 1회만 — 나머지는 같은 promise 공유.

3. Multi-tab — Web Locks API

같은 origin 의 여러 tab 이 동시에 만료된 access token 가지고 있을 때:

Tab A: 401 → Web Lock 'learning-sdk-refresh' 획득 → refresh API 호출
Tab B: 401 → 같은 Lock 대기
Tab A: refresh 성공 → BroadcastChannel('learning-sdk-auth') 로 새 token 전파
Tab B: BroadcastChannel 메시지 받음 + Lock 해제 → memory token 갱신 후 원 request 재시도

→ refresh API 가 한 번만 호출됨. multi-tab race 없음.

4. Refresh 실패 — onAuthFailure

[refresh API 호출]
  → 401 (refresh token 만료 / reuse 감지 / revoked family)
  → SDK 가 hooks.onAuthFailure() 호출
  → frontend 가 sign-in 페이지로 redirect

Reuse detection — 보안 안전망

정상: token A → rotate → token B (A 의 consumedAt set)
                       → rotate → token C (B 의 consumedAt set)
                       ...

도난 시나리오 (A 도난):
  - 정상 사용자: A → rotate → B (정상 흐름)
  - 공격자도 A 가지고 있음 → A 재사용 시도 → 백엔드 reuse 감지 → family 전체 revoke
  - 정상 사용자도 다음 refresh 시 401 받지만, 공격자도 끊김.
  - 정상 사용자가 다시 sign-in → 새 family. 공격자는 다시 도난 안 하는 한 끊김.

frontend 측에서 특별히 처리할 것 없음. 백엔드 + SDK 자동.

Proactive refresh — 만료 직전 미리

setupLearningSdkgetAccessTokenExpiresAt hook 을 제공하면:

[SDK 호출]
  → Request interceptor: 만료까지 30초 미만이면 background 로 refresh 호출
  → 새 token 으로 outgoing request 의 Authorization 헤더 갱신
  → fetch

→ 401 후 retry 비용 줄임 (정상 흐름이 한 번에 200).

비활성: setupLearningSdk({ proactiveRefreshLeadMs: 0, ... }).

Retry — 5xx / 408 / 429 / network

[SDK 호출]
  → 503 또는 network error
  → Error interceptor: exponential backoff (1s → 2s → 4s, jitter ±250ms, max 10s)
  → 최대 3회 재시도
  → 모두 실패 시 caller 로 throw

비활성 / 옵션 조정:

setupLearningSdk({ maxRetries: 0 })       // retry 안 함
setupLearningSdk({ maxRetries: 5 })       // 5회까지
setupLearningSdk({ timeoutMs: 0 })        // timeout 비활성
setupLearningSdk({ timeoutMs: 60000 })    // 60초로

Sign-out

await ClientAuth.signOut({})           // 현재 device 의 refresh family revoke + cookie clear
await ClientAuth.signOutAll({})        // user 의 모든 device revoke

sessionStorage.removeItem('app.access')
location.href = '/sign-in'