認証
認証はプラグイン可能である必要があります。
— Jacob Kaplan-Moss, "RESTワーストプラクティス"
認証とは、受信リクエストと一意の認証情報(リクエストの発信元ユーザーや、リクエストの署名に使用されたトークンなど)を関連付けるメカニズムです。その後、パーミッションとスロットリングポリシーは、これらの認証情報を使用して、リクエストを許可するかどうかを判断します。
REST frameworkは、すぐに使用できる複数の認証スキームを提供し、カスタムスキームを実装することもできます。
認証は、常にビューの開始時に、パーミッションとスロットリングのチェック、およびその他のコードの実行の前に実行されます。
request.user
プロパティは、通常、contrib.auth
パッケージのUser
クラスのインスタンスに設定されます。
request.auth
プロパティは、追加の認証情報に使用されます。たとえば、リクエストの署名に使用された認証トークンを表すために使用できます。
注記: 認証だけでは、受信リクエストを許可または拒否しません。単に、リクエストが行われた際の認証情報を識別するだけです。
APIのパーミッションポリシーの設定方法については、パーミッションのドキュメントを参照してください。
認証の判定方法
認証スキームは常にクラスのリストとして定義されます。REST frameworkは、リスト内の各クラスで認証を試行し、最初に正常に認証されたクラスの戻り値を使用してrequest.user
とrequest.auth
を設定します。
クラスが認証に失敗した場合、request.user
はdjango.contrib.auth.models.AnonymousUser
のインスタンスに設定され、request.auth
はNone
に設定されます。
認証されていないリクエストのrequest.user
とrequest.auth
の値は、UNAUTHENTICATED_USER
とUNAUTHENTICATED_TOKEN
設定を使用して変更できます。
認証スキームの設定
デフォルトの認証スキームは、DEFAULT_AUTHENTICATION_CLASSES
設定を使用してグローバルに設定できます。例:
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.BasicAuthentication',
'rest_framework.authentication.SessionAuthentication',
]
}
APIView
クラスベースビューを使用して、ビューごとまたはビューセットごとに認証スキームを設定することもできます。
from rest_framework.authentication import SessionAuthentication, BasicAuthentication
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView
class ExampleView(APIView):
authentication_classes = [SessionAuthentication, BasicAuthentication]
permission_classes = [IsAuthenticated]
def get(self, request, format=None):
content = {
'user': str(request.user), # `django.contrib.auth.User` instance.
'auth': str(request.auth), # None
}
return Response(content)
または、関数ベースビューで@api_view
デコレータを使用している場合。
@api_view(['GET'])
@authentication_classes([SessionAuthentication, BasicAuthentication])
@permission_classes([IsAuthenticated])
def example_view(request, format=None):
content = {
'user': str(request.user), # `django.contrib.auth.User` instance.
'auth': str(request.auth), # None
}
return Response(content)
未認証と許可拒否のレスポンス
認証されていないリクエストがパーミッションを拒否された場合、適切な2つの異なるエラーコードがあります。
HTTP 401レスポンスには、常にWWW-Authenticate
ヘッダーを含める必要があります。これは、クライアントに認証方法を指示します。HTTP 403レスポンスには、WWW-Authenticate
ヘッダーは含まれません。
使用されるレスポンスの種類は、認証スキームによって異なります。複数の認証スキームが使用されている場合でも、レスポンスの種類を決定するために使用できるスキームは1つだけです。 **ビューに設定されている最初の認証クラスが、レスポンスの種類を決定する際に使用されます。**
リクエストが正常に認証された場合でも、リクエストの実行に対するパーミッションが拒否される可能性があります。その場合、認証スキームに関係なく、常に403 許可拒否
レスポンスが使用されます。
Apache mod_wsgi固有の設定
Apacheでmod_wsgiを使用している場合、認証ヘッダーはデフォルトではWSGIアプリケーションに渡されません。これは、認証がアプリケーションレベルではなくApacheによって処理されると想定されているためです。
Apacheにデプロイしていて、セッションベース以外の認証を使用している場合は、必要なヘッダーをアプリケーションに明示的に渡すようにmod_wsgiを設定する必要があります。これは、適切なコンテキストでWSGIPassAuthorization
ディレクティブを指定し、それを'On'
に設定することで実行できます。
# this can go in either server config, virtual host, directory or .htaccess
WSGIPassAuthorization On
APIリファレンス
BasicAuthentication
この認証スキームは、HTTP Basic Authenticationを使用して、ユーザーのユーザー名とパスワードに対して署名します。Basic認証は、一般的にテストにのみ適しています。
正常に認証された場合、BasicAuthentication
は次の認証情報を提供します。
request.user
は、DjangoのUser
インスタンスになります。request.auth
はNone
になります。
パーミッションが拒否された認証されていないレスポンスは、適切なWWW-Authenticateヘッダーを含むHTTP 401 未認証
レスポンスになります。例:
WWW-Authenticate: Basic realm="api"
注記: 本番環境でBasicAuthentication
を使用する場合は、APIがhttps
経由でのみ使用可能であることを確認する必要があります。また、APIクライアントがログイン時に常にユーザー名とパスワードを再要求し、これらの詳細を永続的なストレージに保存しないようにする必要があります。
TokenAuthentication
注記: Django REST frameworkによって提供されるトークン認証は、かなり単純な実装です。
ユーザーごとに複数のトークンを許可し、セキュリティ実装の詳細を強化し、トークンの有効期限をサポートする実装については、サードパーティパッケージのDjango REST Knoxを参照してください。
この認証スキームは、単純なトークンベースのHTTP認証スキームを使用します。トークン認証は、ネイティブデスクトップクライアントやモバイルクライアントなど、クライアントサーバーの設定に適しています。
TokenAuthentication
スキームを使用するには、認証クラスの設定でTokenAuthentication
を含め、さらにINSTALLED_APPS
設定にrest_framework.authtoken
を含める必要があります。
INSTALLED_APPS = [
...
'rest_framework.authtoken'
]
設定を変更した後、manage.py migrate
を実行してください。
rest_framework.authtoken
アプリは、Djangoデータベースのマイグレーションを提供します。
ユーザーのトークンを作成する必要もあります。
from rest_framework.authtoken.models import Token
token = Token.objects.create(user=...)
print(token.key)
クライアントが認証を行うには、トークンキーをAuthorization
HTTPヘッダーに含める必要があります。キーは文字列リテラル「Token」をプレフィックスとして、2つの文字列を空白で区切る必要があります。例:
Authorization: Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b
ヘッダーでBearer
などの異なるキーワードを使用する場合は、単にTokenAuthentication
をサブクラス化し、keyword
クラス変数を設定します。
正常に認証された場合、TokenAuthentication
は次の認証情報を提供します。
request.user
は、DjangoのUser
インスタンスになります。request.auth
は、rest_framework.authtoken.models.Token
インスタンスになります。
パーミッションが拒否された認証されていないレスポンスは、適切なWWW-Authenticateヘッダーを含むHTTP 401 未認証
レスポンスになります。例:
WWW-Authenticate: Token
curl
コマンドラインツールは、トークン認証されたAPIのテストに役立ちます。例:
curl -X GET http://127.0.0.1:8000/api/example/ -H 'Authorization: Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b'
注記: 本番環境でTokenAuthentication
を使用する場合は、APIがhttps
経由でのみ使用可能であることを確認する必要があります。
トークンの生成
シグナルの使用
すべてのユーザーに自動的に生成されたトークンを持たせたい場合は、ユーザーのpost_save
シグナルをキャッチするだけです。
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
@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)
このコードスニペットをインストール済みのmodels.py
モジュールまたは起動時にDjangoによってインポートされるその他の場所に配置する必要があることに注意してください。
既にいくつかのユーザーを作成している場合は、次のようにして既存のすべてのユーザーのトークンを生成できます。
from django.contrib.auth.models import User
from rest_framework.authtoken.models import Token
for user in User.objects.all():
Token.objects.get_or_create(user=user)
APIエンドポイントの公開
TokenAuthentication
を使用する場合、ユーザー名とパスワードが与えられた場合にクライアントがトークンを取得するためのメカニズムを提供したい場合があります。REST frameworkは、この動作を提供する組み込みビューを提供します。これを使用するには、obtain_auth_token
ビューをURLconfに追加します。
from rest_framework.authtoken import views
urlpatterns += [
path('api-token-auth/', views.obtain_auth_token)
]
パターンのURLの部分は、使用したいものにすることができます。
有効なusername
とpassword
フィールドがフォームデータまたはJSONを使用してビューにPOSTされた場合、obtain_auth_token
ビューはJSONレスポンスを返します。
{ 'token' : '9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b' }
デフォルトのobtain_auth_token
ビューは、設定のデフォルトのレンダラーとパーサークラスを使用するのではなく、明示的にJSONリクエストとレスポンスを使用することに注意してください。
デフォルトでは、obtain_auth_token
ビューにはアクセス許可やスロットリングは適用されていません。スロットリングを適用する場合は、ビュークラスをオーバーライドし、throttle_classes
属性を使用して含める必要があります。
obtain_auth_token
ビューのカスタマイズ版が必要な場合は、ObtainAuthToken
ビュークラスをサブクラス化し、それをURLコンフィグで使用することで実現できます。
たとえば、token
値以外にも追加のユーザー情報を返すことができます。
from rest_framework.authtoken.views import ObtainAuthToken
from rest_framework.authtoken.models import Token
from rest_framework.response import Response
class CustomAuthToken(ObtainAuthToken):
def post(self, request, *args, **kwargs):
serializer = self.serializer_class(data=request.data,
context={'request': request})
serializer.is_valid(raise_exception=True)
user = serializer.validated_data['user']
token, created = Token.objects.get_or_create(user=user)
return Response({
'token': token.key,
'user_id': user.pk,
'email': user.email
})
そして、あなたのurls.py
では
urlpatterns += [
path('api-token-auth/', CustomAuthToken.as_view())
]
Django管理画面を使用する場合
管理インターフェースから手動でトークンを作成することも可能です。ユーザーベースが大きい場合は、TokenAdmin
クラスをモンキーパッチしてニーズに合わせてカスタマイズすることをお勧めします。具体的には、user
フィールドをraw_field
として宣言します。
your_app/admin.py
:
from rest_framework.authtoken.admin import TokenAdmin
TokenAdmin.raw_id_fields = ['user']
Django manage.pyコマンドを使用する場合
バージョン3.6.4以降、次のコマンドを使用してユーザーのトークンを生成できます。
./manage.py drf_create_token <username>
このコマンドは、指定されたユーザーのAPIトークンを返し、存在しない場合は作成します。
Generated token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b for user user1
トークンを再生成する必要がある場合(たとえば、トークンが侵害または漏洩した場合)、追加のパラメータを渡すことができます。
./manage.py drf_create_token -r <username>
SessionAuthentication
この認証スキームは、認証にDjangoのデフォルトのセッションバックエンドを使用します。セッション認証は、Webサイトと同じセッションコンテキストで実行されているAJAXクライアントに適しています。
認証に成功した場合、SessionAuthentication
は次の資格情報を提供します。
request.user
は、DjangoのUser
インスタンスになります。request.auth
はNone
になります。
アクセス許可が拒否された未認証のレスポンスは、HTTP 403 Forbidden
レスポンスになります。
SessionAuthenticationを使用してAJAXスタイルのAPIを使用している場合、PUT
、PATCH
、POST
、DELETE
リクエストなど、「安全ではない」HTTPメソッド呼び出しには有効なCSRFトークンを含める必要があります。詳細は、Django CSRFドキュメントを参照してください。
警告:ログインページを作成する際は、常にDjangoの標準的なログインビューを使用してください。これにより、ログインビューが適切に保護されます。
REST frameworkでのCSRF検証は、同じビューへのセッションベースと非セッションベースの両方の認証をサポートする必要があるため、標準的なDjangoとは多少異なります。これは、認証されたリクエストのみがCSRFトークンを必要とし、匿名のリクエストはCSRFトークンなしで送信できることを意味します。この動作は、常にCSRF検証が適用される必要があるログインビューには適していません。
RemoteUserAuthentication
この認証スキームを使用すると、REMOTE_USER
環境変数を設定するWebサーバーに認証を委任できます。
これを使用するには、AUTHENTICATION_BACKENDS
設定にdjango.contrib.auth.backends.RemoteUserBackend
(またはサブクラス)を含める必要があります。デフォルトでは、RemoteUserBackend
は、まだ存在しないユーザー名に対してUser
オブジェクトを作成します。これと他の動作を変更するには、Djangoドキュメントを参照してください。
認証に成功した場合、RemoteUserAuthentication
は次の資格情報を提供します。
request.user
は、DjangoのUser
インスタンスになります。request.auth
はNone
になります。
認証方法の設定に関する情報は、Webサーバーのドキュメントを参照してください。例:
カスタム認証
カスタム認証スキームを実装するには、BaseAuthentication
をサブクラス化し、.authenticate(self, request)
メソッドをオーバーライドします。このメソッドは、認証に成功した場合は(user, auth)
の2要素タプルを、そうでない場合はNone
を返す必要があります。
状況によっては、None
を返す代わりに、.authenticate()
メソッドからAuthenticationFailed
例外を発生させることができます。
一般的には、次のアプローチを取る必要があります。
- 認証が試行されない場合は、
None
を返します。使用されている他の認証スキームも引き続きチェックされます。 - 認証が試行されたが失敗した場合は、
AuthenticationFailed
例外を発生させます。エラーレスポンスは、アクセス許可チェックに関係なく、他の認証スキームをチェックすることなく、すぐに返されます。
.authenticate_header(self, request)
メソッドをオーバーライドすることもできます。実装されている場合、HTTP 401 Unauthorized
レスポンスのWWW-Authenticate
ヘッダーの値として使用される文字列を返す必要があります。
.authenticate_header()
メソッドがオーバーライドされていない場合、認証スキームは、未認証のリクエストでアクセスが拒否されたときにHTTP 403 Forbidden
レスポンスを返します。
注記:カスタム認証子がリクエストオブジェクトの.user
または.auth
プロパティによって呼び出された場合、AttributeError
がWrappedAttributeError
として再発生することがあります。これは、元の例外が外部のプロパティアクセスによって抑制されるのを防ぐために必要です。Pythonは、AttributeError
がカスタム認証子から発生したことを認識せず、代わりにリクエストオブジェクトに.user
または.auth
プロパティがないと仮定します。これらのエラーは、認証者によって修正または処理される必要があります。
例
次の例では、'X-USERNAME'というカスタムリクエストヘッダー内のユーザー名によって指定されたユーザーとして、着信リクエストを認証します。
from django.contrib.auth.models import User
from rest_framework import authentication
from rest_framework import exceptions
class ExampleAuthentication(authentication.BaseAuthentication):
def authenticate(self, request):
username = request.META.get('HTTP_X_USERNAME')
if not username:
return None
try:
user = User.objects.get(username=username)
except User.DoesNotExist:
raise exceptions.AuthenticationFailed('No such user')
return (user, None)
サードパーティパッケージ
次のサードパーティパッケージも利用できます。
django-rest-knox
Django-rest-knoxライブラリは、組み込みのTokenAuthenticationスキームよりも安全で拡張可能な方法でトークンベースの認証を処理するためのモデルとビューを提供します。シングルページアプリケーションとモバイルクライアントを念頭に置いています。クライアントごとのトークンと、他の認証(通常は基本認証)を提供したときにそれらを生成するためのビュー、トークンを削除する(サーバー強制ログアウトを提供する)、すべてのトークンを削除する(ユーザーがログインしているすべてのクライアントをログアウトする)を提供します。
Django OAuth Toolkit
Django OAuth ToolkitパッケージはOAuth 2.0サポートを提供し、Python 3.4+で動作します。jazzbandによって保守されており、優れたOAuthLibを使用しています。このパッケージは、ドキュメントが充実しており、サポートも充実しており、現在OAuth 2.0サポートにお勧めのパッケージです。
インストールと設定
pip
を使用してインストールします。
pip install django-oauth-toolkit
パッケージをINSTALLED_APPS
に追加し、REST frameworkの設定を変更します。
INSTALLED_APPS = [
...
'oauth2_provider',
]
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'oauth2_provider.contrib.rest_framework.OAuth2Authentication',
]
}
詳細は、Django REST framework - Getting startedドキュメントを参照してください。
Django REST framework OAuth
Django REST framework OAuthパッケージは、REST frameworkに対してOAuth1とOAuth2の両方のサポートを提供します。
このパッケージは以前はREST frameworkに直接含まれていましたが、現在はサードパーティパッケージとしてサポートおよび保守されています。
インストールと設定
pip
を使用してパッケージをインストールします。
pip install djangorestframework-oauth
設定と使用方法の詳細については、認証と権限に関するDjango REST framework OAuthドキュメントを参照してください。
JSON Web Token Authentication
JSON Web Tokenは比較的新しい標準であり、トークンベースの認証に使用できます。組み込みのTokenAuthenticationスキームとは異なり、JWT認証はトークンを検証するためにデータベースを使用する必要はありません。JWT認証のパッケージはdjangorestframework-simplejwtであり、いくつかの機能とプラグ可能なトークンブラックリストアプリも提供します。
Hawk HTTP Authentication
HawkRESTライブラリはMohawkライブラリを基盤として構築されており、APIでHawk署名付きのリクエストとレスポンスを処理できます。Hawkを使用すると、2つの当事者が共有キーで署名されたメッセージを使用して安全に通信できます。これはHTTP MACアクセス認証(OAuth 1.0の一部に基づいていました)に基づいています。
HTTP Signature Authentication
HTTP Signature(現在IETFドラフト)は、HTTPメッセージの送信元認証とメッセージの完全性を達成する方法を提供します。多くのサービスで使用されているAmazonのHTTP署名スキームと同様に、ステートレスなリクエストごとの認証を許可します。Elvio Toccalinoは、使いやすいHTTP署名認証メカニズムを提供するdjangorestframework-httpsignature(古い)パッケージを保守しています。djangorestframework-httpsignatureの更新されたフォークバージョンであるdrf-httpsigを使用できます。
Djoser
Djoserライブラリは、登録、ログイン、ログアウト、パスワードリセット、アカウントアクティブ化などの基本的なアクションを処理するためのビューのセットを提供します。このパッケージはカスタムユーザーモデルと連携し、トークンベースの認証を使用します。これは、Django認証システムのすぐに使用できるREST実装です。
django-rest-auth / dj-rest-auth
このライブラリは、登録、認証(ソーシャルメディア認証を含む)、パスワードリセット、ユーザー詳細の取得と更新などのREST APIエンドポイントのセットを提供します。これらのAPIエンドポイントを使用することで、AngularJS、iOS、Androidなどのクライアントアプリは、ユーザー管理のためにREST APIを介してDjangoバックエンドサイトと独立して通信できます。
現在、このプロジェクトには2つのフォークがあります。
- Django-rest-authは元のプロジェクトですが、現在更新されていません。
- Dj-rest-authは、このプロジェクトの新しいフォークです。
drf-social-oauth2
Drf-social-oauth2は、Facebook、Google、Twitter、Orcidなどの主要なソーシャルoauth2ベンダーで認証するのに役立つフレームワークです。簡単な設定でJWT方式でトークンを生成します。
drfpasswordless
drfpasswordlessは、Django REST FrameworkのTokenAuthenticationスキームに(Medium、Square Cashに触発された)パスワードレスサポートを追加します。ユーザーは、メールアドレスや電話番号などの連絡先に送信されたトークンを使用してログインおよびサインアップします。
django-rest-authemail
django-rest-authemailは、ユーザーのサインアップと認証のためのRESTful APIインターフェースを提供します。ユーザー名ではなく、メールアドレスが認証に使用されます。サインアップ、サインアップメールの検証、ログイン、ログアウト、パスワードリセット、パスワードリセットの検証、メールの変更、メールの変更の検証、パスワードの変更、ユーザーの詳細のためのAPIエンドポイントが用意されています。完全に機能するサンプルプロジェクトと詳細な手順が含まれています。
Django-Rest-Durin
Django-Rest-Durin は、Web/CLI/モバイルAPIクライアントの複数に対して、1つのインターフェースでトークン認証を行う単一のライブラリを構築するという考えに基づいて開発されました。ただし、APIを消費する各APIクライアントごとに異なるトークン設定を許可します。Django-Rest-Frameworkと連携するカスタムモデル、ビュー、パーミッションを介して、ユーザーごとに複数のトークンをサポートします。トークンの有効期限はAPIクライアントごとに異なり、Django管理インターフェースからカスタマイズ可能です。
詳細はドキュメントをご覧ください。