In this article we will learn how to implement user registration, login and JWT authentication in Angular. We will create a simple angular application which will allow user to register, login and then allow user to access application’s home page if the user is authenticated using JWTs.

In previous article we learned how to implement JWT authentication in backend using NodeJS, checkout full article here. We will be using the same backend to communicate to from our Angular client.

Checkout this complete source code for Implementing user registration, login and JWT authentication in Angular as well as the backend part in NodeJS here.

What are we building?

We will develop a simple angular application where a user can register by providing a username and password, then we will allow user to login using those credentials, on successful login we will send a JWT token from NodeJS backend for angular client to store and use for further authentication while accessing other resources.

We will create three separate components in angular for registration, login and home. And will let user access home only when user is logged in with a valid JWT token.

Angular Setup

First we need to setup our Angular app. Lets start by running the following command in terminal

ng new jwt-authentication-angular

Once the project is created, check if its running using the below command.

ng serve

This should run our project on http://localhost:4200/ by default. Check in any browser to confirm.

We will also be using bootstrap a CSS framework, to reduce our effort in CSS styling. So go ahead and add to the application either by directly installing it using below command,

npm install bootstrap

or by adding the following link and script in index.js.

 <!-- BootStrap -->
  <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-0evHe/X+R7YkIZDRvuzKMRqM+OrBnVFBL6DOitfPri4tjfHxaWutUpFmBp4vmVor" crossorigin="anonymous">
  <!-- JavaScript Bundle with Popper -->
  <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0-beta1/dist/js/bootstrap.bundle.min.js" integrity="sha384-pprn3073KE6tl6bjs2QrFaJGz5/SUsLqktiwsUTF55Jfv3qYSDhgCecCxMW52nD2" crossorigin="anonymous"></script>
Registering New User

Now that we have everything set up let’s create a register component to allow users to register. In root folder, open a terminal in ‘/src/app’ and create a component named register. You can use the following command to create one.

ng generate component register 

This command will create the following files in a folder named register.

registerfolderjwt

We will also need a service to send register request to our NodeJS server. We can create a register service using below command

ng generate service register

This will create a register.service.ts file. Our register service code is as below

import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class RegistrationService {

  constructor(private http:HttpClient) { }

  register(username:string,password:string):Observable<any>{
    return this.http.post('http://localhost:8000/users/register',{username,password})
  }
}

Here we are sending post request with username and password in request body to server running on localhost port 8000 to register the user.

let’s start building our register component. Here is the code for our register.component.ts file.

import { HttpClient } from '@angular/common/http';
import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { RegistrationService } from '../services/registration.service';

@Component({
  selector: 'app-register',
  templateUrl: './register.component.html',
  styleUrls: ['./register.component.css']
})
export class RegisterComponent implements OnInit {

  registerForm : FormGroup
  success = false;
  errMessage = ''

  constructor(
    private formBuilder: FormBuilder,
    private http: HttpClient,
    private registerService: RegistrationService
  ) { }

  ngOnInit(): void {
    this.registerForm = this.formBuilder.group({
      username:['',Validators.required],
      password:['',Validators.required]
    }) 
  }

  register(){
    const formValue = this.registerForm.value
    this.registerService.register(formValue.username,formValue.password).subscribe({next:() => {
      this.success = true
    },error : (err) =>{
      if(err.error.code == 11000)
        this.errMessage= 'User already exists!! Try something else.'
      else 
        this.errMessage= 'Something went wrong!!'
    }})
  }

}

Here we can see is a simple code where we have a reactive angular form which accepts username and password. Also we have a register function which will be triggered on submitting the form where we are sending the form values to register service which sends it to NodeJS backend which stores it in MongoDB and if there is any error, we will display the respective error messages on screen. We are checking for error code 11000 because this error is thrown by MongoDB when there is unique constraint violation as we do not want duplicate usernames during registration.

Let’s design the register page now. To build quicker, I have copied the source code from bootstrap examples for sign in form. After making tweaks to the sample code our register.component.html code looks like this

<form class="form-signin" [formGroup]='registerForm' (submit)="register()">
    <h1 class="h3 mb-3 fw-normal">Register</h1>
    <div class="form-floating">
      <input class="form-control" id="floatingInput" formControlName="username">
      <label for="floatingInput">Username</label>
    </div>
    <div class="form-floating">
      <input type="password" class="form-control" id="floatingPassword" formControlName="password">
      <label for="floatingPassword">Password</label>
    </div>
    <button class="w-100 btn btn-lg btn-primary" type="submit">Register</button>
    <br>
    <br>
    <div *ngIf="success" style="color: green;">Registration Successful!! Please <a routerLink="/login" [disabled]="!registerForm.valid">Login</a></div>
    <p *ngIf="errMessage" class="alert alert-danger alert-dismissible fade show">{{errMessage}}</p>
</form>

Here we have a form element where we have given username and password as inputs and on submit we are calling register function is ts file.

Now that we have built our register component we need to add its path in app-routing.module.ts file.

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { RegisterComponent } from './register/register.component';

