JWT Authentication and Authorization with Python Flask and Angular: Complete Guide

Explanation of Flow:

  1. User Registration:

    • The user submits the registration form in the Angular frontend, which sends the details to the AuthService.
    • The AuthService then communicates with the Flask backend to register the user and receives a success response.
  2. User Login:

    • The user submits the login form, and the Angular frontend calls the AuthService with the login credentials.
    • The AuthService sends these credentials to the Flask backend, where they are verified against the UserModel.
    • If successful, Flask generates a JWT token using JWTManager, sends it back to the AuthService, which stores the token in localStorage.
  3. Accessing Protected Route:

    • The user tries to access a protected route. Angular checks if the user is authenticated by verifying the JWT token stored in localStorage.
    • Angular sends the token in the request to Flask. Flask verifies the token using JWTManager and, if valid, returns the protected content to Angular, which then displays it to the user.

This sequence diagram helps visualize the entire JWT-based authentication and authorization flow between the frontend and backend.


Here's a step-by-step guide to implement JWT Authentication and Authorization with Python Flask for the backend and Angular for the frontend.

1. Set Up Python Flask Backend

1.1 Install Required Packages

Start by setting up the virtual environment and installing the necessary libraries:

python -m venv venv
source venv/bin/activate  # On Windows use `venv\Scripts\activate`
pip install Flask Flask-JWT-Extended Flask-SQLAlchemy

1.2 Set Up Flask Application

Create the Flask app structure.

flask_jwt_app/ ├── app.py ├── models.py └── config.py

1.3 Flask Configuration (config.py)

Configure the Flask app with JWT settings.

import os

class Config:
    SECRET_KEY = os.urandom(24)
    SQLALCHEMY_DATABASE_URI = 'sqlite:///users.db'
    SQLALCHEMY_TRACK_MODIFICATIONS = False
    JWT_SECRET_KEY = 'your_jwt_secret'  # Change this in production!

1.4 Database Model (models.py)

Create a simple User model for registration and login.

from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()

class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True, nullable=False)
    password = db.Column(db.String(120), nullable=False)

1.5 Flask App (app.py)

Set up the routes for registration, login, and protected routes using JWT.

from flask import Flask, request, jsonify
from flask_jwt_extended import JWTManager, create_access_token, jwt_required, get_jwt_identity
from models import db, User
from config import Config

app = Flask(__name__)
app.config.from_object(Config)
db.init_app(app)
jwt = JWTManager(app)

@app.route('/register', methods=['POST'])
def register():
    data = request.get_json()
    username = data.get('username')
    password = data.get('password')
    
    if User.query.filter_by(username=username).first():
        return jsonify({"msg": "User already exists"}), 400
    
    new_user = User(username=username, password=password)
    db.session.add(new_user)
    db.session.commit()
    
    return jsonify({"msg": "User registered successfully"}), 201

@app.route('/login', methods=['POST'])
def login():
    data = request.get_json()
    username = data.get('username')
    password = data.get('password')
    
    user = User.query.filter_by(username=username).first()
    if not user or user.password != password:
        return jsonify({"msg": "Bad credentials"}), 401
    
    access_token = create_access_token(identity=username)
    return jsonify(access_token=access_token), 200

@app.route('/protected', methods=['GET'])
@jwt_required()
def protected():
    current_user = get_jwt_identity()
    return jsonify(logged_in_as=current_user), 200

if __name__ == '__main__':
    app.run(debug=True)

1.6 Run the Flask App

Now, run the Flask app:

python app.py

Your backend is ready to authenticate and authorize users using JWT.


2. Set Up Angular Frontend

2.1 Install Angular CLI and Create Project

If you haven’t already, install Angular CLI and create a new project.

npm install -g @angular/cli
ng new jwt-auth-app
cd jwt-auth-app

2.2 Install Dependencies

Install the @auth0/angular-jwt library to help with JWT handling in Angular.

npm install @auth0/angular-jwt

2.3 Create AuthService for JWT Handling

In your Angular project, create a service to manage authentication.

ng generate service services/auth

Edit auth.service.ts to handle login and token storage.

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

@Injectable({
  providedIn: 'root'
})
export class AuthService {
  private apiUrl = 'http://localhost:5000';  // Flask API URL
  private jwtHelper = new JwtHelperService();

  constructor(private http: HttpClient) {}

  login(username: string, password: string): Observable<any> {
    return this.http.post(`${this.apiUrl}/login`, { username, password });
  }

  register(username: string, password: string): Observable<any> {
    return this.http.post(`${this.apiUrl}/register`, { username, password });
  }

  isAuthenticated(): boolean {
    const token = localStorage.getItem('access_token');
    return token && !this.jwtHelper.isTokenExpired(token);
  }

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

  setToken(token: string): void {
    localStorage.setItem('access_token', token);
  }

  logout(): void {
    localStorage.removeItem('access_token');
  }
}

2.4 Create Auth Interceptor

To automatically send the JWT token with requests, create an HTTP interceptor.

ng generate interceptor services/auth

In auth.interceptor.ts, intercept requests and add the JWT token in the authorization header.

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

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

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

2.5 Update App Module

Modify app.module.ts to include the interceptor and other necessary modules.

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
import { AppComponent } from './app.component';
import { AuthService } from './services/auth.service';
import { AuthInterceptor } from './services/auth.interceptor';

@NgModule({
  declarations: [AppComponent],
  imports: [BrowserModule, HttpClientModule],
  providers: [
    AuthService,
    { provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true }
  ],
  bootstrap: [AppComponent]
})
export class AppModule {}

2.6 Create Login and Registration Components

Create components for login and registration forms.

ng generate component login
ng generate component register

For login.component.ts, use the AuthService to log in users.

import { Component } from '@angular/core';
import { AuthService } from './services/auth.service';
import { Router } from '@angular/router';

@Component({
  selector: 'app-login',
  templateUrl: './login.component.html',
})
export class LoginComponent {
  username = '';
  password = '';

  constructor(private authService: AuthService, private router: Router) {}

  login(): void {
    this.authService.login(this.username, this.password).subscribe(
      (response: any) => {
        this.authService.setToken(response.access_token);
        this.router.navigate(['/protected']);
      },
      (error) => {
        console.error('Login failed', error);
      }
    );
  }
}

2.7 Protect Routes

Use route guards to protect specific routes (e.g., /protected).

ng generate guard auth

In auth.guard.ts, check if the user is authenticated.

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

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

  canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
    if (this.authService.isAuthenticated()) {
      return true;
    } else {
      this.router.navigate(['/login']);
      return false;
    }
  }
}

This setup ensures JWT-based authentication and authorization between your Flask backend and Angular frontend. The backend handles user registration, login, and token generation, while the frontend stores the token in localStorage, sends it with requests, and uses route guards to protect specific pages.

Popular posts from this blog

Learn Java 8 streams with an example - print odd/even numbers from Array and List

Java Stream API - How to convert List of objects to another List of objects using Java streams?

Registration and Login with Spring Boot + Spring Security + Thymeleaf

Java, Spring Boot Mini Project - Library Management System - Download

ReactJS, Spring Boot JWT Authentication Example

Top 5 Java ORM tools - 2024

Java - Blowfish Encryption and decryption Example

Spring boot video streaming example-HTML5

Google Cloud Storage + Spring Boot - File Upload, Download, and Delete