Best Practices for JWT Authentication in Angular Apps | Syncfusion Blogs
Detailed Blog page Skeleton loader
Best practices of JWT Authentication in Angular App

TL;DR: Shield your Angular apps with JWT authentication! Unveils secure storage practices, route protection with guards, and interceptor-based token injection for seamless API calls – empowering robust user authentication. Unlock best practices for JWT in Angular and fortify your app’s security.

Data without security is like a treasure box without a lock. In this tech-driven world, hackers are everywhere. To transmit your data’s securely, you need highly reliable standards. Considering this, JSON Web Tokens (JWT) provide the best security and authentication.

Angular is a widely used JavaScript platform. In this blog, we are going to see how to implement authenticated routings in Angular, manage tokens, and pass tokens to servers in client side. For logins, you can use any kind of authentication like OpenID, OAuth, or create your own login application logic.

This blog covers the following topics:

  • What is JWT?
  • What are the possibilities of storing JWT authentication tokens in Angular apps?
  • Where can tokens be stored securely in Angular apps?
  • How to create a service to access JWT tokens and storage?
  • How to protect Angular routing with stored JWT tokens?
  • How to pass a JWT token for every API request?

What is JWT?

JSON Web Tokens (JWT) are an internet standard for creating JSON-based access tokens that assert a number of claims. We can generate JWT with custom claims that may contain user information and permission-based values, information like whether the user is an admin can also be stored in JWT. Best practice is to not store any confidential information in JWT. Please refer to jwt.io for detailed information about JWT.

Note: An Angular project can be created using Angular CLI commands. Please refer to the Angular CLI command documentation for more information on how to create a project.

Syncfusion Angular component suite is the only suite you will ever need to develop an Angular application faster.

What are the ways to store authentication tokens in Angular apps?

There are three possible ways of storing access tokens in an Angular app. They are:

In-memory storage

In this technique, a token is stored in the application page itself. The only drawback of this option is the data is not persistent; it is lost on page refresh and must be retrieved again.

HTML5 web storage (local storage and session storage)

In this technique, data is stored in browser storage. Data stored this way is accessible by all tabs in the same browser within the same domain. The two types of web storage are:

  • Session storage: Data stored in session expires once the browser is closed.
  • Local storage: Data stored in local storage doesn’t expire until we clear the data from the browser. The server cannot directly access data stored in local storage.

In this technique, a token is stored in cookies. Data stored this way can be accessed by the server. The browser automatically appends a cookie in requests sent to the server. Since the browser automatically adds a cookie on each request, tokens are vulnerable to CSRF/XSRF attacks.

Find the right property to fit your requirement by exploring the complete documentation for Syncfusion’s Angular components.

Where can tokens be stored securely in Angular apps?

We can store data in different ways, but we should take proper measures to protect tokens against CSRF and XSRF vulnerabilities. We should store tokens in a place that is not accessible by attackers. Two possible ways of storing tokens to reduce risk of CSRF/XSRF attack are:

  • Local storage: One of the best ways to store data. Local storage is not vulnerable to CSRF attacks.
  • HttpOnly cookie: HttpOnly cookies are not accessible on the client side, i.e. the client cannot read data stored in these cookies.

For additional security, we must consider a few more things on the server side, such as:

  • Token expiration validation.
  • Content security policy.
  • Refresh token mechanism.
  • Anti-forgery token mechanism.

How to create a service to access JWT tokens and storage

Now that we have learned where to store tokens, let’s see how to create an Angular service to decode stored tokens and retrieve values from them in an Angular app.

JWT token service

This service is used for decoding JWT tokens and retrieving values from JWT. Let’s set one up.

First, create an Angular service file for JWT decode and inject it in the application module.

We can use the jwt-decode package for decoding JWT tokens. In this service, functions for getting user claim values like username and email ID have been included. A function has also been added for checking token expiration in this service.

import { Injectable } from '@angular/core';
import * as jwt_decode from 'jwt-decode';

@Injectable()
export class JWTTokenService {

    jwtToken: string;
    decodedToken: { [key: string]: string };

    constructor() {
    }

    setToken(token: string) {
      if (token) {
        this.jwtToken = token;
      }
    }

