Chợ Việt 앱에 Apple Sign In을 추가했습니다. Google OAuth보다 까다로운 부분이 많았는데, 특히 Apple의 cross-origin POST 콜백 방식 때문에 여러 문제를 해결해야 했습니다.
Apple Sign In의 특이점
Apple OAuth는 다른 OAuth 제공자들과 다르게 POST 방식으로 콜백을 보냅니다. 이 콜백은 appleid.apple.com에서 직접 전송되기 때문에:
- Origin 헤더 불일치: Rails CSRF 보호가 Origin 체크에서 실패
- 세션 쿠키 유실: SameSite 쿠키 정책으로 세션이 전달되지 않음
- Nonce 검증 실패: 세션이 없으니 저장된 nonce를 찾을 수 없음
해결책 1: Origin 헤더 제거 미들웨어
Apple 콜백에서 Origin 헤더를 제거하는 Rack 미들웨어를 추가했습니다:
# lib/middleware/apple_origin_fix.rb
module Middleware
class AppleOriginFix
def initialize(app)
@app = app
end
def call(env)
if env["PATH_INFO"] == "/users/auth/apple/callback" &&
env["HTTP_ORIGIN"] == "https://appleid.apple.com"
env.delete("HTTP_ORIGIN")
end
@app.call(env)
end
end
end
해결책 2: Nonce 검증 스킵
omniauth-apple 젬의 nonce 검증을 우회해야 했습니다. Ruby의 prepend를 사용해 메서드를 오버라이드:
# config/initializers/devise.rb
module AppleNonceSkip
private
def verify_nonce!(id_token)
true # 세션 쿠키가 없어 nonce 검증 불가
end
end
OmniAuth::Strategies::Apple.prepend(AppleNonceSkip)
해결책 3: State 검증 비활성화
provider_ignores_state: true 옵션으로 state 파라미터 검증도 스킵:
config.omniauth :apple,
credentials[:client_id],
"",
scope: "email name",
team_id: credentials[:team_id],
key_id: credentials[:key_id],
pem: credentials[:private_key],
provider_ignores_state: true
해결책 4: CSRF 스킵
컨트롤러에서 Apple 콜백과 failure 액션의 CSRF 검증을 스킵:
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
skip_before_action :verify_authenticity_token,
only: [:google_oauth2, :apple, :failure]
end
Apple Developer Console 설정
- App ID 생성 (Sign In with Apple 활성화)
- Service ID 생성 (웹 로그인용)
- Identifier:
chat.choviet.web - Return URL:
https://choviet.chat/users/auth/apple/callback - Key 생성 (.p8 파일 다운로드)
보안 고려사항
Nonce와 State 검증을 스킵하는 것은 보안상 이상적이지 않습니다. 하지만 Apple의 cross-origin POST 방식에서는 다른 선택지가 없습니다. Apple이 제공하는 JWT 서명 검증은 여전히 유효하므로 기본적인 보안은 유지됩니다.
결론
Apple Sign In은 Google OAuth보다 구현이 복잡하지만, iOS 사용자들에게 편리한 로그인 경험을 제공합니다. Cross-origin POST로 인한 세션 문제를 이해하고 적절히 우회하는 것이 핵심입니다.
