Laravel 10 Angular 16 Token-Based Authentication with JWT

Last Updated on by in Angular
Implement Laravel Authentication JSON Web Token-based REST API in Angular 16. In this tutorial, we will learn how to create user registration and authentication system and store the user data in the MySQL database.Generically, Token-Based Authentication provides secure authentication, we have developed JWT API in Laravel, and now in this tutorial, we will learn how to consolidate Laravel and Angular and create a secure user authentication system.

JWT token implements the robust solution to restrain unauthenticated user access. Scilicet, I will try to be more spontaneous and simple.

What we will be learning:

  • Create a user with the name, email, and password values.
  • Hash password to incorporate robustness in password.
  • Setting up CORS middleware in laravel.
  • Signin with email and password.
  • Handle laravel server-side validation with angular.
  • Generate JSON web token when the user logs in.
  • Store and retrieve a JWT token of local storage.
  • Set Bearer token in the Header using angular.
  • Validate JWT payload.
  • Manage user state globally with RxJS.
  • Handle laravel auth API with angular service.
  • Access user profile page, if authenticated successfully.
  • Logout from the app by destroying the JWT token.

Prerequisite

To get along with this tutorial, we must have the following tools frameworks and databases knowledge.

  • Node
  • NPM
  • Composer
  • Laravel (PHP Framework)
  • MAMP or XAMPP
  • Angular
  • IDE or Code Editor

Follow this tutorial to download and install Node.js and npm on your local development system.

Laravel and Angular Project Structure

Create the main project folder, and this folder includes the backend (Laravel API) and frontend (Angular) directories for handling our project.

Laravel Angular Project Structure

Run command to create main project folder:

mkdir laravel-jwt-auth

We can now manage backend and frontend of our application.

Clone Laravel JWT Authentication Repo

I advise you to check our detailed tutorial on Securing Laravel Authentication API using JSON Web Token.

Download the project from GitHub, unzip the project and keep all the files inside the backend folder:

Execute following commands to install required dependencies for node and composer for your laravel auth project:

composer install
cp .env.example .env
php artisan key:generate
php artisan migrate
php artisan serve

Run local PHP web server, you can use either MAMP or XAMPP.

Start the project:

php artisan serve

Use http://127.0.0.1:8000 as a base URL for User Registration and Login, Accessing User Profile, Refreshing the Token and Logging out from the app.

Method Endpoint
POST /api/auth/register
POST /api/auth/login
GET /api/auth/user-profile
POST /api/auth/refresh
POST /api/auth/logout

Install and Configure Angular

Install a brand new Angular app using the below command:

ng new frontend && cd frontend

Create the following components to handle the user registration and authentication process.

ng g c components/signin

ng g c components/signup

ng g c components/user-profile

We are using the Bootstrap to design authentication form, you may skip this step if you want to use your custom CSS.

npm install bootstrap

Define the bootstrap CSS path inside the angular.json.

"styles": [
          "node_modules/bootstrap/dist/css/bootstrap.min.css",
          "src/styles.scss"
         ]

Start angular app on the browser.

ng serve --open

Adding up HttpClient

To handle the HTTP requests, import Http client module inside the app.module.ts file .

import { HttpClientModule } from '@angular/common/http';

@NgModule({
  imports: [
    HttpClientModule
   ]
})

Insert Reactive Form Service

Reactive Form API provides spontaneous support while working with form data, and it is extremely useful to manage the form data filled by the user.

Before we start working with the form, Implementation of ReactiveFormsModule and FormsModule is compulsory, Import and register both the APIs in app.module.ts file.

import { ReactiveFormsModule, FormsModule } from '@angular/forms';

@NgModule({
  imports: [
    ReactiveFormsModule,
    FormsModule
  ],
})

Create CORS Middleware

We need to create CORS middleware, It allows to share resources between two different domain. Our backend is serving from on PORT:8000 and frontend running on PORT:4200.

Cross-origin resource sharing (CORS) is a mechanism that allows restricted resources on a web page to be requested from another domain outside the domain from which the first resource was served.
wikipedia

