Token Authentication in Django
Introduction
The general concept behind a token-based authentication system is simple. Allow users to enter their username and password to obtain a token that allows them to fetch a specific resource — without using their username and password. Once their token has been obtained, the user can offer the token — which offers access to a specific resource for a while — to the remote site.
Several third-party packages are available for authentication in Django. We chose JSON Web Token Authentication of djangorestframework-simplejwt, because it’s a relatively new standard and simple to implement. Each JWT contains specific information that any party can interpret with that token. An advantage of using JWTs is scalability as the backend does not need to do a database lookup for every API call. The drawback is that revoking a single token on demand (before it expires) can be difficult if methods like blacklisting are not used (which impacts the solution's scalability).
Workflow
- User login API provides the token pair, which is stored in the frontend.
- Pass the access token in every network request.
- Before the access token expires, fetch the tokens using the refresh API. e.g. If the token expiry duration is 30 minutes, update the tokens at the 29th minute.
- Update the tokens in the secure cookie.
- In the event of logout, clear the tokens from cookies.
How to store the tokens?
Store the token pair in secure cookies. Since storage involves Cookies, it is vulnerable to CSRF attacks. But it is easier to prevent using CSRF tokens. That means, with every POST request, send a CSRF token and the API server must also validate the same.
How to keep a user logged in after the browser is reopened?
- The user closes the browser and comes back after, say, 10 days.
- The user sends a request to the server, and the server will return 401 Unauthorized responses because the Access token has expired
- The app will send a request to obtain a new Access token using the Refresh token
- If the Refresh token is valid, the server will return a new Access token
- If the Refresh token is expired, the server will return a 401 response. That means the user needs to log in again.
Implementation (Backend)
Install the SimpleJWT package
pip install djangorestframework-simplejwt
In settings.py, add rest_framework_simplejwt.authentication. JWTAuthentication to the list of authentication classes, and rest_framework_simplejwt and Rest_framework_simplejwt.token_blacklist to the list of installed apps in settings.py
REST_FRAMEWORK = {
‘DEFAULT_AUTHENTICATION_CLASSES’: (
...
‘rest_framework_simplejwt.authentication.JWTAuthentication’,...
)
}INSTALLED_APPS = [
...
‘rest_framework_simplejwt’,
‘Rest_framework_simplejwt.token_blacklist’,...
]
Configure the JWT settings in the settings.py file. Set REFRESH_TOKEN_LIFETIME and BLACKLIST_AFTER_ROTATION as True. This causes refresh tokens submitted to the TokenRefreshView to be added to the blacklist if the blacklist app is in use.
SIMPLE_JWT = {
‘ACCESS_TOKEN_LIFETIME’: timedelta(minutes=30),
‘REFRESH_TOKEN_LIFETIME’: timedelta(days=30),
‘ROTATE_REFRESH_TOKENS’: True,
‘BLACKLIST_AFTER_ROTATION’: True,
...
}
Configure urls.py and run the migrate command. There are seven migrations to be applied. Import RefreshToken API in the views.py, and generate and return the token pair (access and refresh) from the login APIs.
urls.py
from rest_framework_simplejwt.views import TokenRefreshViewurlpatterns = [
...
path(‘api/token/refresh/’, TokenRefreshView.as_view(), name=’token_refresh’),
...
]
views.py
from rest_framework_simplejwt.tokens import RefreshTokenClass LoginAPI():
refresh = str(RefreshToken.for_user(user_instance))
access = str(RefreshToken.for_user(user_instance).access_token)