--- title: 'Esquema de Autenticación y Autorización' --- Esquema de Autenticación y Autorización Frontend === * **Coordinador Tecnológico**: Rafael Palau * **Arquitecto**: Horacio Nemeth * **Consultores**: * Ilse Grau * Julio Mello * Marco Aquino * Marcos Benítez * Lauro Segovia --- ## Contenido [TOC] --- ## Introducción El documento presenta el esquema de interacción de la gestión de autenticación y autorización del proyecto frontend. --- ## OpenID Connect Protocol OpenID Connect (OIDC) es una capa de identidad construida sobre el Framework OAuth 2.0. Permite a las aplicaciones de terceros verificar la identidad del usuario final y obtener información básica del perfil del usuario. *OIDC* utiliza tokens web JSON (JWTs), que se pueden obtener mediante flujos conformes a las especificaciones de *OAuth 2.0*. --- ## OpenID vs OAuth2 El Framework *OAuth 2.0* trata sobre el acceso y la compartición de recursos, *OIDC* trata sobre la autenticación de usuarios. Su objetivo es proporcionar un único inicio de sesión para múltiples sitios. Cada vez que tenga que iniciar sesión en un sitio web utilizando *OIDC*, se le redirigirá a su sitio *OpenID* donde iniciará la sesión, y luego se le llevará de nuevo al sitio web. --- ## OpenID vs JWTs Los JWT contienen *claims*, que son afirmaciones (como el nombre o la dirección de correo electrónico) sobre una entidad (normalmente, el usuario) y metadatos adicionales. La especificación de *OpenID Connect* define un conjunto de *claims* estándar. El conjunto de *claims* estándar incluye el nombre, el correo electrónico, el sexo, la fecha de nacimiento, etc. Sin embargo, se puede agregar información adicional al token como parte de las entradas del *claims*. --- ## Flujo de Autenticación En el esquema autenticación utilizando *token JWT*, no se gestiona sesiones propiamente sino *Token* que permiten al usuario interactuar con los APIs REST desde cualquier aplicación. JSON Web Token (JWT) implementa la autenticación distribuida basada en reclamaciones (claims) que puede ser firmada digitalmente. Esto significa que una vez que se valida la identidad de un usuario (es decir, una contraseña en un formulario de acceso), reciben un *token* de reclamación codificado que se puede utilizar para realizar futuras solicitudes a los servicios REST sin tener que volver a validar la identidad del usuario. El servidor puede verificar de forma independiente la validez de esta reclamación y procesar las solicitudes sin requerir ningún conocimiento previo de haber interactuado con este usuario. > JWT implementa la RFC 7519, Internet Engineering Task Force (IETF) : https://datatracker.ietf.org/doc/html/rfc7519 --- ### Flujo JWT Los *token JWT* complementan una arquitectura de API REST sin estado con un mecanismo que permite la gestión de una autenticación y autorización distribuidas. Hay tres componentes principales de un esquema de autenticación basado en tokens: * Cliente : Captura la información del usuario. * Servidor : Valida todas las peticiones realizadas por el cliente. * Proveedor de Identidades : Genera y verifica la valides de un token y chequea el estado del usuario desde un *data store* local o externo. --- :::info En un esquema seguro es necesario que los datos enviados/recibidos entre clientes (aplicaciones y navegadores), sistemas (servidores y servicios) y bases de datos esten encriptados utilizando la seguridad en la capa de transporte (TLS). Esto significa que las APIs REST deben estar expuestas con un certificado SSL, sirviendo todas las llamadas a la API a través de HTTPS, de modo que las credenciales de los usuarios nunca queden expuestas entre el cliente y el servidor. Del mismo modo, cualquier llamada a una base de datos o servicios de terceros require TLS. Esto podrá garantizar la seguridad de los datos en tránsito. ::: --- ### La gestión de la Autenticación > Todos los archivos vinculados al proceso de gestión de autenticación y procesamiento del token se encuentran en el directorio : *src/app/auth* --- ### Esquema de interacción de la aplicación Angular/SPA ![](https://i.imgur.com/1n0RTZD.png) --- ### Esquema de interacción proyecto backend ![](https://i.imgur.com/ytcUNfL.png) --- ### Esquema de clase de gestión de autenticación ![](https://i.imgur.com/eibNYPw.png) --- * Esquema de interacción ![](https://i.imgur.com/gK8ev5D.png) `Flujo de interacción :` 1. Solicitar el acceso al recurso */login* 2. Correspondencia de ruta relativa */login* para acceso al componente de Login > archivo : src/app/app-routing.module.ts ```java= const rootRoutes : Routes = [ {path: 'login', component: LoginComponent}, { path: '', component: PagesComponent}, ] ``` 4. Capturar datos de usuario y contraseña para enviar al servicio que gestiona el llamado al backend 5. Retorna el Observable de autenticacion con los datos del token > El proceso de gestión de autenticacion cuenta con clases de soporte para > realizar llamada al backend y procesar el token recibido en base a una estructura predefinida > los archivos son : * CustomAuthServiceService * TokenValue * TokenPayLoad * Institucion * Perfil * CacheService 7. Redirige la llamada al *path principal* en caso que tenga los permisos de acceso 8. Redirige a la página principal --- ### Archivos de estructura de dato * Token Value * TokenPayload * Institucion * Perfil * AuthStatus --- ### Archivos de gestión de autorización * AuthService * CustomAuthService * AuthGuard --- ### Gestión de Autorización En la gestión de acceso por componente se utiliza la verificación de roles con Guards del Angular : * **Router guards** : permite desacoplar la implementación del control de autorización * **CanActive** y **CanActiveChild** : utilizado para verificar el permiso de acceso a un componente * **CanDeactive** : utilizado para verificar el permiso antes de salir de un componente * **Resolve** : permite agregar datos via router antes de llamar al componete * **CanLoad** : permite agregar logica antes de cargar un componente --- ### Auth guards Los **Guards**, permiten el control de navegación para evitar que accidentalmente un usuario pueda navegar a un módulo o componente que no tiene permiso o que requiere datos para presentarlo al usuario. --- ### Auth guards Angular no sabe si una ruta en particular es accesible para un usuario o no y, sin un **AuthGuard** , se mostrará la página solicitada y lanzará peticiones al servidor que terminarán fallando. --- ```java= import { Injectable } from '@angular/core'; import { ActivatedRouteSnapshot, CanActivate, CanActivateChild, CanLoad, Route, Router, RouterStateSnapshot, UrlSegment, UrlTree } from '@angular/router'; import { Observable } from 'rxjs'; import { map, take } from 'rxjs/operators'; import { CustomToastService } from '../global-service/custom-toast.service'; import { MENU_URLS } from '../util/routes'; import { AuthService, AuthStatus } from './auth.service'; @Injectable({ providedIn: 'root' }) export class AuthGuard implements CanActivate, CanLoad, CanActivateChild { constructor( private authService: AuthService, private router: Router, private mensajeEmergenteService: CustomToastService, ) { } canActivateChild(childRoute: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean | UrlTree | Observable<boolean | UrlTree> | Promise<boolean | UrlTree> { return this.verificarPermisoAcceso(childRoute); } canLoad(route: Route, segments: UrlSegment[]): boolean | UrlTree | Observable<boolean | UrlTree> | Promise<boolean | UrlTree> { return this.verificarPermisoAcceso(); } canActivate( route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree { return this.verificarPermisoAcceso(route); } protected verificarPermisoAcceso(route?: ActivatedRouteSnapshot): Observable<boolean> { return this.authService.obtenerAutenticacion().pipe(map((auth: AuthStatus) => { const isRol = this.verificarCorrespondenciaRol(auth.rol, route); if (!auth.isAuthenticated) { this.router.navigate(['/login']); }else if(!isRol && auth.isAuthenticated){ this.router.navigate(['/' + MENU_URLS.PRINCIPAL.PATH]); this.mensajeEmergenteService.showError('NO POSEE PERMISO PARA ACCEDER AL RECURSO'); } return isRol; }), take(1) ); } private verificarCorrespondenciaRol(usuarioRoles: Array<string>, route?: ActivatedRouteSnapshot) { const isRol = usuarioRoles.includes(route?.data?.rol); if (isRol) { return true } else { return false; } } } ``` --- ### Registro de AuthGuard ![](https://i.imgur.com/e3h5f40.png) ### Gestión de Autenticación > La clase abstracta expone metodos para la gestión de la autenticación y procesamiento del token ```java export abstract class AuthService extends CacheService { login(usuario: string, contrasenha: string): Observable<any> { } logout(limpiarToken?: boolean): void { } obtenerUsuario(): Observable<any> { } obtenerToken(): string { } protected guardarToken(token: string) { } protected limpiarToken() { } protected isTokenExpirado(): boolean { } recargarAuthStatus(): void { } obtenerAutenticacion(): Observable<any>{ return this.authStatus.asObservable(); } obtenerPerfil(): Observable<any>{ } abstract authProvider(usuario: string, contrasenha: string): Observable<any>; abstract procesarTokenJWT(token: TokenPayLoad): Observable<AuthStatus>; abstract setPerfilActual(perfil: any): void; ```