The most common authentication method is cookie cookie authentication. A more modern authentication method is based on using JSON Web Token (literally: web marker in JSON format) and it is quickly gaining popularity. In this article we will focus on it.
What is JSON Web Token?
The markers are actually pretty simple. Imagine a marker as a key in the form of a card with which you enter the office every day. When you were hired by a company, you confirmed your identity (login) by providing a set of credentials, such as a driver's license or a social security number. In exchange for this data, you were given a key (marker) that allows you to be in the building. If you decide to quit your job or you are fired (logged out), the company may simply disable the key. Of course, this key may remain in your pocket, but it will not give you anything good.
JSON Web Token is a simple token in a special format. While the format is not standardized, but many already use one of its implementations (
JWT ).
')
JWT (pronounced jot) consists of three parts:
Let's look at each of them before we delve into the implementation.
Headline
By default, the header contains only the type of the token and the algorithm used for encryption.
Marker type is stored in the “typ” key. The “typ” key is ignored in JWT (this is not important for this article, but you can
read to find out why). If the key "typ" is present, its value must be JWT to indicate that this object is JSON Web Token.
The second key "alg" defines the algorithm used to encrypt the token. By default, it should be set to HS256. There are a number of alternative algorithms that are used in various implementations. A complete list with a breakdown by library can be found at JWT.io.
The header is encoded in base64.
Payload
The payload stores any information that needs to be checked. Each key in the payload is known as a “statement”. For example, consider a social network in which you can join the community only by invitation. When we want to invite someone to the community, we send him an invitation letter. Here it is important to check that the email address belongs to the person who accepts the invitation, so we will include this address in the payload, for this we will save it in the “email” key.
{ "email": "example@jamesbrewer.io" }
The keys in the payload can be arbitrary. However, there are some reserved ones that you need to know:
- iat (Issued At). This key represents the time when the marker was issued and can be used to determine the age of the JWT. iat key must be a timestamp in unix format.
- exp (Expiration Time). This key indicates when the token expires. Standard
JWT requires that, in all its implementations, expired markers be rejected. In
some implementations , additional keys may be added to account for time out-of-sync. The exp key must be a timestamp in unix format.
It is important to understand that the payload is not transmitted in encrypted form (although markers can be nested and then it is possible to transfer encrypted data). Therefore, it can not store any secret information. For example, passwords, social security numbers, etc.
Like the header, the payload is base64 encoded.
Signature
When we have a header and payload, we can calculate the signature.
Base64-encoded header and payload are taken, they are combined into a string through a dot. Then this string and the secret key are fed to the input of the encryption algorithm specified in the header (the “alg” key). The key can be any string. Longer lines will be most preferable, since it will take more time to pick up.
Jwt
Now that we have a header, payload, and signature, we can build JWT. The final JWT is as follows:
<encoded header>.<encoded payload>.<signature>
Note: never save a marker in your database. Because a valid token, equivalent to a password, to store a token is the same as storing a password in the clear. Therefore, always use hashing before saving.
Implementing Authentication with JSON Web Token
Let's go ahead and create our project.
Go to the project directory and run the commands:
$ django-admin.py startproject django_angular_token_auth $ cd django_angular_token_auth/ $ mkdir static templates
You need to make sure that the STATICFILES_DIRS and TEMPLATE_DIRS settings are present in django_angular_token_auth / settings.py and they should look like this:
STATICFILES_DIRS = ( os.path.join(BASE_DIR, 'static'), ) TEMPLATE_DIRS = ( os.path.join(BASE_DIR, 'templates'), )
Django REST Framework
Now you need to install the Django REST Framework. The Django REST Framework is a large open source project for those who want to create one-page applications and APIs for them.
We will not discuss in detail how the Django REST Framework works, so if you are not familiar with it, check the documentation.
To install the Django REST Framework, run the following command:
$ pip install djangorestframework
The Django REST Framework must be added to your installed applications (INSTALLED_APPS in django_angular_token_auth / settings.py :)
INSTALLED_APPS = ( ..., 'rest_framework', )
You also need to add the following parameters in django_angular_token_auth / settings.py:
REST_FRAMEWORK = { 'DEFAULT_PERMISSION_CLASSES': ( 'rest_framework.permissions.IsAuthenticated', ), 'DEFAULT_AUTHENTICATION_CLASSES': ( 'rest_framework.authentication.SessionAuthentication', 'rest_framework.authentication.BasicAuthentication', ), }
We will not use SessionAuthentication or BasicAuthentication, but they are available in the Django REST Framework and offer a working API out of the box.
django-rest-framework-jwt
The last thing to do is to install django-rest-framework-jwt. This package provides JWT support for the Django REST Framework and uses the PyJWT implementation as JSON Web Token. To install django-rest-framework-jwt, run the following command:
$ pip install djangorestframework-jwt
You will need to add the following parameters to django_angular_token_auth / settings.py:
import datetime JWT_AUTH = { 'JWT_EXPIRATION_DELTA': datetime.timedelta(days=14) }
This parameter indicates the lifespan for our markers and in our case it will be 14 days. You can change it based on your own preferences.
You should also add the updated REST_FRAMEWORK settings:
REST_FRAMEWORK = { 'DEFAULT_PERMISSION_CLASSES': ( 'rest_framework.permissions.IsAuthenticated', ), 'DEFAULT_AUTHENTICATION_CLASSES': ( 'rest_framework.authentication.SessionAuthentication', 'rest_framework.authentication.BasicAuthentication', 'rest_framework_jwt.authentication.JSONWebTokenAuthentication', ) }
Please note that we have added:
'rest_framework_jwt.authentication.JSONWebTokenAuthentication', in 'DEFAULT_AUTHENTICATION_CLASSES'.
Creating a django application
Now let's create a Django application for storing views and serializers in it:
$ python manage.py startapp authentication
Add 'authentication' to our INSTALLED_APPS (in django_angular_token_auth / settings.py like so):
INSTALLED_APPS = ( ..., 'rest_framework', 'authentication', )
As a rule, after creating the application, you need to perform migrations to create models in the database. But we will not create our own models, so there is no need to worry about it.
Serializers
To test the functionality of the API, you need to send some data to AJAX requests. The easiest way to do this is to send back user data in our system.
The first thing to do is create a user. Run the following command and follow the instructions on the screen:
$ python manage.py createsuperuser
After creating the user, it is necessary that Django send his data to the Angular application. Obviously, we cannot send Django objects to our application in JavaScript, so we need a serializer to convert objects to JSON and back. It can be easily created using the Django REST Framework.
Create a file called serializers.py and add the following code to it:
from django.contrib.auth.models import User from rest_framework import serializers class UserSerializer(serializers.ModelSerializer): class Meta: model = User fields = (id, username, email)
This code tells the Django REST Framework that we want to serialize the User model and we want to include only the following fields: id, username and email.
Representation
With the serializers figured out, let's now move on to the views.
For our purposes, we need only one view, which will return a list of user objects.
The Django REST Framework offers a variety of views that perform different functions, for example, a list of objects of one type. This functionality is offered by the ListAPIView class.
Add the following to authentication / views.py:
from django.contrib.auth.models import User from rest_framework import generics from authentication.serializers import UserSerializer class UserListAPIView(generics.ListAPIView): queryset = User.objects.all() serializer_class = UserSerializer
Templates
Create a simple template that will represent our application.
Create a file called templates / index.html and add the following code to it:
<!DOCTYPE html> <html> <head> <title>django-angular-token-auth</title> </head> <body> Hello, World! </body> </html>
In the following, we will add some angular code to this file.
URLs
Now you need to configure the URLs for our project.
Open django_angular_token_auth / urls.py and bring it to the following view:
from django.conf.urls import include, patterns, url from django.views.generic import TemplateView from authentication.views import UserListAPIView urlpatterns = patterns('', url(r'api/v1/auth/login/', 'rest_framework_jwt.views.obtain_jwt_token'), url(r'api/v1/users/', UserListAPIView.as_view()), url(r'^.*$', TemplateView.as_view(template_name='index.html')), )
Let's take a look at what's going on here.
The first thing you notice, in the second argument for the first URL pattern is the string. This line points to the django-rest-framework-jwt view that generates the token. We will not consider how it works, but you can see the django-rest-framework-jwt code in the Github repository: django-rest-framework-jwt / rest_framework_jwt / views.py.
We also did not use the TemplateView view before. It is similar to the ListAPIView class in the Django REST Framework, which offers an easy way to handle templates.
You probably noticed that the regular expression used in the last URL pattern will match any URL.
For correct processing of addresses, it is important that this pattern is the last. Because, Django matches the address with the templates, in the order in which they are defined and the first match coincides with the check. Since the regular expression used matches any address, we thereby give a chance to all other addresses to be processed. Anything that does not comply with Django should be processed with Angular. Also, the API / api / v1 / namespace ensures that the Django and Angular templates will not conflict.
In the second part
On this we dwell. In the second part, we will build the client application and mainly focus on the registration and login session. We will write a service in order to be able to correctly log in and out of the system.