Get inside the `backend` folder and run the following command:

php artisan make:middleware CORS

Open app/Http/Middleware/CORS.php file and set the Acess-Control-Allow-Origin headers.

<?php

namespace App\Http\Middleware;
use Illuminate\Http\Request;
use Closure;

class CORS {
    
    public function handle(Request $request, Closure $next) {
        header('Acess-Control-Allow-Origin: *');
        header('Acess-Control-Allow-Origin: Content-type, X-Auth-Token, Authorization, Origin');
        return $next($request);
    }

}

Open app/Http/Kernel.php file and add the define the ‘guest’ CORS middleware inside the $routeMiddleware array.

protected $routeMiddleware = [
    ...
    'guest' => \App\Http\Middleware\CORS::class,
];

Now, restart the php server.

php artisan serve

Consume Laravel REST API with Angular Service

We will create Angular service to consume Laravel authentication API that we protected using JSON Web Token.

An Angular service is a stateless object which can be used to define the handy functions. It subdivides the web application into smaller chunks that we can reuse any time from any component.

Execute command to generate Angular service file.

ng g s shared/auth

Open shared/auth.service.ts file and insert the under-mentioned code.

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

// User interface
export class User {
  name!: String;
  email!: String;
  password!: String;
  password_confirmation!: String;
}

@Injectable({
  providedIn: 'root',
})
export class AuthService {
  constructor(private http: HttpClient) {}

  // User registration
  register(user: User): Observable<any> {
    return this.http.post('http://127.0.0.1:8000/api/auth/register', user);
  }

  // Login
  signin(user: User): Observable<any> {
    return this.http.post<any>('http://127.0.0.1:8000/api/auth/login', user);
  }

  // Access user profile
  profileUser(): Observable<any> {
    return this.http.get('http://127.0.0.1:8000/api/auth/user-profile');
  }
}

The User Interface class maps the incoming and outing data with API data. To handle Laravel API, spontaneously use HttpClient service.

Validate & Configure Laravel JWT Token in Angular

In this step, we will cover up the following tasks:

  • Store the access token in local storage when a user logs in.
  • Construct a function to retrieve the token from local storage.
  • Verify the JWT token by decoding the payload and validating the issuer property of JWT token.
  • Allow authorization based on the valid token.
  • Remove token from local storage when the user signs out.

Run the command to generate service file:

ng g s shared/token

Open shared/token.service.ts file and insert the under-mentioned code.

import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root',
})

export class TokenService {
  private issuer = {
    login: 'http://127.0.0.1:8000/api/auth/login',
    register: 'http://127.0.0.1:8000/api/auth/register',
  };

  constructor() {}

  handleData(token: any) {
    localStorage.setItem('auth_token', token);
  }

  getToken() {
    return localStorage.getItem('auth_token');
  }

  // Verify the token
  isValidToken() {
    const token = this.getToken();

    if (token) {
      const payload = this.payload(token);
      if (payload) {
        return Object.values(this.issuer).indexOf(payload.iss) > -1
          ? true
          : false;
      }
    } else {
      return false;
    }
  }

  payload(token: any) {
    const jwtPayload = token.split('.')[1];
    return JSON.parse(atob(jwtPayload));
  }

  // User state based on valid token
  isLoggedIn() {
    return this.isValidToken();
  }

  // Remove token
  removeToken() {
    localStorage.removeItem('auth_token');
  }
}

You might meet with “not all the code paths return value” issue, you may fix it by setting up given property in tsconfig.json file:

compilerOptions:{
  "noImplicitReturns": false
}

Broadcast Authentication State to Multiple Components

Sometimes, you need to update the user state in multiple components based on logged in or out scenario.

Theoretically, RxJS can help us here. We will create another service that carries user state in the boolean form. When the user is logged in, it holds the true value and vice versa.

Execute command to create auth state service:

ng g s shared/auth-state

Open shared/auth-state.service.ts file and insert the under-mentioned code.

