백엔드

OAuth2

소프트웨어 학부생의 개발 도전기 2024. 10. 19. 17:40

요즘 어플리케이션이나 어떤 특정 사이트에 회원가입을 위해서 타 서비스의 계정을 사용하는 인증 방식이 많이 보인다. 이러한 방식을 OAuth 2.0 인증 방식이라고 한다. 이번 글에서는 OAuth 인증 방식이 어떻게 동작하는지를 구글 서비스를 통해 살펴 보고자 한다.

 

OAuth 2.0 동작 방식

OAuth 인증 방식은 위에서 언급한 대로 인증의 과정을 '타 서비스에게 위임' 하는 인증 방식이다. '위임'한다는 뜻은 내 웹 사이트에 '구글 로그인' 인증을 넣었다고 해서 사용자가구글 웹 사이트에 직접 로그인하는 것이 아니라는 뜻이다. 내 웹 사이트에 접속한 사용자의 정보는 여전히 내 웹 사이트에서 관리해야 한다. 

 

구글이 해주는 일은 웹 사이트 사용자가 '구글 로그인' 기능을 통해 구글에게 전송한 구글 계정 정보가 유효한지(구글 아이디 및 비밀번호가 일치하는지)를 확인한 후, 유효하다면 해당하는 구글 유저 정보 중 일부(유저 이름, 프로필 이미지 등)를 내 웹 사이트에 제공해주는 '인증' 과정만을 처리해주는 것 뿐이다.

 

1. 구글 인증 페이지

우리가 처음 웹 사이트에 접속해 로그인 페이지에서 '구글 로그인' 버튼을 누르면, 다음과 같이 Google 인증 페이지로 이동하도록 구현하고 싶다.

위의 인증 페이지는 구글에서 제공하느 Google 인증 서버 페이지이며, 

https://accounts.google.com/o/oauth2/auth URL에서 제공한다. 하지만, 단순히 이 링크르 주소창에 치면 아래와 같은 에러 메시지를 볼 수 있다. 에러 내용을 보면 필수적으로 제공해줘야 하는 파라미터가 주어지지 않았다는 것을 알 수 있다.

잘 생각해보면 매우 당연한 에러이다. 우리가 구글 로그인 기능을 사용하기 위해 Google 인증 서버 페이지에 접속한다는 뜻은 특정 웹 사이트에서 구글 계정을 사용하는 OAuth 인증을 사용하고 싶다는 것을 의미한다. 그런데 다짜고짜 주소창에 인증 서버 페이지 주소를 입력하면 대체 어떤 웹 사이트에서 OAuth 인증을 요청하는지 알 수 없기 때문에 오류가 발생하는 것이다.

 

이때 '어떤 웹 사이트에서 OAuth 인증을 요청하는가?' 를 구글 인증 서버에 알려주기 위해 사용하는 것이 OAuth 클라이언트 생성 시 받았던 클라이언트 ID이다. 우리가 웹 서비스를 구글에 프로젝트로 등록했고, 등록 시 웹 사이트 URL 정보를 제공했기 때문에 구글 인증 서버에 클라이언트 ID만 주면 구글에서 어떤 웹 사이트에서 OAuth 인증을 요청하는지를 자체적으로 식별할 수 있는 것이다.

 

OAuth 를 사용하기 위해서는 구글 인증 서버에 클라이언트 ID 이외에도 많은 추가 정보들을 제공해 주어야 한다. 그 중 하나 중 중요한게 리디렉션 URL 정보이다. 

 

웹 사이트 사용자가 구글 인증 서버에 올바른 구글 계정 정보를 입력하면, 구글에서는 해당 정보가 유효한지 판단한 후 유효하다면 해당하는 유저의 정보를 우리의 웹 서비스로 전송해야 한다. 웹 사이트로 계정 정보를 전송 해야지 웹 서비스에서도 현재 로그인 한 유저가 어떤 유저인지를 식별할 수 있으며, 웹 서비스에서 자체적으로 유저 정보를 관리할 수 있기 때문이다. 

 

그렇다면 구글 유저 정보를 우리 웹 서비스의 특정 URL로 전송해주어야 한다는 의미인데, 이 URL이 바로 위에서 설명한 리디렉션 URL 이다. OAuth 클라이언트 ID 만들기에서 승인된 리디렉션 URI에 우리가 어떤 정보를 줬는지 확인해보면...

우리가 구글에 웹 서비스를 등록할 때, 리디렉션 URL로

