로그인 기능 통째로 구현해보기 - 1. Oauth편

상준맨·2023년 3월 28일
0

로그인은 당연히 소셜 로그인으로 구현해야지!

사용자 입장에서 소셜 로그인 기능은 정말 편리한 기능중 하나입니다.

저는 비밀번호를 잘 기억하지 못하는 편이라 아이디와 패스워드 입력으로 로그인하는 웹사이트의 경우 항상 비밀번호를 찾곤 하는데요,
소셜 로그인 기능이 있는 서비스의 경우 소셜 로그인 버튼 클릭 하나만으로 로그인 과정을 마칠수 있다는 점에서 사용자에게 편리함을 가져다 주는게 아닌가 싶습니다.

oauth 기본 개념

그렇다면 이런 소셜 로그인 기능 구현의 기반이 되는 Oauth란 무엇일까요?
oauth의 동작 프로세스를 확인하기 전에 oauth가 뭔지 알아봅시다.

oauth가 뭐야? 대단한 "인가" 프로토콜이지~

OAuth 2.0 is the industry-standard protocol for authorization.
출처 : oauth.net

여기서 주의깊게 보아야 할 건, Authorization라는 단어겠죠,
okta 에서는 Authroization(인가, 권한 부여)와 Authentication(인증)을 이렇게 설명하고 있습니다. 링크

인증 : 사용자의 신원을 검증하는 행위
인가 : 사용자에게 특정 리소스나 기능에 액세스할 수 있는 권한을 부여하는 프로세스

한마디로 정리해보면
"인증" 이란 "사용자가 맞는지 확인하기" 것이고,
"인가" 이란 "사용자의 권한을 넘겨받기" 라고 볼 수 있습니다.
즉 어떤 서비스에서 oauth를 통해 로그인 기능을 구현했을때, 그 서비스가 얻을수 있는것은 유저의 권한인 것입니다.

Oauth에 참여하는 주체들

주체설명
Resource Owner리소스를 가지고 있는 유저, 서비스를 사용하려는 유저.
Client보통 우리가 개발하는 서버,
Authorization Server인증 과정을 담당하는 서버
Resource Server리소스 오너의 리소스를 가지고 있는 서버

여기서 Client를 잘 봐야 합니다.
일반적으로 사용하는 Client의 의미와 Oauth 프로세스에서 Client의 의미가 다르기 때문입니다.

일반적인 웹 애플리케이션의 Client는 사용자를 뜻하지만, 반대로 Oauth 과정에서 Client는 우리가 개발하는 서버, 즉 인증 과정을 위임받아 사용하는 고객 역할이므로 Client라는 용어를 사용합니다.

Oauth 프로세스 과정

그렇다면 사용자의 권한은 어떤 과정을 통해 얻어올 수 있을까요?
아래 그림을 보며 oauth 프로세스 과정에 대해 알아봅시다.

0. 애플리케이션 등록

권한을 인증받기 위해 먼저 클라이언트(우리가 개발하는 서비스)를 먼저 인증 서버에 등록해야 합니다.

등록할 때 클라이언트 이름, 설명, 로고, 리디렉션 URI 및 기타 메타데이터와 같은 정보를 입력하고, 사용자의 권한을 인가받을 수 있는 범위에(Scope) 대한 정보도 등록합니다.

입력 이후에 클라이언트 Id, 클라이언트 secret 등이 발급되고 이 값들로 통해 인증 서버에 클라이언트를 인증할 수 있습니다.

1~2. 서비스 접근 및 로그인 요청

사용자는 서비스 이용을 위해 클라이언트(우리가 개발하는 서비스)에 로그인을 요청합니다. 구글로 로그인 버튼을 누르는 경우에 해당합니다.

3. 인증 서버(Authroization Server)에 로그인 요청

