2프로젝트로 sound cloud 스트리밍 사이트를 클론 해서 만들어보게 되었고, 구글로그인 기능을 구현해보았다.
구글로그인에 앞서 일반로그인 과정을 짚고 넘어가자.
- 유저가 브라우저에서 id와 password를 입력해서 front-end서버로 전달된다
- front-end서버에서 id와 password가 back-end서버로 전달되고 아이디가 존재하고 비밀번호가 데이터베이스의 비밀번호와 일치하면 토큰을 발행 해 준다.
- front-end에서는 발행받은 토큰을 세션스토리지에 저장시키고 request를 보낼 때 마다 header의 Authorization에 토큰을 실어서 보낸다.
다른 소셜로그인과 마찬가지로 리소스 서버(resource server)인 구글의 api를 이용한다.
소셜로그인은 구글의 유저정보를 빌려서 사용한다. 유저가 입력한 정보가 구글에 있는 유저정보와 일치하면 내 서비스상의 토큰을 발행 해 주는 것이다. 그렇다면 어떻게 구글의 유저정보를 요청해서 가져올까?
정답은 구글의 auth_token을 통해서 구글의 유저정보를 가져 올 수 있다.
리소스서버(소셜로그인 서비스를 제공하는 서버)의 입장에서는 데이터 유출의 우려가 큰 것이기 때문에 승인과정을 거쳐야한다.
구글 로그인의 과정을 알아보자(backend 위주의 설명)
위와 같이 여기 에서 web app에서 구글 로그인을 위한 소셜로그인 api 서비스에 가입해준다.
유저가 내 서비스 창에서 구글로그인을 누르면 실재 구글의 로그인창이 뜨고 이메일과 페스워드를 입력한다.
로그인에 성공하면 front-end 서버로 id_token이 전달된다.
frontend 서버는 id_token을 backend의 구글로그인 api를 호출할 때 header에 실어서 보낸다. 리소스 서버에서 제공하는 auth_token은 대체로 만료기간이 있다(10~15분). 기간이 만료되면 refresh를 하거나 재발행을 해준다.
backend 서버는 header의 Authorization을 확인해 들어온 id_token을 여기
에서 설명된 구글의 앤드포인트의 query parameter에 id_token을 넣어서 requests를 보낸다.
import requests - [1]
def post(self, request):
data = json.loads(request.body)
id_token = request.headers.get('id_token', None)
[2] user_request = requests.get(f'https://oauth2.googleapis.com/tokeninfo?id_token={id_token}')
[3] user_info = user_request.json()
[4] google_email = user_info.get('email')
google_name = user_info.get('name')
[1] python의 requets 모듈을 사용하여 구글 api에 request를 보낼 준비를한다.
[2] google api 조수를 넣고 id_token을 query parameter에 넣어준다.
[3] request로 가져온 값의 json을 읽어온다.
[4] 읽어온 json에서 email이라는 key의 값을 가져온다.
{
// These six fields are included in all Google ID Tokens.
"iss": "https://accounts.google.com",
"sub": "110169484474386276334",
"azp": "1008719970978-hb24n2dstb40o45d4feuo2ukqmcc6381.apps.googleusercontent.com",
"aud": "1008719970978-hb24n2dstb40o45d4feuo2ukqmcc6381.apps.googleusercontent.com",
"iat": "1433978353",
"exp": "1433981953",
// These seven fields are only included when the user has granted the "profile" and
// "email" OAuth scopes to the application.
"email": "testuser@gmail.com",
"email_verified": "true",
"name" : "Test User",
"picture": "https://lh4.googleusercontent.com/-kYgzyAWpZzJ/ABCDEFGHI/AAAJKLMNOP/tIXL9Ir44LE/s99-c/photo.jpg",
"given_name": "Test",
"family_name": "User",
"locale": "en"
}
구글 auth 2.0페이지에서 보면 [2]의 주소로 request를 보내면 위와같은 return을 얻을 수 있다. 여기서 email을 가져오는 것을 [4]를 통해서 확인할 수 있다.
[1] if User.objects.filter(email = google_email).exists():
user = User.objects.get(email = google_email)
[2] token = jwt.encode({'user_id' : user.id}, SECRET_KEY, algorithm = ALGORITHM)
return_key = {'user' : {'token' : token.decode('utf-8'), 'uuid' : user.uuid}}
return JsonResponse(return_key, status = 200)
[1] id_token을 구글 api에 요청해서 받은 구글 유저의 email이 내 데이터베이스에 존재하는지 확인한다.
[2] 존재한다면 내 서비스의 token을 발행해서 실재 내가 운영하는 사이트에서 로그인한것 과 같은 권한을 준다.
[1] user = User.objects.create(
email = google_email,
name = google_name,
uuid = str(uuid.uuid3(uuid.NAMESPACE_DNS, google_email).hex)
)
[2] token = jwt.encode({'user_id' : user.id}, SECRET_KEY, algorithm = ALGORITHM)
user_uuid = user.uuid
return_key = {'user' : {'token' : token.decode('utf-8'), 'uuid' : user_uuid}}
[1] 데이터베이스에 구글유저의 email이 없다면 생성해서 회원가입시키고,
[2] 자체 app의 토큰을 발행해준다.
감사합니다.