OAuth 2.0 알아보기

DatQueue·2023년 5월 20일
0
post-thumbnail

시작하기에 앞서

이전 포스팅, 그리고 그 이전의 포스팅에서 우린 "인증(Authentication)"과 해당 인증을 통한 "인가(Authorization)"를 알아보았고, 또한 구현해보았다.

사용한 기술로는 JWT를 활용한 "Access Token", 그리고 해당 Access Token을 조금 더 유연하게 갱신하기 위한 "Refresh Token"을 경험해보았고 또한 "OTP"와 "Google Authenticator"를 활용한 "2FA(Two-Factor Authentication)"을 추가로 수행하였다.

이제 인증의 마지막 단계(물론 마지막이 아닐 수도 있다)로 "OAuth"를 통한 인증을 구현해보고자 한다.

이 글을 작성하기 전, 나에게 있어서 "OAuth"는 단지 "그거... google이나 kakao로 로그인 쉽게 하는거 아냐?" 딱 이정도였다...
이 상태로는 OAuth 인증을 구현해봤자 아무 의미가 없을것이므로, OAuth의 개념에 대해 알아보는 시간을 가지도록 하였고, 더하여 가장 많이 언급되는 "OAuth 2.0"은 무엇인가에 대해 중점적으로 알아보도록 하였다.

이번 포스팅은 코드 적 구현이 아닌 "OAuth 2.0"의 개념과 더불어 어떤 원리로 수행될 수 있는가에 대해 알아본다.


💡 참고한 자료 :

Youtube _[NHN] 로그인에 사용하는 OAuth: 과거, 현재 그리고 미래

Youtube _[생활코딩] - OAuth 2.0

Sporify for Developers



💥 OAuth 1.0

OAuth 1.0 인가 프로토콜 작동 계층 :)

※ OAuth 1.0 Resource Owner

: OAuth를 사용하는 자연인인 “유저”에 해당

※ OAuth 1.0 Client

: Resource Owner에 접근해, Resource를 활용하려하는 애플리케이션

※ OAuth Server

: Resource Owner와 “인증”을 통해 상호작용함으로써, OAuth Client에게 “인가”를 하는 역할


✔ OAuth 1.0 flow :)

가정: 만약 특정 이미지를 불러올 수 있는(다른 어떤 것도 상관없다) 애플리케이션이 있다고 하자.

만약 Resource Owner가 이미지를 다운받으려고 하는 등의 특정 움직임을 보인다. 이 때 OAuth ClientOAuth Server에게 해당 동작을 위한 API를 찌를 것이다.

이때 Resource Server가 어느 웹 주소에서 “인증”을 하고, 이어서 OAuth Client에 대한 “인가” 작업을 할 수 있는지 응답을 보내주게 된다.

이 응답을 받은 OAuth ClientResource Owner에게 302 Redirect 또는 비슷한 웹 기반의 Redirect 방식을 사용해서 Resource OwnerOAuth Server가 호스팅하는 웹사이트로 보내게 된다.

그렇다면, 이제 우린 해당 웹사이트에서(Google, Naver, KaKao…) 아이디, 비밀번호 입력등과 같은 인증작업및 여러 플로우를 수행하고, 마지막으로 이미지 애플리케이션에 대한 권한을 “인가”하게 된다.

이 인가 작업이 끝나면, OAuth Server는 또 다시 302 Redirect나 웹 기반 Redirect를 사용해서 받은 인증 값을 (토큰이 될 것이다) OAuth Client에게 넘겨주게 된다.

그리고, 마지막으로 OAuth Client는 이 “인증 값” 을 받아서 API 호출을 통해서 OAuth Server로 부터 이 “인가” 권한을 나타내는 “토큰”이라는 string값을 전달받게 된다.

이후로 OAuth Client는 해당 사용자의 리소스에 접근을 하거나 사용자를 대신해서 어떤 서비스에 접근을 할 때, 이 토큰을 사용해서 리소스에 접근을 하게 된다.


✔ OAuth 1.0의 문제점 :)

뭔가 얼핏 보기에 괜찮은 플로우같다고 느껴진다. 하지만 OAuth 2.0이 나오게 되었다는 뜻은 해당 1.0엔 몇가지 문제점이 존재한다는 뜻이다. 어떤 문제점이 있을까?

  • Scope 개념 없음
  • 역할이 나누어지지 않음
  • 토큰 유효기간의 문제
  • Client 구현의 복잡성
  • 제한적인 사용 환경

