JWT Authentication and Authorization with Laravel and Angular

Explanation of the Flow:

  1. Registration Process:

    • User submits registration details.
    • Laravel backend stores the user in the database and returns a JWT token.
  2. Login Process:

    • User submits credentials.
    • Laravel verifies them, generates a token, and sends it back.
  3. Protected Route Access:

    • Angular sends requests with the JWT token in the Authorization header.
    • Laravel validates the token, fetches user details, and responds with the protected data.
  4. Token Expiration:

    • If the token is expired, Laravel responds with 401 Unauthorized, and Angular redirects the user to log in again.


Here’s a complete guide to setting up JWT authentication and authorization in a Laravel and Angular application:


Laravel: Backend Setup

1. Install Laravel and JWT Library

composer create-project --prefer-dist laravel/laravel jwt-auth-api
cd jwt-auth-api
composer require tymon/jwt-auth

2. Publish and Configure JWT

Publish the JWT configuration file:

php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider"

Generate the JWT secret:

php artisan jwt:secret

This will create a .env entry like JWT_SECRET.

3. Update User Model

In app/Models/User.php, add the Tymon\JWTAuth\Contracts\JWTSubject interface and implement its methods:

use Tymon\JWTAuth\Contracts\JWTSubject;

class User extends Authenticatable implements JWTSubject
{
    public function getJWTIdentifier()
    {
        return $this->getKey();
    }

    public function getJWTCustomClaims()
    {
        return [];
    }
}

4. Create AuthController

Generate the controller:

php artisan make:controller AuthController

Update AuthController with login, register, and user details functionality:

use Illuminate\Http\Request;
use App\Models\User;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Auth;
use Tymon\JWTAuth\Facades\JWTAuth;

class AuthController extends Controller
{
    public function register(Request $request)
    {
        $validated = $request->validate([
            'name' => 'required|string|max:255',
            'email' => 'required|string|email|max:255|unique:users',
            'password' => 'required|string|min:6|confirmed',
        ]);

        $user = User::create([
            'name' => $validated['name'],
            'email' => $validated['email'],
            'password' => Hash::make($validated['password']),
        ]);

        $token = JWTAuth::fromUser($user);

        return response()->json(['user' => $user, 'token' => $token]);
    }

    public function login(Request $request)
    {
        $credentials = $request->only('email', 'password');

        if (!$token = Auth::attempt($credentials)) {
            return response()->json(['error' => 'Unauthorized'], 401);
        }

        return response()->json(['token' => $token]);
    }

    public function me()
    {
        return response()->json(Auth::user());
    }
}

5. Set Up Routes

Update routes/api.php:

use App\Http\Controllers\AuthController;

Route::post('/register', [AuthController::class, 'register']);
Route::post('/login', [AuthController::class, 'login']);
Route::middleware('auth:api')->get('/me', [AuthController::class, 'me']);

6. Set Up Middleware

In app/Http/Kernel.php, add:

'auth:api' => \Tymon\JWTAuth\Http\Middleware\Authenticate::class,

Angular: Frontend Setup

1. Install Angular and JWT Libraries

ng new jwt-auth-angular
cd jwt-auth-angular
npm install @auth0/angular-jwt
npm install bootstrap

Update angular.json to include Bootstrap CSS:

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

2. Set Up Authentication Service

Generate a service:

ng generate service auth

Update auth.service.ts:

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

@Injectable({
  providedIn: 'root'
})
export class AuthService {
  private apiURL = 'http://localhost:8000/api';
  private jwtHelper = new JwtHelperService();

  constructor(private http: HttpClient) {}

  register(user: any): Observable<any> {
    return this.http.post(`${this.apiURL}/register`, user);
  }

  login(credentials: any): Observable<any> {
    return this.http.post(`${this.apiURL}/login`, credentials);
  }

  isLoggedIn(): boolean {
    const token = localStorage.getItem('token');
    return token ? !this.jwtHelper.isTokenExpired(token) : false;
  }

  getToken(): string | null {
    return localStorage.getItem('token');
  }
}

3. Set Up Interceptor

Generate an interceptor:

ng generate interceptor auth

Update auth.interceptor.ts:

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

@Injectable()
export class AuthInterceptor implements HttpInterceptor {
  constructor(private authService: AuthService) {}

  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    const token = this.authService.getToken();
    if (token) {
      request = request.clone({
        setHeaders: {
          Authorization: `Bearer ${token}`
        }
      });
    }
    return next.handle(request);
  }
}

Update app.module.ts to provide the interceptor:

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

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

4. Create Login and Register Components

Generate components:

ng generate component register
ng generate component login

Implement forms for registration and login in their respective components and use AuthService to handle API calls.

5. Protect Routes

Update app-routing.module.ts:

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

const routes: Routes = [
  { path: 'login', component: LoginComponent },
  { path: 'register', component: RegisterComponent },
  { path: 'dashboard', component: DashboardComponent, canActivate: [AuthGuard] },
  { path: '', redirectTo: '/login', pathMatch: 'full' }
];

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

Generate an AuthGuard:

ng generate guard auth

Update auth.guard.ts:

import { Injectable } from '@angular/core';
import { CanActivate, Router } from '@angular/router';
import { AuthService } from './auth.service';

@Injectable({
  providedIn: 'root'
})
export class AuthGuard implements CanActivate {
  constructor(private authService: AuthService, private router: Router) {}

  canActivate(): boolean {
    if (this.authService.isLoggedIn()) {
      return true;
    }
    this.router.navigate(['/login']);
    return false;
  }
}

With these steps, you have a fully functional JWT authentication and authorization setup in Laravel and Angular!

Comments

Popular posts from this blog

Spring Boot OpenAI Integration: Step-by-Step Guide

Orchestration-Based Saga Architecture and Spring Boot Microservices Implementation Guide

Spring Boot 3 + Angular 15 + Material - Full Stack CRUD Application Example