맥락#
기본 이메일 비밀번호 로그인은 구현됐지만 OAuth callback, 공급자 설정, 신규 소셜 사용자의 프로필 완성과 개발·운영 자격증명 분리는 없었다. 프로젝트 오너는 로그인 UI·비즈니스 로직·Supabase Auth·DB를 먼저 완성하고 카카오를 주 간편로그인으로, 구글과 Apple을 향후 앱 심사까지 고려한 동등한 선택지로 제공하기로 했다. React Native 연결은 현재 범위가 아니지만 같은 계정 계약을 재사용해야 한다. [1]
이후 프로젝트 오너는 개발·운영 웹 모두 제품 비밀번호를 만들거나 재설정하지 않고, 이메일 대체 수단도 6자리 일회용 코드로 통일하기로 했다. [2] Supabase는 이메일 OTP 요청과 코드 검증으로 신규 사용자와 세션을 만들 수 있다. [3]
Apple 지침 4.8은 소셜 로그인을 주 계정에 쓰는 앱에 개인정보 보호 특성을 갖춘 동등한 로그인 선택지를 요구하므로 예외에 의존하지 않고 Apple 로그인을 함께 준비한다. [4]
결정#
계정과 프로필#
- Supabase
auth.users가 환경 안에서 사용자의 안정된 식별자다. - 공급자 연결은 Supabase가 관리하는 Auth identity를 기준으로 하고 public DB에 OAuth access token, provider user ID 또는 client secret을 복제하지 않는다.
- 제품이 직접 사용하는 정보는
public.profiles에 두고 Auth user ID와 1:1로 연결한다. - 신규 이메일 OTP 사용자는 코드 검증 뒤 프로필 완성 화면에서 표시 이름을 입력한다. 이름을 주지 않는 Apple 웹 OAuth도 같은 화면을 사용한다. [5] [3]
- 수동 계정 병합과 공급자 연결 해제 UI는 만들지 않는다. 같은·다른 로그인 이메일 규칙은 ADR-0032가 소유한다. [6]
로그인 흐름#
- 카카오, 구글, Apple 버튼은 Supabase
signInWithOAuth를 호출한다. - 공급자 인증 뒤
/auth/callback에서 PKCE code를 Supabase 세션으로 교환한다. - 완료된 프로필은 원래의 안전한 내부 경로 또는 채팅으로 이동한다. 미완료 프로필은 표시 이름을 먼저 받는다.
- 이메일은 주소 입력, 6자리 코드 발송,
verifyOtp검증을 거쳐 같은 쿠키 기반 Supabase 세션을 사용한다. 신규 사용자는 별도 회원가입 화면 없이 첫 코드 검증으로 생성된다. [3] next는/로 시작하는 앱 내부 경로만 허용해 외부 주소로 넘기지 않는다.- 모바일 이메일 입력은 이메일용 영문 키보드를 요청한다. 코드는 10분 동안 유효하고 재요청은 발송 뒤 60초 동안 막으며, 남은 시간과 재요청 가능 시점을 화면에 표시한다. [7]
제품 웹의 이메일 비밀번호 금지는 2026-08-18 ADR-0030으로 대체됐다. 소셜 로그인과 환경 분리 화면은 유지하고 이메일 템플릿·SMTP와 OAuth 자격증명만 환경별로 분리한다. [8]
OAuth 앱에서 쓰는 Supabase callback과 앱에서 쓰는 /auth/callback은 서로 다른 단계다. 각 공급자에는 해당 환경 Supabase의 https://<project-ref>.supabase.co/auth/v1/callback을 등록하고, Supabase Redirect URL 목록에는 해당 환경의 오프테이블 앱 callback을 등록한다. [9]
환경 분리#
| 항목 | Development | Production |
|---|---|---|
| Supabase | offtable-dev |
offtable-prod |
| 웹 | localhost, Vercel Preview, dev.offtable.kr |
offtable.kr |
| 카카오 | 별도 개발 앱·REST key·secret | 별도 운영 앱·REST key·secret |
| 별도 테스트 Cloud project·OAuth client | 별도 게시 Cloud project·OAuth client | |
| Apple | 별도 개발 App ID·Services ID·key | 별도 운영 App ID·Services ID·key |
| Auth 사용자와 프로필 | 테스트 사용자·가상 데이터 | 운영 사용자·운영 데이터 |
두 환경은 공급자 콘솔 앱, OAuth client ID·secret, callback, Supabase Auth 사용자와 DB를 공유하지 않는다. Google도 개발·테스트와 운영·게시 Cloud 프로젝트 분리를 권장한다. [10]
이메일 OTP 메일 템플릿은 6자리 {{ .Token }}을 포함하고 개발과 운영에서 독립 구성한다. Supabase Auth의 OTP 만료는 600초, 이메일 재발송 최소 간격은 60초로 맞춰 화면의 시간 정책과 일치시킨다. 실제 고객 대상 서비스 전환 시 보안 전환 게이트에서 SMTP, 발신 도메인, 발송 제한과 오용 방지를 강화한다. [3] [7]
개발과 운영의 이메일 OTP 발신 주소는 info@offtable.kr로 통일하고 메일 발송용 서브도메인은 만들지 않는다. 같은 발신 주소를 사용하더라도 Resend API 키와 Supabase Custom SMTP 설정은 환경별로 분리한다. [11]
공급자별 최소 설정#
- 카카오: REST API key와 활성화한 Client Secret을 사용한다. 닉네임과 이메일 동의를 요청하고 이메일 없는 사용자는 기본적으로 허용하지 않는다. [12]
- Google:
openid, email, profile만 요청한다. 향후 웹·iOS·Android client ID를 함께 등록할 때 웹 client ID를 첫 번째로 둔다. [13] - Apple: 웹 Services ID를 첫 client ID로 두고 향후 native App ID를 추가한다. 웹 OAuth signing secret의 갱신 일정을 운영 체크리스트에 둔다. [5]
현재 상태#
제품 웹 이메일 로그인은 ADR-0030이 소유한다. 이 문서의 비밀번호 없는 OTP 문장은 이력이다. 소셜 로그인, 환경 분리, 프로필 계약, 발신 주소는 계속 이 문서가 소유한다. 관리자 OTP는 ADR-0027이 소유한다. [8]
React Native 후속 경계#
- RN은 같은 환경의 Supabase 프로젝트, Auth user ID,
profiles와 RLS 계약을 재사용한다. - RN 앱용 redirect scheme과 iOS·Android OAuth client는 웹 자격증명과 별도로 추가한다.
- iOS Apple 로그인은 네이티브 Authentication Services를 사용하고 발급받은 ID token을 Supabase 세션으로 교환하는 방식을 우선 검토한다.
- RN 도입 때문에 public DB 스키마를 다시 만들거나 웹 세션 구현을 모바일에 복사하지 않는다.
검증#
- 각 환경에서 이메일 OTP 발송·코드 검증·신규 프로필 완성·재로그인·로그아웃을 확인한다.
- 각 공급자 로그인 뒤 세션과
profiles가 같은 user ID에 연결되는지 확인한다. - 이름이 없는 신규 사용자가 프로필 완성 전 채팅으로 이동하지 않는지 확인한다.
- 개발 OAuth 자격증명으로 운영 callback을 사용할 수 없고 반대 방향도 사용할 수 없는지 확인한다.
- 로그아웃 뒤 보호 화면이 로그인으로 이동하는지 확인한다.
- 저장소, Wiki, Vercel public 변수에 provider secret과 Apple
.p8가 없는지 확인한다.
관련 문서: 사용자 인증 기획, 비밀번호 로그인, 기본 웹과 Supabase 기반, Supabase 환경 격리, 소셜 로그인 요구, 비밀번호 없는 인증 결정