AWS Cognito + DRF 로그인 구현하기(with React)(2)

Jongwho Park·2021년 5월 1일
1

CognitoWithDRF

목록 보기
2/3

Django 프로젝트 구조

현재 프로젝트의 구조

cognito_login
└app_account
└cognito_login # 프로젝트 폴더, settings.py 포함

현재 프로젝트 구조에서 app_account는 로그인 및 기타 기능을 구현할 앱 입니다. 여기에서 추가적으로 JWT 토큰 인증 및 로그인, Cognito와의 연계를 위한 모듈 그리고 커스터마이징한 유저 모델을 다루기 위해 새로운 cognito_auth 앱을 만들어 관리합니다.

django-admin startapp cognito_auth

우선 유저모델을 커스터마이징 하기 위해 AbstarctBaseUserModel을 만들어 PK 및 생성 날짜, 업데이트 날짜를 설정합니다.

cognito_auth/models.py

import uuid

from django.db import models

class AbstarctBaseUserModel(models.Model):
    uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False, unique=True)
    created_at = models.DateTimeField('Created at', auto_now_add=True)
    updated_at = models.DateTimeField('Updated at', auto_now=True)

    class Meta:
        abstract = True

    def __repr__(self):
        return f"<{self.__class__.__name__} {self.uuid}>"

왜 앱을 새로 만들고 모델을 설정하나요?

cognito를 사용하게 되면 사용자를 구분하기 위해 sub id라는 구분값이 각 유저별로 생성되게 됩니다. Cognito에서 받은 sub id 및 기본 정보만 저장하여 사용하고, 구현하는 서비스에 맞는 정보는 따로 관리하기 위해서 입니다.

model을 구성했으니, JWT 관련 util을 따로 만들어 JWT 로그인에 대한 기능을 구현하겠습니다. cognito_auth 앱 아래에 utils 폴더를 만들고, jwt.py를 생성합니다.

cognito_auth/utils/jwt_utils.py

from jwt.algorithms import RSAAlgorithm
from rest_framework_jwt.settings import api_settings
import jwt

# Cognito token decoder
def cognito_jwt_decoder(token):
    options = {'verify_exp': api_settings.JWT_VERIFY_EXPIRATION}
    # JWT  토큰을 decode 함
    unverified_header = jwt.get_unverified_header(token)
    if 'kid' not in unverified_header:
        raise jwt.DecodeError('Incorrect Authentication credentials')

    kid = unverified_header['kid']
    try:
        public_key = RSAAlgorithm.from_jwk(api_settings.JWT_PUBLIC_KEY[kid])
    except KeyError:
        raise jwt.DecodeError('Can\'t find Proper public key in jwks')
    else:
        decoded_token = jwt.decode(
            token,
            public_key,
            api_settings.JWT_VERIFY,
            options=options,
            leeway=api_settings.JWT_LEEWAY,
            audience=api_settings.JWT_AUDIENCE,
            issuer=api_settings.JWT_ISSUER,
            algorithms=[api_settings.JWT_ALGORITHM]
        )
        return decoded_token

cognito_jwt_decoder 함수에서 액세스 토큰을 Decode하면, cognito에서 제공하는 기본 정보를 가져올 수 있습니다. sub id, email 등 해당 토큰을 받아 user 로그인 또는 user 생성 모듈에 관련 정보를 전달합니다.
디코드 하는 과정에서 이전 settings.py에서 설정한 Decode API Url에서 유효하지 않은 토큰이 전달될 경우 에러가 발생하게 됩니다.

cognito_auth/utils/jwt_utils.py

from django.contrib.auth import authenticate
...
# user login or create user
def user_info_handler(payload):
    username = payload.get('sub')
    authenticate(remote_user=username)
    return username

cognito에서 전달받은 토큰이 유효하다면, 아래 함수를 실행하여 로그인 또는 유저를 생성하게 됩니다.
특히 장고 내부 함수인 authenticate에서 유저 관련 정보를 넘기게 되면, django 시스템에서는 요청받는 토큰에 대해 user 객체를 생성하게 됩니다.
payload에서는 decode한 토큰의 정보를 가져오고, 토큰 정보(cognito에서 생성한 유저 정보)에서 sub_id만 가져오게 됩니다.

JWT 토큰 세팅이 끝났으니, REST Framework에서 permissions 설정을 해줍니다.

cognito_auth/api/permissions.py

from rest_framework.permissions import BasePermission

class DenyAny(BasePermission):
    def has_permission(self, request, view):
        return False
    def has_object_permission(self, request, view, obj):
        return False

모든 설정이 끝났으니, settings.py에서 마무리 해줍니다.

  1. 기존에 settings.py에서 설정했던 REST_FRAMEOWRK를 변경합니다.
...

INSTALLED_APPS = [
...
'cognito_auth',
]

REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': (
        'cognito_auth.api.permissions.DenyAny',
    ),
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
    ),
}

JWT_AUTH = {
    # Login Handler
    'JWT_PAYLOAD_GET_USERNAME_HANDLER': 'cognito_auth.utils.jwt_utils.user_info_handler',
    # Decode Handler
    'JWT_DECODE_HANDLER': 'cognito_auth.utils.jwt_utils.cognito_jwt_decoder',
    'JWT_PUBLIC_KEY': rsa_keys,
    'JWT_ALGORITHM': 'RS256',
    'JWT_AUDIENCE': COGNITO_AUDIENCE,
    'JWT_ISSUER': COGNITO_POOL_URL,
    'JWT_AUTH_HEADER_PREFIX': 'Bearer',
}
profile
Hello Data and Python.

0개의 댓글