Skip to content

Frontend Apple Login Guide

tuna edited this page Sep 7, 2026 · 3 revisions

애플 로그인 연동 가이드 (앱 · 웹)

상태: 리뷰 중 — 앱 PR #164(이슈 #163) / 웹 PR #166(이슈 #165) 왜: App Store 심사 지침 4.8 — 카카오 로그인을 제공하는 앱은 Sign in with Apple도 함께 제공해야 심사를 통과합니다. 범위: iOS 앱(1~5절)과 웹(6절) 둘 다 지원합니다. 웹 로그인은 PR #166(이슈 #165)에서 추가됐습니다.

공통

  • Base URL: https://api.ditto.pics
  • 헤더: X-API-Key: <키> — Authorization은 필요 없습니다(로그인 전 호출)
  • 응답 래핑: HTTP는 항상 200, 성공/실패는 success로 구분

1. 엔드포인트

POST /api/v1/users/social-login/apple/native
Content-Type: application/json

{
  "identityToken": "eyJraWQiOi...",       // 필수
  "rawNonce": "a1b2c3...",                // 선택(권장)
  "name": "김철수"                          // 선택(최초 1회만)
}

응답과 Set-Cookie(refreshToken)는 카카오 네이티브 로그인과 완전히 같습니다.

// 정상
{ "success": true, "data": {
  "accessToken": "eyJhbGciOi...", "signupRequired": true,
  "sanctioned": false, "sanctionCode": null, "suspendedUntil": null
}, "error": null }

// 제재 회원 — accessToken 없음, Set-Cookie 없음
{ "success": true, "data": {
  "accessToken": null, "signupRequired": false,
  "sanctioned": true, "sanctionCode": "MEMBER_SUSPENDED", "suspendedUntil": "2026-09-14 00:00:00"
}, "error": null }

카카오와 같은 타입을 그대로 재사용하면 됩니다. 분기 처리도 동일합니다 — sanctioned 먼저 보고, 아니면 signupRequired로 회원가입 화면 여부를 정합니다.


2. 앱에서 값 얻기

@capacitor-community/apple-sign-in(또는 ASAuthorizationAppleIDProvider)에서:

서버 필드 앱에서 비고
identityToken response.identityToken 애플이 서명한 JWT. 이것만으로 인증이 끝납니다
rawNonce 앱이 만든 원본 nonce 애플 요청에는 sha256(rawNonce)를 넣고, 서버에는 원본을 보냅니다
name response.givenName·familyName ⚠️ 최초 인가 1회만 옵니다 (아래 참조)

authorizationCode는 서버로 보내지 않아도 됩니다. 서버가 인가 코드 교환을 하지 않기 때문입니다.

⚠️ 이름은 최초 1회만 옵니다

애플은 사용자가 처음 이 앱에 로그인을 허용한 그 순간에만 이름을 클라이언트에 줍니다. 그 뒤 재로그인에서는 givenName이 null이고, ID 토큰에도 이름이 없습니다.

  • 값이 있으면 그 요청에 name으로 실어 보내세요. 놓치면 서버에 이름이 영영 안 남습니다
  • 재로그인 때는 name 없이 보내면 됩니다 — 서버가 기존 값을 유지합니다
  • 테스트 중 이름을 다시 받고 싶으면 iOS 설정 > Apple 계정 > 로그인 및 보안 > Apple로 로그인에서 ditto를 지우고 다시 로그인하세요

nonce는 왜 권장인가

토큰 재사용(재생 공격)을 막습니다. 서버는 rawNonce가 오면 sha256(rawNonce)를 토큰의 nonce 클레임과 대조하고, 안 오면 이 검증만 건너뜁니다. 보내는 쪽을 권장합니다.

const rawNonce = crypto.randomUUID();                 // 원본은 우리가 보관
const hashed = await sha256Hex(rawNonce);             // 애플에는 해시를 넘김
const result = await SignInWithApple.authorize({ nonce: hashed, scopes: 'name email' });
await api.post('/api/v1/users/social-login/apple/native', {
  identityToken: result.response.identityToken,
  rawNonce,                                            // 서버에는 원본
  name: joinName(result.response),                     // 최초 1회만 값이 있음
});

3. 알아둘 것

이메일이 없거나 릴레이 주소일 수 있습니다

사용자가 "이메일 가리기"를 고르면 @privaterelay.appleid.com 주소가 오고, 아예 안 올 수도 있습니다. 서버는 그대로 저장하며 로그인은 정상 진행됩니다. 이메일을 계정 식별이나 화면 표시에 쓰는 자리가 있으면 빈 값을 대비해 주세요.

