백엔드

[그리고 시큐리티를 곁들인] #5 : 소셜로그인 방식에 대한 삽질기

0woodev 2024. 9. 16. 16:21

인트로

이전 글(4편)에서는 Jwt 토큰을 이용한 인증/인가 처리에 대해서 다뤘다.
 
 

[그리고 시큐리티를 곁들인] #4 : Spring Security 를 이용한 JWT 인증/인가

인트로이전 글(3편)에서는 Jwt 토큰에 관련된 Util 클래스와 로그인, 회원가입 기능을 구현했다.   [그리고 시큐리티를 곁들인] #3 : ID/PW 방식의 회원가입과 로그인과 JWT 토큰 발급인트로이전 글(2

0woodev.tistory.com

 
 
이번 글에서는 소셜로그인을 구현하는 과정에서의 고민과 트러블 슈팅, 그리고 imgame 프로젝트를 하면서 소셜로그인에 대해 공부한 내용을 정리해보고자 한다.
 
소셜로그인 삽질기 시작합니다!

OAuth2 (Open Authorization 2.0)

인증을 위한 개방형 표준 프로토콜이다. third-party 애플리케이션이 사용자의 리소스에 접근하기 위한 절차를 정의하고 서비스의 제공자의 API 를 사용할 수 있는 권한을 부여한다.
 
인증방식의 표준이 없었을 때에는 기존의 기본인증인 아이디와 비밀번호를 사용했는데, 이는 보안상 취약한 구조일 가능성이 매우 많다.
기본 인증이 아닌 경우, 각 애플리케이션들이 각자 개발한 회사의 방법대로 사용자를 확인했다. 즉, 재각각이었기 때문에, 이를 표준화하기 위한 인증방식으로 OAuth 가 등장했다.
 
인터넷과 많은 글들을 보면, OAuth2 와 Java, SpringBoot 를 이용한 예시가 엄청 많다. 하지만, 대부분 탬플릿엔진을 사용하는 MVC 형태에서의 예시들이다. 필자는 Client-Server 구조를 사용하고 있었기에, 어느부분까지 백엔드에서 담당을 해야하는지 잘 몰랐고, 어려웠다. 이번 포스트에서는 Client 와 Server 가 분리되어 있는 인프라 구조에서 소셜로그인을 구현하기 위해 책임과 역할을 어떻게 분배할지에 대해 깊게 다뤄볼 것이다. 
 
그전에 도메인의 용어와 소셜로그인 절차에 대해서 살펴보자. 

OAuth 용어

용어위키피디아 정의쉬운 풀이
사용자 ( User )서비스 제공자와 소비자를 사용하는 계정을 가지고 있는 개인서비스를 이용하는 사람
소비자 ( Consumer )Open API 를 이용하여 개발된 OAuth 를 사용하여 서비스 제공자에게 접근하는 웹사이트 또는 애플리케이션이용하려고 하는 서비스 (imgame)
서비스 제공자 ( Service Provider )OAuth 를 통해 접근을 지우너하는 웹 어플리케이션 (Open API 를 제공하는 서비스 )소셜로그인 기능을 제공해주는 서비스(네이버, 카카오, 구글, ...)
소비자 비밀번호 ( Consumer Secret )서비스 제공자에서 소비자가 자신임을 인증하기 위한 키소셜로그인에서 imgame 서비스를 구분하기 위한 식별키
요청 토큰 ( Request Token )소비자가 사용자에게 접근권한을 인증받기 위해 필요한 정보가 담겨있으며 후에 접근 토큰으로 변환된다. Access Token 을 발급받기 위한 키, 한번 사용하면 다시 못씀
접근 토큰 ( Access Token )인증 후에 사용자가 서비스 제공자가 아닌 소비자를 통해서 보호된 자원에 접근하기 위한 키를 포함한 값.Provider 에서 사용자의 리소스에 접근하기 위해 필요한 토큰

 

OAuth 인증 방식

  1. 소비자(imgame)가 서비스제공자(카카오)에게 요청토큰을 요청한다.
  2. 서비스제공자(카카오)가 소비자(imgame)에게 요청토큰을 발급해준다.
  3. 소비자(imgame)가 사용자를 서비스제공자(카카오)로 이동시킨다. 여기서 사용자 인증이 수행된다.
  4. 서비스제공자(카카오)가 사용자를 소비자(imgame)로 이동시킨다.
  5. 소비자(imgame)가 접근토큰을 요청한다.
  6. 서비스제공자(카카오)가 접근토큰을 발급한다.
  7. 발급된 접근토큰을 이용하여 소비자(imgame)에서 사용자 정보에 접근한다.

프론트엔드/백엔드 책임 분배

