Django REST Framework Token Authentication

Reading time ~3 minutes

Django REST Framework에서 Token을 이용해서 인증을 하는 방법에 대해서 알아본다. Django REST Framework TokenAuthentication 을 보고 따라해보면 쉽게 적용할 수 있지만 AbstractUser을 상속 받아서 User model을 구현한 경우에는 {'non_field_errors': ['Unable to log in with provided credentials.']} 라는 에러가 발생했다. serializer에서 user의 password를 저장할 때 hashing을 하지 않기 때문에 패스워드가 불일치하여 발생하는 문제로 create메소드를 Overriding하여 set_password를 적용하면 해결된다.


Setup

Django REST Framework Token Authentication을 사용하기 위해 필요한 앱과 패키지를 settings.py 에 설정해준다.

settings.py

INSTALLED_APPS = [
    # ...
    'rest_framework',
    'rest_framework.authtoken',
]

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.BasicAuthentication',
        'rest_framework.authentication.SessionAuthentication',
    )
}


URL

인증을 요청할 때 사용할 원하는 URL을 다음과 같이 지정한다.

urls.py

from rest_framework.authtoken.views import obtain_auth_token

urlpatterns = [
    # ...
    url(r'^api-token-auth/', views.obtain_auth_token)  
    # url(r'^login/', views.obtain_auth_token)  
]


User Model

User model 은 AbstractUser를 상속받아서 구현했고 email을 username으로 사용하고 있다.
User가 생성될 때 Token을 자동으로 생성하기 위해 아래의 코드를 추가해준다.

models.py

from django.conf import settings
from django.db.models.signals import post_save
from django.dispatch import receiver
from rest_framework.authtoken.models import Token

class User(AbstractUser):
    username = models.EmailField(unique=True, null=False, max_length=254)
    # ...


@receiver(post_save, sender=settings.AUTH_USER_MODEL)
def create_auth_token(sender, instance=None, created=False, **kwargs):
    if created:
        Token.objects.create(user=instance)


Result & Error

제대로 동작하는지 확인해보기 위해 User를 생성하고 CLI를 통해 인증 요청을 해본다.

$ curl -X POST -d "username=test@email.com&password=password1234" http://localhost:8000/api-token-auth/
{"non_field_errors":["Unable to log in with provided credentials."]}

인증 요청은 지정된 URL로 로그인 하고자 하는 username, password 의 값을 JSON 혹은 form으로 POST 요청을 하면된다. 기대한 값은 { 'token' : '9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b' } 이었지만 로그인 할 수 없다는 에러메세지가 반환된다. django의 admin 페이지를 통해 확인해봤더니 createsuperuser를 통해 생성된 User에 대해서는 token이 반환되지만 그렇지 않은 일반 User에 대해서는 에러가 발생했다. 그 이유는 상속받아서 구현한 User model을 Serializer 하는 과정에서 password의 hashing이 누락 돼서 발생한 것으로 판단된다. 이를 해결하기 위해 아래와 같이 set_password 을 사용하여 추가적인 코드를 작성해주면 해결된다.

serializer.py

class UserSerializer(serializers.HyperlinkedModelSerializer):
    def create(self, validated_data):
        password = validated_data.pop('password', None)
        instance = self.Meta.model(**validated_data)
        if password is not None:
            instance.set_password(password)
        instance.save()
        return instance

    #...
$ curl -X POST -d "username=test@email.com&password=password1234" http://localhost:8000/api-token-auth/
{ 'token' : '9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b' }

Django REST Framework OneToMany 관계 Serializer에 추가하기

Django REST Framework에서 model을 정의하고 Serializer, ViewSet, Router 만 간단히 구현하면 REST API를 편리하게 이용할 수 있다. Meeting에 대한 REST API가 구현돼 있고 SubIma...… Continue reading