=⇒ 자세한 설명은 아래에서 OAuth 2.0에 대해 다루면서 같이 언급


💥 OAuth 2.0

OAuth 1.0에서 존재하는 “단점”들을 어떻게 “보완”하여 구현되었는가를 중점으로 OAuth 2.0에 대해 알아보자.

Scope 기증 추가

OAuth 1.0엔 없던 Scope 기능이 추가 되었다.

OAuth 1.0에서는 “토큰”이라는 “인가”를 위한 “값”만 있으면 사용자의 “모든 리소스”에 접근할 수 있었다.

하지만, 만약 “사진 애플리케이션” 이라 보았을때, 유저가 “사진을 불러오는 권한”은 OAuth에게 “인가”하였지만 “동영상 혹은 문서”로의 접근은 허용하게 하지 않고 싶어할 것이다. (정확히 말하면 그러지 않고 싶어하는게 아니라, 보안상 문서 혹은 영상으로의 인가는 존재할 경우 위험하다)

그렇기 때문에 OAuth 2.0에서는 Scope라는 기능을 추가해서 해당 “토큰”에 대해서 “얼만큼의 접근 범위가 있는가”를 나타낼 수 있게 되었다.


✔ Client 복잡성 간소화

OAuth 2.0에선 클라이언트의 복잡성을 간소화 시키는데 성공하였다.

OAuth 1.0에선 보안성을 가져가기 위해서 “암호학적 기반 보안책”들을 사용하였다.

하지만, 암호학적 보호를 받기 위해서는 “서명”을 하기 위한 “원문”을 만들어야 한다. 이러한 원문을 만들기 위해서는 Http Method 뿐만 아니라, uri, 그리고 여러가지 파라미터가 필요하다. 그리고 파라미터를 정의할때도 어떤 파라미터를 넣고, 넣지 말아야할 것인지, 더하여 어떤 순서로 정렬을 할 것인지에 대한 정확한 “분류”가 필요했다.

예를 들면 아래와 같다.


왼쪽과 같은 복잡하지 않은 POST 요청을 하기 위해서, OAuth 1.0 Client는 매번 동일하게 오른쪽과 완벽하게 동일한 서명 원문을 만들어야지만 OAuth 1.0 스펙을 확실하게 구현할 수 있다.

이러한 문제를 OAuth 2.0에선 “Bearer Token”“TLS(Transport Layer Security)”를 사용하여 해결할 수 있었다.

“Bearer Token”이란 다른 암호학적 보호나 클라이언트에 바인딩되는 장치들을 모두 배제를 한 채, 해당 “토큰”을 소유하고 있는 것 만으로도 해당 토큰에 대한 사용 권한이 있음을 “인정”을 해주는 토큰을 말한다.

이렇게 “소유를 하고 있는 것” 만으로도 “권한”이 생기기 때문에 “OAuth 2.0”에서는 “TLS”, 가장 대표적인 예로 “HTTPS”를 강제를 하게 된다.


✔ Auth와 Resource 서버 분리

OAuth 1.0에서는 OAuth Server“인증”“리소스 관리”를 전부 맡아서 하였지만, OAuth 2.0에서는 Auth serverResource Server분리함으로써 조금 더 개선된 아키텍쳐를 만들 수 있게 되었다.

이로 인해, 인증 서버는 인증을 담당하고, 리소스 서버는 자신이 가진 리소스를 관리하고 제어한다. 이를 통해 보안성이 강화되고, 클라이언트의 복잡성도 간소화 시킬 수 있게 된다.

( 하지만 처음, OAuth Server에 대해 공부할때는 Resource Server와 Authz Server를 그냥 큰 틀의 Resource Server라 보고 익히는 것이 편하다… )


✔ Token 탈취 문제 개선

OAuth 1.0에서는 토큰의 유효기간이 굉장이 길었다. 만약 해당 토큰이 탈취된다면 악성 공격자가 지속적으로 긴 시간을 어뷰징(공격)할 수 있기 때문에 문제가 되었다.

OAuth 2.0에서는 이러한 문제를 해결하기위해 “Refresh Token”이란 개념을 도입하게 된다.

“Access Token”을 통해 리소스에 접근하게 되는데, 이 때 해당 토큰의 유효기간을 굉장히 짧게 가져간다. 하지만 Server-to-Server 통신을 통해 발급받은 “Refresh Token”을 사용한다면 새로운 Access Token을 받아서 api와 리소스에 접근할 수 있게 된다.

