웹 서핑, 앱을 사용하다 보면 Google과 Facebook, Twitter, github등 외부 소셜 계정을 기반으로 회원가입 및 로그인할 수 있는 어플리케이션을 쉽게 찾아볼 수 있다. 편리하게 회원가입, 로그인을 할 수 있을 뿐 아니라 연동되는 외부 어플리케이션에서 Facebook, Twitter등이 제공하는 기능을 간편하게 사용할 수 있다는 장점이 있다.
예를 들어 외부 어플리케이션에 Google로 로그인하면 API를 통해 연동된 계정의 Google Calendar 정보를 가져와 사용자에게 보여줄 수 있다. 이 때 사용되는 프로토콜이 OAuth이다.
OAuth란?
OAuth는 인터넷 사용자들이 비밀번호를 제공하지 않고 다른 웹사이트상의 자신들의 정보에 대해 어플리케이션의 접근 권한을 부여할 수 있는 공통적인 수단으로 사용되는, 접근 위임을 위한 개방형 표준이다.
쉽게 말하자면,
제 3자의 서비스(구글, 네이버...)의 기능들을 이용할 수 있게 해주는 기술이라고 한다.
제 3자의 서비스의 계정 정보를 자신의 서비스에 기록하여권한을 가져오도록 구현하면 편리하겠지만 제3자 측에서는 자신들의 회원 정보들을 주는것을 허용하지 않을 것이다. 이를 해결하는 것이 OAuth로 제 3자의 서비스의 회원 정보를 직접적으로 노출하지 않고 해당 서비스의 권한을 부여할 수 있다.
이 사진을 예시로 들어보자면, 어플리케이션(원티드)는 사용자 인증을 위해 제 3자 서비스(Facebook, Apple, Google)의 사용자 인증 방식을 지원한다. 이 때 OAuth를 바탕으로 원티드는 외부서비스(Facebook, Apple, Google)의 특정 자원을 접근 및 사용할 수 있는 권한을 인가받게 된다.
OAuth의 구성(원티드앱 예시)
- Client: 자신의 서비스, 제 3자의 서비스를 이용하는 나의 서비스 >> "원티드 앱"
- Resource Owner: Client서비스를 이용하는 사용자, 타 서비스의 회원정보를 가지고 있는 사용자 >> "원티드앱"을 이용하는 사용자로써 다른 서비스의 회원정보를 가지고 있는 개인 사용자로 "나(구글사용자)"
- Resource Server: 제 3자의 서비스를 제공해주는 API 서버 >> "원티드앱"에서 이용하려는 제 3자의 서비스를 제공하는 API서버 "구글API"
- Authorization Server: 제 3자의 서비스 권한을 부여해주는 인증서버 >> "원티드앱"에 대한 인증 및 권한을 부여하는 서버 "구글인증서버"
OAuth의 흐름(GitHub 로그인 연동 예시)
Client 등록
Client가 Resource Server(GitHub API)가 제공해주는 서비스를 이용하기 위해서는 미리 Client 정보를 등록하는 과정이 필요하다.
서비스마다 등록하는 과정은 다르지만 대체적으로 Client를 서비스에 등록할 때 ClientID, Client Secret을 받게되고 Authorized redirection URI를 별도로 제공 서비스에 알려주어야 한다.
- Client ID: 클라이언트 웹 어플리케이션을 구별할 수 있는 식별자이다. 이는 OAuth제공자 (GitHub)에 등록된 클라이언트 어플리케이션을 식별하는데 사용된다.
- Client Secret(클라이언트 비밀 키): Client ID와 함께 사용된다. 이는 클라이언트 웹 애플리케이션이 OAuth제공자에게 자격증명을 인증하는데 사용되는 비밀 키이다 (절대 노출해선 안된다)
- Authorized Redirect URL: 클라이언트 애플리케이션이 OAuth제공자로 부터 인증 결과를 수신하는 URL이다. 사용자가 OAuth제공자에 대한 인증 및 권한 부여를 완료한 후, 인증 결과가 이 리디렉션URL로 전송된다. 클라이언트애플리케이션은 이를 통해 인증 결과를 처리하고 사용자를 알맞은 화면으로 리디렉션한다.
GitHub등 외부 서비스를 통해 인증을 마치면 클라이언트를 명시된 주소로 리다이렉트 시키는데, 이 때 Query String으로 특별한 Code가 함께 전달된다. 클라이언트는 해당 Code와 Client ID 및 Client Secret을 Resource Server로 보내 Resource Server의 자원을 사용할 수 있는 Access Token을 발급받는다. 등록되지 않은 리다이렉트URL을 사용하는 경우, Resource Server가 인증을 거부한다.
Resource Owner의 승인
Github 소셜 로그인을 하기위해 다음과 같은 주소로 GET 요청과 필요한 파라미터들을 보내도록 명시한다.
GET https://github.com/login/oauth/authorize?client_id={client_id}&redirect_uri={redirect_uri}?scope={scope}
- scope: Client가 Resource Server로 부터 인가받을 권한의 범위이다.
Resource Owner는 Client의 웹 어플리케이션을 이용하다가 해당 주소로 연결되는 소셜 로그인 버튼을 클릭한다.
Resource Owner는 Resource Server에 접속하여 로그인을 수행하게된다. 로그인이 완료되면 Resource Server는 Query String으로 넘어온 파라미터들을 통해 Client를 검사한다.
파라미터로 전달된 ClientID와 동일한 ID가 존재하는지 확인한다 >> 해당 ClientID에 해당하는 Redirect URL이 파라미터로 전달된 RedirectURL과 같은지 확인한다.
검증이 마무리 되면 Resource Server는 Resource Owner에게 다음과 같은 질문을 한다.
명시한 scope에 해당하는 권한을 Client에게 정말 부여할거냐?
허용한다면 최종적으로 Resource Owner가 Resource Server에게 Client의 접근을 승인하게된다.
Authorization Server의 승인
Resource Owner가 GitHub로그인 서비스를 승인한 이후에는 Authorization Server도 해당 Resource Server를 이용할 수 있도록 승인을 해주어야 한다. 이 때 Authorization Server에서 바로 Access Token을 발급하는 것이 아닌 Authorization Code를 주게 된다.
초반에 입력한 Authorization Redirect URL로 Authorization code를 전달하게 되고 내부적으로 Client에게도 Authorization code를 보내주게 된다.
이제 Client 애플리케이션은 Resource Owner를 통해서 요청하는 것이 아닌 직접 Resource Server에 접속하게 된다. 요청 시에 Authorization Server에 등록된 Client_ID와 Client_Secret 정보와 Authorization Server에서 승인 시에 발급해 주었던 Authorization Server에서 승인 시에 발급해 주었던 Authorization code 값도 비교하게 된다.
Access Token 발급
모든 정보가 같다면 다음과 같이 Authorization Server에서 Access Token을 발급한 후 Client쪽에 전달하게 된다.
Client 어플리케이션은 내부적으로 가지고 있어야 하며 Resource Server에서 제공해주는 서비스 이용 시에 사용하게 된다.
Client는 해당 토큰을 서버에 저장해두고, Resource Server의 자원을 사용하기 위해 API호출 시 해당 토큰을 헤더에 담아 보낸다.
내가 만드는 앱에서는 GitHub로그인을 이용하기에 Access Token을 발급받으면 해당 Token을 가지고 Firebase credential을 얻어와 로그인을 하도록 해줄 수 있다.
Refresh Token
Refresh Token의 발급 여부와 방법 및 갱신 주기등은 OAuth를 제공하는 Resource Server마다 상이하다.
Access Token은 만료 기간이 있으며, 만료된 Access Token으로 API를 요청하면 401에러가 발생한다.
Access Token이 만료되어 재발급을 받을 때 마다 서비스 이용자가 재로그인 하는것은 매우 번거롭다. 보통 Resource Server는 Access Token을 발급할 때 Refresh Token을 함께 발급한다. Client는 두 Token을 모두 저장해두고, Resource Server의 API를 호출할 때는 Access Token을 사용한다. Access Token이 만료되어 401에러가 발생하면 Client는 보관중이던 Refresh Token을 보내 새로운 Access Token을 발급받게 된다.