일기 slecs

Apple OAuth 콜백 오류와 세션 유실 문제 해결

목차

Apple OAuth를 처음 붙여봤을 때 가장 당황스러웠던 건 콜백이 POST로 온다는 거였다. 구글이나 카카오는 리다이렉트 URL로 GET 요청이 오고, 거기서 code 파라미터를 꺼내면 끝인데, Apple은 response_mode=form_post를 강제한다. 즉 브라우저가 Apple 서버로부터 HTML 폼을 받아서 자동 submit하는 방식이라 콜백 엔드포인트가 POST를 받아야 한다. 이걸 모르고 GET으로만 열어두면 405가 뜨고, 설령 POST를 열어뒀어도 기존 미들웨어가 줄줄이 막아버리는 상황이 생긴다.

이번 작업의 핵심은 Apple OAuth 콜백이 통과할 수 있도록 필터와 CORS 설정을 정비하고, 연동 후 세션이 끊기지 않게 강제 재설정하는 로직을 추가한 것이다.

뭐가 막고 있었나

서비스에 봇 차단 필터(BotBlockFilter)가 붙어 있었다. 평소엔 문제없는데 Apple OAuth 콜백 시점에 요청 주체가 Apple 서버 IP라서 봇으로 판정되어 403이 나왔다. 일반 사용자 브라우저에서 오는 요청처럼 보이지 않기 때문이다. form_post 특성상 콜백 요청은 사용자의 브라우저가 직접 쏘는 게 아니라 Apple의 authorization server가 중계하는 구조라 User-Agent도 다르고 IP 대역도 다르다.

CORS는 또 다른 레이어에서 막혔다. WebMvcConfig에 허용 Origin을 명시해두고 있었는데 Apple 콜백 URL이 빠져 있었다. form_post로 넘어오는 시점에 브라우저가 preflight를 날리고, 거기서 400이 먼저 나왔다.

정리하면 같은 콜백 요청이 두 개의 필터에서 각각 다른 이유로 튕겨나왔고, 설령 둘 다 통과해도 세션이 리셋되는 문제가 남아있었다.

버그 증상 원인 해결
봇 필터 차단 콜백 403 Apple 서버 IP가 봇으로 판정됨 BotBlockFilter 예외 URL 추가
CORS 차단 콜백 400 Apple 콜백 Origin 누락 WebMvcConfig 허용 URL 추가
세션 유실 로그인 실패 POST 콜백 후 세션 컨텍스트 초기화 콜백 핸들러에서 세션 강제 재설정

수정 흐름은 아래 순서대로 타고 들어가는 구조다.

BotBlockFilter → Apple 콜백 URL 예외 추가
WebMvcConfig  → CORS 허용 Origin 추가
OAuthController → 세션 재설정 로직 추가

세션 재설정 쪽은 코드가 단순한데 빠지면 결과가 확실히 달랐다.

// Apple OAuth 콜백 처리 후 세션 강제 재설정
session.setAttribute("loginUser", member);
session.setAttribute("sysId", member.getSysId());

POST 콜백 이후 서버 측 세션 컨텍스트가 정상적으로 이어지지 않는 케이스가 있었다. Spring Security나 자체 인터셉터가 세션을 무효화하거나, 새로운 세션을 만들어버리는 흐름이 POST 요청에 달리 적용될 수 있다. 명시적으로 다시 넣어주는 게 가장 확실한 방어선이다.

Apple이 까다로운 또 다른 이유

이름과 이메일을 처음 인증할 때만 준다. 두 번째부터는 안 준다. 이게 치명적인데, 만약 첫 번째 콜백 처리에서 실패해서 저장을 못 했다면 이후 재시도해도 이름을 받아올 방법이 없다. 사용자가 Apple 계정 설정에서 연결 해제하고 다시 연결해야 다시 받을 수 있다. 그러니 첫 콜백에서 DB에 확실히 박아두는 로직이 필수다.

구글이나 카카오는 매번 프로필을 줘서 후처리가 자유로운데, Apple은 이 제약 때문에 provider 분기 처리를 별도로 가져가야 했다.

  • 구글/카카오: 매 요청마다 이름·이메일 반환 → 갱신 가능
  • Apple: 최초 1회만 이름·이메일 제공 → 반드시 첫 콜백에서 저장
  • Apple: 이후 콜백은 sub(고유 식별자)만 반환

연동 회원 조회 로직도 이걸 반영해서 Apple의 경우 sub로만 조회하고, 이름이 없으면 기저장된 값 그대로 쓰는 방향으로 정리했다.

돌아보면

OAuth 연동을 provider 하나 더 추가하는 작업이라고 가볍게 봤다가 시간이 꽤 걸렸다. 필터가 쌓여 있는 구조에서 어느 레이어에서 막히는지 추적하는 게 생각보다 번거롭다. 403이 나오면 CORS 탓만 하다가 봇 필터를 나중에 발견하는 식이다. 비슷한 작업을 할 때는 새 엔드포인트가 어떤 필터 체인을 타는지 먼저 그려놓고 들어가는 게 낫다.

Apple OAuth는 form_post, 최초 1회 프로필 제공, 봇 필터와의 충돌 이 세 가지를 미리 알고 시작하면 삽질을 상당히 줄일 수 있다. 구글이랑 같은 방식으로 갈 거라는 전제를 버리는 게 첫 번째다.

댓글 0

첫 댓글 달아줘.