이렇게 된다면 Access Token이 탈취된다고 하더라도, 해당 토큰의 “짧은 유효시간”만큼만 어뷰징이 가능하기 때문에 보안성이 개선되었다고 볼 수 있다.


✔ 제한적인 사용 환경

OAuth 1.0301 Redirect와 다양한 웹 브라우저의 Redirect 방식이다. OAuth 1.0은 웹 브라우저 환경에서 동작을 하도록 최적화 되어있고, 다른 환경에서 사용하기는 굉장히 어려운 프로토콜이란 점이다.

다시 한번 OAuth 1.0 동작에 대해 말하자면, 클라이언트 애플리케이션은 사용자를 인증화면으로 리디렉트시키기 위해 301 Redirect를 사용하며, 사용자의 인증이 완료되면 인증 코드를 클라이언트의 애플리케이션으로 리디렉트하기 위해 웹 브라우저의 리디렉트 방식을 활용한다.

이를 통해, OAuth 1.0은 웹 브라우저 환경에서의 인증과 권한 부여에 최적화된 프로토콜로 사용된다.

하지만, 이러한 “웹 브라우저에 최적화”된 프로토콜이 제한으로 다가오게 된다는 것이다. 현재 여러 서비스및 서버 구축에선 “Server-to-Server Interaction(서버 간 상호작용)”이 필수적으로 요하게 된다.

서버 간 상호작용은 간단히 설명하자면 “한 서버가 다른 서버의 API에 접근하여 사용자의 인증과 권한 부여(인가)를 처리하는 상황을 말한다. 예를 들면 “백엔드 서비스 간 통신”이 있을 수 있다.

“소셜 미디어 통합”을 예로써 생각해보자.

애플리케이션에서 소셜 미디어 서비스 (예: Facebook, Twitter, Google 등)의 기능을 활용하고자 할 때, 백엔드 서비스는 해당 소셜 미디어 서비스의 API에 접근해야한다. 백엔드 서비스는 OAuth 2.0을 사용하여 사용자가 해당 소셜 미디어 서비스에 로그인하고 권한을 부여한 후, API를 호출하여 사용자의 정보를 가져오거나 특정 작업을 수행할 수 있다.

이 뿐만 아니라, 여러 개의 독립적인 백엔드 서비스가 상호작용하는 “마이크로서비스 아키텍처”, 여러 개의 백엔드 서비스를 통합하고 클라이언트에게 통일된 인터페이스를 제공하는 중간 계층인 “API Gateway”에서도 OAuth 2.0 토큰만이 사용될 수 있다.


✔ Grant 도입 (제한적 사용 환경 개선을 위해)

위에서 언급된 OAuth 1.0의 문제를 개선하기 위해 OAuth 2.0“Grant”라는 개념을 추가하게 된다.

Grant라는 것은 OAuth 2.0에서 여러가지 사용 환경에 대한 플로우를 나타내는 인증 방식이라 생각하면 좋다.

OAuth 2.0 은 위의 Grant_Type을 통해 해당 결과로 클라이언트에게 access_token을 발급하게 된다.

정의된 Grant_Type은 아래와 같다.

  1. Authorzation Code(인가 코드): 웹 서버에서 실행되는 앱, 브라우저 기반 및 모바일 앱에서 사용된다. 이 Grant_Type은 인증 코드를 얻고 이를 교환하여 access_token과 refresh_token을 얻는 방식으로 동작한다. 이를 통해 앱은 사용자의 권한을 보다 안전한 방식으로 API에 액세스 할 수 있다. ( OAuth 1.0과 동일 개체 )
  2. Password(패스워드): 사용자 이름과 비밀번호로 로그인하는 경우에 사용된다. 주로 1차앱(First-party app)에서 사용되며, 애플리케이션에 직접적으로 신뢰가 주어지는 경우에만 사용해야 한다. 이 Grant_Type은 사용자의 자격 증명을 서버에 직접 제공하여 access_token을 얻는다.

※ First-party app이란?

: 1차 앱은 사용자가 직접 상호작용하는 애플리케이션을 말한다. 사용자에게 직접 제공되며, 사용자는 자신의 자격 증명 (이름과 비밀번호)을 사용하여 로그인하고 앱에 접근한다.

※ Third-party app이란?

