Authentication And Authorization In Angular - Part Twelve

Not often, but in most of the applications, we use authentication and authorization. In this article, we will explore the following topics.

  • JSON Web Tokens (JWT)
  • Stateless Authentication
  • How to Protect Routes in Angular Apps?
  • Redirect the User to “Login” or “Access Denied” page.
  • Show/Hide Elements Depending Upon the Authentication Status of the User
  • How to Get Currently Logged In User?
  • How to Consume Protected API Endpoints?

If you’ve not seen my Angular series yet, you should start your journey from here.

Architecture

Before we get into the mechanics of implementing Authentication and Authorization, let’s have a quick look at high-level architecture. So, let’s start with Authentication.

In order to build authentication, on the client, we need to build the login page and on the server, we should build an API Endpoint to validate the user. When the user clicks on the login button, our Angular app calls this API Endpoint and passes the username and password. Now, on the server, we’re going to validate these credentials and if they are valid, we’re going to return what we called a JSON WEB TOKEN (JWT). This token is basically a JSjson object that includes certain attributes about the logged in user. And we use it to identify the user on the client and also on the server.

Authentication And Authorization In Angular 

Now we get these JSON WEB TOKEN objects on the Client and then we need to store it somewhere persistent, so it can exist across session restarts. So if the user closes your browser and then opens it again, the token still should be there and we use the localStorage for that. So almost all modern browsers have a simple storage per website and they also provide the API for storing key value pairs into this storage. So we’re going to use this native API to store our JSON Web Token inside the browsers’ local storage.

Authentication And Authorization In Angular 

Now on the client, we can use this token to identify the user. For example,

  • We can display their name on the navigation bar.
  • We can show/hide parts of the page.
  • We can prevent access to certain routes (if they don’t have the valid token).

This is how we work with these tokens on the client. Now let’s suppose, the user wants to get the list of orders from the server and this list is only available to an authenticated user. So, on the server, we have an API Endpoint (/api/orders) and in order to get the list of orders on the client, we should include the jwt in the request header. And then on the server, we should extract this token validated. And if it is valid it will return the list of orders otherwise it should return the unauthorized response. The response with the status code is 401.

Authentication And Authorization In Angular 

This is the big picture.

JSON Web Tokens (JWT)

Now let’s explore JWT in more detail. Look here we have a Debugger for working with JSON Web Tokens. Here, we also have a bunch of libraries to work with JSON web tokens. So as we already discussed we need to work with these tokens both on the client and on the server that’s why we have lots of libraries for different languages and platforms. So, on the front-end, we’ll use the library for Angular and for the backend we need a different library depending upon which platform or language you’re working in.

So, come to the Debugger tab in jwt.io where you see 2 tabs - Encoded & Decoded. So Encoded has a really long string of JSON web token.

Authentication And Authorization In Angular 

So, this is the token that the server has sent to the client upon successful authentication. Note the color coding here. This token has 3 parts. The header is indicated by red, the payload is indicated by purple, and the digital signature is indicated by blue. And we can see each of these parts on the decoded tab as well. One more thing - this token is not encrypted but encoded with BASE 64 algorithm. So, we don’t have any sensitive information here. If you paste this string into any Base 64 decoder, you can see the content.

Now, let’s come to the Decoded section.

Authentication And Authorization In Angular 

Here, we have the header which is basically a JSON object with 2 property algorithms, HS256 and type with JWT, this is the standard header. And we don’t have to be worry about it. Below that we have payload which is once again JSON object with a few properties, these properties are attributes about the user. So here we have sub for subject name and admin. This sub property identifies the subject of jwt which is the user's id and this object contains some content of the client. Now we can include a few other attributes here as well, a few other properties. And these are the properties we need to know in a lot of places in the application because we’re going to send this web token within the client and the server by including these properties in the token, we don’t have to go into the database to find let us say the name of the user everytime there is a request. So that’s why we have few basic properties of the user in the payload of the JSON web token. Now below that we have a signature, we use this signature to prevent a malicious user from modifying any of the header and payload properties. Because this signature is based on a secret that exists on the server.

Authentication And Authorization In Angular 

So unless your malicious user can compromise your server, it will not be able to generate the digital signature for a token.

Now let’s see how this digital signature is constructed. Basically we get the header encoded using base64 algorithm and concatenate it with payload encoded using base64. And then we use the secret to encrypt that long string using HMACSHA256 algorithm. So there are 2 interesting points here, first is this digital signature is based on the content of the JSON web token so if we change the payload the signature needs to be regenerated. In other words, a malicious user can’t come back here and simply modify any of the payload property for someone else JSON web token and if he modify any of the payload property he need to regenerate the digital signature. And the 2nd interesting point here that this signature is generated through the secret that exists only on the server. So unless the hacker knows this secret, he can’t generate this digital signature.

Let’s see it in action. Let’s change the value of any payload property.

 Authentication And Authorization In Angular 

Now look here the payload encoded string is also changed.

Authentication And Authorization In Angular 

So every time we modify the header or payload of the JSON web token, the signature is regenerated. Now we have the good understanding of high level architecture and JSON web token. Now let’s get started to implement authentication and authorization in our application.

