로그인 연동 목적
레벨4를 시작하면서 우리팀의 레벨3까지의 결과와 다음 방향에 대해서 레벨4가 시작함과 동시에 회의를 진행하였습니다.
그 결과, 단순한 가챠맵으로는 사용자를 모으고 DAU를 올릴 수 없다는 결론이 나왔습니다. 그래서 처음 팀 빌딩때부터 기획했던 가챠 교환기능을 추가하기로 했습니다. 이를 위해서는 사용자의 정보 관리가 필요했고, 그에 따라서 OAuth2 기반 로그인을 적용하려고 했습니다. 기존에 많이 구현 해보았던 기능이기에 같은 방식으로 구현하려던 찰나에 새롭게 OAuth2를 보완해서 나온 OIDC에 대해서 알게되었습니다.
기존에 OAuth2 제공 회사마다 달랐던 사용자 정보를 가져오는 방식을 하나로 통일 할 수있다는 점에서 이점을 느끼고 학습 및 적용해보기로 했습니다.
무엇이 바뀌었는가?
기존 OAuth2의 한계
기존의 로그인 시스템은 토큰의 정체성이 부족했습니다. Access Token은 권한의 증표일 뿐이고 토큰을 소유한 사용자가 어떤 유저인지 표준화된 정보는 없었습니다. 이 때문에 소셜 로그인 서비스를 제공하는 회사마다 프로필 정보를 가져오는 방식이 달랐고, 회사마다의 응답구조에 맞게 연동 코드를 짜야 했습니다.
그래서 OIDC가 나오기 전까지 각 회사마다 다른 방식으로 소셜 로그인을 제공하기 때문에, 개발자들은 연동의 번거로움과 보안 취약점이 발생하기 쉬운 구조에 있었습니다.
여담으로, 저는 이러한 문제를 이전에 Slack, Github, Kakao, Naver. Google 등등을 연동해보면서 몸으로 체감했었습니다.
OIDC가 해결한 변화
OIDC는 OAuth2 위에 인증 계층을 얹은 표준입니다. OAuth2를 그대로 사용하면서 기능들을 표준화했습니다.
먼저 ID 토큰의 도입입니다. 기존에는 로그인 성공 시에 권한 토큰인 Access Token만을 제공했었습니다. 여기에 추가로 표준화된 JWT 토큰인 ID Token을 발급해줍니다. 이 토큰 안에는 사용자의 고유 식별자(Subject, Sub), 이메일, 이름, 토큰 발행 시간, 만료 시간 등의 암호화 및 서명된 데이터가 들어있습니다. 덕분에 클라이언트가 직접 사용자 정보를 안전하게 파싱해서 쓸 수 있게되었습니다.
즉, 권한 부여에 인증이 추가된 것입니다. 이 때문에 어떤 OIDC를 사용하든 표준화된 규격으로 유저의 프로필 정보를 요청 할 수 있습니다.
우리팀이 OIDC를 선택한 이유
저희팀 또한 사용자 인증을 통한 보안을 챙기면서 소셜 로그인 연동을 단순화해서 적용하기로 결정했습니다. 다만 여기서 한 가지 문제가 있었습니다. 네이버의 경우에는 인증 증명용 최소한의 식별자 위주로만 ID Token에 담겨있어서 회원 가입에 필요한 정보들을 다 채울 수 없었습니다. 결국 따로 다시 프로필 조회 API를 호출 할 수밖에 없었습니다.
현재는 카카오와 네이버 로그인 연동을 구현 해놓은 상태이고, 추후에 구글과 애플 등의 소셜 로그인도 연동 할 계획입니다. 현재까지는 OIDC 소셜 로그인 기능 구현의 이점이 프로젝트에서 직접적으로 들어나고 있지는 않지만, 단순 권한 부여에서 인증을 도입했다는 것에서 보안적 취약점은 잡을 수 있었다고 생각합니다. 또한 기존에 회원가입을 위해서 소셜 로그인 제공사에 프로필 정보를 다시 요청하는 과정이 줄어들어서 이점에서도 기존 방식보다 괜찮아졌다고 생각합니다.
어떻게 구현했는가?
아래의 그림처럼 각 회사마다의 OIDC를 관리 할 컴포지트들을 두고, 여기서 해당하는 회사의 구현체를 통해서 연동 할 수 있도록 설계했습니다.

여기서 SessionManager는 Replay Attack을 막기위해 적용한 장치입니다.
OIDC에서 Replay Attack이란?
공격자가 사용자의 로그인 과정에서 오가는 인증 코드나 ID Token을 네트워크 스니핑 등을 통해서 가로챈 뒤, 나중에 다시 서버에 전송하여 사용자의 권한을 탈취하거나 비정상적인 로그인을 시도하는 공격입니다.
이를 막기 위해서 nonce(Number used ONCE, 일회용 난수)를 파라미터로 넣었습니다. 로그인 요청시 nonce를 생성해서 사용자에게 같이 보내주고, 인증 제공자인 소셜 로그인 회사는 로그인이 성공하면 발급해주는 ID Token에 이 nonce 값을 포함시켜서 서명한 뒤 클라이언트에게 전달해줍니다. 그러면 ID Token을 전달받은 클라이언트는 토큰 안에 들어있는 nonce 값이 세션에 저장해둔 nonce 값과 일치하는지 검증합니다. 그러면 다시 재전송(replay) 하더라도, 현재 세션의 nonce와 맞지 않아 이미 소모된 값은 차단 할 수 있습니다.
아래의 그림은 카카오를 예시로 실제 컴포지트에 들어가는 구현체를 보여주는 그림입니다.

Auth Code 발급을 위한 Redirect Url 제공 구현체인 Auth Code Request Provider와 Auth Code를 통한 Access Token, ID Token 발급 구현체인 Client그리고 ID Token의 검증을 위한 Validator를 표현했습니다.
OAuth2의 권한 부여에 '인증' 계층을 더한다는 것, 그리고 표준화된 ID Token을 통해 프로필 조회 구조를 개선한다는 점은 확장성 있는 아키텍처를 설계할 때 큰 장점으로 다가왔습니다. 네이버처럼 예외적인 케이스를 마주하기도 했지만, 공통 구조 안에서 유연하게 대처할 수 있도록 설계 구조를 잡은 덕분에 향후 다른 프로바이더를 추가할 때도 수월할 것 같습니다. 소셜 로그인 연동을 고민하고 계신 분들이라면, 보안성과 표준화를 모두 챙길 수 있는 OIDC 도입을 적극 추천해 드립니다.
