Whenever we send a request to the server, it should be Authorize and Authenticate, Now what is the main difference between them?
Authorization:
The authorization will check the following points for logged-in user
Verifies is access allowed through policies and rules
Determine what the user can or cannot access
Usually this part check after the successful completion of Authentication
Authentication:
The first step is authentication itself, in which it will check whether is the user is valid or not via token ID. Token ID is the unique key/ identification to say I'm the valid customer/owner of an application.
we have many authentication types such as Basic Authentication, Session Authentication, Custome Authentication (We can create our Authentication), and JSON Web Tokens(JWT).
In this tutorial, we are going to see how to implement Authentication in DRF (Django REST Framework ) using JWT. We first see a normal method by login using the username, password, and then we create JWT token with mobile number login also.
JWT(JSON Web Tokens):
JSON Web Token (JWT) is a compact claims representation format intended for space-constrained environments such as HTTP Authorization headers and URI query parameters. JWTs encode claims to be transmitted as a JSON object that is used as the payload of a JSON Web Signature (JWS) structure or as the.
Plaintext of a JSON Web Encryption (JWE) structure, enabling the claims to be digitally signed or integrity protected with a Message Authentication Code (MAC) and/or encrypted. JWTs are always represented using the JWS Compact Serialization or the JWE Compact Serialization.
Working Flow Of JWT.
In the example, the user signs into the server using the authentication server’s login system (e.g. username and password, Facebook login, Google login, etc). The authentication server creates the JWT and sends it to the user. When the user makes API calls to the application, the user passes the JWT along with the API call.
In this setup, the application server would be configured to verify that incoming JWT is created by the authentication server (the verification process will be explained in more detail later). So, when the user makes API calls with the attached JWT, the application can use the JWT to verify that the API call is coming from an authenticated user.
Now, the JWT itself, and how it’s constructed and verified, will be examined in more depth with the below topics
Point of the refresh token
The JWT is just an authorization token that should be included in all requests:
curl http://127.0.0.1:8000/ -H 'Authorization:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.
eyJ1c2VyX2lkIjo1LCJ1c2VybmFtZSI6ImFqaXQiLCJleHAiOjE1NzEyOTU0NDAsIm9yaWdfaWF0IjoxNTYyNjU1NDQwfQ.
SlGhifEmjOJoExRUe4Sfk0GzyykT9tOB3b9VTwKcQxI'
The JWT is acquired by exchanging a username + password for an access token and a refresh token.
The access token is usually short-lived (expires in 5 min or so, can be customized though).
The refresh token lives a little bit longer (expires in 24 hours, also customizable). It is comparable to an authentication session. After it expires, you need a full login with username + password again.
Why is the Token Structure?
It’s a security feature and also it’s because the JWT holds a little bit more information. If you look closely the example I gave above, you will see the token is composed of three parts separated by a dot(‘.’)
xxxxx.yyyyy.zzzzz
This is three distinctive parts that compose a JWT:
header.payload.signature
see like below
Header:eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9,
Payload:eyJ1c2VyX2lkIjo1LCJ1c2VybmFtZSI6ImFqaXQiLCJleHAiOjE1NzEyOTU0NDAsIm9yaWdfaWF0IjoxNTYyNjU1NDQwfQ,
Signture:SlGhifEmjOJoExRUe4Sfk0GzyykT9tOB3b9VTwKcQxI
This information is encoded using Base64. If we decode, we will see something like this:
Header :(ALGORITHM & TOKEN TYPE)
The header indicates what type of web token we are using and which algorithm we opt as below
{
"typ": "JWT",
"alg": "HS256"
}
Payload:(DATA)
Payload indicates the data of the user, you can see below which contains the user_id and some of the user details.
{
"user_id": 5,
"username": "ajit",
"exp": 1571295440,
"orig_iat": 1562655440
}
Signature:
The signature is issued by the JWT backend, using the header base64 + payload base64 + SECRET_KEY
. Upon each request, this signature is verified.
If any information in the header or the payload was changed by the client it will invalidate the signature. The only way of checking and validating the signature is by using your application’s SECRET_KEY
. Among other things, that’s why you should always keep your "SECRET_KEY"
secret!
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
your-256-bit-secret
)
Installation and Setup:
For this tutorial, we are going to use the djangorestframework_simplejwt
library, recommended by the DRF developers.
pip install djangorestframework-jwt
Paste line in installed App;
'rest_framework',
'rest_framework.authtoken',
settings.py
JWT_AUTH = {
'JWT_EXPIRATION_DELTA': datetime.timedelta(days=100),
'JWT_REFRESH_EXPIRATION_DELTA': datetime.timedelta(days=100),
'JWT_ALLOW_REFRESH': True,
'JWT_AUTH_HEADER_PREFIX': 'Token',
'JWT_DECODE_HANDLER':
'rest_framework_jwt.utils.jwt_decode_handler',
'JWT_SECRET_KEY': settings.SECRET_KEY,
}
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly'] }
Refresh Token:
Whatever we got the token that called access token is usually short-lived (expires in 5 min or so, can be customized though).
The refresh token lives a little bit longer (expires in 24 hours, also customizable). It is comparable to an authentication session. After it expires, you need a full login with username + password again.
Point Of Refresh Token:
At first glance, the refresh token may look pointless, but in fact, it is necessary to make sure the user still has the correct permissions. If your access token has a long expire time, it may take longer to update the information associated with the token. That’s because the authentication check is done by cryptographic means, instead of querying the database and verifying the data. So some information is sort of cached.
There is also a security aspect, in the sense that the refresh token only travels in the POST data. And the access token is sent via HTTP header, which may be logged along the way. So this also gives a short window, should your access token be compromised.
In Django, We gave the access token time in settings.py
'JWT_EXPIRATION_DELTA': datetime.timedelta(days=100),
'JWT_REFRESH_EXPIRATION_DELTA': datetime.timedelta(days=100),
'JWT_ALLOW_REFRESH': True,
With this, we can customize the time delta fro access token and fresh token.
If you fetch any problem while installing follow below command
pip install djangorestframework
python3 -m pip install --upgrade pip
pip install djangorestframework-jwt
Example:
Urls.py
from django.contrib import admin
from rest_framework_jwt.views import obtain_jwt_token, refresh_jwt_token, verify_jwt_token
from jwt_username_app.views import userview
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^auth-jwt/', obtain_jwt_token),
url(r'api/v1/emp', userview.as_view()),
url(r'^auth-jwt-refresh/', refresh_jwt_token),
url(r'^auth-jwt-verify/', verify_jwt_token),
]
Views.py
from django.shortcuts import render
from rest_framework.authentication import TokenAuthentication
from rest_framework.generics import ListCreateAPIView
from jwt_username_app.models import emp
from jwt_username_app.serializers import empSerializer
class userview(ListCreateAPIView):
queryset = emp.objects.all()
serializer_class = empSerializer
authentication_classes = [TokenAuthentication]
Results :
So first it asked for user credentials, It will generate Token if you provide the correct credentials as below image
Now let's see How was the behavior when we don't have the token and what if we have a token
The Lates picture talks about when we don't pass the token to the server, its saying Authentication not provided, Let's see what if I provide the correct token
That's All about the JWT via Username and password,
Models.py
from django.db import models
class userdetails(models.Model):
phone=models.BigIntegerField(max_length=10)
username=models.CharField(max_length=30)
gender=models.CharField(max_length=6)
state=models.CharField(max_length=20)
obeject=models.Manager()
def __str__(self):
return self.username
Serializers.py
from django.contrib.auth import get_user_model
from rest_framework import serializers
from rest_framework_jwt.settings import api_settings
from .models import userdetails
class jwtserializer(serializers.ModelSerializer):
UserModel = get_user_model()
token = serializers.SerializerMethodField()
def get_token(self, obj):
jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
payload = jwt_payload_handler(obj)
token = jwt_encode_handler(payload)
return token
class Meta:
model = userdetails
fields = '__all__'
Views.py
from django.contrib.auth import get_user_model
from django.shortcuts import render
from rest_framework import generics
from rest_framework import permissions
from jwtapp.models import userdetails
from jwtapp.serializer import jwtserializer
class UserView(generics.ListCreateAPIView):
model=get_user_model()
permission_classes = [
permissions.AllowAny
]
queryset = userdetails.obeject.all()
serializer_class = jwtserializer
'''To Display All Data present in userdetails'''
class Userdetails(generics.RetrieveUpdateDestroyAPIView):
queryset = userdetails.obeject.all()
serializer_class = jwtserializer
'''To Display The data according the PK value from userdetails data'''
class Detailstodo(generics.RetrieveUpdateDestroyAPIView):
model = get_user_model()
permission_classes = [
permissions.AllowAny
]
queryset = userdetails.obeject.all()
serializer_class = jwtserializer
URLs:
This is from project URLs, make one more URLs file at the application level so we can include the main URL from project/URLs to project/application/URLs as below
project/URLs
from django.contrib import admin
from django.urls import include
from django.urls import path
urlpatterns = [
path('admin/', admin.site.urls),
path('',include('jwtapp.urls')),
]
project/application/URLs
from django.urls import path
from jwtapp.views import UserView,Userdetails,Detailstodo
urlpatterns = [
path('',UserView.as_view()),
path('<int:pk>',Userdetails.as_view())
]
Result:
For more Code see the code section with the same title or you can visit my GitHub link