nikkie-ftnextの日記

イベントレポートや読書メモを発信

Django REST frameworkでトークン認証のAuthorizationヘッダをTokenからBearerに変える中で、DRFが認証していてdjoserはエンドポイントを追加する役割と理解しました

はじめに

5/18、琴葉ちゃんの日!! nikkieです。

Django REST framework(以下、DRF)ネタです。
私、何もわかっていませんでした...

目次

Authorizationヘッダ Token で認証できるエンドポイント

先日djoserを復習しました。
djoserリポジトリにあるサンプルアプリtestprojectを動かしています

% curl http://127.0.0.1:8088/auth/token/login/ --data 'username=djoser&password=alpine12'
{"auth_token":"0d0bca572490495255f1f626d96bfb61ffaba86a"}
% curl -L http://127.0.0.1:8088/auth/users/me/ -H 'Authorization: Token 0d0bca572490495255f1f626d96bfb61ffaba86a'
{"email":"","id":1,"username":"djoser"}

ここではAuthorizationというヘッダに「Token ...」という形式で指定しています。
トークンを払い出して使うAPIの場合、Authorizationヘッダは「Bearer ...」という形式をよく見かけますよね。
APIのクライアントがBearerと指定できるようにするのをどうやるのか調べました。

『現場で使える Django REST Framework の教科書』より

akiyokoさんの教科書シリーズには大変お世話になっています。
7.4でトークン認証を扱っていて、次の設定で'Authorization: Bearer ...'で認証できると書かれています。

# settings.py
INSTALLED_APPS = [
    # ...

    "rest_framework",
    "rest_framework.authtoken",
    "djoser",
]

REST_FRAMEWORK = {
    "DEFAULT_AUTHENTICATION_CLASSES": [
        "rest_framework.authentication.TokenAuthentication",
    ],
}

urls.pyはdjoserのtestprojectに寄せました

# プロジェクトの urls.py
urlpatterns = [
    path("auth/", include("djoser.urls.base")),
    path("auth/", include("djoser.urls.authtoken")),
]

ところが、どうにもBearerではなくTokenのようで、私には未解決の課題となっていました1

% curl http://127.0.0.1:8088/auth/token/login/ --data 'username=djoser&password=alpine12' 
{"auth_token":"42850e913ae42209670b9df3f584103923880cfb"}

% curl -L http://127.0.0.1:8088/auth/users/me/ -H 'Authorization: Bearer 42850e913ae42209670b9df3f584103923880cfb'
{"detail":"Authentication credentials were not provided."}

% curl -L http://127.0.0.1:8088/auth/users/me/ -H 'Authorization: Token 42850e913ae42209670b9df3f584103923880cfb'
{"email":"","id":1,"username":"djoser"}

脱線:Authorizationヘッダ

この機にMDNのドキュメントを見てみました。

認証方式(HTTP 認証 - HTTP | MDN

Bearer
RFC 6750 を参照。 OAuth 2.0 で保護されたリソースにアクセスするベアラートークンです。

また、レスポンスの「WWW-Authenticate」ヘッダを知りました。
https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/WWW-Authenticate

上でBearerトークンを使ったcurl-vを指定すると、対応しているのはTokenと情報がありました

< WWW-Authenticate: Token

DRFのドキュメントより

TokenAuthentication

djoserとDRFの役割が分かれていることを私が認識できていなかったために錯綜しました。
DRFのドキュメントに記載がありました。
TokenAuthentication
https://www.django-rest-framework.org/api-guide/authentication/#tokenauthentication

  • INSTALLED_APPSに追加した'rest_framework.authtoken'トークン認証を担う
  • 認証にはAuthorizationヘッダにTokenで始めてトークンの値を含める

Tokenの代わりにBearerを使いたい場合への言及もありました

If you want to use a different keyword in the header, such as Bearer, simply subclass TokenAuthentication and set the keyword class variable.

意訳 TokenAuthenticationを継承したクラスで、クラス変数keywordを設定する

AuthorizationヘッダをBearerにする設定例

TokenAuthenticationを継承したクラスを用意します(プロジェクトの中のauthentication.py

from rest_framework.authentication import TokenAuthentication


class BearerTokenAuthentication(TokenAuthentication):
    keyword = "Bearer"

settings.pyREST_FRAMEWORKの指定を変更します

REST_FRAMEWORK = {
    "DEFAULT_AUTHENTICATION_CLASSES": [
-        "rest_framework.authentication.TokenAuthentication",
+        "authentication.BearerTokenAuthentication",
    ],
}

settings.pyと同じディレクトリにあるauthentication.pyの中のクラスを指定しています)

これで、Bearerに続けてトークンを指定するように変わりました!

% curl http://127.0.0.1:8088/auth/token/login/ --data 'username=djoser&password=alpine12'
{"auth_token":"5ca5887fd6e1113a162e76a329b13f054a495930"}

% curl -L http://127.0.0.1:8088/auth/users/me/ -H 'Authorization: Bearer 5ca5887fd6e1113a162e76a329b13f054a495930'
{"email":"","id":1,"username":"djoser"}

% curl -L http://127.0.0.1:8088/auth/users/me/ -H 'Authorization: Token 5ca5887fd6e1113a162e76a329b13f054a495930'
{"detail":"Authentication credentials were not provided."}

djoserはエンドポイント追加

『現場で使える Django REST Framework の教科書』にはdjoserについて

Webアプリケーションで利用可能なREST APIの認証系エンドポイントを簡単に追加することができるDjangoパッケージ

と書かれています(p.112)

djoser.urls.baseのおかげで
https://github.com/sunscrapers/djoser/blob/2.2.2/djoser/urls/base.py

  • /users/ にPOSTしてユーザを作ったり
  • /users/me/ にGETして自分の情報を取得したり

できています。

そして、djoser.urls.authtokenによって、/token/login//token/logout/ が追加されています
https://github.com/sunscrapers/djoser/blob/2.2.2/djoser/urls/authtoken.py

終わりに

AuthorizationヘッダでBearerと書けるようにする中で、ごっちゃにしていたDRFとdjoserの役割が整理されました。

  • トークンで認証する機能はDRF
  • djoserはエンドポイントを追加しているだけ

✍️Authorizationヘッダ中のTokenをBearerに変えるには、DRFTokenAuthenticationを継承してkeywordを設定する