본문 바로가기

사이드 프로젝트/OAuth2.0을 활용한 토큰 발급 서버 개발 프로젝트

OAuth2.0을 활용한 로그인 및 토큰 발급 서비스 개발 후기 1

안녕하세요.

OAuth2.0을 활용한 로그인 및 토큰 발급 서비스를 개발한 개인 프로젝트 후기를 남겨봅니다.

 

1부에서는 OAuth2.0의 흐름과 Kakao OAuth2.0을 이용하여 Access Token을 발급하기 위해 WAS를 어떻게 구현했는지에 대해서 설명하고, 2부에서는 OAuth2.0이 대체 왜 이와 같은 흐름으로 설계가 되었을지에 대한 개인적인 분석과 고찰을 남겨보려고 합니다. 

 

프로젝트 진행을 리마인드하고 해당 분야에 관심있는 다른 방문자님들께 내용 공유하기 위한 목적으로 작성했습니다. 내용에 대한 피드백은 언제든 환영합니다.


아래 링크에서 프로젝트의 소스 전체를 확인할 수 있고, README를 참고하여 Docker Compose로 로컬환경에서 실행하여 데모해보실 수 있습니다.

 

[Github Link] https://github.com/chrismrkr/common-kakao-auth

 

GitHub - chrismrkr/common-kakao-auth: implementing common auth api server using Kakao OAuth2.0

implementing common auth api server using Kakao OAuth2.0 - chrismrkr/common-kakao-auth

github.com

 

 

 

1. OAuth2.0 동작 흐름

OAuth2.0의 동작방식은 아래와 같습니다.

 

 

OAuth2.0 Flow

 

그리고, OAuth2.0 흐름을 분석하기 위해 참고했던 공식 문서는 아래와 같습니다.

 

https://developers.kakao.com/docs/latest/ko/kakaologin/rest-api

 

Kakao Developers

카카오 API를 활용하여 다양한 어플리케이션을 개발해보세요. 카카오 로그인, 메시지 보내기, 친구 API, 인공지능 API 등을 제공합니다.

developers.kakao.com

 

OAuth2.0의 동작 흐름을 순서대로 분석하면 아래와 같습니다.

 

1. Request Login Page

로그인 페이지를 요청하는 단계입니다. 
클라이언트가 WAS에 로그인 페이지 요청을 보내면, WAS는 OAuth2.0 Provider에게 로그인 페이지를 내려주도록 요청을 리다이렉트합니다. 

WAS에서 Spring Security Config와 application.properties를 통해 로그인 페이지 요청을 OAuth Provider에 리다이렉트하도록 설정합니다.

        // SecurityConfig.java 일부분
        http.oauth2Login()
                .authorizationEndpoint().baseUri("/api/oauth2/authorize/")
# http://login-api-was/api/oauth2/authorize로 GET 요청을 보내면, kauth.kakao.com/...으로 Redirect
oauth2.base.auth-endpoint=/api/oauth2/authorize/
spring.security.oauth2.client.provider.kakao.authorization-uri=https://kauth.kakao.com/oauth/authorize

 

2. Response Login Page

클라이언트는 Redirect 과정에 의해 최종적으로 OAuth Provider로부터 로그인 페이지를 받습니다. Redirect URL의 Query Parameter에는 WAS가 리다이렉트 시 추가한 cliend-id와 redirect-uri가 포함됩니다.  redirect-uri와 client-id는 WAS에 아래와 같이 properties로 저장되어 있습니다.

SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_CLIENT-ID: your-rest-api-key
SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_REDIRECT-URI: http://localhost/api/login/oauth2/kakao

 

두 파라미터는 추후 Authorization code 발급에 사용됩니다. 여기서 사용자는 로그인 페이지에서 ID/Password를 입력하면 됩니다. 

 

3. Try Login & Request Authorization Code

OAuth2.0의 핵심입니다. 클라이언트는 ID/Password를 입력한 후, OAuth2.0 Provider에 Authorization Code를 요청합니다.

Authorization Code Request에 포함되는 주요 Query Parameter는 아래와 같습니다. 이전 단계에서 전달받은 파라미터입니다.

  • client_id : 카카오 개발자 포털에서 발급받는 key입니다. OAuth Provider에 본인이 사용할 Login WAS를 등록하면 client_id를 발급받을 수 있습니다.
  • redirect_uri : Authorization Code 발급 후의 Redirect Location을 클라이언트가 지정합니다.

자세한 Query Parameter는 위 링크된 Kakao Developer 페이지에서 확인할 수 있습니다.

 

4. 302 Redirect to WAS

Authorization Code Request에 대한 Response는 아래와 같습니다.

HTTP/1.1 302 Found
Content-Length: 0
Location: ${REDIRECT_URI}?code=${AUTHORIZE_CODE}

 

Authorization Code 발급이 완료되었고, 클라이언트는 Redirect_URI로 Authorization Code를 활용하여 Access Token을 발급하도록 Redirect 합니다. 이때 REDIRECT_URI는 직접 구현한 WAS의 주소입니다.

 

5. Request Access Token

