[Django]トークン認証にdjango-rest-knoxを利用する

## はじめに
自分用の個人開発のメモ/備忘録として記録していきます。

## 環境
django5.0
↓ dockerでの開発環境作成はこちら

https://qiita.com/mkthrkw/private/588226d30690c707d2b8

↓ 認証ユーザーモデルはこちらをそのままそのまま利用

https://qiita.com/mkthrkw/private/b2ea8fb8f33a68faab87


## やりたいこと
簡単に有効期限を設定したトークン認証を実装したい
トークンだけじゃなく、他の情報も取得出来るようにしたい

### djangorestframework-simplejwtと比較したい

https://qiita.com/mkthrkw/private/57a4296917a54ca34c12

## 導入

### インストール
pipでインストール
```
pip install django-rest-knox
```

### settings.pyの設定変更
・INSTALLED_APPSに　rest_framework　と　knox　を追加
・全てのviewをトークン認証にするためDEFAULT_AUTHENTICATION_CLASSESを設定
・Knoxの有効期限、自動更新を設定

``` python:settings.py
INSTALLED_APPS = (
  ...
  'rest_framework',
  'knox',
  ...
)

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'knox.auth.TokenAuthentication',
    ],
}

REST_KNOX = {
    "TOKEN_TTL": timedelta(hours=2),    # 有効期限を2時間に設定する
    'AUTO_REFRESH': True,    # トークンの自動更新を有効にする
}
```

### serializers.py作成

・認証用 email/password のLoginSerializer作成
・レスポンス用の UserSerializer作成

``` python:serializers.py
from rest_framework import serializers
from users.models import CustomUser


class LoginSerializer(serializers.ModelSerializer):
    email = serializers.EmailField()

    class Meta:
        model = CustomUser
        fields = ["email", "password"]

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = CustomUser
        fields = ["id", "email", "name"]
```

### views.py作成

・トークン認証のみにするため、こちらを参考にLoginViewをオーバーライド
・認証出来たらログインも実行
・有効期限ではなくユーザー情報を返却出来るように「get_post_response_data」をオーバーライド

https://github.com/jazzband/django-rest-knox/blob/develop/docs/auth.md

``` python:views.py
from rest_framework import permissions
from django.contrib.auth import authenticate, login
from knox.views import LoginView as KnoxLoginView
from users.serializers import LoginSerializer, UserSerializer
from rest_framework import status
from rest_framework.response import Response

class LoginView(KnoxLoginView):
    permission_classes = (permissions.AllowAny,)

    def get_post_response_data(self, request, token, instance):
        return {
            'user': UserSerializer(instance.user).data,
            'token': token
        }

    def post(self, request, format=None):
        serializer = LoginSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        user = authenticate(
            request=request,
            username=serializer.validated_data["email"],
            password=serializer.validated_data["password"],
        )
        if user is not None:
            login(request, user)
            return super(LoginView, self).post(request, format=None)
        else:
            return Response(
                data={"error": "email or password is incorrect"},
                status=status.HTTP_400_BAD_REQUEST,
            )
```


### urls.py作成
・オーバーライドしたLoginViewを設定
・ログアウトはデフォルトをそのまま利用

``` python:urls.py
from django.urls import path
from knox import views as knox_views
from users.views import LoginView

urlpatterns = [
    path(r'login/', LoginView.as_view(), name='knox_login'),
    path(r'logout/', knox_views.LogoutView.as_view(), name='knox_logout'),
    path(r'logoutall/', knox_views.LogoutAllView.as_view(), name='knox_logoutall'),
]
```

## アクセスしてみる

ログインで無事ユーザーデータも取得OK
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3680973/ec206ad5-b0f1-605b-9907-85208c9657dc.png)

ログアウトも無事成功
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3680973/1b631efd-a68a-8042-647c-39629429bcc3.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3680973/022f8f9a-3555-11d3-6c19-df6fc3523001.png)


## おわりに
・比較的簡単に導入できた
・リフレッシュトークンもデフォルトで利用できたら嬉しかった

## 参考
https://qiita.com/shun198/items/0c235b90df96c894b5f0

https://github.com/jazzband/django-rest-knox