http://localhost:8080/login/oauth2/code/google 이라는 URL을 제공했다. 따라서, 구글 인증 서버에서 이 OAuth 를 사용하기 위해서는 인증 서버에 이 http://localhost:8080/login/oauth2/code/google 이라는 URL 리디렉션 URL 정보로 줘야 하며, 구글은 인증이 끝나면 해당 URL로 로그인 한 구글 계정 정보를 POST로 전송해준다.

 

 

2. 엑세스 토큰(Access  Token)

위에서는 구글이 리디렉션 URL로 구글 계정 정보를 POST로 전송해준다고 말했다. 하지만 실제로는 바로 계정 정보를 전송해버리면 보안 상으로 취약하므로 OAuth 2.0 에서 하나의 단계를 더 추가한다. 이 단계를 구현하기 위해 사용되는 것이 바로 엑세스 토큰(Access Token) 이다.

 

사용자가 구글 인증에 성공하면, 구글에서는 우리의 리디렉션 URL인 http://localhost:8080/login/oauth2/code/google 로 리다이렉트 하는데, 이 때 사용자 정보를 POST 로 보내주는 대신 다음과 같이 리디렉션 URL에 code 라는 매개변수 정보를 추가해준다.

 

http://localhost:8080/login/oauth2/code/google?code=코드정보

 

웹 서비스에서는 이 code라는 매개변수 값을 URL로부터 얻어온 후, 이 code를 통해 구글 계정 정보에 접근할 수 있는 액세스 토큰을 얻는다. 액세스 토큰을 얻기 위해서는 https://oauth2.googleapis.com/token 이라는 URL에 code 정보를 포함해 POST 요청을 해야 한다.

 

이 때, 우리는 보안을 위해 code 정보 이외에도 추가 정보를 제공해줘야 하는데, 이 경우 사용되는 것이 바로 구글에 웹 서비스 등록 시 발급받은 클라이언트 ID와 클라이언트 비밀번호 정보이다. 또한, 이 POST 요청에 대한 응답으로 구글에서 엑세스 토큰 정보를 전송해줄 때에도 위에서도 동일한 리디렉션 URL을 사용하므로 리디렉션 URL 정보도 함께 제공해줘야 한다.

 

https://www.googleapis.com/oauth2/token
POST Body 정보: code=코드정보, client_id=클라이언트-아이디, client_secret=클라이언트-비밀번호, redirect_uri=https://www.test.com/auth/google/callback

 

위의 POST 요청에 대한 결과로 구글 서버는 다음과 같이 액세스 토큰 정보가 포함 된 JSON 데이터를 리디렉션 URL로 전송해준다.

{
  "access_token" : "ya29.AHES6ZTtm7SuokEB-RGtbBty9IIlNiP9-eNMMQKtXdMP3sfjL1Fc",
  "token_type" : "Bearer",
  "expires_in" : 3600,
  "refresh_token" : "1/HKSmLFXzqP0leUihZp2xUt3-5wkU7Gmu2Os_eBnzw74"
}

우리는 이제 이 access_token에 명시된 액세스 토큰 값을 통해 웹 서비스에서 다양한 구글 API들을 사용할 수 있고, 물론 유저의 구글 계정 프로필 정보도 가져올 수 있다. 예를 들어 https://www.googleapis.com/oauth2/v3/userinfo 라는 URL에 우리의 액세스 토큰 정보를 포함하여 GET 요청을 보내면, 아래와 같은 유저 정보를 JSON 형태로 얻을 수 있다.

{
 "sub": "110248495921238986420",
 "name": "Aaron Parecki",
 "given_name": "Aaron",
 "family_name": "Parecki",
 "picture": "https://lh4.googleusercontent.com/-kw-iMgD
   _j34/AAAAAAAAAAI/AAAAAAAAAAc/P1YY91tzesU/photo.jpg",
 "email": "aaron.parecki@okta.com",
 "email_verified": true,
 "locale": "en",
 "hd": "okta.com"
}

참고로 이러한 액세스 토큰은 브라우저의 캐시로 저장되거나 웹 서비스에 세션으로 저장될 수 있다. 만약 캐시나 세션에 액세스 토큰 정보가 저장된다면, 액세스 토큰이 만료되지 않는 한 브라우저를 종료했다가 다시 접속해도 자동으로 이전 로그인 정보를 로그인 되도록 웹 사이트를 구축할 수도 있다.