Official original text link this series of articles github address reprint please indicate the source

The authentication

Authentication is the mechanism for associating incoming requests with a set of identifying credentials, such as the requesting user or the token signed by him or her. Permission and restriction policies can then use these credentials to determine whether the request should be allowed.

The REST Framework provides many authentication schemes out of the box, but also allows you to implement custom schemes.

Authentication is always run at the beginning of the view, before permission and limit checks are performed, and before any other code is allowed to continue.

The request.user property is usually set to an instance of the User class in the contrib.auth package.

The Request.auth attribute is used for other authentication information, for example, it can be used to represent a signed authentication token requested.


Note: Don’t forget that authentication itself does not (or does not allow) incoming requests, it just identifies the credentials of the request.


How do I determine authentication

An authentication scheme is always defined as a list of classes. The REST Framework will attempt to authenticate with each class in the list, and will set Request. user and Request.auth using the return value of the first successfully authenticated class.

If there is no class for authentication, will request the user set to django. Contrib. Auth. Models. AnonymousUser instance, and the request. The auth is set to None.

The values of request.user and request.auth for unauthenticated requests can be modified using the UNAUTHENTICATED_USER and UNAUTHENTICATED_TOKEN Settings.

Setting an Authentication Scheme

The default authentication scheme can be set globally using DEFAULT_AUTHENTICATION_CLASSES setting. For example.

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

You can also set up the authentication scheme on a per-view or per-view set basis using views based on the APIView class.

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': unicode(request.user),  # `django.contrib.auth.User` instance.
            'auth': unicode(request.auth),  # None
        }
        return Response(content)
Copy the code

Or, if you use the @API_VIEW decorator with function-based views.

@api_view(['GET'])
@authentication_classes((SessionAuthentication, BasicAuthentication))
@permission_classes((IsAuthenticated,))
def example_view(request, format=None):
    content = {
        'user': unicode(request.user),  # `django.contrib.auth.User` instance.
        'auth': unicode(request.auth),  # None
    }
    return Response(content)
Copy the code

Unauthorized and disallow responses

When an unauthenticated request is rejected, two different error codes may be appropriate.

  • [HTTP 401 Unauthorized][http401]
  • [HTTP 403 Permission Denied][http403]

The HTTP 401 response must always include the www-Authenticate header, which instructs the client how to Authenticate. The HTTP 403 response does not contain the www-Authenticate header.

Which response will be used depends on the authentication scheme. Although multiple authentication schemes may be in use, only one scheme can be used to determine the type of response. Use the first authentication class set on the view when determining the response type.

Note that when a request can be authenticated successfully, it may still be rejected because of Permission, in which case the 403 Permission Denied response will always be used regardless of the authentication scheme.

Apache mod_WSGI specific configuration

Note that if deployed to Apache using mod_WSGI, the authorization header is not passed to the WSGI application by default, because it assumes that authentication will be handled by Apache, not at the application level.

If you are deploying to Apache and using any non-session-based authentication, mod_WSGI needs to be explicitly configured to pass the required HEADERS to the application. This can be done by specifying the WSGIPassAuthorization directive in the appropriate context and setting it to ‘On’.

# this can go in either server config, virtual host, directory or .htaccess
WSGIPassAuthorization On
Copy the code

API reference

BasicAuthentication

The Authentication scheme uses HTTP Basic Authentication and is signed based on the user name and password. Basic Authentication is usually only used for testing.

If authenticated successfully, BasicAuthentication provides the following credentials.

  • request.userIs a DjangoUserStrength.
  • request.authNone.

An unauthenticated response being rejected will result in an HTTP 401 Unauthorized response and the corresponding WWW-Authenticate header. Such as:

WWW-Authenticate: Basic realm="api"
Copy the code

Note: if you use BasicAuthentication in a production environment, you must ensure that your API is accessible only through HTTPS. You should also ensure that your API clients will always re-request the username and password at login and never store these details in a persistent store.

TokenAuthentication

This authentication scheme uses a simple token-based HTTP authentication scheme. Token authentication works with client-server architectures, such as native desktop and mobile clients.

To use the TokenAuthentication scheme, you need to configure the authentication class to include TokenAuthentication and also include rest_framework.authToken in the INSTALLED_APPS setting:

INSTALLED_APPS = (
    ...
    'rest_framework.authtoken'
)
Copy the code

Note: Make sure you run manage.py Migrate after changing your Settings. The rest_framework. authToken application provides Django database migration.


You also need to create tokens for your users.

from rest_framework.authtoken.models import Token

token = Token.objects.create(user=...)
print token.key
Copy the code

For client authentication, the token key should be included in the Authorization HTTP header. The keyword should be prefixed with character string Token and separated by Spaces. Such as:

Authorization: Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b
Copy the code

Note: If you want to use different keywords in the header (such as Bearer), simply subclass TokenAuthentication and set the keyword class variable.

If authenticated successfully, TokenAuthentication will provide the following credentials.

  • request.userIs a DjangoUserInstance.
  • request.authIs arest_framework.authtoken.models.TokenInstance.

An unauthenticated response being rejected will result in an HTTP 401 Unauthorized response and the corresponding WWW-Authenticate header. Such as:

WWW-Authenticate: Token
Copy the code

The curl command line tool may be useful for testing the API for token authentication. Such as:

curl -X GET http://127.0. 01.:8000/api/example/ -H 'Authorization: Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b'
Copy the code

Note: If you use TokenAuthentication in production, you must ensure that your API is accessible only through HTTPS.


To generate the token

By using signals

If you want each user to have an automatically generated token, you simply capture the post_save signal for the user.

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)
Copy the code

Note that you need to make sure that you place this snippet in the installed models.py module or somewhere else that you will import when Django starts.

If you have already created some users, you can generate tokens for all existing users, for example:

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)
Copy the code
By exposing an API endpoint

When using TokenAuthentication, you might want to provide a mechanism for customers to get tokens for a given username and password. The REST Framework provides a built-in view to support this behavior. To use it, add the obTAIN_auth_token view to your URLconf:

from rest_framework.authtoken import views
urlpatterns += [
    url(r'^api-token-auth/', views.obtain_auth_token)
]
Copy the code

Note that the URL portion of the schema can be anything you want to use.

When you post valid username and password fields to the view using form data or JSON, the obTAIN_AUTH_token view returns a JSON response:

{ 'token' : '9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b' }
Copy the code

Note that the default obtain_auth_token view explicitly uses JSON requests and responses, rather than the default renderer and parser classes you set up.

By default, no permissions or restrictions apply to the obTAIN_auth_token view. If you want to apply throttling, you need to rewrite the view classes and include them using the throttLE_classes attribute.

If you need to customize the obTAIN_Auth_token view, you can do so by inheriting the ObtainAuthToken view class and using it in your URL conf.

For example, you might return additional user information that exceeds the token value:

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
        })
Copy the code

And urls. Py:

urlpatterns += [
    url(r'^api-token-auth/', CustomAuthToken.as_view())
]
Copy the code
Using the Django admin

You can also create tokens manually through the admin interface. If you have a large user base, we recommend that you patch the TokenAdmin class to customize it as needed, and more specifically declare the user field as raw_field.

your_app/admin.py:

from rest_framework.authtoken.admin import TokenAdmin

TokenAdmin.raw_id_fields = ('user'.)Copy the code

Use the Django manage.py command

Starting with version 3.6.4, you can generate user tokens using the following command:

./manage.py drf_create_token <username>
Copy the code

This command returns the API token for the given user and creates it if it does not exist:

Generated token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b for user user1
Copy the code

If you want to regenerate the token (for example, it has been leaked), you can pass an additional argument:

./manage.py drf_create_token -r <username>
Copy the code

SessionAuthentication

This authentication scheme uses Djangos default session backend for authentication. Session authentication applies to AJAX clients running in the same Session environment as your site.

If authenticated successfully, SessionAuthentication provides the following credentials.

  • request.userIs a DjangoUserInstance.
  • request.authNone.

An unauthenticated response rejected will result in an HTTP 403 Forbidden response.

If you use ajax-style apis in SessionAuthentication, make sure you include a valid CSRF token for any “insecure” HTTP method calls (such as PUT, PATCH, POST, or DELETE requests).

Warning: You should always use Django’s standard login view when creating a login page. This will ensure that your login view is properly secured.

CSRF authentication in the REST framework is slightly different from standard Django in that it requires support for both session-based and non-session-based authentication. This means that only authenticated requests require a CSRF token, and anonymous requests can be sent without a CSRF token. This behavior does not apply to login views where CSRF validation should always be applied.

RemoteUserAuthentication

This authentication scheme allows you to delegate authentication to your Web server, which sets the REMOTE_USER environment variable.

To use it, you must be in your AUTHENTICATION_BACKENDS Settings django. Contrib. Auth. Backends. RemoteUserBackend (or a subclass). By default, RemoteUserBackend creates User objects for non-existent User names. To change this and other behavior, refer to the Django documentation.

If authenticated successfully, RemoteUserAuthentication will provide the following credentials:

  • request.userIs a DjangoUserInstance.
  • request.authNone.

For information on configuring authentication methods, refer to your Web server’s documentation, for example:

  • Apache Authentication How-To
  • NGINX (Restricting Access)

Custom identity authentication

To implement a custom authentication scheme, inherit BaseAuthentication and rewrite the.authenticate(self, request) method. The method should return a binary of (User, auth) if authentication succeeds, or None otherwise.

In some cases, you might want to raise the AuthenticationFailed exception from the.authenticate() method instead of returning None.

The usual approach you should take is:

  • Returns if authentication is not attemptedNone. Any other authentication schemes in use will still be checked.
  • Raise if authentication is attempted and failsAuthenticationFailedThe exception. Whether or not any permission checks are performed, an error response is immediately returned and no other authentication schemes are checked.

You can also override the.authenticate_header(self, request) method. If implemented, it should return a string that will be used as the value of the WWW-Authenticate header in the HTTP 401 Unauthorized response.

If the.authenticate_header() method is not overridden, the authentication scheme returns an HTTP 403 Forbidden response when an unauthenticated request is denied access.


Note: When the request object’s.user or.auth attribute calls your custom authenticator, you might see AttributeError reraised as WrappedAttributeError. This is necessary to prevent the original exception from being suppressed by external attribute access. Instead of recognizing AttributeError as coming from your custom authenticator, Python assumes that the request object does not have a.user or.auth attribute. These errors should be fixed or otherwise handled by your validator.


Take a chestnut

The following example authenticates any incoming request based on the user name in the custom request header named “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('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)
Copy the code