How to secure Ionic application and Django API with JWT authentication ?

Want to learn how to secure a Ionic application using JWT authentication on backend with Django ? This tutorial is for you ! We will see how to protect our API on backend but also how to automatically refresh expired tokens in our Ionic application to avoid logged out users.

The source code repository is here

Add JWT authentication to Django API

JWT (Json Web Token) is an authentication system based on secured token. To obtain a token, the user needs to provide his credentials. Once the user is logged in, each subsequent request will include the JWT, allowing the user to access routes, services, and resources that are permitted with that token.

Django Simple JWT is a simple package to manage JWT authentication with Django.

Django DJOSER is another package with more features and which include Django Simple JWT. So we will use this package.

I’m assuming that you already know Django and have an existing project. To add Django DJOSER and required packages to our Django project, we add them to our requirements.txt file

djoser
djangorestframework_simplejwt
djangorestframework==3.11.0

Then we can reference them in our INSTALLED_APP dictionnary

INSTALLED_APPS = [
      ... 
    'rest_framework',
    'djoser',
   ....
]

and declare in our urls.py of our main django project the DJOSER endpoints

urlpatterns = [
    ...
    path('auth/', include('djoser.urls')),
    path('auth/', include('djoser.urls.jwt')),
    ...
]+ static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)+ static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

Ok now with all that in place, we can use Django DJOSER to get our JWT token. Available endpoints can be found in the documentation but we will focus on the:

  • /jwt/create/ (JSON Web Token Authentication)
    • Use to authenticate and obtain a JWT TOKEN
  • /jwt/refresh/ (JSON Web Token Authentication)
    • Use to refresh an expired JWT Token
  • /jwt/verify/ (JSON Web Token Authentication)
    • Use to verify if a JWT Token is still valid

Usually JWT Tokens expires very quickly (5 minutes). It’s possible to modify this expiration value in the settings

