Skip to content

feat: 소셜 로그인 계정 복구 흐름 추가 - #299

Merged
hyoj-dev merged 2 commits into
developfrom
feat/social-login-recovery
Aug 24, 2026
Merged

feat: 소셜 로그인 계정 복구 흐름 추가#299
hyoj-dev merged 2 commits into
developfrom
feat/social-login-recovery

Conversation

@hyoj-dev

@hyoj-dev hyoj-dev commented Aug 24, 2026

Copy link
Copy Markdown
Collaborator

변경 사항

  • 서버 판정 기반 소셜 로그인 복구 API를 추가했습니다.
    • POST /api/v2/auth/social/login
    • POST /api/v2/auth/social/email/start
    • POST /api/v2/auth/social/email/verify
  • 소셜 계정 식별자와 이메일 인증 정보를 관리하는 DB 구조를 추가했습니다.
    • social_identities
    • social_login_sessions
    • email_verification_challenges
  • 기존 활성 회원의 소셜 식별자를 social_identities로 이관하는 Flyway V47 마이그레이션을 추가했습니다.
  • 새로운 소셜 식별자가 들어오면 즉시 가입시키지 않고 이메일 OTP 인증을 요구하도록 변경했습니다.
  • OTP 인증 완료 후 서버에서 계정 상태를 판정합니다.
    • 같은 이메일의 동일 제공자 회원이 한 명이면 기존 users.id에 새 식별자 연결
    • 일치하는 활성 회원이 없으면 신규 회원 생성
    • 같은 이메일 회원이 여러 명이거나 다른 제공자 회원만 있으면 SUPPORT_REQUIRED 반환
  • 기존 /api/v1/auth/login에서 알 수 없는 식별자의 신규 회원 자동 생성을 차단하고 ACCOUNT_VERIFICATION_REQUIRED를 반환하도록 변경했습니다.
  • Google ID Token의 iss, aud, exp를 검증하고 허용된 Client ID만 승인하도록 강화했습니다.
  • Kakao 웹 인가코드를 백엔드에서 액세스 토큰으로 교환하고 토큰의 app_id를 검증하도록 변경했습니다.
  • 이메일 OTP에 만료시간, 최대 시도 횟수, 재전송 제한, 일회성 세션 처리를 적용했습니다.
  • 만료된 소셜 로그인 세션과 이메일 인증 정보를 기존 일일 정리 작업에서 삭제하도록 추가했습니다.
  • Apple 로그인은 설정 완료 전까지 OAUTH_APPLE_ENABLED=false로 비활성화할 수 있도록 했습니다.
  • 개발 배포 워크플로가 Google·Kakao OAuth용 GitHub Secrets를 개발 서버 .env에 전달하도록 수정했습니다.
    • DEV_OAUTH_GOOGLE_ALLOWED_CLIENT_IDS
    • DEV_OAUTH_KAKAO_APP_ID
    • DEV_OAUTH_KAKAO_REST_API_KEY
    • DEV_OAUTH_KAKAO_CLIENT_SECRET
    • DEV_OAUTH_KAKAO_REDIRECT_URI
    • Apple 로그인은 개발 환경에서 false로 고정
  • 운영 환경변수, DB 사전 점검 SQL, 배포 순서와 스모크 테스트 항목을 정리한 배포 가이드를 추가했습니다.

변경 이유

Google, Kakao, Apple 소셜 애플리케이션을 새로운 관리 계정으로 이전하면 제공자 식별자가 달라질 수 있습니다. 기존 구조는 알 수 없는 식별자를 즉시 신규 회원으로 생성하기 때문에 기존 회원의 회고·프로젝트 데이터와 분리된 중복 계정이 만들어질 위험이 있었습니다.

클라이언트가 기존 회원과 신규 회원을 선택하지 않고, 이메일 소유권 확인 후 서버가 기존 계정 연결 여부를 결정하도록 변경해 기존 데이터를 보호하기 위해 수정했습니다. 또한 Kakao Client Secret과 액세스 토큰 교환 과정을 브라우저에서 제거하고 제공자별 토큰 검증을 강화했습니다.