    decodeToken() {
      if (this.jwtToken) {
      this.decodedToken = jwt_decode(this.jwtToken);
      }
    }

    getDecodeToken() {
      return jwt_decode(this.jwtToken);
    }

    getUser() {
      this.decodeToken();
      return this.decodedToken ? this.decodedToken.displayname : null;
    }

    getEmailId() {
      this.decodeToken();
      return this.decodedToken ? this.decodedToken.email : null;
    }

    getExpiryTime() {
      this.decodeToken();
      return this.decodedToken ? this.decodedToken.exp : null;
    }

    isTokenExpired(): boolean {
      const expiryTime: number = this.getExpiryTime();
      if (expiryTime) {
        return ((1000 * expiryTime) - (new Date()).getTime()) < 5000;
      } else {
        return false;
      }
    }
}

To store the token, you can use either a cookie or local storage service. Code examples for implementing the services are provided below. Depending on where you are storing tokens, cookie or local storage service can be implemented.

Be amazed exploring what kind of application you can develop using Syncfusion Angular components.

Cookie service

  1. Create an Angular service file AppCookieService and inject it in the application module.
  2. Include the get, set, and remove functions to perform cookie operations.
  3. Include the functionality to automatically add the cookie on initialization of the service.
import { Inject, Injectable } from '@angular/core';

@Injectable({
    providedIn: 'root',
  })
export class AppCookieService {
    private cookieStore = {};

    constructor() {
        this.parseCookies(document.cookie);
    }

    public parseCookies(cookies = document.cookie) {
        this.cookieStore = {};
        if (!!cookies === false) { return; }
        const cookiesArr = cookies.split(';');
        for (const cookie of cookiesArr) {
            const cookieArr = cookie.split('=');
            this.cookieStore[cookieArr[0].trim()] = cookieArr[1];
        }
    }

    get(key: string) {
        this.parseCookies();
        return !!this.cookieStore[key] ? this.cookieStore[key] : null;
    }

    remove(key: string) {
      document.cookie = `${key} = ; expires=Thu, 1 jan 1990 12:00:00 UTC; path=/`;
    }

    set(key: string, value: string) {
        document.cookie = key + '=' + (value || '');
    }
}

Local storage service

  1. Create an Angular service file LocalStorageService and inject it in the application module.
  2. In this basic local storage, add get, set, and remove functions for performing the operations.
import { Injectable } from '@angular/core';

@Injectable()
export class LocalStorageService {

    set(key: string, value: string) {
        localStorage.setItem(key, value);
    }

    get(key: string) {
        return localStorage.getItem(key);
    }

    remove(key: string) {
        localStorage.removeItem(key);
    }
}

Syncfusion Angular components are:

  • Lightweight
  • Modular
  • High-performing

How to protect Angular routing with stored JWT tokens

So far, we have learned about JWT tokens, where to store them, and how to access and pass them to the server using Angular services. Now, we are going to see how to use these tokens to protect Angular routing.

Before diving into this topic, we have to understand the routing guard canActivate.

{
    path: 'app',
    component: AppComponent,
    canActivate: [AuthorizeGuard]
}

The above routing configuration is protected by the AuthorizeGuard service.
AuthorizeGuard supports ‘Boolean | Promise | Observable’. If the AuthorizeGuard returns false/Promise reject/Observable error, then routing won’t happen. We need to resolve the error for the routing configuration to be successful.

AuthorizeGuard service

The guard service in the sample code below checks whether the user is logged in. If the user is logged in, then it checks whether the token is expired. If both cases are satisfied, then the routing process will be successful. Otherwise, it will request the login service to get a new token or redirect the user to a custom login or access denied page.

import { Injectable } from '@angular/core';
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { Observable } from 'rxjs'; 
import { JWTTokenService } from './jwt-token.service';
import { LocalStorageService } from './storage.service';
import { LoginService } from './login.service';