: 서드 파티 앱은 서비스 제공 업체나 플랫폼 외부에서 개발된 애플리케이션을 일컫는다. 이러한 앱은 원래 서비스의 소유자나 운영자가 아닌 다른 개인이나 조직에 의해 개발되고 배포된다. OAuth와의 관련성에대해 말하자면, OAuth는 알다시피 해당 서드 파티 앱이 서비스 제공 업체의 API에 안전하게 액세스할 수 있도록 권한을 부여하기 위한 프로토콜이다. OAuth는 사용자의 동의를 통해 서드 파티 앱이 사용자의 계정에 접근하거나 사용자를 대신하여 서비스에 액세스할 수 있는 권한을 부여한다.
“Password” Grant_Type은 주로 “Firtst-party app”에서 사용되며, 사용자의 자격 증명을 서버에 직접 제공하여 액세스 토큰을 얻는 방식이다. 이는 “Third-party app”에선 권장되지 않는 방식이다. 보안의 측면에서 서드 파티 앱은 OAuth의 다른 Grant_Type을 사용하여 사용자의 인가를 처리하고, 서비스 제공 업체의 API에 접근할 수 있도록 한다.


  1. Client Credentials(클라이언트 자격 증명): 사용자가 없이 애플리케이션 자체적으로 API에 액세스해야 할 때 사용된다. 애플리케이션 자체의 인증 정보를 활용하여 access_token을 얻는다.

  2. Implicit(암시적 흐름): 이 Grant_Type은 이전에 시크릿(secret)없이 클라이언트가 사용되었을 때 권장되었으나, 이제는 인가 코드 그랜트를 PKCE(Proof Key for Code Exchange)와 함께 사용하는 것으로 대체되었다. 암시적 Grant_Type은 보안 측면에서 취약할 수 있기 때문에 권장되지 않는다.

이러한 Grant가 어떻게 효과적인 동작을 이루어내는 것일까?

사실 상 Resource OwnerOAuth Client가 동일한 하나의 개체일 때, 복잡한 플로우를 가져가기 보다, 직접적으로 API 호출을 통해 토큰을 발급받을 수 있는 것이 OAuth 2.0Grant이다.


💥 Resource Owner, Client Application, Resource Server 승인 과정 및 동작 진행

위에서 OAuthOAuth 2.0에서 등장하는 여러 개념및 특징을 알아보았다. 하지만 여전히 화끈?하게 와닿지가 않는다.

바로 이전에 OAuth 2.0Grant에 대해 언급하면서 첫 Grant_Type으로 Authorzation Code(인가 코드)에 대해 설명하였다. refresh_token을 제외하곤 해당 Grant는 OAuth 1.0OAuth 2.0에 동일하게 적용된다. 해당 개념이 사실상 OAuth에 대한 가장 간단한 개념이자 전부를 설명할 수 있을 거 같다. 아래에서 간단히 정리하면

“나의 서비스(My Service _ client application)”“그들의 서비스(Their Service _ resource server)”에 접근하기 위해 “유저(User _ resource owner)”의 계정 id, pw를 가지고 있는 것이 아닌, 그들의 서비스가 “access_token”을 발급함으로써 인가를 가능하게 한다.

“access_token”id, pw가 아닐 뿐더러 그들의 서비스에 대한 모든 요청을 허락하는 것이 아니므로, 부분적 허용(인가 허용)을 가능케 한다.

자, 위의 내용이 기본 OAuth protocol의 베이스이다. 해당 원리를 바탕으로 아래에서 조금 더 구체화 해보자.


✔ Resource Owner의 승인

Resource Server에서 OAuth를 사용할 것을 “등록”하게 되면 Resource Server는 “Client_id”“Client Secret”, 그리고 “redirect URL”을 가지게 된다.

동시에, Client Application 또한 “Client_id”“Client Secret”을 얻게 된다.

(Client는 Resource Server가 가지게 되는 redirect url을 준비해놓고 대기시켜놓아야한다)

만약, Resource Server가 A, B, C, D의 기능을 가지고 있다고 하자. 그 때 Client가 A, B의 기능만을 사용하고 싶다고 한다면, 모든 리소스에 대한 접근을 할 수 있게 하는 것 보단 A, B의 기능에 대해서만 인증을 받게 되는 것이 Resource Server와 Client 모두에게 좋을 것이다.

=⇒ 이것또한 앞서 설명하였던 OAuth 2.0“Scope”에 관한 것이다.

