Skip to content

feat(auth): 구글·Apple 로그인 서버 구현 - #18

Merged
likell1 merged 2 commits into
mainfrom
feat/social-login-server
Aug 13, 2026
Merged

likell1 merged 2 commits into
mainfrom
feat/social-login-server

Conversation

@likell1

@likell1 likell1 commented Aug 13, 2026

Copy link
Copy Markdown
Contributor

카카오만 있던 소셜 로그인에 구글·Apple 을 붙입니다. 앱 연결은 Apple 개발자 계정이
나온 뒤라 서버만 먼저 끝냅니다.

자격증명 종류가 제공자마다 다르다

카카오        access_token  → 제공자 API 에 되물어 소유자 확인 (기존)
구글·Apple    id_token(JWT) → 제공자 공개키(JWKS)로 서명 검증

Apple 은 프로필 조회 API 자체가 없어 토큰 검증 외에 방법이 없습니다.
인터페이스는 fetch(String) 그대로 두고 파라미터 의미만 문서화했습니다.

aud 검증이 핵심

서명과 만료만 보면 다른 앱이 같은 제공자에서 받은 토큰으로도 로그인이 됩니다.
우리 클라이언트 ID 로 발급된 토큰인지 확인해야 막힙니다.

audiences 가 비어 있으면 조용히 통과시키지 않고 로그인을 거부합니다. 설정
누락이 보안 구멍으로 이어지지 않게 하려는 것입니다. 서버는 정상 기동하고 해당
제공자만 막힙니다.

JWKS 처리는 NimbusJwtDecoder 에 맡겼습니다. 직접 짜면 키 회전과 alg 혼동
공격(RS256 을 none/HS256 으로 바꿔치기)을 놓치기 쉽습니다.

Apple 은 이름을 최초 1회만 준다

identity token 에 이름이 없고 최초 인증 응답에서만 한 번 옵니다. 두 번째
로그인부터는 어떤 경로로도 못 받습니다. 요청에 displayName 을 선택 항목으로 받아
가입 시 표시 이름의 기본값으로만 씁니다 — 클라이언트가 보낸 값이라 신원 판단에는
쓰지 않습니다.

이메일

구글·Apple 모두 email_verified 가 참일 때만 이메일을 채웁니다. 미검증 이메일을
받으면 남의 이메일로 가입하는 경로가 열립니다. Apple 은 이 값을 문자열 "true" 로
줄 때가 있어 양쪽을 봅니다.

카카오 이메일은 비즈 앱 전환이 필요해 지금은 못 받습니다. 서버가 이미 이메일 없는
경우를 처리하므로 코드 변경 없이 나중에 켜면 됩니다.

테스트

JWKS 를 흉내내는 서버를 JDK 내장 HttpServer 로 띄우고 직접 서명한 토큰으로
검증합니다. 정상 통과 · 다른 앱 aud · 다른 발급자 · 만료 · 서명 불일치 · 미설정
6가지. ./gradlew build 통과.

배포

운영 .env 에 OAUTH_GOOGLE_AUDIENCES(웹+iOS 클라이언트 ID)와
OAUTH_APPLE_AUDIENCES=com.purut 를 이미 넣어뒀습니다.

카카오만 있던 소셜 로그인에 구글·Apple 을 붙인다. 앱 연결은 Apple 개발자
계정이 나온 뒤라 서버만 먼저 끝내둔다.

## 자격증명 종류가 제공자마다 다르다

  카카오        access_token — 제공자 API 에 되물어 소유자 확인 (기존)
  구글·Apple    id_token(JWT) — 제공자 공개키(JWKS)로 서명 검증

Apple 은 프로필 조회 API 자체가 없어서 토큰 검증 외에 방법이 없다.
인터페이스는 fetch(String) 그대로 두고 파라미터 의미만 문서화했다.

## aud 검증이 핵심이다

서명과 만료만 보면 다른 앱이 같은 제공자에서 받은 토큰으로도 로그인이 된다.
우리 클라이언트 ID 로 발급된 토큰인지 반드시 확인해야 한다.

audiences 설정이 비어 있으면 조용히 통과시키지 않고 로그인을 거부한다.
설정 누락이 보안 구멍으로 이어지지 않게 하려는 것이다. 서버는 정상 기동하고
해당 제공자만 막힌다.

JWKS 처리는 NimbusJwtDecoder 에 맡겼다. 직접 짜면 키 회전과 alg 혼동 공격
(RS256 을 none/HS256 으로 바꿔치기)을 놓치기 쉽다.

## Apple 은 이름을 최초 1회만 준다

identity token 에는 이름이 없고 최초 인증 응답에서만 한 번 온다. 두 번째
로그인부터는 어떤 경로로도 못 받는다. 그래서 요청에 displayName 을 선택 항목으로
받아 가입 시 표시 이름의 기본값으로만 쓴다 — 클라이언트가 보낸 값이라 신원
판단에는 쓰지 않는다.

## 이메일

구글·Apple 모두 email_verified 가 참일 때만 이메일을 채운다. 미검증 이메일을
받으면 남의 이메일로 가입하는 경로가 열린다. Apple 은 이 값을 문자열 "true" 로
줄 때가 있어 양쪽을 본다.

## 테스트

JWKS 를 흉내내는 서버를 JDK 내장 HttpServer 로 띄우고 직접 서명한 토큰으로
검증한다. 정상 통과 · 다른 앱 aud · 다른 발급자 · 만료 · 서명 불일치 ·
미설정 6가지.
OAUTH_GOOGLE_AUDIENCES / OAUTH_APPLE_AUDIENCES.

구글은 클라이언트 라이브러리 설정에 따라 id_token 의 aud 가 web 인지 iOS 인지
갈린다. 둘 다 넣어두면 어느 쪽이든 통과한다.

비어 있으면 해당 제공자 로그인이 거부된다. 조용히 통과시키면 다른 앱의 토큰으로도
로그인이 되기 때문에, 설정 누락을 실패로 드러내는 쪽을 택했다.
@likell1
likell1 merged commit 8ee4f23 into main Aug 13, 2026
1 check passed
@likell1
likell1 deleted the feat/social-login-server branch August 13, 2026 18:06
likell1 added a commit that referenced this pull request Aug 20, 2026
feat(auth): 구글·Apple 로그인 서버 구현
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