@Injectable({
  providedIn: 'root'
})
export class AuthorizeGuard implements CanActivate {
  constructor(private loginService: LoginService,
              private authStorageService: LocalStorageService,
              private jwtService: JWTTokenService) {
  }
  canActivate(
    next: ActivatedRouteSnapshot,
    state: RouterStateSnapshot): Observable | Promise | boolean {
      if (this.jwtService.getUser()) {
          if (this.jwtService.isTokenExpired()) {
            // Should Redirect Sig-In Page
          } else {
            return true;
          }
      } else {
        return new Promise((resolve) => {
          this.loginService.signIncallBack().then((e) => {
             resolve(true);
          }).catch((e) => {
            // Should Redirect Sign-In Page
          });
        });
      }
  }
}

Note: In the above code, logInService is used for login resolution purposes only. You can create your own service to resolve logins.

How to pass a JWT token for every API request

In order to have authenticated calls with APIs, we have to send the authorization token in every HTTP request being sent to the server so that the server can verify authentication of the request. It’s difficult to include code to add a token in every place an API call is made—it causes code duplication. To remedy this, Angular has an interceptor service for handling all HTTP requests and responses in a single place. By using this interceptor, we can handle including an authentication token in every API call globally.

How the interceptor works
How the interceptor works

HttpInterceptor controls all the HTTP requests and responses. Every request and response comes and goes through this service, so we can easily append custom headers containing authorization tokens in a single place.

Override HTTP_INTERCEPTORS as shown in the following app module.

{ provide: HTTP_INTERCEPTORS, useClass: UniversalAppInterceptor, multi: true },

HttpInterceptor service

The interceptor intercepts all Angular HTTP requests and adds authorization headers with the token.

Process of using authorization headers with a token
Process of using authorization headers with a token
import { Injectable, Inject, Optional } from ‘@angular/core’;
import { HttpInterceptor, HttpHandler, HttpRequest } from ‘@angular/common/http’;
import { AuthService } from ‘./auth.service’;
@Injectable()
export class UniversalAppInterceptor implements HttpInterceptor {

  constructor( private authService: AuthService) { }

  intercept(req: HttpRequest, next: HttpHandler) {
    const token = this.authService.getJWTToken();
    req = req.clone({
      url:  req.url,
      setHeaders: {
        Authorization: `Bearer ${token}`
      }
    });
    return next.handle(req);
  }
}

Note: In the above code, AuthService is used for JWT token retrieval purposes only.

See the possibilities for yourself with live demos of Syncfusion Angular components.

Conclusion

In this blog, I have explained the best practices for authentication in Angular apps using JWT tokens and the management of JWT tokens on the client side.

For Angular developers, Syncfusion offers over 65 high-performance, lightweight, modular, and responsive Angular components to speed up development.

For current customers, the latest version of our controls is available for download from the License and Downloads page. If you are not yet a customer, you can try our 30-day free trial to check out all our Angular components have to offer. You can also explore samples in our GitHub repository.

Relate blogs

Be the first to get updates

Pradeep Kumar

Meet the Author

Pradeep Kumar

Pradeep Kumar works as a product manager at Syncfusion, where he specializes in the development of web components with cutting-edge technologies. He is interested in the latest web technologies and provides solutions for great products.

Comments (26)

Really informative and on the point, Keep it up, brother.

@ Sumit Sharma  

Hi Sumit Sharma,

Thanks for your feedback.

Thanks,
Pradeep Kumar B

you don’t mention XSS at all. seems like a big vulnerability that you didn’t address with your approach

@ Kumar Kumardeep  

Hi Kumar KumarDeep,

XSS also one of the big vulnerability, Mainly Angular prevents XSS in-built, many ways are there to prevent if byPass angular security, one of the way is to use ‘CSP(Content Security Policy)’ if cookie-based storage used means AntiForgeryToken must use, both were we mentioned in this blog.

for more information please check.

https://angular.io/guide/security#xss

https://docs.microsoft.com/en-us/aspnet/web-api/overview/security/preventing-cross-site-request-forgery-csrf-attacks

Thanks,
Pradeep Kumar B

why do you create a cookie service, i mean, you say that browser cant access to them, javascript can? and that is not dangerous?
sorry for typos.

@ zigreth  

Hi Zigreth,