Starter Code

So to speed things up, I’ve attached the starter code of Auth-Demo. So we’ll start authentication and authorization in this project. To configure this project to properly work, execute these commands one by one.

  • PS C:\Users\Ami Jan\auth-demo\auth-demo> npm install
  • PS C:\Users\Ami Jan\auth-demo\auth-demo> npm i angular2-jwt --save
  • PS C:\Users\Ami Jan\auth-demo\auth-demo> ng serve

Now when you run this application,

Authentication And Authorization In Angular 

This is what you get. If you go to Login, you’ll navigate to Login page but nothing gonna happen on sign in.

Authentication And Authorization In Angular 

So we’re getting this application and adding authentication and authorization into it. Before getting into the implementation details, let’s review few things of this project. So open app.module.ts. Look here in the imports array, we can see we have few routes.

imports: [  
  BrowserModule,  
  FormsModule,  
  HttpModule,  
  RouterModule.forRoot([  
    { path: '', component: HomeComponent },  
    { path: 'admin', component: AdminComponent },  
    { path: 'login', component: LoginComponent },  
    { path: 'no-access', component: NoAccessComponent }  
  ])  
]

Now look at the providers array,

providers: [  
  OrderService,  
  
  AuthService,  
  
  // For creating a mock back-end. You don't need these in a real app.   
  fakeBackendProvider,  
  MockBackend,  
  BaseRequestOptions  
],  

Here we have OrderService which is a very simple service getting the list of orders from the server. Next we have AuthService which we use to implement login and logout. Now let’s take a look of AuthService.

export class AuthService {  
  constructor(private http: Http) {  
  }  
  
  login(credentials) {   
   return this.http.post('/api/authenticate',   
      JSON.stringify(credentials));  
  }  
  
  logout() {   
  }  
  
  isLoggedIn() {   
    return false;  
  }  
}

So it is the very simple class that is dependent upon the http service as you can see in class constructor. We’re injecting Http as the dependency to AuthService. Here we have the login() method which is kind of half implemented, which we’ll finish in this complete article. Here we have other methods as well (logout(), isLoggedIn()) and it looks like they are not implemented as well. Now back in app.module.ts, after AuthService we have few providers that are purely implementing mock backend. So you don’t need to add them in the real world application

// For creating a mock back-end. You don't need these in a real app.  
fakeBackendProvider,  
MockBackend,  
BaseRequestOptions 

Now let’s see how we implemented this mock backend. So let’s go to fakeBackendProvider

import { Http, BaseRequestOptions, Response, ResponseOptions, RequestMethod } from '@angular/http';  
import { MockBackend, MockConnection } from '@angular/http/testing';  
  
export function fakeBackendFactory(  
    backend: MockBackend,   
    options: BaseRequestOptions) {  
          
  let token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6Ik1vc2ggSGFtZWRhbmkiLCJhZG1pbiI6dHJ1ZX0.iy8az1ZDe-_hS8GLDKsQKgPHvWpHl0zkQBqy1QIPOkA';  
      
  backend.connections.subscribe((connection: MockConnection) => {  
    // We are using the setTimeout() function to simulate an   
    // asynchronous call to the server that takes 1 second.   
    setTimeout(() => {  
      //  
      // Fake implementation of /api/authenticate  
      //  
      if (connection.request.url.endsWith('/api/authenticate') &&  
        connection.request.method === RequestMethod.Post) {  
        let body = JSON.parse(connection.request.getBody());  
  
        if (body.email === '[email protected]' && body.password === '1234') {  
          connection.mockRespond(new Response(  
            new ResponseOptions({  
              status: 200,  
              body: { token: token }  
           })));  
        } else {  
          connection.mockRespond(new Response(  
            new ResponseOptions({ status: 200 })  
          ));  
        }  
      }  
  
  
       //   
       // Fake implementation of /api/orders  
       //  
       if (connection.request.url.endsWith('/api/orders') &&   
           connection.request.method === RequestMethod.Get) {  
         if (connection.request.headers.get('Authorization') === 'Bearer ' + token) {  
            connection.mockRespond(new Response(  
              new ResponseOptions({ status: 200, body: [1, 2, 3] })  
         ));  
       } else {  
           connection.mockRespond(new Response(  
             new ResponseOptions({ status: 401 })  
           ));  
       }  
    }  
  
  
    }, 1000);  
  });  
  
  return new Http(backend, options);  
}  
  
export let fakeBackendProvider = {  
    provide: Http,  
    useFactory: fakeBackendFactory,  
    deps: [MockBackend, BaseRequestOptions]  
};  

So here we have exported the function fakeBackendFactory, this function takes 2 parameters, MockBackend and BaseRequestOptions. Don’t be scared by watching the above code, always write the simple code. It doesn't need to be more complex. The above code is just for demonstration.

So here we have declared a variable token and we have set this to a valid JSON web token that extracted from a jwt.io and if you see the comment ‘Fake implementation of /api/authenticate’ in the code. So here we have some basic logic to check the url of the request.

if (connection.request.url.endsWith('/api/authenticate') &&  
  connection.request.method === RequestMethod.Post) {  
} 

