들어가며
이전 포스팅에서는 Authorization Code Grant
방식을 이용한 기본적인 OAuth2 인증 및 인가 프로세스에 대해 알아보았습니다. 이번 포스팅에서는 우리의 애플리케이션, 즉 Client 가 Frontend 와 Backend 로 분리된 환경에서 Authorization Code Grant
방식의 OAuth2 인증 및 인가 과정이 어떻게 이루어지는지 살펴보겠습니다.
특히,
- Frontend 와 Backend 가 각각 어떤 역할을 수행해야 하는지
- 이 방식이 가지는 장점과 단점은 무엇인지
에 대해 자세히 알아보고자 합니다.
전제 조건
이번 포스팅에서는 다음과 같은 상황을 가정합니다.
- 우리의 Application(Client) 이
Frontend
와Backend
로 분리되어 있습니다. - OAuth2 인증 방식으로
Authorization Code Grant
를 사용합니다. - 인가 서버로부터 발급받은 Access Token 은 Application 을 인증하기 위한 용도 이외에는 사용되지 않습니다.
- Application 은 인가 서버의 Access Token 및 Refresh Token 을 직접 사용하지 않고, 자체적인 Access Token 및 Refresh Token을 사용합니다.
이제, 이러한 환경에서 OAuth2 인증 및 인가 프로세스가 어떻게 진행되는지 단계별로 살펴보겠습니다.
OAuth2 인증 흐름과 역할
React 와 Spring 으로 나누어진 환경에서의 OAuth2 인증 인가 프로세스를 그림으로 그려보면 다음과 같습니다.
이 과정중 Frontend 와 Backend 의 역할을 구분하면 다음과 같이 분리할 수 있습니다.
Frontend
Frontend 는 인가서버로부터 Authorization Code 를 발급받고, Backend 에 Authorization Code 를 전달하는 역할을 수행합니다.
- 사용자(Resource Owner)는 React의
OAuth2 로그인 버튼
을 클릭합니다. - React는
client_id
,redirect_uri
,scope
등의 정보를 포함하여 인가 서버의authorization_uri
로 페이지를 이동시킵니다. - 사용자가 인가 서버의 로그인 UI를 통해 인증을 진행한 후, 동의 화면에서 권한을 승인하면 인가 서버는
Authorization Code
를 발급합니다. - 발급된 Authorization Code는
redirect_uri
를 통해 Frontend 로 전달됩니다.
Backend
Backend 는 Frontend 로부터 전달받은 Authorization Code 를 이용하여 AccessToken 을 발급받습니다. 그리고 AccessToken 을 이용하여 사용자를 대신해 Resource Server 로부터 사용자의 정보를 조회해옵니다. 또한, 죄회해온 사용자의 정보를 토대로 애플리케이션 자체적으로 사용하는 AccessToken & RefreshToken 을 발급하여 Frontend 에게 응답합니다.
- Frontend 는 인가 서버로부터 받은
Authorization Code
를 Backend 의 특정 Endpoint로 전달합니다. - Backend 는 전달받은
Authorization Code
를 이용하여인가 서버에서 Access Token을 발급
받습니다. - Backend는 발급받은 Access Token 을 사용하여
Resource Server 에서 사용자 정보를 조회
합니다. - 사용자 정보를 기반으로
신규 사용자
라면 자동 회원가입을 진행하고,기존 사용자
라면 로그인 처리를 수행합니다.
- 이후, Backend는 애플리케이션 자체적으로 관리하는
Access Token & Refresh Token 을 생성하여 Frontend 에 반환
합니다.
Frontend
Authorization Code
가장 먼저, 사용자(Resource Owner) 는 React의 OAuth2 로그인 버튼을 클릭
합니다.React 는 사용자를 다음과 같은 정보를 포함하여 Authorization Server 의 인증 페이지(authorization_uri) 로 이동
시킵니다. 최종적으로 이동되는 페이지의 URL 은 아래와 같은 형식을 갖습니다.
/authorization_uri?redirect_uri={redirect_uri(인가코드를 받고자하는 Front Endpoint)}&client_id={id}&scope={scope}
client_id
: 클라이언트 애플리케이션을 식별하는 IDredirect_uri
: 인증 후 Authorization Code를 전달받을 URIscope
: 애플리케이션이 요청하는 권한 범위
그 후,
- 사용자(Resource Owner) 는 이동된 authorization_uri 의 Login UI 에 로그인하여 자신임을 인증합니다.
- 인증을 거치면 동의화면으로 넘어가게 됩니다. 이 동의화면은
"Application 필요로 하는 정보들(예를 들어 프로필 정보)을 사용자(Resource Owner)를 대신해 Resource Server 로부터 가져올 건데, 이것에 동의하겠습니까?"
라는 의미입니다.
사용자가 "동의 및 승인"
을 선택하면,
- Application 은 Resource Owner 를 대신하여
Resource Server
에서 필요한 자원을 가져올 수 있는 권한을 부여 및 승인받게 됩니다. - 인가서버는
Authorization Code(인가코드)
를 발급하고 authrozation_uri 의 Query String 에 존재하는redirect_uri
Query Param 주소로인가코드를
붙여 아래와 같이 Redirection 시키게 됩니다.
HTTP/1.1 302
Location: {redirect_uri(인가코드를 받고자하는 Front 의 Endpoint)}?code={code}
사전에 인가서버의 Redirect URI 를 Frontend 가 Code 를 받고자하는 Endpoint 로 설정해놓아야 합니다.
Frontend 에서 Authorization Code 를 받기 위해서는, 인가 서버의 redirect_uri 를 Frontend 의 특정 Endpoint 로 등록시켜야 합니다(여러개 설정 가능합니다.). 인가 서버는 등록된 redirect_uri 와 요청에 포함된 redirect_uri 를 비교하여 일치하는 경우에만 Authorization Code 를 발급하게 됩니다. 이는 허용되지 않은 URL 로 Authorization Code 가 전달되는 것을 방지하기 위한 인가서버의 보안조치입니다.
마지막으로 Frontend 는 Authorization Code 를 Backend 의 특정 Endpoint 에 전달합니다.
Backend
AccessToken
Backend 는 Frontend 에서 전달한 Authorization Code
특정 Endpoint 에서 인가코드를 전달받습니다. 그 후, Backend 는 client_id, client_secret, backend redirect_uri, grant_type, 전달받은 code(Authroziation Code)
를 사용해서 인가서버의 AccessToken 발급 주소인 token_uri
주소에 AccessToken 을 요청합니다.
이때, /token_uri
요청은 다음과 같은 형태로 보내게 됩니다.
POST /token_uri
Content-Type: application/x-www-form-urlencoded
client_id={id}&client_secret={secret}&redirect_uri={redirect uri(인가코드를 받고자하는 Front Endpoint)}&grant_type={type}&code={authorization code}
인가서버는 token_uri 에 전달된 Query Paramter 를 검증하고 AccessToken 를 Backend Redirect URI
로 응답을 하게 되는데, Backend 는 이를 전달받으면 됩니다.
AccessToken 요청에 쓰이는 Redirect URI 주소는 인가코드를 발급받을 때 사용한 Frontend Endpoint 주소와 동일해야 합니다.
AccessToken 을 요청을 받게 되면, 인가서버는 AccessToken 요청에 함께 전송된 redirect_uri 와 Authorization Code 를 요청할 때 사용되었던 redirect_uri 가 일치하는지 확인합니다. 이를 통해 Authorization Code 가 정확한 출처에서 온 것인지 검증합니다.
Resource Owner 의 Resource
Backend 는 전달받은 AccessToken
을 이용하여 사용자를 대신해 Resource Server 로부터 Resource Owner 의 Resource(사용자의 프로필 정보) 를 조회하여 전달받습니다.
이 때, 사용자의 Resource 를 조회하는 Endpoint
, AccessToken 은 Authoriation Header 에 붙일지, QueryParamter 로 붙여서 보낼지에 대한 여부
는 인가서버 혹은 리소스서버마다 상이합니다. 이는 플랫폼마다 공식문서에서 확인하는 거나 platformURL/.well-known/configuration
주소에서 확인해야 합니다.
GET /user-profile
Content-Type: application/x-www-form-urlencoded
Authorization: Bearer {access_token}
자체 AccessToken & RefreshToken
사용자. 즉 Resource Owner 의 Resource(가령 프로필정보) 를 얻어온 Backend 는 이 정보를 토대로 Application 자체적으로 사용하는 AccessToken & RefreshToken
을 발급합니다.
하지만 토큰을 발급하기 전, 사용자 정보를 기반으로
신규 사용자
라면 자동 회원가입을 진행하고,기존 사용자
라면 로그인 처리를 수행합니다.
그 후, AccessToken & RefreshToken
을 발급하여 Frontend 에게 토큰을 전달합니다.
단점
지금까지 Frontend 와 Backend 로 분리된 Application 환경에서 Authorization Code Grant
방식의 OAuth2 인증 및 인가 프로세스를 알아보았습니다. 하지만 이 방식에는 한가지 단점이 있습니다.
그 단점은 바로 Frontend에서 OAuth2 인증에 필요한 client_id 를 알고 있어야 한다는 점
입니다.
Frontend는 OAuth2 인증 및 인가 프로세스의 첫 단계에서 사용자(Resource Owner) 를 인가서버의 authorization_uri
로 이동시키는 역할을 수행하는것을 확인했습니다. 하지만 이를 위해서는 client_id
를 포함한 인증 URL 을 Frontend 에서 직접 생성해야 하며, 이는 두 가지 문제점을 초래할 수 있습니다.
client_id
가 외부에 노출될 가능성- Frontend 코드에서
client_id
를 직접 사용되므로, 악의적인 사용자가 이를 획득하여 임의로 OAuth2 인증 요청을 조작할 수 있습니다.
- Frontend 코드에서
Frontend 가 OAuth2 인증 방식에 종속
- Frontend 가
client_id
를 직접 다루는 구조이므로, 인증 방식이 변경될 경우 Frontend 코드도 수정해야 합니다. - 또한, OAuth2 Provider 여러 개(카카오, 구글 등)일 경우, 각각의
client_id
를 Frontend가 관리해야 하므로 복잡성이 증가합니다.
- Frontend 가
마치며
지금까지 Frontend 와 Backend 로 분리된 Application 환경에서 Authorization Code Grant
방식의 OAuth2 인증 및 인가 프로세스, Frontend 와 Backend 의 역할
, 단점
에 대해서 알아봤습니다. 다음 포스팅에서는 해당 방식에서의 단점을 해결하는 OAuth 인증 Flow 에 대해 자세히 서술해보겠습니다.