클라이언트는 인증 절차를 시작하기 위해 인증 서버의 /auth 엔드포인트에 접근합니다.
이때 필요한 정보를 쿼리 파라미터 형식으로 요청 Url에 포함하여 전달하게 되는데요, 이때 필요한 정보는 다음과 같습니다.

  1. response_type: 공식 문서 에 지정되어있는 대로 “Code”를 사용합니다.클라이언트가 권한 부여 코드를 요청하고 있음을 나타내기 위함입니다.
  2. client_id: 애플리케이션 등록시 발급받은 클라이언트 ID
  3. redirect_uri: 사용자가 동의를 승인하거나 거부한 후 인증 서버가 user-agent 를 리디렉션할 URI를 지정합니다. 애플리케이션을 등록할 때 입력한 값이어야 합니다.
  4. scope: 클라이언트가 사용자에게 인가 받을 엑세스 권한 목록입니다.
    /* 요청 url 예시 */
    https://accounts.google.com/o/oauth2/v2/auth? //요청 Url
     scope=https%3A//www.googleapis.com/auth/drive.metadata.readonly& //범위
     response_type=code& //code 고정
     redirect_uri=https%3A//oauth2.example.com/code& //Authroization code 전달받을 url
     client_id=client_id //애플리케이션 등록 이후 발급된 클라이언트 id

3~4 로그인 페이지 연결, ID PW 입력

사용자는 인증 서버가 제공하는 페이지에서 ID와 PW를 입력하여 본인을 인증합니다.
사용자는 이 과정에서 액세스 범위에 대한 정보를 확인하고 서비스에 해당 권한을 부여할지 여부를 결정합니다.

5~6 Authorization code(권한 부여 코드) 발급, 클라이언트로 Authorization Code 전달

먼저 액세스 토큰을 발급받기 위한 grant 유형은 4가지가 있는데요
그중 하나인 Authroization Code grant(권한 코드 부여 방식) 방식에서 권한 코드를 사용합니다.

인증 서버는 액세스 토큰을 발급하는 데 필요한 Authroization code(권한 코드)를 생성합니다. 권한 부여 코드는 애플리케이션 등록시 입력한 리디렉션 URI로 다시 전송되며, 이 코드를 가지고 클라이언트는 액세스 토큰을 요청하는데 사용합니다.

권한부여 코드 발급을 하는 이유는 인증 과정에서의 보안성을 높이기 위해 사용됩니다, 권한 부여 코드는 OAuth 2.0 사양에서 몇 분을 넘지 않는 짧은 수명으로 인증 코드를 발급하고, 한번만 사용될 것을 것을 권장합니다. https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.2 짧은 만료 시간과 일회성인 코드로 무단 액세스토큰 발급을 방지할수 있습니다.

또한 사용자의 user-agent(ex. 브라우저)는 사용자의 상황에 따라 안전하지 않을 수 있는데요(XSS 공격). 이런 유저 에이전트에 엑세스 토큰을 바로 전달하는게 아니라 권한 부여 코드를 주고 이 권한 코드를 클라이언트에서 넘겨받아 엑세스 토큰을 요청하여 user-agent에서 제 3자에 의해 엑세스 토큰이 탈취당할 위험성을 줄일수 있게됩니다.

애플리케이션의 리다이렉트 uri로 전달된 권한부여 코드는 사용자의 User-agent를 거쳐 클라이언트에게 전달됩니다.

7~8. Access Token 요청과 발급

클라이언트는 전달받은 Authroization 코드와 추가 정보를 통해 인증 서버에 access token을 요청합니다.

이때 필요한 Authroization Code, Client ID , Client secret값을 POST 요청에 담아 /token 엔드포인트로 access token을 요청합니다.

  1. grant_type: 액세스 토큰을 얻기 위해 사용되는 승인 유형입니다. 권한 부여 코드 부여 유형의 경우 이값은 "authorization_code"입니다.
  2. code: 권한 부여 코드를 여기에 담습니다.
  3. redirect_uri: 인증 요청에 사용된 리디렉션 URI와 일치해야 합니다.
  4. client_id: 애플리케이션 등록시 발급받은 클라이언트 ID
  5. client_secret: 애플리케이션 등록시 발급받은 클라이언트 secret
