Social Auth
2026-09-09 개정 (이슈 #1456·#1457·#1458, 오너 결정) — 명령·워크플로 이름이 바뀌었다. 작업 PR 통합 요청
npm run merge:request(구 landing:request), PR 전 사전 검증npm run ci:precheck-local(최신 release 합치기 → verify → 서버 변경 시 DB 단계, 브라우저 없음), 전체 검증npm run ci:full-local(릴리스 큐 runner 안에서만; 세션은--only <단계>재현만). 워크플로 표시 이름:3-Release Merge Request(release-merge-request.yml) ·GitHub Full CI(policy-contract.yml) ·1-Production Deploy/2-Staging Deploy(production-deploy.yml·staging-deploy.yml, 단계 정의는 deploy-steps.yml) ·Production smoke (manual)·Social login daily check·Daily data backup.landing:lock·db:preflight스크립트는 폐기됐다(GitHub concurrency 가 잠금, DB 검사는 사전 검증과 큐가 한다). 아래 본문의 옛 이름은 그 개정 전 기록이다.
Barbelic 앱은 Kakao, Google, Apple을 독립 provider 모듈로 취급하고, 모든 앱 데이터의 소유자는 로그인 제공자가 아니라 Supabase auth.users.id로 유지한다.
Runtime contract
- 웹:
signInWithOAuth({ provider })→ 현재 origin의 callback →exchangeCodeForSession(code) - iOS: 동일한 Supabase authorize URL을
ASWebAuthenticationSession으로 열고barbelic://auth/callback의 일회성 code를 WebView가 교환 - iOS PKCE verifier는 전체 session과 분리해 Keychain에 최대 10분만 보관한다. callback 중 WebView 또는 앱이 재시작돼도 verifier를 복구하고, 교환 직후 삭제한다.
- custom scheme에는 access token, refresh token, provider ID token을 싣지 않는다.
- iOS는 네이티브 로그인 완료 뒤에만 WebView를 앱 진입으로 리셋한다(새 세션으로 재부팅). 계정 연결 완료는 이미 로그인된 WebView 안에서 결과를 표시하고 리셋하지 않는다 — 주입 스크립트가
nativeAuthComplete에action을 실어 보내고 네이티브가link면 리셋을 건너뛴다. - 한 시점에 소셜 로그인 또는 계정 연결 요청은 하나만 허용한다. Supabase PKCE verifier 저장소가 provider별로 분리되지 않기 때문이다.
- 로그인 이후 온보딩, 프로필, 운동 데이터는 provider 이름이나 이메일이 아니라
auth.uid()만 사용한다.
Provider 정의는 다음 파일로 분리한다.
src/react/services/auth/providers/kakao.ts
src/react/services/auth/providers/google.ts
src/react/services/auth/providers/apple.ts
src/react/services/auth/providers/index.tsAccount expansion by identity linking
로그인된 사용자는 모바일·데스크톱 내 정보의 로그인 연결에서 아직 연결되지 않은 provider를 현재 auth.users.id에 추가할 수 있다.
- 시작 API:
supabase.auth.linkIdentity({ provider, options }) - 완료 API: 로그인과 동일한 PKCE
exchangeCodeForSession(code) - 웹 callback은
auth_action=link를 유지하고 프로필 화면으로 복귀한다. - iOS는 action을 transaction에 함께 묶는다. 로그인은 Supabase
/auth/v1/authorize만 허용하고, 연결은 Supabase가 인증된 요청 뒤 반환한 provider별 공식 authorize host/path와 Supabase callbackredirect_uri를 함께 검증한다. - 성공 표시는 callback query만 신뢰하지 않고
getUserIdentities()에서 해당 provider가 실제 연결됐는지 다시 확인한 뒤 노출한다. - 이미 현재 사용자에게 연결된 provider는 다시 시작하지 않는다.
- 이미 다른 Barbelic 사용자에게 연결된 identity는 병합하지 않고 안전한 충돌 오류로 종료한다.
이 기능은 “한 가지 방식으로만 사용하던 계정에 새 로그인 방식을 추가”하는 경우만 지원한다. 연결 해제와 서로 운동 데이터가 있는 두 auth.users.id의 데이터 병합은 포함하지 않는다.
Supabase Dashboard > Authentication > General Configuration에서 Allow manual linking을 활성화해야 한다. 비활성 상태에서는 UI가 원본 서버 오류를 노출하지 않고 운영 설정이 필요하다는 안내를 표시한다.
app-config.js의 socialAuthProviders는 로그인 버튼과 계정 연결 버튼의 production 노출 gate다. provider 코드와 외부 설정을 준비하는 것만으로 버튼을 노출하지 않는다. 개인정보처리방침 v3 게시와 동의 전환(2026-08-21) 뒤 출시 조건을 채워 ["kakao", "google", "apple"]로 전환했다. gate에 올린 provider는 Supabase에서도 활성화돼 있어야 하며, 이 불변식은 provider-assumed 스모크(social-login-daily-check.yml)가 scripts/check-social-provider-config.mjs --gated로 매 실행 검사한다. 특정 provider에 문제가 생기면 Supabase 비활성화 대신 gate에서 해당 provider만 빼는 최소 롤백을 쓴다.
Supabase redirect URLs
Supabase Dashboard > Authentication > URL Configuration에 실제 사용하는 origin을 등록한다.
https://www.barbelic.com/**
http://127.0.0.1:5173/**
barbelic://auth/callback**Site URL과 웹·iOS redirect origin은 https://www.barbelic.com으로 통일한다.
Preview 배포에서 로그인해야 한다면 소유한 Vercel preview hostname 패턴만 별도로 허용한다. PKCE verifier는 origin 저장소에 묶이므로 preview에서 production으로 callback을 강제 이동하지 않는다.
각 외부 provider에 등록할 OAuth callback은 Barbelic 웹 도메인이 아니라 Supabase callback이다.
https://kobxeylancdimqhfkbnl.supabase.co/auth/v1/callbackProvider setup
Kakao
Supabase Dashboard > Authentication > Providers > Kakao에 REST API key와 client secret을 등록한다. 예전 /api/auth/kakao/start 및 /api/auth/kakao/callback Vercel 함수와 KAKAO_* 환경변수는 사용하지 않는다.
Google
Google Cloud Console에서 Web application OAuth client를 만들고 위 Supabase callback URL을 Authorized redirect URI로 등록한다. Client ID/secret을 Supabase Google provider에 저장하고 provider를 활성화한다.
Apple
Apple Developer에서 Services ID, Sign in with Apple key, Team ID를 준비한다. Services ID의 Return URL에 위 Supabase callback URL을 등록하고 생성한 client secret을 Supabase Apple provider에 저장한다. Apple OAuth secret은 6개월마다 갱신해야 하므로 만료일을 운영 캘린더와 secret rotation 절차에 반드시 등록한다.
iOS 앱의 Apple 로그인은 네이티브 Sign in with Apple(시스템 시트)을 쓴다: 네이티브가 무작위 nonce를 만들어 SHA-256 해시를 ASAuthorizationAppleIDRequest.nonce에 넣고, 돌려받은 identity token과 원문 nonce만 barbelic:native-auth 이벤트로 웹에 넘기면 웹이 signInWithIdToken({ provider: "apple", token, nonce })로 세션을 만든다 (Supabase가 해시 일치를 검증). 이 토큰의 audience는 앱 번들 ID라 Supabase Apple provider Client IDs에 com.dekerd.barbelic.web, com.dekerd.barbelic 둘 다 있어야 하고, Xcode App target에 Sign in with Apple capability(App/App.entitlements)가 필요하다. Apple 계정 연결과 Kakao·Google은 계속 같은 검증 가능한 PKCE 브라우저 세션 (ASWebAuthenticationSession)을 쓴다.
계정 삭제 시 provider 연결 해제 (이슈 #663 Phase 3)
계정 삭제(api/account/delete.js)는 Auth 사용자 파기 전에 provider 연결 해제를 시도한다. 해제 실패는 경고로만 남고 삭제를 막지 않는다. 단계·재개 지점·오류 코드·시간 제한의 정본은 인증·계정·관리자 서버 API 계약 §2-2·§3(이슈 #1331).
- Apple — 앱은 refresh token을 보관하지 않으므로(위 네이티브 로그인은 id_token만 사용), App Store 5.1.1(v)의 토큰 revoke는 삭제 시점 재인증으로 이행한다: iOS 셸이 Sign in with Apple을 한 번 더 돌려 authorization code를 받고 (
appleAccountDeletionReauth브리지 —docs/contracts/native-bridge.md), 서버가 client secret(ES256 JWT,api/account/_apple.js)으로 코드를 교환해/auth/revoke한다. code의 audience는 번들 IDcom.dekerd.barbelic다. 웹·Android 세션에는 code가 없어 revoke 없이 삭제만 진행된다. - Kakao — 유저의 kakao identity subject를 Admin 키로
POST https://kapi.kakao.com/v1/user/unlink한다(api/account/_kakao.js). - Google — 삭제 시 revoke 의무가 없어 생략한다.
Vercel(프로젝트 barbelic) 환경변수 — 미설정이면 해당 해제가 *_skipped_no_config/*_skipped_no_admin_key 경고로 강등된다:
| 변수 | 값 |
|---|---|
APPLE_TEAM_ID | Apple Developer Team ID |
APPLE_KEY_ID | Sign in with Apple key의 Key ID |
APPLE_PRIVATE_KEY | .p8 PEM 전문(줄바꿈은 \n 이스케이프 가능) |
APPLE_CLIENT_ID | 생략 시 com.dekerd.barbelic |
KAKAO_ADMIN_KEY | Kakao Developers 앱의 Admin 키 |
Release checklist
이 체크리스트의 실행 절차(콘솔별 단계·검증 명령·rotation)는 social-login-rollout-runbook.md에 있다.
- Supabase에서 Kakao, Google, Apple이 모두 enabled인지 확인한다.
- production, local, iOS custom-scheme redirect allowlist를 확인한다.
- 각 provider의 실제 계정으로 신규 가입과 기존 로그인 모두 확인한다.
- 한 provider로 로그인한 뒤 사용 이력이 없는 다른 provider를 연결하고, 로그아웃 후 새 provider로 동일한 사용자 데이터에 진입하는지 확인한다.
- 로그인·연결 callback URL에 token이 없고
code만 있는지 확인한다. - iOS에서 취소, provider 오류, 앱 복귀, callback 도중 WebView/앱 재시작을 확인한다.
- Google/Apple 수집 항목과 외부 로그인 서비스를 반영한 개인정보처리방침 v3를 법적 검토 후 게시하고, 필요한 재동의 정책을 적용한다.
- 마지막으로
app-config.js의socialAuthProvidersrollout gate에 Google과 Apple을 추가한다.
게시된 privacy-v1.html과 privacy-v2.html은 불변 문서이며 Kakao만 명시한다. 그래서 Google/Apple을 반영한 v3 공개와 동의 버전 전환 전에는 해당 provider를 production에서 활성화하지 않는 것이 원칙이었고, 2026-08-21 v3 게시·동의 전환(20260821120000)으로 이 전제 조건은 충족됐다.