성별·나이는 애플도 주지 않습니다

카카오 일반 앱과 똑같이 온보딩에서 직접 받습니다. 애플로 로그인해도 signupRequired: true면 기존 회원가입 플로우(성별·나이 필수)를 그대로 태우면 됩니다 → Frontend-Kakao-General-App-Guide

카카오 계정과 이어지지 않습니다

같은 사람이 카카오로 가입한 뒤 애플로 로그인하면 별도 회원이 됩니다(이메일이 같아도). 애플 릴레이 주소는 신뢰할 수 없고, 이메일 일치를 계정 병합 근거로 삼는 건 계정 탈취 경로라 잇지 않기로 했습니다.

화면에서 이걸 어떻게 안내할지 정해야 합니다. 로그인 화면에서 "이전에 카카오로 시작하셨다면 카카오로 로그인해 주세요" 정도의 문구를 권합니다. 계정 연결이 필요하다는 판단이면 별도 기능으로 논의해 주세요.

탈퇴·재가입

애플도 카카오와 동일합니다 — 탈퇴 후 30일 안에 같은 애플 계정으로 다시 로그인하면 계정이 복구됩니다.


4. 실패 코드

code statusCode 상황 앱 처리
1002 401 ID 토큰 검증 실패 — 만료·서명 불일치·다른 앱에서 발급·nonce 불일치 애플 로그인을 다시 시도. 반복되면 로그인 화면
0001 400 identityToken 누락/빈 값, name 50자 초과 요청 점검
0003 403 X-API-Key 누락·오류 헤더 점검
9999 500 애플 공개키 서버 장애 등 잠시 후 재시도 안내

1002는 재시도로 풀리는 오류입니다(토큰 만료가 대부분). 사용자에게 "다시 시도해 주세요"로 안내하세요.

5. FE 체크리스트

확인 내용
☐ 웹: 로그인 화면에 애플 버튼 추가 → window.location.href = "/api/v1/users/social-login/APPLE"
☐ 앱: 로그인 화면에 애플 버튼 추가 (심사 요건 — 애플 버튼 디자인 가이드 준수)
☐ 최초 인가에서 받은 이름을 name으로 전달
☐ rawNonce 전달 (권장)
☐ signupRequired: true → 기존 온보딩(성별·나이 필수) 재사용
☐ 이메일이 없거나 릴레이 주소인 경우의 화면 처리
☐ 카카오/애플이 별도 계정임을 로그인 화면에서 안내

6. 웹 로그인

웹은 앱과 달리 기존 카카오 로그인과 똑같은 리다이렉트 방식입니다. FE가 할 일은 버튼 하나 추가하는 것뿐입니다.

// 카카오와 같은 패턴 — provider 만 다릅니다
window.location.href = "/api/v1/users/social-login/APPLE";

그 다음은 서버와 애플이 처리하고, 최종적으로 지금 쓰는 그 콜백 페이지로 돌아옵니다:

/auth/callback?accessToken=...&signupRequired=...        (정상)
/auth/callback?sanctioned=true&sanctionCode=...          (제재)

refreshToken도 카카오와 동일하게 HttpOnly 쿠키로 내려갑니다. /auth/callback 처리 코드를 고칠 필요가 없습니다.

알아둘 것

  • 중간에 애플이 우리 서버로 POST 콜백을 보냅니다(response_mode=form_post). 이건 브라우저↔애플↔서버 사이의 일이라 FE가 볼 일은 없습니다 — 다만 네트워크 탭에 POST가 찍히는 게 정상입니다
  • 웹은 앱과 다른 식별자(Services ID) 를 쓰지만 같은 애플 계정이므로, 같은 회원으로 이어집니다 (앱에서 가입 → 웹에서 애플 로그인 OK)
  • 이름은 여기서도 최초 인가 1회만 옵니다. 서버가 user 폼 필드에서 읽어 저장하므로 FE가 할 일은 없습니다
  • 애플 로그인 버튼 디자인은 웹에서도 애플 가이드라인을 따라야 합니다

서버 배포 전 준비 (BE·운영)

애플 개발자 콘솔에 Services ID와 Return URL 등록이 필요합니다. 등록 전에는 애플이 인가 요청을 거부하므로, FE에서 버튼을 켜기 전에 백엔드에 준비 완료 여부를 확인해 주세요.

참고

Clone this wiki locally