/* 요청 http 메시지 예시 */
POST /token HTTP/1.1
Host: oauth2.googleapis.com
Content-Type: application/x-www-form-urlencoded

code=4/P7q7W91a-oMsCeLvIaQm6bTrgtp7&
client_id=your_client_id&
client_secret=your_client_secret&
redirect_uri=https%3A//oauth2.example.com/code&
grant_type=authorization_code

9 ~ 11. 발급받은 엑세스 토큰으로 리소스 서버에 리소스 요청

클라이언트는 발급받은 엑세스 토큰으로 리소스 서버에 사용자의 권한으로 리소스를 요청할 수 있습니다.

리소스 서버로 api 요청을 보낼 때, 엑세스 토큰은 http 메시지에 Authrization 헤더에 Bearer ${엑세스 토큰 } 과 같은 형식으로 요청을 보내야 합니다.



그런데,, 나는 “인가” 말고 “인증”을 하고싶은데..

애플리케이션 등록 - 사용자 인증 후 권한코드 전달받기 - 엑세스토큰 발급

위와 같은 oauth 플로우를 통해 사용자의 권한을 인가받을 수 있다는 건 이제 알았는데, 가만히 다시 생각해보면 제가 하고싶은 것은 "인증"입니다.

구현하고 싶었던 내용은 구글 oauth를 통해 저희 서비스를 사용하는 사용자를 식별하고 싶은 것이지 유저의 권한을 위임받아 구글 api에 접근하고 싶은 건 아니기 때문입니다.

물론 oauth로 권한을 위임받아 리소스 서버에서 사용자의 신원 정보를 조회하는 방식으로 “인증”을 구현할 수는 있지만, 이는 올바른 방식이 아니라고 합니다. https://www.okta.com/kr/identity-101/authentication-vs-authorization/

OIDC (Open ID Connect)

OIDC(OpenIDConnect) 라는 프로토콜이 있습니다.
이는 Oauth의 인가 목적이 아닌, 사용자의 신원을 “인증”하기 위한 프로토콜로써 Oauth 2.0을 확장한 프로토콜이고, Oauth 2.0을 기반으로 동작합니다.

Oauth 프로세스 결과가 리소스 서버에 대한 엑세스 토큰이라고 하면, OIDC의 프로세스 결과는 사용자의 신원 정보가 담긴 jwt 형식의 id_token 값입니다.

구글 Oauth의 경우, 애플리케이션 등록 과정에서 스코프 를 정할 때 openId값을 범위로 체크하여 인가에 필요한 사용자의 정보를 받아볼 수 있게 됩니다

id_token는 어떻게 받는거고 jwt는 또 뭘까?

인증은 oauth 2.0 기반으로 동작하는 OIDC 프로토콜로!

결국 제가 원하는 구글 로그인을 통한 “인증” 과정은 oauth 2.0 프로토콜의 위에서 동작하는 oidc 프로토콜을 통해 진행할 수 있고, 이 결과는 id_token 라는 키값으로 jwt 토큰 형식의 밸류로 얻을수 있다는 사실을 알게 되었습니다.

알아야 하는게 저도 모르게 하나둘씩 늘어나지만 소셜 로그인을 구현하고 싶으니까요..! 다음 장에서는 jwt에 대한 내용과 실제 백엔드에서 로그인 로직 구현을 위한 Passport 라이브러리와 관련된 내용으로 로그인 정복하기 시리즈를 이어가보도록 하겠습니다!

참고 자료

https://www.okta.com/kr/identity-101/authentication-vs-authorization/
https://developers.google.com/identity/protocols/oauth2?hl=ko
https://hudi.blog/open-id/
https://datatracker.ietf.org/doc/html/rfc6749

profile
아자아자 화이팅

0개의 댓글