만약, Resource Owner가 Client Application을 통해 Resource Server에서 제공하는 기능(A, B)을 사용하고 싶을 때, Client는 Resource Server로의 인증을 위한 (로그인 인증 폼 등) UI를 띄울 것이다. 이것은 버튼으로 되어있는 요청일 것이다. 해당 버튼의 url은 아래와 같은 형태를 띄게 된다.

https://resource_server/
	?client_id=1
	&scope=A,B
	redirect URL:
		https://client/...

Resource Owner가 Resource Server로의 접속을 위의 주소를 통해 하게 되면, Resource Server는 만약 Resource Owner가 로그인 되어있지 않을 시(인증되어 있지 않을 시) 로그인을 요구하는 아래와 같은 페이지를 보내게 된다.

만약, Resource Owner가 로그인을 수행 해, 인증에 성공했을 경우 그때서야 Resource Server는 위의 주소에 보여지는 client_id값과 같은 client_id 값을 Resource Server가 가지고 있는지 확인한다.

또한 다음으로 요청 주소의 redirect url값과 Resource Server에서 확인한 client_id가 가지고 있는 redirect url이 같은지 아닌지를 확인하게 된다. 만약 다를 경우, 해당 상태에서 작업을 끝내게 된다.

만약 redirect url이 같을 경우, Resource Server는 Resource Owner에게 “Scope”에 해당하는 권한 (A, B)을 Client Application에게 부여 할 것인지를 확인하는 메시지를 전송하게 된다.

예를 들면 아래와 같다.

위에서도 보여지듯이, “어떠한 기능”“어떠한 Client”가 요청하고 있다는 것을 알려준 뒤 “허용할 것인가? (IsAllow)”에 대한 선택을 요구하게 된다.

만약, 허용하게 된다면 해당 정보는 Resource Server로 전달될 것이다.

[Resource Server]

Client id: 1
Client Secret: 2
redirect url:
	https://client/callback
	user id: 1  // user 1이
	scope: a,b  // client가 scope a,b의 기능에 대한 권한을 가짐을 허용 

Resource Server는 이제 위와 같은 정보를 저장하고 있게 될 것이다. (db일 수도 있고,, 파일일 수도 있고..)

자, 이제 우린 Resource Owner로 부터 Resource Server에 접속하는 것에대한 동의를 구하는 과정을 거쳤다.

그러면, 이제 Resource Server가 실제로 “인증(Authentication)”을 어떻게 하느냐에 대해 알아보자.


✔ Resource Server의 승인

(3자간의 소통인만큼 단순하지 않다는 것을 염두해두자)

위의 과정에서 “Resource Owner”가 승인을 하였으므로 이젠 “Resource Server”가 승인을 해 주어야 한다. 하지만 여기서 Resource Server가 승인을 하기 위해서 바로 “access_token”을 발급하지 않고 그 전에 일련의 절차를 더 거쳐야 한다.