개발 배포 시 OAuth 설정이 서버에 전달되지 않으면 Google의 Client ID 검증과 Kakao의 인가코드 교환이 동작하지 않으므로, 기존 개발 배포 워크플로의 .env 생성 단계에도 환경변수 전달을 추가했습니다.

영향

  • 기존에 등록된 소셜 식별자는 이메일 인증 없이 동일하게 로그인할 수 있습니다.
  • 새로운 소셜 식별자는 이메일 OTP 인증 전까지 users 데이터가 생성되지 않습니다.
  • 같은 이메일의 동일 제공자 기존 회원은 기존 users.id가 유지되어 기존 서비스 데이터를 그대로 사용합니다.
  • 중복 이메일이나 다른 소셜 제공자 계정은 자동 병합하지 않고 고객지원 대상으로 분리됩니다.
  • 기존 v1 클라이언트가 알 수 없는 식별자로 로그인하면 신규 계정을 생성하지 않고 이메일 인증 필요 오류를 받습니다.
  • Kakao Client Secret, Gmail 앱 비밀번호 등 비밀값은 백엔드 환경변수로만 관리해야 합니다.
  • 개발 배포 전 백엔드 저장소에 DEV_OAUTH_* GitHub Secrets가 등록되어 있어야 합니다.
  • Flyway V47 적용 전 활성 회원의 (provider, provider_id) 중복 여부를 반드시 점검해야 합니다.
  • Apple 로그인은 별도 콘솔 설정과 검증이 끝날 때까지 비활성화됩니다.

검증

  • 전체 테스트 통과
  • pre-push 전체 테스트 통과
  • 소셜 로그인 핵심 테스트 --rerun-tasks 통과
    • 기존 식별자 로그인
    • v1 신규 자동가입 차단
    • OTP 후 기존 계정 연결
    • 타 제공자 자동 병합 차단
    • 신규 회원 생성
    • Google Client ID 검증
    • Kakao 앱 ID 검증
  • spotlessCheck 통과
  • git diff --check 통과
  • GitHub Actions build 통과

참고

  • 개발 프런트 주소는 https://dev-app.didit.io.kr, 개발 API는 https://dev-api.didit.io.kr 기준으로 설정해야 합니다.
  • 운영 프런트 주소는 https://app.didit.io.kr, 운영 API는 https://api.didit.io.kr입니다.
  • 개발·운영 환경별 Kakao Redirect URI가 백엔드 환경변수와 Kakao 콘솔에 동일하게 등록되어야 합니다.
  • 운영 배포 워크플로의 PROD_OAUTH_* 전달은 운영 Secret 등록과 Apple 설정 확정 후 별도로 반영해야 합니다.
  • 운영 배포 전 최신 DB 백업과 deploy/SOCIAL_LOGIN_RECOVERY.md의 사전 점검 SQL 실행이 필요합니다.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e73843d0d3

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

}.getOrElse { throw OAuthUserInfoFailedException() }
?: throw OAuthUserInfoFailedException()

if (tokenInfo.appId != expectedAppId) throw OAuthUserInfoFailedException()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Allow both Kakao app IDs during the cutover

During the documented backend-first rollout, the old frontend continues calling /api/v1/auth/login, whose access tokens now pass through this same exact app-ID check. Configuring the singular OAUTH_KAKAO_APP_ID for the new app therefore rejects every old-app token before the frontend is switched, while leaving it set to the old ID makes the new v2 smoke test fail. Accept both old and new Kakao app IDs during the migration window so existing users can continue logging in.

Useful? React with 👍 / 👎.

return authenticated(user, session.provider, false)
}

val matchingUsers = userRepository.findAllActiveByNormalizedEmail(normalizedEmail)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Serialize account creation by normalized email

If two valid OTP sessions for the same previously unused email are verified concurrently, both transactions can observe an empty result here and then create separate active users, because they lock different session/challenge rows and neither the query nor the schema enforces uniqueness for normalized email. This leaves duplicate accounts that subsequently resolve to SUPPORT_REQUIRED; serialize this resolution by normalized email or add an equivalent database invariant before creating the user.

Useful? React with 👍 / 👎.

@hyoj-dev
hyoj-dev merged commit c4383e4 into develop Aug 24, 2026
3 checks passed
@hyoj-dev
hyoj-dev deleted the feat/social-login-recovery branch August 24, 2026 17:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant