⬆️ ⬇️

Authentication based on JSON Web Token in Django and AngularJS: part two

In the first part, we looked at what was needed to create JSON Web Token: serializers and views.



Now we will create templates and work on services for authentication and data acquisition.



Bower, package manager for web applications



Before we go to the code, let's install all the necessary dependencies. For this we will use Bower, it is an ideal tool for managing dependencies of web applications.

')

It is assumed that you already have Node.js installed. To install bower just run the following command:

$ npm install -g bower 


Note: You may need administrator rights.

In order to change the default directory in which bower will install packages, in the root of your project, create a file called “.bowerrc” and add the following lines to it:

 { "directory": "static/bower_components" } 


We have specified the “static” directory so that these components are available in Django.



Run the command:

 $ bower init 


and configure the proposed options.

Now you can install dependencies by running the following command:

 $ bower install --save angular angular-route bootstrap jquery 




Server side template processing



When the necessary components are installed, you can go to the code.

Remember the templates / index.html file that we created in the first part. It's time to fill it.

Replace the contents of templates / index.html with the following:

Hidden text
 <!DOCTYPE html> <html ng-app="application"> <head> <title>django-angular-token-auth</title> <base href="/" /> <link rel="stylesheet" href="/static/bower_components/bootstrap/dist/css/bootstrap.css" /> </head> <body> <div class="container-fluid"> <div class="ng-view row"></div> </div> <script type="text/javascript" src="/static/bower_components/jquery/dist/jquery.js"></script> <script type="text/javascript" src="/static/bower_components/bootstrap/dist/js/bootstrap.js"></script> <script type="text/javascript" src="/static/bower_components/angular/angular.js"></script> <script type="text/javascript" src="/static/bower_components/angular-route/angular-route.js"></script> <script type="text/javascript" src="/static/javascripts/app.js"></script> <script type="text/javascript" src="/static/javascripts/app.config.js"></script> <script type="text/javascript" src="/static/javascripts/app.routes.js"></script> </body> </html> 




Thanks to the use of bootstrap styles, the page will look pretty pretty.

Also for AngularJS we included the <base /> tag inside the <head /> tag. It is required because we will use HTML 5 routing.



Getting started with AngularJS



Let's go ahead and create the file /static/javascripts/app.js and add to it:

 angular.module('application', [ 'application.config', 'application.routes' ]); angular.module('application.config', []); angular.module('application.routes', ['ngRoute']); 


This is where AngularJS modules are created, which we will use for configuration and routing.

Create a file /static/javascripts/app.config.js. And fill it with the following content:

 angular.module('application.config') .config(function ($httpProvider, $locationProvider) { $locationProvider.html5Mode(true).hashPrefix('!'); }); 


Here we want to enable HTML 5 routing. It eliminates the use of the # symbol in a URL that should be used for other purposes. This also explains why we included <base /> in <head />.

Note: We will use $ httpProvider later, so make sure that it is written in dependencies.

Create the file /static/javascripts/app.routes.js and add the following to it:

 angular.module('application.routes') .config(function ($routeProvider) { $routeProvider.when('/', { controller: 'IndexController', templateUrl: '/static/templates/static/index.html' }); }); 




Creating an Auth Service



It's time to start implementing the Auth service, which will be engaged in the registration, entry and exit of users.

Start by creating the file /static/javascripts/auth/auth.module.js.

In this file we will configure the modules that we will use for authentication:

 angular.module('application.auth', [ 'application.auth.controllers', 'application.auth.interceptors', 'application.auth.services' ]); angular.module('application.auth.controllers', []); angular.module('application.auth.interceptors', []); angular.module('application.auth.services', []); 


You might have noticed that the author is a big fan of the modular structure when using AngularJS.

We simply create various modules for controllers, interceptors, services and add them to the application.auth module.

Remember to add auth.module.js to index.html

 <script type="text/javascript" src="/static/javascripts/auth/auth.module.js"></script> 


After adding auth.module.js, open /static/javascripts/app.js and enable the application.auth module depending on our application.

Now let's create the Auth service. Create the file /static/javascripts/auth/services/auth.service.js and include the following:

Hidden text
 angular.module('application.auth.services'). service('Auth', function ($http, $location, $q, $window) { var Auth = { getToken: function () { return $window.localStorage.getItem('token'); }, setToken: function (token) { $window.localStorage.setItem('token', token); }, deleteToken: function () { $window.localStorage.removeItem('token'); } }; return Auth; }); 




We have created some methods that we will use with the Auth service. Here we use only one dependency $ window. The rest will be used in other methods: login (), logout (), and register (). Let's create them.

Add the login () function to the Auth service, like this:

Hidden text
 login: function (username, password) { var deferred = $q.defer(); $http.post('/api/v1/auth/login/', { username: username, password: password }).success(function (response, status, headers, config) { if (response.token) { Auth.setToken(response.token); } deferred.resolve(response, status, headers, config); }).error(function (response, status, headers, config) { deferred.reject(response, status, headers, config); }); return deferred.promise; }, 




Here we make a simple AJAX request to one of our APIs, which we did in the first part. If there is a token in the answer, then it is saved for subsequent API calls.

User exit is more simple. Due to the use of JWT-based authentication, we only need to erase the token from the repository. Add the logout () function to the Auth service:

 logout: function () { Auth.deleteToken(); $window.location = '/'; }, 


The last method to add to the Auth service is register (), it is very similar to login (). In it, we make a request to our API, to create a user, and then by the second call go to the system on behalf of this user.

Add the register () method to the Auth service:

Hidden text
 register: function (user) { var deferred = $q.defer(); $http.post('/api/v1/auth/register/', { user: user }).success(function (response, status, headers, config) { Auth.login(user.username, user.password). then(function (response, status, headers, config) { $window.location = '/'; }); deferred.resolve(response, status, headers, config); }).error(function (response, status, headers, config) { deferred.reject(response, status, headers, config); }); return deferred.promise; } 




The Auth service is ready to include it in index.html:

 <script type="text/javascript" src="/static/javascripts/auth/services/auth.service.js"></script> 


Now we have the opportunity to register, log in and log out.

Now we need to add an interceptor in order for it to add our marker to each AJAX request.

Create /static/javascripts/auth/interceptors/auth.interceptor.js and paste the following into it:

Hidden text
 angular.module('application.auth.interceptors') .service('AuthInterceptor', function ($injector, $location) { var AuthInterceptor = { request: function (config) { var Auth = $injector.get('Auth'); var token = Auth.getToken(); if (token) { config.headers['Authorization'] = 'JWT ' + token; } return config; }, responseError: function (response) { if (response.status === 403) { $location.path('/login'); } return response; } }; return AuthInterceptor; }); 




Here we have done a few things, let's look at them.

This service has two main methods: request and responseError. The request method is called for each AJAX request, and the responseError method is called when an AJAX request returns an error.

It was previously mentioned that we add a token to each AJAX request. This is done in the request method. Every time the request method is called, we check if we have a token (Auth.getToken () method) and if so, we add it to the request header. If there is no token, a simple query is executed.

Note: You may notice that the $ injector service is used here. It is necessary, because sometimes due to interceptors, cyclic dependencies arise, which in turn cause errors. Using $ injector is the best way to get an Auth service, bypassing this problem.

In responseError, we check the response for 403 errors. This error is thrown by djangorestframework-jwt when there is no token or when it has expired. In any case, we must redirect the user to the login page. This does not require updating the page.

Now that we’re done with the interceptor, we need to do two things: add it to index.html and app.config.js.

Open index.html and add the following line:

 <script type="text/javascript" src="/static/javascripts/auth/interceptors/auth.interceptor.js"></script> 


Now open app.config.js and where we enabled HTML 5 mode add the following line:

 $httpProvider.interceptors.push('AuthInterceptor'); 


This will add the AuthInterceptor to the list of interceptors used by the $ http service. Each interceptor is invoked with every request, so there should not be too many of them.



Service to get users



The last thing we do in the second part is a service for getting users.

This service is very simple. In fact, it is only needed to test the performance of our application.

Create the file /static/javascripts/auth/services.users.service.js and add the following to it:

 angular.module('application.auth.services') .service('Users', function ($http) { var Users = { all: function () { return $http.get('/api/v1/users/'); } }; return Users; }); 


Now add it to Index.html under the Auth service:

 <script type="text/javascript" src="/static/javascripts/auth/services/users.service.js"></script> 




Completing the second part



In the second part, we looked at the services and interceptors that are needed to build an authentication system. Affected mechanism Django, which handles static files. It remains to define several controllers and templates.

In the third part we will talk about controllers and templates. This will be another long article.

If you are interested in the code, you can check out my repository on Github.



From translator
  • The author greatly overdid modularity, there is practically nothing in the application, and the files and modules are already ...
  • The third part of the translation does not see the point, because the topic of using JWT is disclosed.
  • I also consider it unnecessary to put js into a separate template and then connect it. In the original, the author uses templates / javascripts.html for this.
  • And also, in the original, the author used the global window object (window.angular.module ('application.auth.services')) although the repository already contains a rather beautiful code.


Source: https://habr.com/ru/post/243763/



All Articles