해당 과정 (일련의 절차)에서 등장하는 개념이 바로 “Authorization Code(인가 코드)”이다. 이것은 “임시 비밀번호”라 불리기도 한다. Resource Server는 해당 Authorization Code를 Resource Owner에게 전달하게 된다. ( ex. Location: [https://client/...?code=3](https://client/...?code=3) ⇒ 이때 Location은 Redirection을 의미하고, 웹 브라우저에게 Location 을 통해 제시된 주소로 이동하란 명령이다, Resource Server to Resource Owner) 이때 code=3이라 하는 것이 Resource Server에서 생성된 임시 비밀번호 authorization code=3인 것이다.

이제 Location을 통한 헤더값(주소)에 의해 Resource Owner(사용자)가 인식하지도 못하게, 은밀하게 [https://client/...?code=3](https://client/...?code=3)해당 주소로 이동을 하게 된다. 즉, “Client Application”으로 이동하게 되는 것이다. 그렇게 된다면 Client는 자연스럽게 Resource Server가 생성하였던 authorization code=3이란 값을 갖게 된다.

자, Resource Server가 Client에게 authorization code를 넘겨줌으로써 “access_token”을 발급하기 전 상태까지 도달했다. 이제 Client는 Resource Owner를 거치지 않고, Resource Server에게 “직접” 접속할 수 있게 된다. 아래와 같은 주소를 통해서 말이다.

https://resource.server/token?
	grant_type=authorization_code&  // 위의 개념에서 설명하였지만 authorization_code를 제외하고도 다른 방법들이 있다.
	code=3&
	redirect_url=https://client/...&
	client_id=1&
	client_secret=2  // 중요

Resource Server는 Client로 부터 위의 주소 접속을 받게 되면 유효한 접속인지의 일치성을 판별하게 된다. Resource Server는 기존에 아래와 같은 값을 가지고 있었다.

Client id: 1
Client Secret: 2
redirect URL:
	https://client/...
user id: 1
	scope: a, b
	authorization code: 3

위의 값을 가진 Resource Server는 Client로 부터 받아온 authorization_code값을 통해 해당 코드에 맞는 유저 데이터를 찾게된다. 또한 해당 유저의 secret값과 redirect주소값을 비교하여 일치할 경우 다음 단계로 넘어간다.


✔ Access Token 발급

이전 단계에서 “Resource Server”“Client”“승인”하는 단계를 살펴보았다. 해당 단계는 authorization code를 통해 (인증) 수행하였고 그 후엔 Client와 Resource Server에서 사용된 해당 authorization code를 지워줘야한다. 위에서도 언급했다시피 해당 코드는 “임시 비밀번호” 즉, 일회성 코드이므로 오로지 액세스 토큰을 얻기 위한 값이다. 즉, 보안과 인증 과정의 안전성 보장을 위해서 한번 사용되었으면 삭제 혹은 만료 처리를 해주어야 한다.

이제, 드디어 Resource Server는 “access_token”을 발급하고 Client에게 “응답”하게 된다. Client는 이렇게 해당 access_token값을 내부적으로 저장하게 된다.

만약, Client가 access_token=123이란 access_token을 발급받게 된다면, Resource Server는 해당 access_token=123에 해당하는 user id와 지정된 “scope”를 파악하고 해당 정보에 대한 인가를 Client에게 가능케 한다.


✔ API 호출 (with Spotify)

우린 위에서 “access_token”을 발급받기까지의 과정을 진행하였다. 이제 해당 access_token을 활용하여 “Resource Server”를 핸들링해야한다. 그것이 곧 “API” 과정이다.

간단히 API 호출과정을 진행해보자. 참고로, 현재 코드를 통해 “access_token”을 얻는 과정을 직접 수행하진 않았으므로, 특정 서비스의 튜토리얼 과정을 토대로 알아보고자 한다.

우리가 사용할 서비스(Resource Server)는 “Spotify”이다.

먼저 access_token을 얻어보자. spotify developers 튜토리얼 과정에선 “Client Credentials”를 통해 access_token을 요청하는 방법을 설명한다. 세밀한 방법들을 당장 진행해볼 순 없으니 간단히 알아보자. 요청 명령은 아래와 같다.

curl -X POST "https://accounts.spotify.com/api/token" \
     -H "Content-Type: application/x-www-form-urlencoded" \
     -d "grant_type=client_credentials&client_id=your-client-id&client_secret=your-client-secret"

해당 요청을 수행하면 아래와 같은 응답을 받게 된다. “access_token”이 포함된 것을 확인할 수 있다.

(spotify의 access_token은 1hour 동안 유효하다)

{
  "access_token": "BQDBKJ5eo5jxbtpWjVOj7ryS84khybFpP_lTqzV7uV-T_m0cTfwvdn5BnBSKPxKgEb11",
  "token_type": "Bearer",
  "expires_in": 3600
}

만약 해당 액세스 토큰이 (물론 위의 액세스 토큰 값은 임의로 spotify tutorials에서 보여주는 유효하지 않은 값이다) 정말 유효한 값이라면 해당 요청을 Header에 Bearer 타입으로 담아 요청을 보내게 될 경우 아래와 같은 JSON 응답을 받을 것이다.

(Header에 Authorization: Bearer 형식으로 요청을 보내는 것을 권장한다. 물론, query parameter 형식으로도 보낼 수는 있다 ⇒ 하지만 해당 방식은 서버에서 제공할 수도 있고, 제공하지 않을 수도 있다)

{
  "external_urls": {
    "spotify": "https://open.spotify.com/artist/4Z8W4fKeB5YxbusRsdQVPb"
  },
  "followers": {
    "href": null,
    "total": 7625607
  },
  "genres": [
    "alternative rock",
    "art rock",
    "melancholia",
    "oxford indie",
    "permanent wave",
    "rock"
  ],
  "href": "https://api.spotify.com/v1/artists/4Z8W4fKeB5YxbusRsdQVPb",
  "id": "4Z8W4fKeB5YxbusRsdQVPb",
  "images": [
    {
      "height": 640,
      "url": "https://i.scdn.co/image/ab6761610000e5eba03696716c9ee605006047fd",
      "width": 640
    },
    {
      "height": 320,
      "url": "https://i.scdn.co/image/ab67616100005174a03696716c9ee605006047fd",
      "width": 320
    },
    {
      "height": 160,
      "url": "https://i.scdn.co/image/ab6761610000f178a03696716c9ee605006047fd",
      "width": 160
    }
  ],
  "name": "Radiohead",
  "popularity": 79,
  "type": "artist",
  "uri": "spotify:artist:4Z8W4fKeB5YxbusRsdQVPb"
}

만약, access_token 값이 없이 요청을 날리게 되면 아래와 같은 응답을 받을 것이고

유효하지 않은 토큰을 날려 보낼시엔 아래와 같은 응답을 받을 것이다.


✔ Refresh Token (with Spotify)

일전에 OAuth가 아닌 일반 로컬 로그인 인증및 인가 과정에서 “refresh_token”을 직접 구현해 보았었다. 당시에 refresh_token을 통한 인증및 처리 과정들을 가시적으로 알기 위해 여러 설계 이미지를 찾아보았는데 아래의 이미지가 가장 많이 보였었다.

OAuth가 아닌 경우는 Resource Server 하나만으로 생각해도 무방하지만 OAuth에선 위와 같이 Resource ServerAuthorization Server 두 가지로 구분지어 설명한다. 정확히 말하면 “OAuth 2.0”이다.

항상 말하지만, 인증 및 인가에 사용되는 토큰은 오로지 “Access Token”이다. 위의 이미지 상 F과정(Invalid Token Error)은 대부분 access_token이 만료될 시 발생한다. 즉, 그 경우에 Client가 가지고 있는 “Refresh Token”“Authorization Server”에 전송함으로써 access_token을 재발급 받는 형식이다. (경우에 따라 Refresh Token도 새로 발급되는 케이스도 있다)

대부분 “OAuth 2.0”에서 “Refresh Token”은 표준 스펙으로 제시된다.


Token Swap and Refresh | Spotify for Developers


Spotify Developers에서도 Refresh Token에 대한 개념을 설명하고 과정을 제시한다.

Request Headers와 Request Body에 알맞은 형식으로써 요청을 날리면

curl -X POST "https://example.com/v1/swap” -H "Content-Type: application/x-www-form-urlencoded" --data “code=AQDy8...xMhKNA”

아래와 같은 형식의 토큰 정보를 얻게 된다. access_token과 refresh_token이 전부 포함된 응답이다.

{
 "access_token" : "NgAagA...Um_SHo",
 "expires_in" : "3600",
 "refresh_token" : "NgCXRK...MzYjw"
}

이제 우린 refresh_token을 얻게 되었으므로, 해당 토큰 값을 또 다시 POST 요청으로 날려준다.

curl -X POST "https://example.com/v1/refresh" -H "Content-Type: application/x-www-form-urlencoded" --data "refresh_token=NgCXRK...MzYjw"

만약 해당 refresh_token이 올바른(유효한) 값이라면 아래와 같이 access_token을 재발급 받을 수 있게 된다.

{
 "access_token" : "NgAagA...Um_SHo",
 "expires_in" : "3600"
}

생각정리 및 다음 포스팅 예고

간단하다면 간단히, 자세하다면 자세히 "OAuth 2.0"에 대해 알아보았다. 흔히 알고 있는 OAuth를 통한 로그인 구현(Federal Identity)뿐만 아니라 OAuth는(OAuth 2.0) 정말 넓은 범위에서 굉장히 유용한 프로토콜이 아닐까 하는 생각이 드는 시간이었다.

다음 포스팅에선 이번 포스팅에서 다룬 내용및 구현 매커니즘을 토대로 NestJS를 활용해 OAuth2.0 로그인 인증을 수행해보도록 하겠다.
(내용이 마무리되는 대로 업로드 예정입니다)

profile
You better cool it off before you burn it out / 티스토리(Kotlin, Android): https://nemoo-dev.tistory.com

1개의 댓글

comment-user-thumbnail
2023년 12월 20일

깔끔하고 상세한 설명 감사합니다!

답글 달기