import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs/internal/BehaviorSubject';
import { TokenService } from '../shared/token.service';

@Injectable({
  providedIn: 'root',
})

export class AuthStateService {
  private userState = new BehaviorSubject<boolean>(this.token.isLoggedIn()!);
  userAuthState = this.userState.asObservable();

  constructor(public token: TokenService) {}

  setAuthState(value: boolean) {
    this.userState.next(value);
  }
}

Set JWT Token in Header with Angular HttpInterceptor

In general, when implementing token-based authentication, we need to set the token in the request header. It authenticates the request so that we can get the data securely.

To accomplish this task, we will be using Angular HttpInterceptor. It Intercepts and handles an HttpRequest or HttpResponse.

create the shared/auth.interceptor.ts inside the frontend folder and place the following code.

import { Injectable } from "@angular/core";
import { HttpInterceptor, HttpRequest, HttpHandler } from "@angular/common/http";
import { TokenService } from "../shared/token.service";

@Injectable()

export class AuthInterceptor implements HttpInterceptor {
    constructor(private tokenService: TokenService) { }

    intercept(req: HttpRequest<any>, next: HttpHandler) {
        const accessToken = this.tokenService.getToken();
        req = req.clone({
            setHeaders: {
                Authorization: "Bearer " + accessToken
            }
        });
        return next.handle(req);
    }
}

Incorporate the given code inside the app.module.ts file.

import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
import { AuthInterceptor } from './shared/auth.interceptor';

@NgModule({
  providers: [
    {
      provide: HTTP_INTERCEPTORS,
      useClass: AuthInterceptor,
      multi: true
    }
  ]
})

User Registration with Laravel and Angular

The given below code covers the following tasks in User registration module:

  • Creating a form with Bootstrap.
  • Consumption of an Auth API built with Laravel in Angular.
  • Registering and Signing up a user and storing the user data spontaneously in the MySQL database.
  • Getting the user data using React Forms in Angular.
  • Handling the server-side validation in Angular extracted form the Laravel Auth API.

Open signup.component.ts file and append the following code.

import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { AuthService } from './../../shared/auth.service';
import { FormBuilder, FormGroup } from '@angular/forms';

@Component({
  selector: 'app-signup',
  templateUrl: './signup.component.html',
  styleUrls: ['./signup.component.scss'],
})

export class SignupComponent implements OnInit {
  registerForm: FormGroup;
  errors: any = null;

  constructor(
    public router: Router,
    public fb: FormBuilder,
    public authService: AuthService
  ) {
    this.registerForm = this.fb.group({
      name: [''],
      email: [''],
      password: [''],
      password_confirmation: [''],
    });
  }

  ngOnInit() {}

  onSubmit() {
    this.authService.register(this.registerForm.value).subscribe(
      (result) => {
        console.log(result);
      },
      (error) => {
        this.errors = error.error;
      },
      () => {
        this.registerForm.reset();
        this.router.navigate(['login']);
      }
    );
  }
}

Open signup.component.html file and insert the following code.

<div class="auth-wrapper">
  <form class="form-signin" [formGroup]="registerForm" (ngSubmit)="onSubmit()">
    <h3 class="h3 mb-3 font-weight-normal text-center">Register User</h3>

    <!-- Errors -->
    <div *ngIf="errors?.name" class="alert alert-danger mt-3">
      {{ errors?.name }}
    </div>
    <div *ngIf="errors?.email" class="alert alert-danger mt-3">
      {{ errors?.email }}
    </div>
    <div *ngIf="errors?.password" class="alert alert-danger mt-3">
      {{ errors?.password }}
    </div>
    <div *ngIf="errors?.password_confirmation" class="alert alert-danger mt-3">
      {{ errors?.password_confirmation }}
    </div>

    <!-- Signup form -->
    <div class="form-group">
      <label>Name</label>
      <input type="text" class="form-control" formControlName="name" />
    </div>
    <div class="form-group">
      <label>Email address</label>
      <input type="email" class="form-control" formControlName="email" />
    </div>
    <div class="form-group">
      <label>Password</label>
      <input type="password" class="form-control" formControlName="password" />
    </div>
    <div class="form-group">
      <label>Confirm Password</label>
      <input
        type="password"
        class="form-control"
        formControlName="password_confirmation"
      />
    </div>
    <button type="submit" class="btn btn-block btn-primary">
      Register User
    </button>
  </form>
