Daeseon Yoo
Back to project
·Decision·4 min

소셜 로그인에 안 막히는 자체 인증 — 이메일+비밀번호 붙이기

로그인이 계속 우리가 통제 못 하는 OAuth 콘솔 상태에 막혔다(카카오 KOE006/KOE010/KOE237, iOS 배포 인증서 Apple 2FA). 그래서 첫 단추를 '사용자를 일단 들여보내는 일'을 외부 의존 없이 하도록, scrypt 기반 이메일+비밀번호 인증을 OAuth 위에 추가로 붙였다. users에 password_hash(nullable) 컬럼을 idempotent하게 더하고, /api/auth/signup·/password-login을 새로 뒀다. 로컬 서버에 curl로 전 시나리오(가입 200/중복 409/오답 401/로그인 200/Bearer 세션 200/짧은비번 400, password_hash 비노출) 통과, root·mobile tsc 모두 깨끗.

왜 자체 인증인가

이 앱의 로그인은 그동안 전적으로 소셜(카카오/구글/애플)에 기대고 있었다. 그런데 "사용자를 일단 들여보내는" 그 첫 단추가 자꾸 우리가 코드로 통제할 수 없는 외부 콘솔 상태에 막혔다 — 카카오 KOE006(리다이렉트 URI·플랫폼 도메인 미등록), KOE010(client secret), KOE237(토큰 요청 레이트리밋), 그리고 iOS는 eas build가 "Distribution Certificate is not validated for non-interactive builds"로 멈춰 Apple 2FA를 요구했다. 전부 콘솔/계정 작업이지, 우리 결함이 아니다.

그래서 결정: 소셜은 그대로 두고(additive), 외부 의존 0으로 동작하는 이메일+비밀번호 인증을 첫 번째 길로 깐다.

비밀번호는 의존성 없이

lib/password.ts는 Node 내장 cryptoscrypt만 쓴다(새 패키지 추가 없음). 저장 형식은 scrypt$<salt_hex>$<hash_hex>, 검증은 crypto.timingSafeEqual. bcrypt/argon2 라이브러리를 끌어오지 않고도 솔트+키스트레칭을 갖춘다.

스키마는 한 컬럼, 그것도 idempotent하게

userspassword_hash(nullable) 한 칸만 추가했다. 소셜/dev 사용자는 null로 둔다. 핵심은 lib/db/init.ts의 부트스트랩 DDL에 ALTER TABLE users ADD COLUMN IF NOT EXISTS password_hash text를 넣은 것 — 이미 떠 있는 프로덕션 DB도 마이그레이션 단계 없이 부팅하면서 스스로 올라온다.

인증 로직의 디테일

lib/auth에 두 함수를 더했다.

라우트는 POST /api/auth/signup(중복 409), POST /api/auth/password-login(오답 401 invalid_credentials). 둘 다 응답은 기존 dev-login/session과 똑같이 { id, displayName } + box + sessionToken만 — password_hash는 어떤 응답에도 안 나간다.

웹·모바일 UI

components/LoginForm.tsx는 로그인/가입 토글 폼으로 바꾸고, 기존 이름-only dev 로그인은 __DEV__ 뒤로 숨겼다(프로덕션 로그인 화면이 더 이상 "막다른 길"이 아니다). 모바일 mobile/App.tsx + src/api.ts도 같은 이메일/비밀번호 흐름을 추가하고 세션은 SecureStore에 저장한다. 애플/카카오/구글 버튼은 그대로다.

검증

로컬 dev 서버(localhost:3100)에 직접 요청해서 전부 확인했다:

시나리오결과
가입200 + Set-Cookie: bk_session + 토큰 + 보관함 자동생성
같은 이메일 재가입409
틀린 비밀번호401
올바른 로그인200 — 가입 때와 같은 user.id/box.id
Bearer 토큰 세션200 (모바일 인증 경로)
3자 비밀번호400 (검증)
응답의 password_hash없음

tsc --noEmit은 root·mobile/ 모두 exit 0. iOS 번들도 에러 없이 컴파일(iOS Bundled … 823 modules).