콘텐츠로 이동

@gj-kit/expo-auth 핵심 개념

Expo·React Native·웹을 한 벌의 코드로 다루는 token refresh 코어입니다. transient 분기를 빠뜨리면 컴파일이 실패합니다.

직접 짠 refresh 코드는 5xx·timeout·CORS 실패를 확정 거절로 오분류해 사용자를 로그아웃시킵니다. 갱신 결과를 raw switch로 소비하는데 TypeScript가 exhaustiveness를 강제하지 않고, 빠뜨리는 분기는 늘 transient이기 때문입니다. 여기에 Platform.OS 분기가 웹 번들까지 expo-secure-store를 끌고 들어오고, 두 탭이 같은 단일 사용 refresh token을 두고 경합하고, 재진입 여부를 boolean 플래그로 다루다 401 재시도가 다시 refresh 경로로 떨어지고, signOut 뒤 옵션 없이 다시 로그인하면 세션 로그인이 조용히 durable로 승격돼 공용 PC에서 사용자가 고른 범위가 무너집니다.

여러 요청이 동시에 만료된 token을 발견할 수 있습니다. 주입한 storage와 lock으로 호스트는 플랫폼 메커니즘을 선택하고 세션은 refresh를 조정합니다.

일시적 실패는 로그아웃이 아닙니다

섹션 제목: “일시적 실패는 로그아웃이 아닙니다”

닫힌 refresh outcome은 재시도 가능한 서비스 실패와 무효 credential을 구분하므로 5xx가 유효한 세션을 조용히 지울 수 없습니다.

  • transient 누락은 컴파일 에러입니다 — matchRefreshOutcome은 다섯 결말을 전부 핸들러 키로 받으므로, 하나라도 빠지면 “Property ‘transient’ is missing”으로 컴파일이 실패합니다.
  • 5xx에서는 저장된 토큰에 손대지 않습니다 — transient 결말에서는 코어가 storage에 아무것도 쓰지 않고, runAuthorized는 원래 에러를 그대로 다시 던져 사용자를 로그인 화면으로 보내지 않습니다.
  • 재시도 두 번은 표현할 수 없습니다 — runAuthorized는 기본값 없는 shouldRetryAfterRefresh를 요구하고, 단 한 번 수행하는 재시도는 refresh 경로 밖에서 실행되므로 공개 API에 재진입 스위치 자체가 없습니다.
  • signIn은 persistence를 잊을 수 없습니다 — signIn의 두 번째 인자에서 persistence는 필수 옵션이라, “세션 로그인 → signOut → 옵션 없는 재로그인”이 토큰을 조용히 durable로 승격시키는 일이 생기지 않습니다.
  • 웹 번들에 SecureStore가 없습니다 — ./storage 하나가 exports 조건으로 갈라지므로 앱에는 Platform.OS 분기가 한 줄도 없고, 브라우저 그래프에는 expo-secure-store가 들어가지 않습니다.

토큰은 secret으로 취급하세요. 제공된 오류 계약을 사용하고 토큰 문자열이나 원본 authorization 응답을 로그에 남기지 마세요.

다음은 검증된 레시피 두 개입니다.