So if the url of the request ends with /api/authenticate and the request method is post, that means the client sends the http post request to this endpoint. Here we want to validate the username and password and if they are valid, it will return jwt

if (body.email === '[email protected]' && body.password === '1234') {  
  connection.mockRespond(new Response(  
    new ResponseOptions({  
      status: 200,  
      body: { token: token }  
   })));  
} else {  
  connection.mockRespond(new Response(  
    new ResponseOptions({ status: 200 })  
  ));  
}  

In else block the response status should be 400, but it doesn’t really matter here for the purpose of this article. So all this logic is to implement the fake api endpoint like we have here. And after this, we have fake implementation of orders. And here we’re also doing something very similar.

if (connection.request.url.endsWith('/api/orders') &&   
    connection.request.method === RequestMethod.Get) {  
  if (connection.request.headers.get('Authorization') === 'Bearer ' + token) {  
     connection.mockRespond(new Response(  
       new ResponseOptions({ status: 200, body: [1, 2, 3] })  
  ));  
} else {  
    connection.mockRespond(new Response(  
      new ResponseOptions({ status: 401 })  
    ));  
}

As you can see we’re returning 401 status which means Unauthorized.

In the real world application, you’re going to build this logic on the server, not on the client. All these logics are for implementing api endpoint, you can see we’ve wrapped all the code in setTimeout() to simulate asynchronous call to the server.

setTimeout(() => {  
}, 1000); 

And one last thing, at the end of this file we have exported an object called fakeBackendProvider. Note that this object has 3 properties

export let fakeBackendProvider = {  
    provide: Http,  
    useFactory: fakeBackendFactory,  
    deps: [MockBackend, BaseRequestOptions]  
};

Provide, useFactory, deps (represents dependencies).

So what are these dependencies? Let’s go back to the app.module.ts. Here in the providers array earlier in the article about CRUD operation with Fake Http Service, we use the provider array like this

providers: [  
  OrderService,  
  
  AuthService,  
  
  { provide: ErrorHandler, useClass: AppErrorHandler },  
  
  // For creating a mock back-end. You don't need these in a real app.   
  fakeBackendProvider,  
  MockBackend,  
  BaseRequestOptions  
]  

So with this object, we told angular that wherever you need to provide ErrorHandler instead use this class AppErrorHandler. So this is what we called provider object. Now here in this article, we’re using useFactory instead of using useClass and we set it to a function that will create an instance of provide class.

export let fakeBackendProvider = {  
    provide: Http,  
    useFactory: fakeBackendFactory,  
    deps: [MockBackend, BaseRequestOptions]  
};  

So here we’re telling  Angular, that hey, whenever you use an instance of Http class, use this useFactory function. And we already know that Factory methods and Factory functions create objects. So here fakeBackendFactory is a function that will create an instance of Http class. And you can see the last line of the fakeBackendFactory method, we’re returning an instance of Http class with relevant arguments.

return new Http(backend, options);  

And here backend is MockBackend and options is BaseRequestOptions, so we take these arguments and modify them to respond to those fake api endpoints. And one last thing,

deps: [MockBackend, BaseRequestOptions]

These are the dependencies of our factory function. And Angular needs to know this before it can call factory function. It was all about the starter code and the fakeBackend.

Implementing Login

So let’s see how to implement login. So open the login.component.ts

export class LoginComponent {  
  invalidLogin: boolean;   
  
  constructor(  
    private router: Router,   
    private authService: AuthService) { }  
  
  signIn(credentials) {  
    this.authService.login(credentials)  
      .subscribe(result => {   
        if (result)  
          this.router.navigate(['/']);  
        else    
          this.invalidLogin = true;   
      });  
  }  
}

It is the very basic component. In the constructor we have 2 dependencies, router and authService. Here in the signIn method, we get the credentials object which is the value behind our form. Just to refresh your memory, go to login.component.html and on the top we have form element and here we have set it to the variable ngForm and then submit this form f.value to signIn method.

<form class="form-signin" #f="ngForm" (ngSubmit)="signIn(f.value)">  
  <h2 class="form-signin-heading">Please sign in</h2>  
  <div *ngIf="invalidLogin" class="alert alert-danger">Invalid username and/or password.</div>  
  <label for="inputEmail" class="sr-only">Email address</label>  
  <input type="email" id="inputEmail" name="email" ngModel class="form-control" placeholder="Email address" required autofocus>  
  <label for="inputPassword" class="sr-only">Password</label>  
  <input type="password" id="inputPassword" name="password" ngModel class="form-control" placeholder="Password" required>  
  <button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button>  
</form>

So the value of this form is an object with 2 properties, email and password. Now back in our login.component.ts,  here in the singIn() method we call the login() method of authService and pass the credentials and this returns an Observable and then we subscribe to that.

signIn(credentials) {  
  this.authService.login(credentials)  
    .subscribe(result => {   
      if (result)  
        this.router.navigate(['/']);  
      else    
        this.invalidLogin = true;   
    });  
} 

If the result is true, it navigates to the Home page otherwise we set invalidLogin to true and we use this field to toggle the validation error. And in login.component.html, you can see here we have a div for displaying the message and we render this invalid username or password.

<div *ngIf="invalidLogin" class="alert alert-danger">  
Invalid username and/or password.  
</div> 

Now, we don’t change any code here in signIn() method but let’s take a deeper look in authService. So let’s go to the authService,

login(credentials) {   
 return this.http.post('/api/authenticate',   
    JSON.stringify(credentials));  
}  

Here in the login() we’re sending the post request to this endpoint and in the body of the request we’re including the credentials which is an object with 2 properties, email and password. As you know, this post() returns an Observable of Response. But in our Login(), we don’t want to expose the Response object. I just want to return true or false. So here we use the map operator and then we convert it to true or false. So we map this observable get the response and then display the response object in the console. And here with http method it automatically displays in the form of json so you don’t need to manually mention the .json() here

login(credentials) {   
 return this.http.post('/api/authenticate',   
    JSON.stringify(credentials))  
    .pipe(  
      map(response => {  
        console.log(response);  
      })  
    );  
}  

So let’s go on the top and import the map reference statement.

import { map } from 'rxjs/operators'; 

Now let’s go into the browser and let’s go to the login page. And enter the correct credentials and sign in. Here you’ll see the validation message. Don’t worry about this validation message.

 

Authentication And Authorization In Angular 

As you can see it is the response object containing token. But here we want to just show this object into json. So,

login(credentials) {   
 return this.http.post('/api/authenticate',   
    JSON.stringify(credentials))  
    .pipe(  
      map(respose => {  
        console.log(respose['_body']);  
      })  
    );  
} 

Now let’s take the results,

Authentication And Authorization In Angular 

So this is how it works. Now if we input the invalid username password.

Authentication And Authorization In Angular 

Now let’s store the response token in the variable and if it is true or contains the value in the token property, we’ll store it into the localStorage.

login(credentials) {   
 return this.http.post('/api/authenticate',   
    JSON.stringify(credentials))  
    .pipe(  
      map(respose => {  
        let result = respose['_body'];  
        if (result && result.token) {  
          // We wanna store it in localStorage  
          localStorage.setItem('token', result.token);  
          return true;  
        }  
   return false;  
      })  
    );  
}

So we are mapping the response object to boolean. Otherwise if it is not the valid login we return false.

Now let’s test the application. On valid login, we’re redirected to the Home page and that seems to be working. Now open the chrome developer tools and go to the Application tab and then Local Storage.

Authentication And Authorization In Angular 

So let’s recap, when the user logins. It sends the post request to an api endpoint to validate the credentials. If the credentials are valid we should get the json web token and store it in the localStorage. And then it is better to return boolean instead of Response object. So this is how we implement login.

Implementing Logout

Implementing the logout is very easy. So let’s go to our home.component.html and here we have logout link

<ul>  
  <li><a routerLink="/admin">Admin</a></li>  
  <li><a routerLink="/login">Login</a></li>  
  <li><a>Logout</a></li>  
</ul> 

And here we want to call the logout method of authService. So first we need to go to our home component.

export class HomeComponent {  
  constructor(private authService: AuthService) { }  
} 

And here we have already injected the authService in the constructor. So now we can bind the link with authService.logout()

<li><a (click)="authService.logout()">Logout</a></li> 

Now let’s implement this method. So authService, here we have logout(). All we have to do here is to delete the token here because of the existence of the valid token inside localStorage which means we have a loggedIn user otherwise if there is no token there that means this user is logged out. So,

logout() {   
  localStorage.removeItem('token');  
} 

Now open the browser,

Authentication And Authorization In Angular 

Showing or Hiding Elements

So we have implemented the login and logout. But when we logged in, we should not see this Login link once again.

Authentication And Authorization In Angular 

And also when we logout, we should not see the Admin and Logout links. So let’s see how to implement this. Here in authService, we have the method isLoggedIn() which is currently returning false.

isLoggedIn() {   
  return false;  
}

Now instead we want to get our token decoded, get its expression date and if it is not expired that means user is logged in. So in other words, if we have the token a valid token in localStorage and this token is not expired that means the user is logged in and here we should return true, otherwise we should return false.

As we have discussed earlier, in order to work with json web token we’ll use the library jwt.io. So in Angular applications, we need to install the library called Angualr2-jwt. And we have already installed this package.

Now back here again in auth.service.ts, and here we use JwtHelper

Auto import adds this import statement automatically.

import { JwtHelper } from 'angular2-jwt'; 

And here is the function code.

isLoggedIn() {   
  let jwtHelper = new JwtHelper();  
  return false;  
}

jwtHelper has a bunch of useful methods. Let’s use these methods here.

jwtHelper.getTokenExpirationDate(); 

This method expects the token as argument. So,

isLoggedIn() {   
  let jwtHelper = new JwtHelper();  
  let token = localStorage.getItem('token');  
  let expirationDate = jwtHelper.getTokenExpirationDate(token);  
  let isExpired = jwtHelper.isTokenExpired(token);  
  
  // Now let's log the above values on the console.  
  console.log("Expiration", expirationDate);  
  console.log("isExpired", isExpired);  
    
  return false;  
}

Now we’re going to use this method and the template for our home component. So here in home.component.html I want to render the links dynamically.

<ul>  
  <li><a routerLink="/admin">Admin</a></li>  
  <li><a routerLink="/login">Login</a></li>  
  <li><a (click)="authService.logout()">Logout</a></li>  
</ul>

So let’s make the links dynamic,

<li *ngIf="authService.isLoggedIn()"><a routerLink="/admin">Admin</a></li> 

Let’s test this back in the browser. And here Admin Link vanishes before sign in and after sign in successfully. So, let’s modify some code.

isLoggedIn() {   
  let jwtHelper = new JwtHelper();  
  let token = localStorage.getItem('token');  
  
  if(!token)  
    return false;  
  
  let expirationDate = jwtHelper.getTokenExpirationDate(token);  
  let isExpired = jwtHelper.isTokenExpired(token);  
  
  // Now let's log the above values on the console.  
  console.log("Expiration", expirationDate);  
  console.log("isExpired", isExpired);  
  
  return !isExpired;  
}

Now let’s take a look in the browser,

Authentication And Authorization In Angular 

Look in the beginning, Admin link is not visible because we have not any token. And after login we have a token in the localStorage and it's showing Admin link. And look in the console --  this token doesn’t have Expiration date.

  • Expiration null
  • isExpired false

But when you generate tokens on the server using one of the libraries provided by jwt.io, you can set the expiration date on your tokens. So back in authService, remove the console logs statements. So this is the implementation of isLoggedIn()

isLoggedIn() {   
  let jwtHelper = new JwtHelper();  
  let token = localStorage.getItem('token');  
  
  if(!token)  
    return false;  
  
  let expirationDate = jwtHelper.getTokenExpirationDate(token);  
  let isExpired = jwtHelper.isTokenExpired(token);  
  
  return !isExpired;  
}

But there is also an easier way to implement this method. Instead of writing all this code, we can use one of the global helper functions provided by angular2-jwt. So the function is,

tokenNotExpired(); 

This function does exactly what we have done above in the code. So here we can simply return tokenNotExpired().

isLoggedIn() {   
  return tokenNotExpired();  
}

So if the token is not expired, the user is loggedIn. Now you have a better understanding of what is happening under the hood.

Now come back to the home.component.html and complete the task.

<ul>  
  <li *ngIf="authService.isLoggedIn()"><a routerLink="/admin">Admin</a></li>  
  <li *ngIf="!authService.isLoggedIn()"><a routerLink="/login">Login</a></li>  
  <li *ngIf="authService.isLoggedIn()"><a (click)="authService.logout()">Logout</a></li>  
</ul> 

And here is the result of what we have done.

Authentication And Authorization In Angular 

Showing and Hiding Elements Based on Users Role

So let’s explore something more and take our application to the next level. So what if we want to display this Admin link only to the users who are in the Admin Role. Let’s get started.

Authentication And Authorization In Angular 

So this is the content of our json web token. In the payload we have 3 properties UserId, Name, Admin. So on the server when the user authenticates, if the user is in Admin Role we can set this property to true. Another implementation would be include all the roles the user is part of. So here instead of the Admin property, we have another property called Roles which will be an array. And in this array, user can list all the Roles user can be part of. So the server generates this token, now on the client we need to decode this token to access the payload properties and then paste on these properties we can show or hide various elements on the page. So let’s see how can we do this.

Back in authService, we define another property

get currentUser() {  
  let token = localStorage.getItem('token');  
  if(!token) return null;  
  
  let jwtHelper = new JwtHelper();  
  return jwtHelper.decodeToken(token);  
} 

And we can make this code a little bit shorter. So,

get currentUser() {  
  let token = localStorage.getItem('token');  
  if(!token) return null;  
  
  return new JwtHelper().decodeToken(token);  
}

So back in home.component.html, here is our admin link

<li *ngIf="authService.isLoggedIn()"><a routerLink="/admin">Admin</a></li>

We want to render this only if the user is logged in and he’s in the admin user

<li *ngIf="authService.isLoggedIn() && authService.currentUser.admin">  
  <a routerLink="/admin">Admin</a>  
</li>

Now back in the browser.

Authentication And Authorization In Angular 

So because I’m an admin user that’s why Admin link is showing to me. But let’s log out and generate a new token that doesn’t have admin property. So back to jwt.io and change the admin to false,

Authentication And Authorization In Angular 

Now copy the token and we're going to use this and hard code this token in our fakeBackend. So copy the token and go to the fakeBackend and replace the token.

let token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IlVzYW1hIFNoYWhpZCIsImFkbWluIjpmYWxzZX0.14FzmpTgliDscuwsfJAebGqXwVS8rye_EE0URrCohTk';

Now test the application.

Authentication And Authorization In Angular 

So this user is not the admin. So admin link is not showing to us. As we already discussed this token has a digital signature that is generated based on the content of the token and a secret. So if the malicious user goes into the application tab and gets the token

Authentication And Authorization In Angular 

And then decodes it here in jwt.io and decodes the token and accesses the payload and header content. But he can’t change the content of any property in payload or in header. Because in order to generate the signature, we need to have the secret and the secret is not on the client

Authentication And Authorization In Angular 

Secret is only on the server.

Come again in the fakeBackend and revert changes and use the old token that has the admin access. So this is how we can check to see if the user is the part of the given roles.

Getting the Current User

Now let’s display the name of the current user. So open the home.component.html and here we use string interpolation with our currentUser property.

<p>  
  Welcome {{ authService.currentUser.name }}  
</p>

And of course we want to render this only if the user is logged in. So,

<p *ngIf="authService.isLoggedIn()">  
  Welcome {{ authService.currentUser.name }}  
</p>

Now let’s test the application.

Authentication And Authorization In Angular 

And if we Logout, the welcome message disappears.

CanActivate Interface

Let’s see a vulnerability in our application. Currently I’m logged out and I don’t see the Admin link. However if I go to the browser address bar and access the admin page, it is accessible.

Authentication And Authorization In Angular 

So how can we solve this issue? In Angular, we have the concept called Route Guard that we can apply to various routes to protect them. Let’s see how that works. Back in the terminal and here we generate a new service.

PS C:\Users\Ami Jan\auth-demo\auth-demo> ng g s services/auth-guard

Here services is the name of the folder where scaffolded service is placed. And auth-guard is the name of the service. Now let’s go to the new service. Here we use the naming convention of AuthGuard instead of AuthGuardService and here we activate the CanActivate interface. So let’s go to the angular.io and search for CanActivate and here you can see the shape of CanActivate interface. So we need to add a method in our class called canActivate which takes 2 parameters route which is of ActivatedRouteSnapshot and state which is of type RouterStateSnapshot but now we’ll exclude these parameters and simplify things but in the next we’ll see where we use these parameters.

import { Injectable } from '@angular/core';  
import { CanActivate } from '@angular/router';  
  
@Injectable()  
export class AuthGuard implements CanActivate {  
  
  constructor() { }  
  
  canActivate() {  
      
  }  
} 

Now in this canActivate() we need to check to see if the user is logged in or not. If he’s logged in, we’ll return true otherwise we should navigate the user to log in page and return false. So in order to check to see if the user is log in or not, we need to inject authService into the constructor of this class. And then in canActivate() we can check,

export class AuthGuard implements CanActivate {  
  
  constructor(private authservice: AuthService) { }  
  
  canActivate() {  
    if(this.authservice.isLoggedIn()) return true;  
  }  
}

And if the user is not logged in we’ll navigate to the home page. So here we need to inject the router service into the constructor of this class.

export class AuthGuard implements CanActivate {  
  
  constructor(  
    private router: Router,  
    private authservice: AuthService) { }  
  
  canActivate() {  
    if(this.authservice.isLoggedIn()) return true;  
  
    this.router.navigate(['/login']);  
    return false;  
  }  
}

So this is what we called Route Guard. Now we need to apply this guard on admin route. So let’s go to the app.module.ts

RouterModule.forRoot([  
  { path: '', component: HomeComponent },  
  {   
    path: 'admin',   
    component: AdminComponent,  
    canActivate: [AuthGuard]  
  },  
  { path: 'login', component: LoginComponent },  
  { path: 'no-access', component: NoAccessComponent }  
])

canActivate has an array containing the RouteGuard classes. So with the help of this technique, we can use reuse this Auth Guard and apply it on any routes that should be protected from the anonymous user. Now let’s test the application; back in the browser if we request the url localhost:4200/admin. It navigates to the home page localhost:4200 but here we’re watching a white screen, it means here is some error in the console.

Authentication And Authorization In Angular 

So the error first line is Error: No Provider for AuthGuard! So before registering AuthGuard as a provider for dependency injection, back in app.module.ts, in the providers array we’ll add AuthGuard here as well.

providers: [  
  OrderService,  
  
  AuthService,  
  AuthGuard,  
  
  // For creating a mock back-end. You don't need these in a real app.   
  fakeBackendProvider,  
  MockBackend,  
  BaseRequestOptions  
]

Now back in the browser,

Authentication And Authorization In Angular 

So in Angular, we use RouteGuard to protect the routes from anonymous users.

Redirecting Users After Logging In

Now let’s suppose I’m the admin user. So when I logged out in the application, and request for the admin page I navigate to the login page. But after successfully signing in, it should go to the url which was requested and we navigated to the login page. So when we navigate to the login page, we should also have the query parameter like returnUrl

http://localhost:4200/login?returnUrl=xyz

So back in our AuthGuard where we navigate the user to login page, here we’ll pass the 2nd argument to the navigate method. And this 2nd argument is an object with 1 property and this property is also an object containing returnUrl property.

this.router.navigate(['/login'], { queryParams: { returnUrl: }}); 

Now what should we use here? We have already seen the documentation of CanActivate interface. And we know that CanActivate interface takes 2 parameters, route and state. Now with this state parameter we can get access to the url that the user wanted to access. So,

canActivate(route, state: RouterStateSnapshot) {  
  if(this.authservice.isLoggedIn()) return true;  
  
  this.router.navigate(['/login'], { queryParams: { returnUrl: state.url }});  
  return false;  
} 

This is all we have to do to send this query parameter to the login page. Here we can also optionally annotate route parameter but it doesn’t add any value. Now we should go to our login component and get this query parameter. So open login.component.ts, in order to access route parameters we need to inject ActivatedRoute service into the constructor of this class. And in the signIn(), when the user successfully logs in, instead of navigating it to the home page we should check the query parameters and if it has returnUrl we want to send the user then we’ll use returnUrl for navigation otherwise return them to the home page.

export class LoginComponent {  
  invalidLogin: boolean;   
  
  constructor(  
    private router: Router,   
    private route: ActivatedRoute,  
    private authService: AuthService) { }  
  
  signIn(credentials) {  
    this.authService.login(credentials)  
      .subscribe(result => {   
        if (result) {  
          let returnUrl = this.route.snapshot.queryParamMap.get('returnUrl');  
          this.router.navigate([returnUrl || '/']);  
        }  
        else    
          this.invalidLogin = true;   
      });  
  }  
}

So if the user has returnUrl, it will navigate to the returnUrl otherwise to the home page. Now let’s test it in the browser.

Authentication And Authorization In Angular 

Protecting Routes Based on User Role

Now let’s take this application to the next level, I want to make this admin route accessible only to users with the admin role. Currently anyone who is logged in can access the admin page.

Authentication And Authorization In Angular 

So let’s see how to fix this problem. Earlier in app.module.ts, we have applied this AuthGuard in our admin route.

{   
  path: 'admin',   
  component: AdminComponent,  
  canActivate: [AuthGuard]  
}

Here we saw, we have an array. And in this array we can have multiple guards. And these guards are applied in sequence. So first we want to our AuthGuard to be applied. Now we gonna implement another guard which ensures that only admin users can access this route. So come back to the terminal and generate one more service.

PS C:\Users\Ami Jan\auth-demo\auth-demo> ng g s services/admin-auth-guard

Now open adminauthguard class. And here once again rename the class name and implement CanActivate interface as well

import { Router } from '@angular/router';  
import { CanActivate } from '@angular/router';  
import { Injectable } from '@angular/core';  
import { AuthService } from './auth.service';  
  
@Injectable()  
export class AdminAuthGuard implements CanActivate {  
  
  constructor(  
    private router: Router,  
    private authService: AuthService  
  ) { }  
  
  canActivate() {  
      
  }  
} 

Now in canActivate() first we need to check to see if the user is an admin or not. So,

canActivate() {  
  this.authService.currentUser.admin      
}

In the last dot, we don’t get the intellisense because we haven’t annotated this property. So potentially in the future we can define an interface, applied on this property and then we’ll get compile time checking as well as intellisense. Now come back again to the point,

canActivate() {  
  if(this.authService.currentUser.admin) return true;  
  
  // Navigate to the Permission Denied Page  
  this.router.navigate(['/no-access']);  
  return false;  
}

Now let’s go back to our app.module.ts and apply this new guard as well.

{   
  path: 'admin',   
  component: AdminComponent,  
  canActivate: [AuthGuard, AdminAuthGuard]  
}

And providers array is,

providers: [  
  OrderService,  
  
  AuthService,  
  AuthGuard,  
  AdminAuthGuard,  
  
  // For creating a mock back-end. You don't need these in a real app.   
  fakeBackendProvider,  
  MockBackend,  
  BaseRequestOptions  
]

Now one tiny problem in the code is, here we’re expecting that currentUser.admin always has a value but as you know currentUser will be null if the user is not logged in. This is actually not the issue because we have applied AdminAuthGuard after AuthGuard. So this code, currentUser.admin is executed only if the user is logged in which means current user will have a value. But to make it little bit more robust, it is better to change the if statement and ensure that current user is not logged. So,

canActivate() {  
  let user = this.authService.currentUser;  
  if(user && user.admin) return true;  
  
  // Navigate to the Permission Denied Page  
  this.router.navigate(['/no-access']);  
  return false;  
} 

Now it looks more clean as well. Now let’s test the result in the browser.

Authentication And Authorization In Angular 

Accessing Protected API Resources

So far our focus has been purely on showing/hiding parts of the page and also on protecting the routes. But when it comes to implementing authentication and authorization in real world applications, we should also know how to access protected api resources. For example, here we have admin page containing list of orders and this list is supposed to come from our api endpoint. As you imagine, this api endpoint is only be accessible to logged in users who are in admin role.

Authentication And Authorization In Angular 

And currently on this admin page, we don’t see the list of orders. And here we’ll see how to fix this problem. So here we have the api/orders endpoint fake implementation,

if (connection.request.url.endsWith('/api/orders') &&   
    connection.request.method === RequestMethod.Get) {  
  if (connection.request.headers.get('Authorization') === 'Bearer ' + token) {  
     connection.mockRespond(new Response(  
       new ResponseOptions({ status: 200, body: [1, 2, 3] })  
  ));  
} else {  
    connection.mockRespond(new Response(  
      new ResponseOptions({ status: 401 })  
    ));  
}

So look at the logic here, we look at the request headers and if we have a header with this name Authorization and the value of the header is this specific and on condition true we send the response with status 200 and in the body of the request we're going to include orders. As we already discussed, we should implement this logic at our api endpoint on the server. So we need to check the existence of Authorization header and its value and the token should not be expired then you would grant access to api resources. Once again, you don’t need to write all these code instead you can use one of the provider libraries on jwt.io for your platform.

Come back to the point and focus on the front end. Open the order.service.ts and here we have a method getOrders() sending get request to our orders endpoint. The reason we don’t get the list of orders is because we don’t have included the authorization header. So let’s see how we can add authorization header to this request.

getOrders() {   
  return this.http.get('/api/orders')  
    .map(response => response.json());  
}

Here first we need to create headers object and this instance has a method called append. And here we need to pass name and the value

getOrders() {   
  let headers = new Headers();  
  let token = localStorage.getItem('token');  
  headers.append('Authorization', 'Bearer' + token);  
  
  return this.http.get('/api/orders')  
    .map(response => response.json());  
}

Next we need to create RequestOptions object and on initialization we pass the object and in this object we set the headers property to the headers object we created.

let options = new RequestOptions({ headers: headers });  

Here we have compilation error because type mismatches. The reason for this is because this headers type (new Headers()) is defined natively in the browser but we wanna use headers type defined in Angular. So we need to explicitly import Headers type of Angular.

import { Http, RequestOptions, Headers } from '@angular/http';  

And function is,

getOrders() {   
  let headers = new Headers();  
  let token = localStorage.getItem('token');  
  headers.append('Authorization', 'Bearer' + token);  
  
  let options = new RequestOptions({ headers: headers });  
  
  return this.http.get('/api/orders')  
    .map(response => response.json());  
}

And now the compilation error is gone. Now we pass this options object to the 2nd argument of the get() method.

return this.http.get('/api/orders', options)  
  .map(response => response.json());  

Now let’s test the application,

Authentication And Authorization In Angular

We can see we have 3 orders. So when building APIs on the server, if we want to secure web apis endpoints we should ensure that the real ‘Authorization’ header  is in the request. And the value of this header should be a valid json token. And this means on the client in your Angular apps whenever you need to access protected api endpoints, we should always supply this ‘Authorization’ header

let headers = new Headers();  
let token = localStorage.getItem('token');  
headers.append('Authorization', 'Bearer' + token);  
let options = new RequestOptions({ headers: headers }); 

But as you can see, writing all this code in multiple places is repetitive and time consuming. So let’s see the faster way, in Angular2-jwt we have AuthHttp class. And with the help of AuthHttp instance object we can request the same http endpoints implementing the same logic (get the token from localStorage and add this token in Authorization key in Header and then assign this header in the RequestOptions). So the new implementation is,

import { map } from 'rxjs/operators';  
import { Http, RequestOptions, Headers } from '@angular/http';  
import { Injectable } from '@angular/core';  
import 'rxjs/add/operator/map';  
import { AuthHttp } from 'angular2-jwt';  
  
@Injectable()  
export class OrderService {  
  
  constructor(private authHttp: AuthHttp) {  
  
  }  
  
  getOrders() {  
    return this.authHttp.get('/api/orders')  
      .map(response => response.json());  
  }  
} 

And if you want to use standard Http class to request the api endpoints then we can add one more dependency in the constructor as well and we can use it in other methods for our ease.

constructor(  
  private authHttp: AuthHttp,  
  private http: Http  
) {  

When we’re working with AuthHttp we also need to define it in the app.module.ts as well. So here is our providers array in app.module.ts

providers: [  
  OrderService,  
  
  AuthService,  
  AuthGuard,  
  AdminAuthGuard,  
  AuthHttp,  
  
  // For creating a mock back-end. You don't need these in a real app.   
  fakeBackendProvider,  
  MockBackend,  
  BaseRequestOptions  
] 

Conclusion

Let’s quickly recap the important points of this lesson. So we learned Json Web Tokens (JWT) to implement Authentication and Authorization in our Angular apps. These json web tokens have a header, a payload and a digital signature that is based on the combination of header and payload and is generated based on a secret. This secret is known only on the server. We also looked at jwt.io where we have json web token debugger as well as libraries we can use both on the clients and on the server. And in angular apps, we use npm package angular2-jwt as for as Authorization.

There are 2 things on the client that you need to look for, you need to show/hide elements on the page depending on the authentication status of the user whether they’re logged in or not or maybe they are logged in but part of the specific role. And here we use ng directive to show and hide parts of the page. And we should never ever ever have sensitive information on the client. Because a malicious user can obviously look in the DOM look for the things that you have hidden. So when we hide certain elements on the page, we’re talking about the content that is not sensitive like links to an Admin page. And the second thing is, we should protect our routes. And we use guards to achieve that. So these are things we should keep in mind on the client.

And on the server, we want to protect all api endpoints where the subset of the api endpoints from unauthorized access. And once again to do that, we use json web tokens. So on these api endpoints, we should expect an authorization header with the valid json web token in the request.


Similar Articles