</div>

Token Based Secure Login in Angular and Laravel

In this step, we will cover up the following tasks:

  • Log in to the application using correct credentials
  • Set the authentication state through auth state service, which hides and shows specific elements based on user state.
  • Set Bearer token in the header using HttpInterceptor, It renders the user data by making the consensus between client and server.
  • We have completed this task earlier, but we will execute when the login API is being requested.
  • Use reactive to get and validate the form values.
  • Redirect to profile page when successfully login.
  • Display form error in the frontend.

Open signin.component.ts file and insert the following code.

import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { AuthService } from './../../shared/auth.service';
import { FormBuilder, FormGroup } from '@angular/forms';
import { TokenService } from '../../shared/token.service';
import { AuthStateService } from '../../shared/auth-state.service';

@Component({
  selector: 'app-signin',
  templateUrl: './signin.component.html',
  styleUrls: ['./signin.component.scss'],
})

export class SigninComponent implements OnInit {
  loginForm: FormGroup;
  errors:any = null;

  constructor(
    public router: Router,
    public fb: FormBuilder,
    public authService: AuthService,
    private token: TokenService,
    private authState: AuthStateService
  ) {
    this.loginForm = this.fb.group({
      email: [],
      password: [],
    });
  }

  ngOnInit() {}

  onSubmit() {
    this.authService.signin(this.loginForm.value).subscribe(
      (result) => {
        this.responseHandler(result);
      },
      (error) => {
        this.errors = error.error;
      },
      () => {
        this.authState.setAuthState(true);
        this.loginForm.reset();
        this.router.navigate(['profile']);
      }
    );
  }

  // Handle response
  responseHandler(data:any) {
    this.token.handleData(data.access_token);
  }
}

Open signin.component.html file and include the given below code.

<div class="auth-wrapper">
  <form class="form-signin" [formGroup]="loginForm" (ngSubmit)="onSubmit()">
      <h3 class="h3 mb-3 font-weight-normal text-center">Sign in</h3>

      <!-- Errors -->
      <div *ngIf="errors?.email" class="alert alert-danger mt-3">
          {{ errors?.email }}
      </div>
      <div *ngIf="errors?.password" class="alert alert-danger mt-3">
          {{ errors?.password }}
      </div>
      <div *ngIf="errors?.error" class="alert alert-danger mt-3">
          {{ errors?.error }}
      </div>

      <!-- Login -->
      <div class="form-group">
          <label>Email address</label>
          <input type="email" class="form-control" formControlName="email">
      </div>
      <div class="form-group">
          <label>Password</label>
          <input type="password" class="form-control" formControlName="password">
      </div>
      <button type="submit" class="btn btn-block btn-primary">Log in</button>
  </form>
</div>

jwt authentication laravel angular

Display User Profile

To display user profile subscribe to the profileUser() method via AuthService, in response, we get the user data that we fetch by making the HTTP POST request through Laravel API. Display the data using the interpolation sign inside the user profile template.

Open user-profile.component.ts file and paste the following code.

import { Component, OnInit } from '@angular/core';
import { AuthService } from './../../shared/auth.service';

// User interface
export class User {
  name: any;
  email: any;
}

@Component({
  selector: 'app-user-profile',
  templateUrl: './user-profile.component.html',
  styleUrls: ['./user-profile.component.scss'],
})

export class UserProfileComponent implements OnInit {
  UserProfile!: User;

  constructor(public authService: AuthService) {
    this.authService.profileUser().subscribe((data: any) => {
      this.UserProfile = data;
    });
  }

  ngOnInit() {}
}

Open user-profile.component.html file and incorporate the following code within.

