최근 프로젝트를 진행하면서 로그인 기능을 구현할 일이 생겼다.
웹 서핑을 하다 보면 Google, Kakao 등의 외부 소셜 계정을 기반으로 간편히 회원가입 및 로그인 할 수 있는 웹 어플리케이션을 쉽게 찾아볼 수 있는데 이때 사용되는 프로토콜이 OAuth이다. 아래는 OAuth의 정의이다.
OAuth는 보안된 리소스에 엑세스하기 위해 클라이언트에게 권한을 제공 (위임)하는 프로세스를 단순화한 표준 프로토콜의 한 종류이다.
예시로 외부 어플리케이션은 사용자 인증을 위해 Google, Kakao 등의 사용자 인증 방식을 사용한다. 이때, OAuth를 바탕으로 제 3자 서비스는 외부 서비스 (Google, Kakao)의 특정 자원을 접근 및 사용할 수 있는 권한을 인가받게 된다.
OAuth 2.0은 1.0에서 알려진 보안 문제 등을 개선한 버전이다.
OAuth는 일반적으로 아래 2가지의 용도로 사용이 된다.
연합된 신원은 사용자가 우리 서비스 계정이 아닌 자신의 다른 계정으로 애플리케이션에 로그인할 수 있는 것을 말한다. 예를 들면 Google 계정으로 Velog과 Github에 로그인하는 경우, 사용자는 Google 계정 하나만 관리하면 된다. 이런 의미에서, 여러 사이트들이 연합해 하나의 신원을 사용한다고 말할 수 있다.
권한 위임은 우리 서비스가 사용자를 대신해서 다른 서비스의 리소스에 접근할 수 있게하는 권한을 가질 수 있는 것을 말한다.
예를 들어 위 그림에서 사용자는 Velog에게 사용자의 구글 연락처를 통해 친구 추천을 요청하고 있다. 이때 Velog는 당장 구글 서버의 자원에 접근할 수 있는 권한이 없으므로 권한을 얻기 위해 사용자에게 권한을 달라 요청한다.
그리고 사용자는 구글 서버에게 Velog에게 권한을 달라 요청을 하게 되고, 구글 서버는 Velog에게 권한이 담긴 토큰 (엑세스 토큰)을 주게 된다. 그 결과, Velog 서비스는 Google 서버의 리소스에 접근할 수 있는 권한이 담긴 토큰 값을 가지게 되고, 이제 토큰 값을 통해 구글의 연락처 정보를 받을 수 있게 된다.
다음으로 OAuth 기술로 어떻게 사용자 신원을 인증하고, 서비스에 권한을 위임하여 다른 서비스의 리소스에 접근할 수 있는지에 대한 동작 흐름을 살펴볼 것이다.
먼저 사용자는 클라이언트에게 로그인을 요청한다.
여기서 클라이언트는 우리 서비스 서버로 생각하면 된다.
클라이언트는 미리 세팅된 여러 매개변수와 같이 카카오 서버로 로그인 요청을 보낸다.
3~4. 카카오 서버는 사용자에게 카카오 로그인 페이지를 제공하고, 사용자에게 로그인을 받는다.
카카오 서버에서 로그인 인증이 성공되면 Redirect URI로 콜백한다.
이때 Redirect URI에는 Authorization Code를 포함하여 사용자에게 전송하게 된다. (그림의 Authenticaion Code는 오타이다)
Authorization Code 값을 가지고 Client로 리다이렉션이 되면 Client는 카카오 서버로부터 사용자에게 정상적으로 인증이 되었다는 사실을 알게 된다.
여기까지가 인증이 완료된 것이다. 여기서 끝나지 않고 Client (우리 서비스)가 카카오의 자원 서버에게 접근하기 위해서 Authorization Code 값을 카카오 서버에게 전달해야 한다. Authorization Code란 Client가 Access Token을 획득하기 위해 사용하는 임시 코드이다.
Authorization Code와 카카오의 자원 서버을 이용하기 위한 (Client ID, Client secrets)를 가지고 카카오 서버에게 전달하여 권한을 요청한다.
카카오 서버로부터 Access Token을 응답 받는다.
이때 Access Token은 카카오 자원 서버를 이용하기 위해 사용되므로 유출되어서는 안된다. 따라서 제 3자가 가로채지 못하도록 HTTPS 연결을 통해서만 사용될 수 있다.
지금까지의 과정을 마치면 Client는 사용자에게 로그인이 성공하였음을 알린다.
그래서 Client에서 인증이 완료했다는 것은 Authorization Code를 받았다는 것이고, 리소스 자원에 접근할 수 있는 권한을 부여받았다는 것은 Access Token을 받았다는 뜻으로 해석할 수 있다.
이후에 Client는 Access Token 값으로 카카오 자원 서버에게 사용자 닉네임이나 프로필 사진과 같은 리소스를 호출할 수 있고, 카카오 자원 서버에서는 Access Token 값을 검증하고 Client에게 리소스를 제공 해주게 된다.
마지막으로 OAuth에서 사용되는 용어에 대해 정리해보면 다음과 같다.
Resource Owner : 리소스 소유자. 우리의 서비스를 이용하면서 리소스를 소유하고 있는 사용자를 말한다.
Client : Resource Server의 자원을 이용하고자 하는 서비스를 말한다.
Authorization Server : Resource Owner를 인증하고, Client에게 Access Token을 발급 해주는 역할의 서버이다.
Resource Server : Resource Owner의 정보가 저장되어 있는 서버를 뜻한다.
구현에 앞서, 개발하면서 고민했던 부분은 Client 쪽 구현을 프론트에서 할지 백에서 할지에 대한 부분이었다. 결론부터 말하자면 백엔드에서 구현하게 되었다. 그 이유는 프론트 측에 만약 Access Token으로 권한 부여를 하게 된다면 서버에는 로그인 기록이 남지 않을 뿐더러, 프론트에서 백으로 사용자 정보(카카오 자원 서버로부터 얻은 사용자 정보)를 넘기게 되면 탈취 가능성이 존재한다는 것을 알게되었다.
프레임워크
라이브러리
Kakao Deverlops에 들어가서 애플리케이션을 추가한다. (추가 과정은 다른 참고 자료가 많아 생략하였다)
oauth 설정 파일의 이름을 application-oauth.yml로 하여 application.yml에서 group으로 프로필을 이용했다.
카카오는 스프링에서 provider 정보를 제공하지 않으므로 위와 같이 직접 설정해주었다.
설정한 부분을 요약하보면, registration 부분과 provider 부분으로 나뉜다.
클라이언트 애플리케이션의 등록 정보를 담고 있다. 설정한 애플리케이션의 정보를 사용하여 AccessToken을 Authorization Server에게 발급받을 때 사용된다.
OAuth2.0 제공자에 대한 정보를 담고 있다. Access Token을 발급 받은 후, Resource Server에게 접근할 때 provider 부분의 정보를 사용하여 API를 호출한다.
감사합니다. 이런 정보를 나눠주셔서 좋아요.