앞선 단계에서의 ${REDIRECT_URI}와 ${AUTHORIZATION_CODE}를 활용하여 OAuth Provider에 Access Token 발급을 요청합니다. 

${REDIRECT_URI}는 아래와 같았습니다.

SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_KAKAO_REDIRECT-URI: http://localhost/api/login/oauth2/kakao

 

위 URL로 요청을 보냈을 때, WAS에는 아래와 같이 application.properties에 설정이 되어 있으므로 요청을 받을 수 있습니다.

oauth2.redirect.auth-endpoint.kakao=/api/login/oauth2/

 

WAS는 요청을 받아 properties에 지정된 아래의 URL로 OAuth Provider에게 Access Token 발급 요청을 합니다.

spring.security.oauth2.client.provider.kakao.token-uri=https://kauth.kakao.com/oauth/token

 

6. Response Access Token

 

Authorization code를 통해 Access Token 발급이 끝나면, 이를 WAS가 전달 받습니다.

 

7. Create JWT & Save in Repository

전달받은 Access Token을 파싱하여 권한, 성별, 닉네임 등의 정보를 받아 적절한 Access Token과 Refresh Token을 생성합니다.

Access Token과 Refresh Token로 JWT를 생성합니다.

Refresh Token은 Redis Repository에 저장하여 TTL을 갖도록 합니다. 적절한 Access Token 및 Refresh Token 정책을 통해 클라이언트에서 Access Token이 만료되면 재발급할 수 있도록 합니다.

8. Return

JWT를 최종적으로 클라이언트에 전달합니다. 클라이언트는 추후 리소스 접근을 위해 해당 JWT를 사용할 수 있습니다.

 

 

 

2. WAS 구현

위의 흐름을 정리해보면, 결국 OAuth2.0을 활용하여 클라이언트 인증 및 권한 서비스를 제공하기 위해서는 6 ~ 8번 Step에 대한 내용을 제공할 수 있는 WAS를 적절히 구현하는 것이 필요했습니다. 또한, Spring Security OAuth2.0을 활용하여 1 ~ 5번 Step에 대한 설정을 properties 파일에 추가하는 것이 필요했습니다.

 

단계 별로 정리해보면 아래와 같습니다.

1. 1 ~ 5 단계

# 로그인 페이지 요청을 위한 설정 
oauth2.base.auth-endpoint=/api/oauth2/authorize/
spring.security.oauth2.client.provider.kakao.authorization-uri=https://kauth.kakao.com/oauth/authorize

# Authorization Code 발급 요청을 위한 설정
spring.security.oauth2.client.registration.kakao.client-id=restapikey
spring.security.oauth2.client.registration.kakao.authorization-grant-type=authorization_code
spring.security.oauth2.client.registration.kakao.scope=profile_nickname,account_email
spring.security.oauth2.client.registration.kakao.client-name=Kakao
spring.security.oauth2.client.registration.kakao.client-authentication-method=POST

# Authorization Code를 통해 Access Token 발급을 위한 설정
oauth2.redirect.auth-endpoint.kakao=/api/login/oauth2/
spring.security.oauth2.client.registration.kakao.redirect-uri=http://localhost/api/login/oauth2/kakao
spring.security.oauth2.client.provider.kakao.token-uri=https://kauth.kakao.com/oauth/token

 

2. 6 ~ 8 단계

github 링크에서 대표적으로 SecurityConfig.java, CustomOAuth2UserService.java 확인하여 구현 내용을 확인할 수 있습니다.

SecurityConfig.java에 등록된 CustomOAuth2UserService 클래스를 통해 발급받은 AccessToken을 처리해 Repository에 저장하고, OAuth2AuthenticationSuccessHandler를 통해 적절히 JWT를 생성하여 클라이언트에 전달합니다.

 

 

3. 보안성 점검

클라이언트에 전달된 JWT 토큰은 리소스 접근을 위한 권한 정보 등을 갖고 있기 때문에 악의적인 사용자가 이를 사용할 수 없도록 관리하는 것이 필요했습니다.

  • XSS  : 악의적인 사용자가 스크립트를 실행하여 토큰을 탈취하는 공격입니다. httpOnly=true 옵션으로 JWT를 쿠키를 통해 전달하여 이에 대응하도록 하였습니다. httpOnly=true 옵션을 가진 쿠키는 브라우저에서 임의로 이를 추출할 수 없습니다.
  • CSRF : 악의적인 사용자에 의해 생성된 버튼 등을 눌렀을 때, 특정 HTTP 요청을 통해 악성 링크를 실행하도록 만드는 공격입니다. 예를 들어, POST, DELETE와 같은 safe하지 않은 HTTP 요청을 강제로 보낼 수 있습니다. HTTP 요청 시, 쿠키가 전달되므로 해당 공격에 대한 대응이 필요했습니다. 이에 대응하기 위해 Domain Check 또는 Cookie Same Site 정책을 활용할 수 있었습니다.

 

 

이어서 2부에서는 OAuth2.0이 왜 이와 같은 흐름으로 설계되었는지에 대한 개인적인 분석을 설명하도록 하겠습니다.

감사합니다.