<div class="container">
  <div class="card inner-main">
    <div class="card-header">
      User Profile
    </div>
    <div class="card-body">
      <p class="card-text">Name: <strong>{{UserProfile?.name}}</strong></p>
      <p class="card-text">Email: <strong>{{UserProfile?.email}}</strong></p>
    </div>
  </div>
</div>

Logout

We are going to use simple logic to make the user log out from the app. Remove the JWT token from local storage, set the authentication state to false.

Create a isSignedIn variable, It hide and show navigation items based on auth state. Access userAuthState observable through AuthStateService, subscribe and assign the response to the
variable.

Open app.component.ts file and insert the following code.

import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { TokenService } from './shared/token.service';
import { AuthStateService } from './shared/auth-state.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss'],
})

export class AppComponent implements OnInit {
  isSignedIn!: boolean;

  constructor(
    private auth: AuthStateService,
    public router: Router,
    public token: TokenService
  ) {}

  ngOnInit() {
    this.auth.userAuthState.subscribe((val) => {
      this.isSignedIn = val;
    });
  }

  // Signout
  signOut() {
    this.auth.setAuthState(false);
    this.token.removeToken();
    this.router.navigate(['login']);
  }
}

Create the app-routing.module.ts file and insert the code within.

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';

import { SigninComponent } from './components/signin/signin.component';
import { SignupComponent } from './components/signup/signup.component';
import { UserProfileComponent } from './components/user-profile/user-profile.component';

import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
import { ReactiveFormsModule, FormsModule } from '@angular/forms';

import { AuthInterceptor } from './shared/auth.interceptor';

@NgModule({
  declarations: [
    AppComponent,
    SigninComponent,
    SignupComponent,
    UserProfileComponent,
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    HttpClientModule,
    ReactiveFormsModule,
    FormsModule,
  ],
  providers: [
    {
      provide: HTTP_INTERCEPTORS,
      useClass: AuthInterceptor,
      multi: true,
    },
  ],
  bootstrap: [AppComponent],
})

export class AppModule {}
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

import { SigninComponent } from './components/signin/signin.component';
import { SignupComponent } from './components/signup/signup.component';
import { UserProfileComponent } from './components/user-profile/user-profile.component';

const routes: Routes = [
  { path: '', redirectTo: '/login', pathMatch: 'full' },
  { path: 'login', component: SigninComponent },
  { path: 'register', component: SignupComponent },
  { path: 'profile', component: UserProfileComponent },
];

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

export class AppRoutingModule {}

Finally, make sure to update the app.module.ts file.

Open app.component.html file and insert the following code.

<div class="d-flex flex-column flex-md-row align-items-center p-3 px-md-4 mb-3 bg-white border-bottom shadow-sm fixed-top">
  <h5 class="my-0 mr-md-auto font-weight-normal">Laravel Angular JWT Auth</h5>
  <nav class="my-2 my-md-0 mr-md-3">
    <a class="p-2 text-dark" routerLink="/profile" *ngIf="isSignedIn">User Profile</a>
    <a class="p-2 text-dark" *ngIf="!isSignedIn" routerLink="/login">Log in</a>
    <a class="p-2 text-dark" routerLink="/register">Register</a>
  </nav>
  <button class="btn btn-outline-primary" (click)="signOut()" *ngIf="isSignedIn">Log out</button>
</div>

<router-outlet></router-outlet>

Start The Laravel App

Start the local web server, get inside the laravel project folder and run the app.

cd backend && php artisan serve

Start The Angular App

Head over to angular project folder and run the app.

cd frontend && ng serve --open

Conclusion

We have completed the Laravel and Angular tutorials. In this tutorial, we learned how to authenticate with the JWT token. A user can register, signin, and view user profiles securely.

In the upcoming tutorial, we will also learn to build forget and reset passwords, log in with social media platforms, route protection with guards, and sending a verification email functionality.

Anyway, If you are new to Laravel and Angular development, after completing this tutorial, you will be able to comprehend almost the entire user authentication process.

You can get the complete code of this tutorial on GitHub.