const routes: Routes = [
  {path: 'register', component: RegisterComponent},
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

Also we need to add the following code in app.component.html.

<router-outlet></router-outlet>

Now if we run our app and check http://localhost:4200/register in browser we will see the below result,

Let’s try to register a user.

As you can see we successfully registered a user with a username ‘test’. As we are giving option to login, let’s create login component to let user do so.

User Login and storing JWT

Now let’s create a login component. Run the following command to create one.

ng g c login

Here you will notice that I have not used full words like we did when we created register components, this is another shortcut way to write the same command.

Also lets create a login service as well to send login request to server. Our login service code in login.service.ts file is as below

import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class LoginService {

  constructor(private http:HttpClient) { }

  login(username:string,password:string):Observable<any>{
    return this.http.post('http://localhost:8000/users/login',{username,password})
  }
}

Here, similar to register service we are sending post request to server with username and password in request body.

Our login.component.ts file contain the below code.

import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Router } from '@angular/router';
import { LoginService } from '../services/login.service';

@Component({
  selector: 'app-login',
  templateUrl: './login.component.html',
  styleUrls: ['./login.component.css']
})
export class LoginComponent implements OnInit {

  loginForm : FormGroup
  message = '';

  constructor(
    private formBuilder: FormBuilder,
    private loginService: LoginService,
    private router:Router
  ) { }

  ngOnInit(): void {
    this.loginForm = this.formBuilder.group({
      username:['',Validators.required],
      password:['',Validators.required],
    }) 
  }

  login(){
    const formValue = this.loginForm.value
    this.loginService.login(formValue.username,formValue.password).subscribe({next: (res) => {
      console.log(res)
      localStorage.setItem('token',res.token)
      this.router.navigate(['/'])
    },error : (err)=>{
      this.message='Wrong username or password!!'
    }})
  }

}

Let’s break down the code. Here we can see that login form is same as the register form, only change here is the login function where we are sending form values to login service which returns a JWT token. We are storing this JWT token in local storage so we can retrieve it from there whenever required. Also we are navigating to home on successful login.

Let’s see what our code is in login.component.html.

<form class="form-signin" [formGroup]='loginForm' (submit)="login()">
  <h1 class="h3 mb-3 fw-normal">Login</h1>

  <div class="form-floating">
    <input class="form-control" id="floatingInput" formControlName="username">
    <label for="floatingInput">Username</label>
  </div>
  <div class="form-floating">
    <input type="password" class="form-control" id="floatingPassword" formControlName="password">
    <label for="floatingPassword">Password</label>
  </div>
  <button class="w-100 btn btn-lg btn-primary" type="submit" [disabled]="!loginForm.valid">Login</button>
  <br><br>
  <p *ngIf="message" class="alert alert-danger alert-dismissible fade show">{{message}}</p>
</form>

As you can see the, this is exactly same as the register form. Here we trigger login function on submit.

Now let’s add login path as well in app-routing.module.ts file. So the code will be as below

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { LoginComponent } from './login/login.component';
import { RegisterComponent } from './register/register.component';

const routes: Routes = [
  {path: 'login', component: LoginComponent},
  {path: 'register', component: RegisterComponent},
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

We can now test login component by running the app and checking http://localhost:4200/login in browser. We will see the below result,

login screen

As we can see that we get a login screen and we have entered credentials of the user we registered above. On login we will store the JWT in local storage lets see it that is happening.

local storage jwt

Here we see that our token is getting stored on login.

Lets test one negative scenario, where we will try to login with wrong password.

login negative

As you can see we get a error message when we try to enter wrong credentials.

JWT Authentication in Angular

Now that we have our register and login component set up, we have to validate the user before allowing it to access home.

Handling JWT Authentication using Angular HTTP Interceptors

To do that first we will have to send the JWT token in authorization header of any request we make to the server. We can do that using angular http interceptors. Create a interceptor using below command

ng g interceptor auth

This will create a interceptor file named auth.interceptor.ts and it will contain the below code

import { Injectable } from '@angular/core';
import {
  HttpRequest,
  HttpHandler,
  HttpEvent,
  HttpInterceptor
} from '@angular/common/http';
import { Observable } from 'rxjs';

@Injectable()
export class AuthInterceptor implements HttpInterceptor {

  constructor() {}

  intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
    
    if(localStorage.getItem('token'))
      return next.handle(request.clone({setHeaders:{authorization : `Bearer ${localStorage.getItem('token')}`}}));
    return next.handle(request)
  }
}

Here we have a injectable class which will intercept all the http requests and append the authorization header with our JWT token.

We also need to add it in providers in app.module.ts file as below

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { RegisterComponent } from './register/register.component';
import { LoginComponent } from './login/login.component';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http'
import { AuthInterceptor } from './interceptors/auth.interceptor';