Browser cookie also able to read from the client-side and it’s used to store the data, if you use HttpOnly cookie, it won’t access, from the client-side. if you implement below functionalities in server-side means it will be more secure.

1. Token expiration validation.
2. Content security policy.
3. Refresh token mechanism.
4. Anti-forgery token mechanism.

Thanks,
Pradeep Kumar B

@ Pradeep Kumar  

hi, but if i add HttpOnly to the cookie, it will be more secure but how i will verify if my user is still logged or other things like that ?

@ zigreth  

Hi Zigreth,

If you want to user login validation at client-side means token should be accessible, you can store local-storage or some other storages, but server-side you have to increase more securities (like Anti-forgery, CSP, Refresh Token) to avoid attacks.

Regards,
Pradeep Kumar B

@ Pradeep Kumar  

where is the method to verify if the signature of the token is valid? anyone can create a token and they will all be valid lol

Great article. One query I had , as we know for every request we would be adding the token using interceptor but would it not have impact on the performance as for every request a token would be added in the headers.If it will have performance issues then what steps can we take.

@ Frontenddev  

Hi @FrontEndDev,

Angular Interceptor is just one of the processes in HttpClientModule. if you use HTTP_INTERCEPTORS, it will call before the request initialization and after completion . so mostly it won’t lead any performance issue. might be raise when you use multiple like, If you provide interceptors A, then B, then C, requests will flow in A->B->C and responses will flow out C->B->A.

for my suggestion avoid using multiple interceptors in App until needed.

Regards,
Pradeep Kumar B

Makes sense, thanks!

thenmetho isTokenExpire has error

@ Guillermo Cotilla  

Hi GUILLERMO COTILLA,

If the Token has been expired we must get a new token(refresh token) before making any request.

Regards,
Pradeep Kumar B

@ Pradeep Kumar  

`const expiryTime: number = this.getExpiryTime();`
will have this error: Type ‘string’ is not assignable to type ‘number’.ts(2322)
It is not allow to compare string with number

@ tom  

Hi Tom,

If you used date string format, convert to milliseconds. In token mostly it’s number type only. based on implementation it may vary.

Regards,
Pradeep Kumar B

@ Pradeep Kumar  

I cast the string to a number to get around this.

getExpiryTime() {
this.decodeToken();
return +this.decodedToken ? +this.decodedToken.exp : null;
}

Hi! Thanks a lot sir! But as a beginner I will suggest you to add a video tutorial, actually I need à login with handling token in angular! I have already set up the the backend nestJS with login a registration systems and everything works properly! But I don’t master well angular! So actually I will need a little help, so that you post a video tutorial! I know how to get data from nestJS to angular by crud ways but to have token works with every request, I know not!

@ Jyhn Dtaylor  

Hi Jyth Dtaylor,

Thank you for your suggestion on posting video tutorials. We will consider doing it.

Regards,
Pradeep Kumar B

Hi, Pradeep,
great thanks for this content… Can you show how the code for login in the AuthService will look like , as well as the getJWTToken() function.
Regards,
Hiba

Hi Hiba,

Thanks for your feedback. The getJWTToken function is exclusively used to retrieve tokens from the AuthService. It may differ depending on how the backend is implemented and how the token is stored.

Regards,
Pradeep Kumar B

Could you tell me what the signIncallBack() method should look like?

@ Deniel  

Hi @Daniel,

The signIncallBack method in the above code is a callback function that will be called after login or a token refresh, and you must handle it on your own depending on how OAuth/OpenId is implemented.

Regards,
Pradeep Kumar B

Hello @Kumar,

Thanks for the very informative article on securing Angular apps.
We are already using similar approach and some Synfusion components in our application.

Our problem is that number of components do not consider existing interceptors at all.

For example we utilize RichTextBrowser and FileManager componens but both appear to go around existing interceptors when downloading image resources.

Even more, on number of forum posts Synfusion support underlined that for some reason this will never be supported. My guess is that this is since components are built as wrappers around native JS components.

Please advise?

Kind regards,
Muamer

Is still available with angular 18?

Pradeep Kumar Bose
Pradeep Kumar Bose
@ Yll Rukiqi  

Hi Yll,

It is also compatible with Angular 18.

Comments are closed.