Cómo utilizar JWT en Angular de forma fácil

Aprende cómo implementar el uso de JWT para sesiones en Angular de forma fácil.

Creo que sin querer estuve generando tutoriales que se pueden conectar para generar sitios web, pero me puse a pensar que me hace falta esta sección, por lo que antes de empezar quiero darte los pasos previos si los necesitas para llegar hasta este punto, si no, puedes saltarte esta parte e ir “directo al grano”:

Lo primero es tener un API que pues nos permita mover datos:

Después implementarle el uso de JWT:

Después conectar esta API local (o prod) a nuestro ambiente:

Posteriormente entender qué es el APP_INITIALIZER de Angular:

Y por último los HTTP Interceptors:

Obvio, necesitas saber cómo hacer peticiones HTTP, pero dejaré este hueco para otro tutorial más tarde. Entonces, si ya sabes todo esto manos a la obra.

Lo primero que necesitamos es la dependencia (ni tan necesaria realmente pero somos flojos y preferimos algo ya creado) para validar nuestra token.

Para ello instalamos @auth0/angular-jtw como dependencia con el comando:

npm install @auth0/angular-jwt

Ya teniendo este, vamos a nuestro APP_INITIALIZER (que deberemos crear previamente) y agregar lo siguiente:

const token = localStorage.getItem('auth_token') === null ? false : localStorage.getItem('auth_token');
if (token !== false) {
  try {
    if (helper.isTokenExpired(token) === true) {
      localStorage.removeItem('auth_token');
    }
  } catch (exception) {
    localStorage.removeItem('auth_token');
  }
}
resolve(true);

El resolve(true) lo pueden poner en otra parte dependiendo el cómo vaya su lógica, esto en sí es cómo iría la lógica super-básica de la promesa para el load() . También hay que considerar que yo nombro la token como auth_token ustedes pueden cambiar esto por lo que gusten.

Y la lógica no es la mejor, pero, para cosas simples nos funciona. A grandes rasgos, simplemente validamos que la token exista y que no esté expirada. En caso de que esté expirada, pues “explotarla”. Aquí pueden, también hacer quizá una petición al API que tengan para que el back-end sea quien también expire o invalide esa token. Es al gusto del cliente.

Ahora que ya tenemos nuestro código para el arranque, vamos a hacer 3 cosas más, la primera será crear un servicio que nos permita como “intermediario” para cada petición y nos integre las peticiones. En este caso es algo muy sencillo e iría de la siguiente forma:

import {Injectable} from '@angular/core';
// Atento a esta línea, deberás hacer que empate con tu environment
import {environment} from '../../../environments/environment';
import {HttpHeaders, HttpClient, HttpRequest} from '@angular/common/http';

import {Observable} from 'rxjs';

const API_URL = environment.apiUrl;

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

  public headers: any;

  public apiUrl = API_URL;

  constructor(
    private http: HttpClient
  ) {
    this.headers = {
      headers: new HttpHeaders({
        'Content-Type': 'application/json',
        Accept: 'application/json'
      })
    };
  }

  public get(url: string): Observable<any> {
    return this.http.get(
      this.apiUrl + url,
      this.headers
    );
  }
}

¿Qué se supone hace este servicio?. Bueno, a grandes rasgos lo que hará será crearnos un intermediario simple entre un servicio que haga peticiones HTTP al API y el API como tal. De esta forma, si necesitamos un dato por ejemplo llamamos este servicio en el que se utilizará en el componente como:

obtenerAlgo() { return this.api.get('/algo').pipe(timeout(30000)); }

Y así pues podemos tener los verbos más comúnes en un servicio y reutilizarlos, además de manejar las cabeceras comúnes y demás.

Como digo, presta atención en el environment y la apiUrl para ello se sigue la guía del proxy, con esto tenemos la URL a la cual conectar.

¿Qué sigue entonces?, nuestro interceptor HTTP. Este como ya sabemos por la guía (o porque ya los conocen) va a inyectar la token, como ya sabemos que desde un inicio es “válida” (o al menos tiene tiempo) la token pues entonces, vamos a incluirla en cada petición.

Para ello agregamos el siguiente código en nuestro interceptor:

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

@Injectable()
export class AuthInterceptor implements HttpInterceptor {

  constructor() {
  }

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    const token = localStorage.getItem('auth_token');

    if (!token) {
      return next.handle(req);
    }

    const request = req.clone({
      headers: req.headers.set('Authorization', `Bearer ${token}`)
    });

    return next.handle(request);
  }
}

A grandes rasgos solo validamos que la token exista y la integramos en la cabecera.

¡Listo!, ¿qué sigue?. Ya tenemos casi todo, faltan 2 cosas:

Hay que hacer el login, esta parte no la explicaré aquí (espero hacer la guía de hacer peticiones HTTP pronto) ya que es una petición HTTP, pero debemos hacer la petición, si nos responde bien nuestro back-end con un acceso, guardar nuestra token en localStorage. Algo como lo siguiente:

// Lógica donde revisen el email sea válido, password o lo que quieran ...
this.loginService.login(email, password).subscribe(response => {
  // Lógica para revisar que el login haya sido exitoso, o inválido o como sea... y por fin, setear la token en el storage.
  localStorage.setItem('auth_token', response.data.token);
});

Una vez que esté guardada, pues entonces, deberemos hacer validaciones, en realidad aquí, no es algo “sumamente necesario” ya que puede haber secciones donde, el acceso sea público, sin embargo, habrá donde no, y para ello usaremos Guards (aquí tampoco lo explico, esperemos otra guía para ello) e incluir la validación de que pueda “activar” la ruta.

Algo como lo siguiente:

if (localStorage.getItem('auth_token') === null) {
  this.router.navigate(['/login'], {queryParams: {returnUrl: state.url}});
  return false;
} else {
  this.user.setLogin(true);
  return true;
}

Donde this.user es un servicio que en mi caso me permite conocer el estado del login por si la token la modifican manualmente, cerrarle la sesión, en tiempo real por ejemplo ya saben la frase “Su sesión expiró” y mandarlo al login.

Entonces, a grandes rasgos con estos simples pasos, pueden implementar una JWT y sus validaciones correspondientes.

Realmente es sumamente sencillo utilizar JWT en Angular, al final es “hacer un login -> si es exitoso -> guardar en localStorage -> y en cada petición -> leer el valor y mandarlo” pero aquí le metimos algunas validaciones extra.

Como adicional y para terminar, pueden implementar un Interceptor para que, si retorna un error de acceso (como con la header 401 unauthorized) le borren la token y lo saquen, más o menos así:

intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
  return next.handle(req).pipe(
    catchError((err: HttpErrorResponse) => {
      if (this.router.url !== '/login' && err.status === 401) {
        this.router.navigate(['/logout']);
        localStorage.removeItem('auth_token'); // O la pueden eliminar en la ruta de "logout"
      }
    })
  );
}

¡Y listo!.

Nos vemos en el siguiente tutorial, donde les mostraré a hacer cómo hacer un sistema para subir archivos con Drag and Drop, el de cómo hacer peticiones HTTP en Angular y quizá el de los Guards si lo veo necesario.

¡Saludos y no te olvides de dejar tu clap!

¿Cuál es tu reacción?
+1
0
+1
0
+1
0
+1
0
+1
1
Total
0
Shares
Publicación anterior

Cómo tener múltiples sitios WordPress con Nginx, PHP y MySQL

Siguiente Publicación

Creando un widget para compartirlo entre webs…

Publicaciones Relacionadas