SIMPLE_JWT = {
    'ACCESS_TOKEN_LIFETIME': timedelta(minutes=5),

But it’s not recommended to increase this value a lot. This is why a /verify endpoint is provided: to check if the token has expired or not. When a token expires, we can obtain a new one using the refresh token that was provided at the same time than the JWT token. This is the purpose of the /create endpoint. Let’s have a closer look. Imagine we have an existing user and we want to authenticate to obtain a JWT token. We can call the endpoint with a POST requests containing our user credentials:

curl -X POST \
  https://app-4d83da5d-f938-46bf-8359-4d0c2fb1f9e7.cleverapps.io/auth/jwt/create/ \
  -H 'Content-Type: application/json' \
  -d '{"email": "myuser@gmail.com", "password": "mypassword"}'

If credentials are correct, we will get a response with our tokens:

{
    "refresh": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoicmVmcmVzaCIsImV4cCI6MTYwNDY1MzQyOSwianRpIjoiMGQyMTFjOWYwMzc5NDUwN2JiZTgzNTIxYjE3OTllNjMiLCJ1c2VyX2lkIjoiNWQ3MWY5N2QtNGU1Mi00NzQxLWEzZmItMzk5N2EyYTkyMWJjIn0.hXGIr_baGgMhjpFDlP0Aryf5LTWG6riHVX6B93RqYC0",
    "access": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNjA0NjUzMTI5LCJqdGkiOiIxNmFjYjUxOWZjY2Y0ZWFmOWI2MjMwOWQzNmNjMmI3YyIsInVzZXJfaWQiOiI1ZDcxZjk3ZC00ZTUyLTQ3NDEtYTNmYi0zOTk3YTJhOTIxYmMifQ.Y50lo6KKlEY8v2uW5myKqaITWs_1pizIMlyx26kaHgU"
}

The response contains the access token (our JWT token) and the refresh token to use when the access token expires.

If credentials are not correct, the endpoint will return an HTTP 401 response (unauthorised).

If we wait 5 minutes and check the access token validity we should obtain an HTTP 401 unauthorised response meaning the token is not valid anymore.

curl -X POST \
  https://app-4d83da5d-f938-46bf-8359-4d0c2fb1f9e7.cleverapps.io/auth/jwt/verify/ \
  -H 'Content-Type: application/json' \
  -d '{"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNjA0NjUzMTI5LCJqdGkiOiIxNmFjYjUxOWZjY2Y0ZWFmOWI2MjMwOWQzNmNjMmI3YyIsInVzZXJfaWQiOiI1ZDcxZjk3ZC00ZTUyLTQ3NDEtYTNmYi0zOTk3YTJhOTIxYmMifQ.Y50lo6KKlEY8v2uW5myKqaITWs_1pizIMlyx26kaHgU"}'

At this point we can use the /refresh endpoint and send the refresh token to get a new access token:

curl -X POST \
  https://app-4d83da5d-f938-46bf-8359-4d0c2fb1f9e7.cleverapps.io/auth/jwt/refresh/ \
  -H 'Content-Type: application/json' \
  -d '{"refresh": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoicmVmcmVzaCIsImV4cCI6MTYwNDY1MzQyOSwianRpIjoiMGQyMTFjOWYwMzc5NDUwN2JiZTgzNTIxYjE3OTllNjMiLCJ1c2VyX2lkIjoiNWQ3MWY5N2QtNGU1Mi00NzQxLWEzZmItMzk5N2EyYTkyMWJjIn0.hXGIr_baGgMhjpFDlP0Aryf5LTWG6riHVX6B93RqYC0"}'
{
    "access": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNjA0NjUzMTc1LCJqdGkiOiI1Y2VmODc2OTFlODc0MzhkYjQ3OTI3ZmRkN2M3M2Y4NCIsInVzZXJfaWQiOiI1ZDcxZjk3ZC00ZTUyLTQ3NDEtYTNmYi0zOTk3YTJhOTIxYmMifQ.KHIDvKPwqCU0nPAHxbHv9ElISG1nvjH4VH7EBdiV_Ig", 
}

Ok the response contains the new access token to use. The refresh token has also a lifetime that can be configured

SIMPLE_JWT = {
    'ACCESS_TOKEN_LIFETIME': timedelta(minutes=5),
    'REFRESH_TOKEN_LIFETIME': timedelta(days=1),

The value is a little bit more bigger (one day) but what happens after one day ? At this moment, the /refresh endpoint will return an HTTP 401 unauthorised response meaning there is no other solution to logout the user to ask him it’s credentials and then to begin a new cycle again by calling /create endpoint.

Ok but what the f…k here ? Don’t forget we want to add authentication in our Ionic application. We are not going to logout users every day. The user experience will be terrible… One solution could be to increase the refresh token validity such as one year:

SIMPLE_JWT = {
    'ACCESS_TOKEN_LIFETIME': timedelta(minutes=5),
    'REFRESH_TOKEN_LIFETIME': timedelta(days=365),

Meaning that every year our mobile application users would have to login again. Why not.

Another solution is to modify the configuration to obtain an access and refresh token on /refresh endpoint. with the ROTATE_REFRESH_TOKENS option

SIMPLE_JWT = {
    'ACCESS_TOKEN_LIFETIME': timedelta(minutes=5),
    'REFRESH_TOKEN_LIFETIME': timedelta(days=180),
    'ROTATE_REFRESH_TOKENS': True,
    'BLACKLIST_AFTER_ROTATION': True,

With that in place we can always obtain a new access . We just need to save/replace both values (access and refresh tokens) and use it. Of course if a user don’t connect to the mobile application during 6 months (our refresh token lifetime), the refresh token will be expired and then we will have no other solution to logout user to ask him again it’s credentials.

Secure our Ionic application with JWT

Now that we have seen how to manage and obtain a JWT token from our django backend, let’s learn how to implement and use it within our ionic application.

I will assume that you already have a Ionic project ready to use with login and register pages. If not you can generate a new one with

ionic start secureionic blank --capacitor --project-id=secureionic --package-id=com.idevotion.secureionic

To shorten this tutorial, i will focus on main features but you will find the full source code on my github repository as usual.

First step, we need to create a user and then we need to obtain a JWT token for the created user. Django Djoser exposes the /users/ endpoint with a POST query to let us create users. Of course this endpoint can be called without authentication because at this stage we don’t have one.

 let params = {
        "email": email,
        "password": password,
        "first_name": firstName,
        "last_name": lastName
      }
 this.createAccount(params)

The email, password, firstName and lastName are parameters coming from our register form page.

Then we can send a request to create the account and once done to obtain the JWT credentials by login the user:

createAccount(params) {
    if (this.apiService.networkConnected) {
      // Account creation
      this.apiService.registerUser(params).subscribe((resultat) => {
        let status = resultat["status"];
        if (status=="OK") {
          let data = resultat["data"]
          this.userManager.setUser(data)
            // create a token
              // Check email
              let paramsToLogin = {
                "email": params.email,
                "password": params.password,
              }
              this.authentificationService.login(paramsToLogin).then((resultat) => {
                this.apiService.stopLoading();
                if (resultat) {
                   //Authentified
                   this.router.navigateByUrl("/home")
                }
                else {
                  this.apiService.showError(this.translateService.instant("A technical error occured. Account creation impossible"))
                }
              })
        }
        else {
          this.apiService.stopLoading();
          let error = resultat["error"]
          if (error.status==400){
            this.apiService.showError(this.translateService.instant("An account already exists with this email. Please login !"))
          }
          else{
            this.apiService.showError(this.translateService.instant("A technical error occured. Account creation impossible"))
          }
         
        }
      })
    }
    else {
      this.apiService.showNoNetwork()
    }
  }

The registerUser method is doing a POST query to the endpoint /auth/users and then return the results

 registerUser(params){
        const options = {
            headers: new HttpHeaders({
                'Content-Type': 'application/json'
            })
        };
        this.getUserUrl  =  this.virtualHostName + "auth/users/"
        return new Observable(observer=>{
            this.http.post(this.getUserUrl, params, options).subscribe(
                (val) => {
                    observer.next({"status":"OK","data":val})
                    observer.complete()
                 },
                 response => {
                     console.log("POST call in error", response);
                     observer.next({"status":"KO","error":response})
                     observer.complete()
                 },
               
                () => {
                   
                });
        })
    }

Ok now that the user has been created, we need to obtain our JWT tokens (access and refresh) and store them. This is the role of the AuthenticationService that we will see soon. But first let’s implement our api login service

  login(params){
        const options = {
            headers: new HttpHeaders({
                'Content-Type': 'application/json'
            })
        };
         this.getLoginUrl =  this.virtualHostName + "auth/jwt/create/"
        return new Observable(observer=>{
            this.http.post(this.getLoginUrl, params, options).subscribe(
                (val) => {
                    observer.next(val)
                    observer.complete()
                 },
                 response => {
                     console.log("POST call in error", response);
                     observer.next()
                     observer.complete()
                 },
                () => {
                   
                });
        })
        
    }

We call our /auth/jwt/create endpoint with our user credentials and if successfull we will get our access and refresh tokens that we need to save. We can use Capacitor storage to do that.

The goal of the Authentication service is to manage these tokens, to save them, to logout our users or to refresh the tokens. Let’s implement it:

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { map, tap, switchMap } from 'rxjs/operators';
import { BehaviorSubject, from, Observable, Subject } from 'rxjs';
 
import { Plugins } from '@capacitor/core';
import { ApiserviceService } from './apiservice.service';
const { Storage } = Plugins;
 
const TOKEN_KEY = 'access';
const REFRESH_TOKEN_KEY = 'refresh';

 
@Injectable({
  providedIn: 'root'
})
export class AuthenticationService {
  // Init with null to filter out the first value in a guard!
  isAuthenticated: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(null);
  token = '';
  refresh=''
  constructor(private apiService:ApiserviceService) {
    this.loadToken();
  }
 
  loadToken() {
    this.apiService.getLocalData(TOKEN_KEY).then((value:string)=>{
      if (value){
        console.log('set token: ', value);
        this.token = value["access"];
        this.apiService.getLocalData(REFRESH_TOKEN_KEY).then((value:string)=>{
          this.refresh=value["refresh"]
          this.isAuthenticated.next(true);
        })       
      }
      else{
        console.log("=PAS DE TOKEN PAS LOGGE")
        this.isAuthenticated.next(false);
      }
    })
   
  }

  login(params){
    return new Promise(async resolve => {
    this.apiService.login(params).subscribe((resultat)=>{
       if (resultat) {
          let accessToken = resultat["access"]
          let refreshToken = resultat["refresh"]
          this.apiService.setLocalData("access",{"access":accessToken})
          this.apiService.setLocalData("refresh",{"refresh":refreshToken})
          this.token = accessToken;
          this.refresh=refreshToken
          this.isAuthenticated.next(true);
         
         resolve(true)
        }
      else{
        resolve(false)
      }
      })
    })
  }
 
  logout(): Promise<void> {
    this.isAuthenticated.next(false);
    return new Promise(async resolve => {
       this.apiService.removeLocalData(TOKEN_KEY).then(()=>{
          this.apiService.removeLocalData(REFRESH_TOKEN_KEY).then(()=>{
            resolve()
          }
        )
       });
    })
  }

  refreshToken(){
    return this.apiService.refreshToken(this.refresh)
  }
}

On our login page, the process is simple, we just need to verify that the credentials are correct and then save them. So we can use our same Authentication service.

async login(){
   
      let email = this.credentials.value["email"]
      let password = this.credentials.value["password"]
      
      if (this.apiService.networkConnected){
        this.apiService.showLoading();
            // Check email
            let params = {
              "email":email,
              "password":password,
            }
            this.authentificationService.login(params).then((resultat)=>{
              this.apiService.stopLoading();
              if (resultat) {
                this.router.navigateByUrl('/home', { replaceUrl: true });
              }
              else{
                this.displayWrongLogin()
            }
          })
            
      }
      else {
        this.apiService.showNoNetwork();
      }
    }

Ok now we have seen how to register or login a user and then go to our home page. But we still have lot of work to do.

First of all, when launching the app, we need to verify if the user is already login and them redirect him directly to the home page or if not redirect him to the login or register pages (your choice). To do so, we will modify our routing file and introduce some guards.

const routes: Routes = [
  {
    path: 'home',
    loadChildren: () => import('./pages/home/home.module').then( m => m.HomePageModule),
    canLoad: [AuthGuard] 
  },
  {
    path: 'login',
    loadChildren: () => import('./pages/login/login.module').then( m => m.LoginPageModule),
    canLoad: [AutoLoginGuard]
  },
  {
    path: 'register',
    loadChildren: () => import('./pages/register/register.module').then( m => m.RegisterPageModule),
    canLoad: [AutoLoginGuard]
  },
  {
    path: '',
    redirectTo: 'register',
    pathMatch: 'full'
  }
];

The AuthGuard will check that our user is authentified and can access the page. The AutoLoginGuard will check if the user is already identified and if so redirects him to the home page automatically (no need to login or register again). You can learn more here thanks to Simon incredible tutorials.

import { UserManagerProviderService } from './../services/user-manager-provider.service';
import { AuthenticationService } from './../services/authentication.service';
import { Injectable, NgZone } from '@angular/core';
import { CanLoad, Router } from '@angular/router';
import { Observable } from 'rxjs';
import { filter, map, take } from 'rxjs/operators';
 
@Injectable({
  providedIn: 'root'
})
export class AuthGuard implements CanLoad {
  constructor(private authService: AuthenticationService,private userManager:UserManagerProviderService,private router: Router) { }
 
  canLoad(): Observable<boolean> {    
    return this.authService.isAuthenticated.pipe(
      filter(val => val !== null), // Filter out initial Behaviour subject value
      take(1), // Otherwise the Observable doesn't complete!
      map(isAuthenticated => {
        if (isAuthenticated) {  
          this.userManager.getUser().then((user)=>{
            
          })        
          return true;
        } else {          
          this.router.navigateByUrl('/login')
          return false;
        }
      })
    );
  }
}

If we have a token the user is authentified and if not we redirect to the login page. Same behaviour for AutoLoginGuard

import { UserManagerProviderService } from './../services/user-manager-provider.service';
import { Injectable } from '@angular/core';
import { CanLoad, Router } from '@angular/router';
import { Observable } from 'rxjs';
import { AuthenticationService } from '../services/authentication.service';
import { filter, map, take } from 'rxjs/operators';
 
@Injectable({
  providedIn: 'root'
})
export class AutoLoginGuard implements CanLoad {
  constructor(private authService: AuthenticationService, private router: Router,private userManager:UserManagerProviderService) { }
 
  canLoad(): Observable<boolean> {    
    return this.authService.isAuthenticated.pipe(
      filter(val => val !== null), // Filter out initial Behaviour subject value
      take(1), // Otherwise the Observable doesn't complete!
      map(isAuthenticated => {
        console.log('Found previous token ?, automatic login '+isAuthenticated);
        if (isAuthenticated) {
          //Load user 
          this.userManager.getUser().then((user)=>{
            console.log("Log user",user)
            
             // Directly open inside area       
           this.router.navigateByUrl('/home', { replaceUrl: true });
          })
         
        } else {          
          // Simply allow access to the login
          return true;
        }
      })
    );
  }
}

Ok we have all logic in place except for the most important part !

To see the problem let’s display the information of the logged in user:

import { Component } from '@angular/core';
import { ApiserviceService } from 'src/app/services/apiservice.service';
import { UserManagerProviderService } from 'src/app/services/user-manager-provider.service';

@Component({
  selector: 'app-home',
  templateUrl: 'home.page.html',
  styleUrls: ['home.page.scss'],
})
export class HomePage {
  infoAboutMe : any;
  title = "Authenticated"
  constructor(public userManager:UserManagerProviderService,
    public apiService:ApiserviceService) {
   

    //Get info 
    this.apiService.getUserMe().subscribe((data)=>{
      if (data["status"]=="OK"){
        this.infoAboutMe = data;
      }
      else{
        this.title ="Unauthenticated"
        this.infoAboutMe=null;
      }
    
    })  
  }
}

and our home.page.html

<ion-header [translucent]="true">
  <ion-toolbar>
    <ion-title>
      eShop
    </ion-title>
  </ion-toolbar>
</ion-header>

<ion-content [fullscreen]="true">
  <ion-header collapse="condense">
    <ion-toolbar>
      <ion-title size="large">{{title}}</ion-title>
    </ion-toolbar>
  </ion-header>
  <div *ngIf="infoAboutMe">
    Info about me:<br><br>
    <ion-label>Email: {{infoAboutMe.data.email}}</ion-label><br>
    <ion-label>ID: {{infoAboutMe.data.id}}</ion-label>
  </div>
</ion-content>

Just after creating or login you should see

Ionic JWT authenticated page

Well nice. Now wait 5 minutes, and try to access again

Ionic unauthenticated

Remember that tokens expire ? Here we are… The backend returns an HTTP 401 response code. Obviously we need to manage this behaviour.

How to refresh expired JWT tokens automatically with Ionic ?

We could modify every request code of our existing api (if any of course) to check the status return code and then make appropriate actions : call the refresh endpoint , obtain a new token and then make the request again. But it’s a lot to manage !

A better way to manage this is to use Angular Http Interceptors to do the job. I found this useful article and adapt it a little bit.

import { UserManagerProviderService } from './user-manager-provider.service';
import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest, HttpErrorResponse } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Router } from '@angular/router';

import { BehaviorSubject, Observable, throwError } from 'rxjs';
import { catchError, filter, switchMap, take } from 'rxjs/operators';
import { AuthenticationService } from './authentication.service';

@Injectable()
export class TokenInterceptor implements HttpInterceptor {
  private refreshingInProgress: boolean;
  private accessTokenSubject: BehaviorSubject<string> = new BehaviorSubject<string>(null);

  constructor(private authService: AuthenticationService,
    private userManager: UserManagerProviderService,
    private router: Router) { }

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    const accessToken = this.authService.token;
    // console.log("=== Intercept token ")
    // console.log(accessToken)
    return next.handle(this.addAuthorizationHeader(req, accessToken)).pipe(
      catchError(err => {
        // in case of 401 http error
        console.log("==== ERROR 401 url:" + req.url)
        if (req.url.indexOf("/refresh") > 0) {
          //REFRESHTOKEN KO 
          console.log("==== 401 LOGOUT CAR ERROR")
          // otherwise logout and redirect to login page
          return this.logoutAndRedirect(err);
        }
        if (err instanceof HttpErrorResponse && err.status === 401) {
          // get refresh tokens
          const refreshToken = this.authService.refresh;

          // if there are tokens then send refresh token request
          if (refreshToken && accessToken) {
            return this.refreshToken(req, next);
          }

          // otherwise logout and redirect to login page
          return this.logoutAndRedirect(err);
        }

        // in case of 403 http error (refresh token failed)
        if (err instanceof HttpErrorResponse && err.status === 403) {
          // logout and redirect to login page
          return this.logoutAndRedirect(err);
        }
        // if error has status neither 401 nor 403 then just return this error
        return throwError(err);
      })
    );
  }

  private addAuthorizationHeader(request: HttpRequest<any>, token: string): HttpRequest<any> {
    if (token) {
      let bearer = Bearer ${token};
      //console.log("=== Clone request et add header avec token ",bearer)

      return request.clone({ setHeaders: { Authorization: bearer } });
    }

    return request;
  }

  private logoutAndRedirect(err): Observable<HttpEvent<any>> {
    console.log("==== LOGOUT CAUSE ERROR")
    this.userManager.logoutUser().then(() => {

    })
    this.router.navigateByUrl('/login');
    return throwError(err);
  }

  private refreshToken(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    if (!this.refreshingInProgress) {
      this.refreshingInProgress = true;
      this.accessTokenSubject.next(null);

      return this.authService.refreshToken().pipe(
        switchMap((res) => {
          this.refreshingInProgress = false;
          this.authService.token = res["access"]
          this.authService.refresh = res["refresh"]
          console.log("==== REFRESH SET VALUES")

          this.accessTokenSubject.next(res["access"]);
          // repeat failed request with new token
          return next.handle(this.addAuthorizationHeader(request, res["access"]));
        }), catchError((err, caught) => {
          console.log("=========== ERROR REFRESH TOKEN")
          // return throwError(err);
          return this.logoutAndRedirect(err);
        })
      );
    } else {
      // wait while getting new token
      return this.accessTokenSubject.pipe(
        filter(token => token !== null),
        take(1),
        switchMap(token => {
          // repeat failed request with new token
          return next.handle(this.addAuthorizationHeader(request, token));
        }));
    }
  }
}

The code is quite long but to sum up on HTTP 401 status code detected, it will ask for new tokens, save them and retry the request automatically. It is explained in the article mentionned before.

I just adapt it for one use case : when the refresh token has expired too. In that case, we need to manage it otherwise the code will run in an infinite loop : detect HTTP 401, call refresh endpoint (which is now also returning an HTTP 401 because of expired refresh token), detect HTTp 401, call refresh, …

This is the purpose of these lines

 // in case of 401 http error
        console.log("==== ERROR 401 url:"+req.url)
        if (req.url.indexOf("/refresh")>0){
          //REFRESHTOKEN KO 
          console.log("==== 401 LOGOUT CAR ERROR")
          // otherwise logout and redirect to login page
          return this.logoutAndRedirect(err);
        }

If we get an HTTP 401 and the requested url was /refresh endpoint , we stop the process and log out our user.

To use our token.interceptor service, we just need to declare it in our app.module.ts file

@NgModule({
  declarations: [AppComponent],
  entryComponents: [],
  imports: [
    BrowserModule,
    TranslateModule.forRoot({
      loader: {
        provide: TranslateLoader,
        useFactory: (LanguageLoader),
        deps: [HttpClient]
      }
    }),
    IonicModule.forRoot({
      animated:false
    }),
      NativeHttpModule,
      AppRoutingModule,
    HttpClientModule,
 ],
  providers: [
    { provide: RouteReuseStrategy, useClass: IonicRouteStrategy },
    TranslateConfigService,
    ScreenOrientation,
    HTTP,
     {provide: HttpBackend, useClass: NativeHttpFallback, deps: [Platform, NativeHttpBackend, HttpXhrBackend]},
    { provide: HTTP_INTERCEPTORS, useClass: TokenInterceptor, multi: true } ,
  ],
  bootstrap: [AppComponent]
})

Ok now eveything should be ok. You could log in, see your information, wait 5 minutes for HTTP 401 and you will see in your browser console log, that a new token will be obtain and you will be still logged in. Then wait more than 10 minutes, both access and refresh token will be expired, and you should be log out from the application (if you set the REFRESH Token expiration to ten minutes in django backend of course).

'REFRESH_TOKEN_LIFETIME': timedelta(minutes=10),

And voila. We learned how to implement and manage JWT tokens with a Ionic application and a Django backend.

Christophe Surbier