Protege tu api .NET y tu aplicación Angular con Keycloak(Pt. 2)
Ha pasado mucho tiempo desde la última vez, pero vengo a reivindicarme. Esta es la segunda entrega de esta temática y espero la disfrutes(No hay devolución de dineros!!)
En el artículo anterior (Parte 1) cubrimos como configurar Keycloak y como preparamos nuestro backend NET6 para apalancar la autenticación con los tokens emitidos por Keycloak. Ahora veremos como preparar nuestra aplicación Angular para autenticar al usuario y consumir los servicios del backend.
Para los impacientes, aqui esta el acceso al repositorio con los fuentes del backend y el frontend https://github.com/cheoalfredo/net-angular-keycloak.
Bueno, en esencia el flujo es como sigue :
- El cliente(nuestra aplicación Angular) que se presenta ante Keycloak ya fué creado y se llama “front”. Esta aplicación, a demanda, “navegará” a Keycloak para pedirle que autentique un usuario.
- Keycloack presenta al usuario un formulario para el inicio de sesión, el usuario presenta las credenciales y si todo está bien, Keycloak devuelve el usuario a la aplicación Angular con código que luego el cliente redime por un token JWT el cual contiene información útil para la aplicación y los servicios del backend. Esta información fué la que se acordó compartir a traves del Scope que se negocia con Keycloak y podría contener entre otros : nombre, email, roles, etc.
- El cliente (aplicación Angular) ahora tiene un token que se puede usar para consumir servicios del backend que construimos en el artículo anterior (Parte 1).
Claro el flujo, entonces empecemos a construir nuestra aplicación Angular, esta aplicacion sencilla nos presentará los claims (JWT Claims) contenidos en el token JWT y nos permitirá consumir un endpoint de backend al cual solo se puede acceder autenticado. Apoyandonos en Angular CLI, creamos la aplicación e instalamos 2 paquetes para dar soporte oauth/oidc y poder procesar tokens JWT y ademas lanzamos Visual Studio Code para editar nuestra aplicación (elige el editor de tu preferencia):
ng new securedfront --routing true
cd securedfront
npm i angular-oauth2-oidc
npm i jwt-decode
code .
Ahora procedemos a instrumentar nuestra aplicación para poder comunicarse con Keycloak.
En app.module.ts, vamos a cargar el soporte para usar el cliente http de angular via HttpClientModule y ademas el modulo OAuthModule y lo cargamos al inicio (no de forma lazy) y establecemos el parametro sendAccessToken del objeto resourceServer para enviar el token JWT en la cabecera de Authorization en las llamadas del cliente http.
Ahora en el app.component.ts(tambien lo puedes llevar a environment), creamos un objeto de tipo AuthConfig encargado de mantener la configuración para conectarnos con nuestro IAM.
A continuación se detallan los valores de los atributos del objeto presentado :
- issuer : la url de nuestro IAM (Keycloak) con el realm creado previamente.
- redirectUri: esta representa la url a la cual Keycloak retorna al usuario posterior al proceso de autenticación, esta se debe configurar cuando se crea el cliente en Keycloak.
- client_id: es el identificador de la aplicación en Keycloak.
- scope: este se creó para poder añadir al token JWT los roles asignados al usuario creado y la audiencia que representa la aplicación en la cual el token generado es válido (“webapp” para el backend).
- responseType: en este caso es code. En esta aplicación estamos usando un flujo de autorización llamado Authorization Code Flow, en esencia Keycloak no retorna al cliente un token JWT, retorna un código que puede ser redimido para obtener un JWT
Para continuar necesitamos un los elementos de interfaz de usuario con la misión de permitir autenticarnos y abandonar la aplicación
Vamos a usar el AppComponent para popular estas funcionalidades, la idea es que una vez el usuario venga redireccionado de Keycloak con un login exitoso, podamos redimir el codigo que nos devuelve Keycloak y obtengamos un token JWT adicionalmente vamos a hacer navegar al usuario a un componente que nos va a “explotar” el token exponiendo los claims que lo componen.
Empecemos por cablear el AppComponent. A continuación inyectamos el servicio OAuthService, Router y HttpClient(el cual usaremos luego para consumir el backend); preparamos el OAuthService con los métodos configure, setupAutomaticSilenceRefresh y loadDiscoveryDocument para luego hacer navegar al usuario a la ruta /userinfo que carga un componente que mas adelante describiremos.
La url que se pasa al método loadDiscoveryDocument expone un conjunto de endpoints OAuth/OpenID con los scopes, llaves públicas usadas para firmar los tokens y otros detalles. Keycloak puede popular este endpoint a partir del atributo issuer asignado al objeto AuthConfig más la especificacion de OpenId Connect, de cualquier forma para Keycloak ese url se arma de la siguiente forma :
scheme + host[:port] + “/auth/realms/” + realm + /”.well-known/openid-configuration"
en nuestro caso
http://localhost:8080/auth/realms/iam/.well-known/openid-configuration
Antes que lo olvide, tenemos otros métodos dentro del componente para disparar los flujos de iniciar/cerrar sesión y consumir el endpoint en la api una vez estemos auténticados.
Esta es la plantilla del AppComponent con menú de iniciar/cerrar sesión.
Como se mencionó antes, vamos a usar un componente para exponer los claims presentes en el token, lo creamos en la misma carpeta del proyecto con el siguiente comando :
ng generate component UserInfo
Este es el contenido del componente :
Y La plantilla :
No olvidemos añadir este componente a las rutas para poder navegar hacia el mismo.
Estamos ready, lancemos la aplicación y probemos el flujo, recordemos ejecutar el backend del proyecto anterior :
ng serve
las credenciales usadas para iniciar sesión son las mismas del usuario creado en la parte 1 de este artículo.
Si te das cuenta proveer tu aplicación con capacidades de autenticación/autorización es relativamente sencillo, sin embargo siempre da sentido tener claridad de conceptos básicos como OAuth u OpenId. Cuentame como te pareció el articulo, que puedo hacer para mejorarlo ?.