@NgModule({
  declarations: [
    AppComponent,
    RegisterComponent,
    LoginComponent,
    HomeComponent,
    NavComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    FormsModule,
    ReactiveFormsModule,
    HttpClientModule,
  ],
  providers: [
    {provide:HTTP_INTERCEPTORS,useClass:AuthInterceptor,multi:true}
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }
Handling User Authentication State using Event Emitter

We need to store user authentication state for the client to know if user is authenticated or not. We can do that with the help of event emitters. So go ahead and create a folder name emitters and add a file named authEmitter.ts. Copy the below code to the file

import { EventEmitter } from "@angular/core";

export class Emitter {
    static authEmitter = new EventEmitter<boolean>()
}

Here we are create a event emitter named authEmitter which will store a boolean value that is true if user is authenticated and false if not.

Allowing Home access to Authenticated User

Let’s create a home component which user can access on login.

We also need a service call to check if user is allowed access to the home page or not. Let’s create one and the code for home.service.ts file is as below

import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class HomeService {

  constructor(private http:HttpClient) { }

  accessHome():Observable<any>{
    return this.http.get('http://localhost:8000/home')
  }
}

Here we are making a call to server to check check access for home. As we have already setup the http interceptor, the JWT token will get appended in the authorization header as below

Code for home.component.ts file is as below,

import { Component, OnInit } from '@angular/core';
import { Emitter } from '../emitters/auth.emitter';
import { HomeService } from '../services/home.service';

@Component({
  selector: 'app-home',
  templateUrl: './home.component.html',
  styleUrls: ['./home.component.css']
})
export class HomeComponent implements OnInit {

  userName = null ;

  constructor(private homeService : HomeService) { }

  ngOnInit(): void {
    this.homeService.accessHome().subscribe({next: (res) => {
      this.userName = res.username
      Emitter.authEmitter.emit(true)
    },error : (err) =>{
      Emitter.authEmitter.emit(false)
    }})
  }

}

Here we are first making a backend call to check if user has access to home, if successful we get the username back from server and set the authEmitter as true. In case of error authEmitter remains in false state as user is not authenticated.

Let’s finish up the html part for home component. Code for home.component.html is as below

<p *ngIf="!userName" class="text-center">You are not Authenticated!! Please <a routerLink="/login">Login</a></p>
<p *ngIf="userName" class="text-center">Welcome, {{userName}}</p>

Here we only showing two messages based on the user authentication status.

Also we need to add the path for home component in app-routing.module. We will give the default path to home component as shown below.

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { HomeComponent } from './home/home.component';
import { LoginComponent } from './login/login.component';
import { RegisterComponent } from './register/register.component';

const routes: Routes = [
  {path: '', component: HomeComponent},
  {path: 'login', component: LoginComponent},
  {path: 'register', component: RegisterComponent},
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

Let’s now connect all the pieces together by allowing user to navigate between register, login and home component using a navigation bar.

Creating Navigation Bar in angular

Let’s create a separate component named nav. By now use should be aware of the command to create one.

Copy the below code for nav.component.html.

<nav class="navbar navbar-expand-md navbar-dark bg-dark mb-4">
    <div class="container-fluid">
      <a class="navbar-brand" href="#">Home</a>
      <div>
        <ul *ngIf="!isLoggedIn" class="navbar-nav me-auto mb-2 mb-md-0">
          <li class="nav-item">
            <a class="nav-link" routerLink="/login">Login</a>
          </li>
          <li class="nav-item">
            <a class="nav-link" routerLink="/register">Register</a>
          </li>
        </ul>
        <ul *ngIf="isLoggedIn" class="navbar-nav me-auto mb-2 mb-md-0">
          <li class="nav-item">
            <a class="nav-link" routerLink="/login" (click)="logout()">Logout</a>
          </li>
        </ul>
      </div>
    </div>
</nav>

Here we added all our components for user to navigate to as well a logout option. Let’s check what will be the code in nav.component.ts

import { Component, OnInit } from '@angular/core';
import { Emitter } from '../emitters/auth.emitter';

@Component({
  selector: 'app-nav',
  templateUrl: './nav.component.html',
  styleUrls: ['./nav.component.css']
})
export class NavComponent implements OnInit {

  isLoggedIn = false ;

  constructor() { }

  ngOnInit(): void {
    Emitter.authEmitter.subscribe(res=>{
      this.isLoggedIn = res
    })
  }

  logout(){
    localStorage.removeItem('token')
    Emitter.authEmitter.emit(false)
  }

}

Here we first check what is the authentication state of user using event emitter. Then we have the logout logic where we are removing the token from local storage as well as setting the authentication state to false.

We need to add the nav component in app.component.html as below

<app-nav></app-nav>
<router-outlet></router-outlet>

Now let’s run and see the entire application in action.

Summary

In this detailed article we learned how to build Angular application from scratch and implement user registration, login and JWT authentication in Angular using Angular interceptors. Also we learned about state management in Angular using event emitters. If we combine this article with previous article we now know to implement JWT authentication in a MEAN stack application covering both frontend and backend.

Missed out on a key concept, JWT refresh token process. Learn about it here.

Leave a Reply

Your email address will not be published. Required fields are marked *