OAuth 구현하다 만난 지옥들
소셜 로그인 붙이기가 이렇게 복잡한 줄 몰랐다. 카카오, 구글, 애플 세 곳의 OAuth를 구현한 삽질기.
소셜 로그인 붙이기, 하루면 되겠지
그렇게 생각했다. 카카오, 구글, 애플 세 가지 소셜 로그인을 붙이는 일정을 2일로 잡았다. 결과적으로 8일이 걸렸다. 4배 초과.
OAuth 2.0 스펙 자체는 간단하다. Authorization Code를 받고, Access Token으로 교환하고, 유저 정보를 가져오면 끝. 근데 각 플랫폼마다 구현이 미묘하게 달라서 문제다.
카카오는 그나마 수월했다
카카오 개발자 문서가 한국어라 읽기 편했다. Redirect URI 설정하고, REST API로 토큰 교환하고, 유저 정보 가져오는 데까지 4시간쯤 걸렸다.
근데 문제가 하나 있었다. 이메일 동의 항목이 선택이라 유저가 동의를 안 하면 이메일이 안 온다. 우리 시스템이 이메일 기반 계정이라 이메일이 없으면 회원가입이 안 되는 상황이었다. 동의를 안 한 유저에게 별도로 이메일을 입력받는 플로우를 추가로 만들어야 했다.
(이거 미리 알았으면 설계부터 달랐을 텐데.)
구글은 토큰 관리가 짜증
구글은 구현 자체는 간단했는데 Refresh Token 정책이 까다로웠다. 첫 인증 시에만 Refresh Token을 주고, 그 다음부터는 안 준다. access_type=offline에 prompt=consent를 넣어야 매번 Refresh Token을 받을 수 있는데, 그러면 유저가 매번 동의 화면을 봐야 한다.
이 정책을 몰라서 Refresh Token 저장을 안 했다가 기존 유저들의 토큰이 1시간 후에 만료되면서 전부 로그아웃됐다. 72명의 유저가 동시에 "로그인이 풀렸어요" CS를 보냈다.
애플은 차원이 다르다
애플이 진짜 지옥이었다. 먼저 Apple Developer Program에 가입해야 하고($99), Service ID를 만들고, 도메인 인증을 하고, Private Key를 받아서 JWT를 직접 만들어야 한다.
토큰 교환할 때 client_secret이 일반 문자열이 아니라 JWT다. 이 JWT를 ES256 알고리즘으로 서명해야 하는데, Node.js에서 ES256 서명하는 코드를 처음 짜봤다. jsonwebtoken 라이브러리 문서를 3번이나 읽었다.
그리고 애플은 유저 정보(이름, 이메일)를 딱 한 번만 준다. 첫 인증 때. 그때 못 저장하면 끝이다. 테스트하다가 이걸 놓쳐서 Apple ID 앱 연동을 해제하고 처음부터 다시 테스트했다. 연동 해제하는 법을 찾는 데만 20분 걸렸다.
계정 통합 문제
사용자가 카카오로 가입했다가 구글로 로그인하면 어떻게 되나? 이메일이 같으면 같은 계정으로 봐야 하나? 다른 계정으로 만들어야 하나?
이 정책을 안 정하고 구현을 시작한 게 실수였다. 나중에 같은 이메일이면 계정 연동을 제안하는 방식으로 바꿨는데, 이미 중복 계정이 23개 생겨 있었다. 수동으로 합쳤다.
배운 것
OAuth 구현 전에 반드시 정해야 할 것: 이메일 없을 때 대응 방안, 계정 통합 정책, Refresh Token 관리 전략, 각 플랫폼 특이사항 파악. 코드 짜기 전에 이것부터 정리해야 한다.
다음에 또 하라고 하면 솔직히 NextAuth(지금은 Auth.js) 같은 라이브러리를 쓰겠다. 직접 구현해서 배운 건 많지만, 그 시간에 다른 기능을 만드는 게 나았을 것 같다.