Je vais partir du principe que vous avez déjà votre Front avec Angular 10 et votre Back avec ExpressJS 4 et je ne montrerais pas la partie de création de ces deux projets.
Création du formulaire de login sous Angular
Le côté back du formulaire :
Rendez vous sur votre fichier formulaire.component.ts (Faites en fonction de vos composants…) et nous allons le construire comme sur l’exemple :
import { Component, OnInit } from '@angular/core'; // Import du constructeur de formulaire import { FormBuilder } from '@angular/forms'; @Component({ selector: 'app-login-form', templateUrl: './login-form.component.html', styleUrls: ['./login-form.component.scss'] }) export class LoginFormComponent implements OnInit { // Nos champs requis dans le formulaire email: String = ''; password: String = ''; // Le formulaire loginForm; constructor(private formBuilder: FormBuilder) { // Lors de la création du composant nous définissons le formulaire, sans validation, le tutoriel ne traitera pas de cette partie this.loginForm = this.formBuilder.group({ email: '', password: '' }); } ngOnInit(): void {} }
Nous voyons que nous importons le constructeur de formulaire qui n’est d’autre que le FormBuilder, nous instancions nos variables de formulaire et nous définissons la base du formulaire à partir du FormBuilder.
Le côté front du formulaire :
<div id="login"> <h1>Login form</h1> // La notion formGroup défini vers quel variable est construit le formulaire dans le côté back <form [formGroup]="loginForm"> <div class="form-input"> <label for="email">Email</label> // La notion formControlName permet de dire à notre formBuilder vers quelle variable les données du input sont liées // Le tag [(ngModel)]="email" permet lors de la modification de l'email dans l'input, d'actualiser la données de la variable <input type="text" formControlName="email" [(ngModel)]="email"> </div> <div class="form-input"> // Voir email ce sont les mêmes explications <label for="password">Password</label> <input type="password" formControlName="password" [(ngModel)]="password"> </div> // Le (click)="submit" indique que lors du clic sur le bouton, la fonction submit() se lancera <button type="button" (click)="submit()">Submit</button> </form> </div>
Nous créeons un formulaire basique avec les directives formGroup, formControlName et ngModel qui sont nouvelles et expliquées dans le code, nous allons poursuivre et créer le service de login qui va intéragir avec le serveur ExpressJS
Service de login et fonction de soumission au serveur :
Avant d’écrire la fonction de soumission des identifiants au serveur, il faut écrire un service de login. Cela nous permettra si on le désire de le réutiliser à un autre endroit de nos composants Angular.
import { Injectable } from '@angular/core'; // Permet d'envoyer des requêtes http import { HttpClient } from '@angular/common/http'; @Injectable({ providedIn: 'root' }) export class LoginService { // L'utilisateur sera stocké ici currentUser; // Son token JWT ici token; // l'url vers l'api, ne pas oublier le http:// /!\ urlApi = 'http://localhost:3000/api/'; constructor(private http: HttpClient) { } login(data) { // Cette fonction sera appelée dans notre soumission de formulaire, elle permet d'appeler l'api en post sur la route 'http://localhost:3000/api/login', le fait de retourner this.http.post renverra un Observable que l'on pourra souscrire pour recevoir les données dans le formulaire return this.http.post(this.urlApi + 'login', data); } }
Maintenant que nous avons notre service qui est réutilisable, ne pas oublier de l’importer dans notre formulaire.component.ts pour pouvoir l’appeler.
Il est maintenant temps de créer la fonction de soumission.
import { Component, OnInit } from '@angular/core'; import { FormBuilder } from '@angular/forms'; import { LoginService } from '../services/login-service.service' @Component({ selector: 'app-login-form', templateUrl: './login-form.component.html', styleUrls: ['./login-form.component.scss'] }) export class LoginFormComponent implements OnInit { email: String = ''; password: String = ''; loginForm; currentUser; // Nous appelons le loginService pour faire appel à sa fonction login constructor(private formBuilder: FormBuilder, private loginService: LoginService) { this.loginForm = this.formBuilder.group({ email: '', password: '' }); } ngOnInit(): void {} // Fonction lancée au submit du formulaire submit(){ // Nous créeons un objet data avec les champs du formulaire rempli let data = { email: this.email, password: this.password } // Nous appelons le login service sur la méthode login avec l'objet data // Le login service va envoyer la requête HTTP en post au serveur avec les données de l'utilisateur // Nous appliquons la méthode subscribe qui va écouter l'observable retourné // La première fonction (value) est le retour de la valeur, il existe aussi la deuxième (err) qui permet de gérer l'erreur et ne troisième () => {} qui permet de définir du code lorsque le subscriber est fini this.loginService.login(data).subscribe( (value) => { this.loginService.currentUser = value this.loginService.token = value.token, this.currentUser = value localStorage.setItem('token', value.token) }, (err) => console.log(err) ) } }
Si vous n’êtes pas familier avec les Observables vous pouvez allez sur la documentation officielle Angular qui vous l’expliquera.
Maintenant que nous avons notre formulaire construit, un service qui interroge le serveur, et un subscriber qui nous retourne la donnée du serveur, nous pouvons maintenant travailler sur le serveur en lui même.
Serveur ExpressJS
Comme pour le front d’Angular, je vais rentre dans le vif du sujet tout de suite, si besoin de connaitre ExpressJS, allez sur le site directement.
Pour faire fonctionner notre authentification nous avons besoin de la librairie mysql et celle de JWT pour générer un token. Je vous laisse les installer pour continuer la suite.
Les requêtes Cross Origin peuvent aussi gêner, installez CORS
Service de la base de données MySQL
Voici le service de BDD a mettre dans un fichier à part pour appeler la base de données facilement.
var mysql = require('mysql'); var connection = mysql.createConnection({ host : 'localhost', user : 'root', password : '', database : 'angular-express' }); connection.connect(function(err) { if (err) throw err; }); module.exports = connection;
Remplacez avec vos données.
Le serveur
const express = require('express') // Permet de recevoir les requêtes en Cross origin const cors = require("cors"); const app = express() const port = 3000 // Permet de convertir les données reçues en HTTP en variable lisable const bodyParser = require('body-parser'); //Permet d'utiliser la base de données, changez en fonction de vous var db = require('./database'); // Permet d'utiliser le JWT var jwt = require('jsonwebtoken'); // On convertie les données reçues app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: false })); // On permet de cross origin (Angular en port 4200 et l'api en 3000...) app.use(cors()); // Notre fameuse route de login app.post('/api/login', (req, res) => { // On recupère les variables envoyées par notre formulaire à partir du service de login let { email, password } = req.body; // On instancie un variable qui accueil l'utilisateur let user; // Nous faisons notre requête nous vérifier que l'utilisateur existe bien sous l'email envoyée db.query('SELECT * FROM user WHERE email = ?', [email], function(err, result) { if (err) throw err; // On stocke notre utilisateur dans notre variable user = result[0] // Petite parenthèse, pour ce tuto les mdp ne sont pas hashé dans le base, ce n'est pas le sujet, alors nous comparons sans décrypter les mots de passe if(user.password == password){ // Si toutes les informations sont ok alors on génère le jwt avec les données de l'utilisateur (on evite de mettre le mot de passe c'est pas sécurisé), et on ajoute un mot clé 'secret', à changer selon vos envies, il faut qu'il soit secret justement, on définit le jwt valide une heure var token = jwt.sign({ id: user.id, email: user.email, name: user.name }, 'secret', { expiresIn: '1h' }); // On donne le token à l'user et on retourne l'user user.token = token res.send(user) } else { // Sinon on prévient l'utilisateur que personne n'est trouvé res.send(["User don't find"]) } }); }) // Le serveur écoute app.listen(port, () => { console.log(`Example app listening at http://localhost:${port}`) })
Tout est bon, pour ma part voici mes données de test.
Mon formulaire (un peu stylisé) :
Pour vérifier l’état de la requête retournée par votre serveur, ouvrez la console de développeur de votre navigateur (Ctrl MAJ + I sous chrome) et allez dans network.
Soumettez le formulaire et vous aurez un aperçu de la requête envoyée.
Nous voyons vers quelle URL la reqûete a été envoyée, la méthode, et le statut. Allez dans l’onglet Preview pour voir les données retournée par ExpressJS.

Nous avons bien reçue les données de notre utilisateur avec ce fameux token JWT, que nous pouvons décrypter sur le site JWT.io
D’où l’intéret de ne pas stocker le mot de passe dans le JWT ! Nous avons également les donnée iat pour quand est ce que le token a été crée et exp pour son expiration.
Notre formulaire est maintenant capable de recevoir l’utilisateur qui souhaite se connecter, lui attribuer un token qui prouve que l’utilisateur a bien saisi ses identifiants, et le service login a stocké tout ca en mémoire pour être réutilisé où l’on veut, il est maintenant temps de voir les routes de l’api sécurisées via le token.
Routes sécurisées via JWT sur ExpressJS
Prenons cette route en exemple :
app.get('/api/dataNeedLogged', (req, res) => { res.send({ message: "You are logged with JWT you can see the data"}) })
Elle nous renvoi un simple message mais l’a n’est pas le sujet, elle n’a rien de différent de la route de login pour le moment, nous allons la sécuriser. Il faut mettre en place un Middleware d’authentification JWT. Si je devais expliquer en gros qu’est ce que le Middleware, c’est comme une fonction qui se lance avant votre fonction principale, voir exemple :
//Nous importons notre middleware let authMiddleware = require('./auth_jwt.middleware') //Nous ajoutons notre middleware AVANT la fonction principale de notre route app.get('/api/dataNeedLogged', authMiddleware, (req, res) => { res.send({ message: "You are logged with JWT you can see the data"}) })
Contenu du middleware :
//Imports des dependances const jwt = require("jsonwebtoken") const db = require('./database') //Voici la fonctionqui va être lancée avant la fonction principale de la route //Elle dispose des mêmes paramètres que notre route à une EXCEPTION, le paramètre NEXT, il est obligatoire et permet de dire au MIDDLEWARE ok tu peux passer à la fonction principale const auth = (req, res, next) => { try { // Nous récupérons le token de la requête HTTP const token = req.header('Authorization').replace("Bearer ", "") // On décode le token avec la clef "secret", remplacez avec votre clef secrète ! const decoded = jwt.verify(token, "secret") //Grâce au token décodé nous avons l'id de l'utilisateur, nous pouvons donc le retrouver //et juger si oui ou non il a le droit d'accès db.query('SELECT * FROM user WHERE id = ?', [decoded.id], function (err, result) { if (err) throw err; user = result[0] //Si aucun user trouvé on passe à l'erreur 401 if (!user) { throw new Error() } req.token = token req.user = user //On passe à la requête principale next() }); } catch (e) { res.status(401).send({ error: 'Please authenticate.' }) } } //On exporte la fonction pour l'inclure avant notre fonction de route module.exports = auth;
Il faut maintenant qu’Angular nous envoie à chaque requête le token d’authentification, cela se gère avec un interceptor.
Angular, ajouter le token JWT à la requête HTTP
Voici la classe d’interceptor qui va permettre d’ajouter le token à nos requêtes HTTP :
import {Injectable} from '@angular/core'; import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest } from '@angular/common/http'; import { Observable } from 'rxjs'; @Injectable() export class AuthInterceptor implements HttpInterceptor { constructor() {} //Avant l'envoi d'une requête HTTP l'interceptor va ajouter le token à l'authentification intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { //Récupération du token const token = localStorage.getItem('token'); //On clone la requête en y ajoutant un headers Authorization avec la chaine de caractères Bearer suivie du token req = req.clone({ setHeaders: { 'Authorization': `Bearer ${token}` }, }); return next.handle(req); } }
Pour qu’il intercepte toutes les requête il faut aller dans app.module.ts et ajouter le provider suivant :
//On importe HTTP_INTERCEPTORS import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http'; //Et notre interceptor import { AuthInterceptor } from './interceptor/auth.interceptor' ... ... ... //Et on dit à Angular de l'utiliser providers: [ {provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true}, ],
Voilà nos requêtes venant d’Angular sont prêtes à communiquer avec les routes sécurisée de l’API ExpressJS, le tutoriel est maintenant fini.
Merci d’avoir lu, n’hésitez pas à partager !