위에서 얘기한 7개의 절차에 대해서 프론트엔드와 백엔드의 책임을 분배할 수 있을지 같이 고민해보자. 이 설계에 있어서 클라이언트의 종류에 따라 권장되는 구현방법이 다르다. 우선, 웹서비스를 개발하는데 있어서 소셜로그인의 책임을 어떻게 분배하는지를 먼저 다뤄보자.

1. 프론트엔드가 모든 책임을 가져간다.

백엔드 입장에서 소셜로그인 관련된 것을 알 필요가 없어지기에 구현이 단순해진다..? (프론트입장에서는 단점이네) 실제 네이티브앱을 개발하는 경우 SDK 를 활용하여 위 방식과 유사하게 구현한다. 하지만, 프론트가 보내는 유저정보의 진위 여부를 파악할 수 있는 추가적인 방법이 필요하다. 즉, 보안적으로 굉장히 취약한 상태이다. 네이티브에서는 웹보다 상대적으로 네트워크 통신을 뜯어보기 번거롭기 때문에 다음과 같은 방법을 취한다.

2. 프론트엔드와 백엔드가 적절히 분배한다. 

필자가 생각했을 때, 크게 보면 인증코드와 어세스토큰을 기준으로 역할을 분배할 수 있다고 생각한다. 인증코드까지 발급을 받는 과정을 프론트엔드에서 담당해주는 경우와 어세스토큰까지 발급받는 것을 담당해주는 경우 이렇게 2가지에 대해 그림으로 살펴보자.
 

2-1. 인증코드까지 프론트엔드에서 담당한다.

인증코드는 OAuth2 에서 한번 사용하면 더 이상 사용할 수 없도록 규정했기에 상대적으로 탈취되더라도 보안적인 문제점은 적다.
토이프로젝트에서 소셜로그인을 적용하는데 있어 간편하다. 하지만 카카오와 같은 인증서버를 제공하는 대형 서비스 개발 포럼 및 보안 규격에서 위와 같은 방법을 지양한다고 한다.

2-2. 어세스토큰까지 프론트엔드에서 담당한다.

어세스토큰은 탈취되었을 때 문제가 크다. 따라서 필자는 이 방법은 좋지 않다고 생각한다.

3. 백엔드가 모든 책임을 맡는다.

백엔드에서 소셜로그인을 통한 로그인 플로우를 설계해야하며, 로그인을 성공한 경우, 서비스 내의 인증/인가와의 연계까지 고민해야 한다. (세션 방식 또는 JWT 토큰 방식) 하지만, 보안적인 부분에서 굉장히 안전하다고 생각하며, 실제로 웹에서의 소셜로그인을 구현하는데 있어 다음과 같은 구현방식이 권장된다고 한다.
 
필자가 개발에 대한 지식이 더 없었을 때 백엔드에서 전담한다면 API 서버에서 클라이언트로 JWT 토큰을 어떻게 보내지? 라는 생각이 들었던 순간이 있었다.

내가 로그인한 페이지는 카카오에서 만든 페이지이고, 거기서 성공했을 때, 카카오 페이지가 redirect url 로 정의한 백엔드 API 를 호출할 것이고, 응답 또한 카카오 페이지가 받는것일텐데, 어떻게 나의 서비스로 JWT 토큰을 넘기지..?

이건 분명 필자의 모자란 지식때문이다. 다만, 이러한 생각을 가지고 있는 사람이 한 명이라도 있다면, 이 글을 읽을 그 분에게 도움이 되고 싶어 브라우저 관점에서 순서도를 그려보았다.

 
필자는 위 4개의 구현방식중, 2-2만 제외하고 적절하게 사용하면 되지 않을까 싶다. 다만, 백엔드 개발자 입장에서, Spring Security 를 잘 다뤄보고자 했기에, "3. 백엔드가 모든 책임을 맡는다." 로 구현하기로 결정했다.

마침

소셜로그인을 구현하기 위해서는 크게 3가지 작업이 필요하다.

  1. Security 에서 OAuth2 에 대한 정의
  2. 소셜로그인 페이지(kakao)에서 로그인이 성공했을 때, 해당 유저에 대해서 서비스(imgame)의 유저와의 맵핑(식별)
  3. 서비스 내에서 식별까지 완료가 되었다면, 로그인 성공에 대한 처리

자세한 것은 다음 블로그 포스트에서 다뤄보고자 한다.
 

[그리고 시큐리티를 곁들인] #6 : 백엔드 전담의 소셜로그인 구현, Spring Security, OAuth2 를 이용한

0woodev.tistory.com

 
 

참고

개발자 유미 | 커뮤니티

www.devyummi.com