diff --git a/tutorial keycloack.md b/tutorial keycloack.md
deleted file mode 100644
index 2d94751..0000000
--- a/tutorial keycloack.md
+++ /dev/null
@@ -1,963 +0,0 @@
-# Tutorial: Implementación de Keycloak con LDAP para Angular
-
-Este tutorial explica cómo configurar un sistema de autenticación completo utilizando Keycloak como proveedor de identidad, OpenLDAP como directorio de usuarios y una aplicación Angular que consume estos servicios.
-
-## Índice
-
-1. [Preparación del entorno Ubuntu](#1-preparaci%C3%B3n-del-entorno-ubuntu)
-2. [Instalación y configuración de OpenLDAP](#2-instalaci%C3%B3n-y-configuraci%C3%B3n-de-openldap)
-3. [Crear estructura LDAP para correos.com](#3-crear-estructura-ldap-para-correoscom)
-4. [Instalación y configuración de Keycloak](#4-instalaci%C3%B3n-y-configuraci%C3%B3n-de-keycloak)
-5. [Configuración de Keycloak en la interfaz web](#5-configuraci%C3%B3n-de-keycloak-en-la-interfaz-web)
-6. [Configuración de una aplicación Angular](#6-configuraci%C3%B3n-de-una-aplicaci%C3%B3n-angular)
-7. [Arquitectura del sistema](#7-arquitectura-del-sistema)
-8. [Resolución de problemas comunes](#8-resoluci%C3%B3n-de-problemas-comunes)
-9. [Verificación y prueba del sistema](#9-verificaci%C3%B3n-y-prueba-del-sistema)
-10. [Resumen](#10-resumen)
-
-## 1. Preparación del entorno Ubuntu
-
-Primero, actualizamos el sistema e instalamos Java:
-
-```bash
-sudo apt update
-sudo apt upgrade -y
-sudo apt install openjdk-17-jdk -y
-
-```
-
-Verifica la instalación de Java:
-
-```bash
-java -version
-
-```
-
-## 2. Instalación y configuración de OpenLDAP
-
-### Instalar OpenLDAP y utilidades
-
-```bash
-sudo apt install slapd ldap-utils -y
-
-```
-
-Durante la instalación, se te pedirá configurar una contraseña de administrador para LDAP.
-
-### Reconfigurar LDAP con el dominio correcto
-
-```bash
-sudo dpkg-reconfigure slapd
-
-```
-
-En la configuración:
-
-1. "¿Omitir configuración del servidor LDAP?" → No
-2. "Nombre de dominio DNS:" → **correos.com**
-3. "Nombre de la organización:" → Correos Org
-4. "Contraseña de administrador:" → [tu contraseña segura]
-5. "Confirmar contraseña:" → [repetir la contraseña]
-6. "Motor de base de datos:" → MDB
-7. "¿Quiere que se elimine la base de datos cuando se purgue slapd?" → No
-8. "¿Mover la base de datos antigua?" → Sí
-
-### Verificar que LDAP se esté ejecutando correctamente
-
-```bash
-sudo systemctl status slapd
-
-```
-
-### Comprobar la conexión LDAP básica
-
-```bash
-ldapsearch -x -H ldap://localhost -b dc=correos,dc=com -D "cn=admin,dc=correos,dc=com" -W
-
-```
-
-### Instalar phpLDAPadmin para la gestión gráfica
-
-```bash
-sudo apt install phpldapadmin -y
-
-```
-
-### Configurar phpLDAPadmin
-
-Edita el archivo de configuración:
-
-```bash
-sudo nano /etc/phpldapadmin/config.php
-
-```
-
-Busca y modifica las siguientes líneas:
-
-```php
-$servers->setValue('server','host','127.0.0.1');
-$servers->setValue('server','base',array('dc=correos,dc=com'));
-$servers->setValue('login','bind_id','cn=admin,dc=correos,dc=com');
-
-```
-
-Y cambia esta línea:
-
-```php
-$servers->setValue('login','anon_bind',true);
-
-```
-
-por:
-
-```php
-$servers->setValue('login','anon_bind',false);
-
-```
-
-Reinicia el servidor web:
-
-```bash
-sudo systemctl restart apache2
-
-```
-
-## 3. Crear estructura LDAP para correos.com
-
-### Crear unidades organizativas
-
-Crea un archivo para las unidades organizativas:
-
-```bash
-nano ~/ou.ldif
-
-```
-
-Con el siguiente contenido:
-
-```ldif
-dn: ou=grupos,dc=correos,dc=com
-objectClass: organizationalUnit
-ou: grupos
-
-dn: ou=usuarios,dc=correos,dc=com
-objectClass: organizationalUnit
-ou: usuarios
-
-```
-
-Aplica los cambios:
-
-```bash
-ldapadd -x -D cn=admin,dc=correos,dc=com -W -f ~/ou.ldif
-
-```
-
-### Crear grupos LDAP
-
-Crea un archivo para los grupos:
-
-```bash
-nano ~/grupos.ldif
-
-```
-
-Con el siguiente contenido:
-
-```ldif
-dn: cn=administradores,ou=grupos,dc=correos,dc=com
-objectClass: posixGroup
-cn: administradores
-gidNumber: 1000
-
-dn: cn=desarrolladores,ou=grupos,dc=correos,dc=com
-objectClass: posixGroup
-cn: desarrolladores
-gidNumber: 1001
-
-dn: cn=usuarios,ou=grupos,dc=correos,dc=com
-objectClass: posixGroup
-cn: usuarios
-gidNumber: 1002
-
-```
-
-Aplica los cambios:
-
-```bash
-ldapadd -x -D cn=admin,dc=correos,dc=com -W -f ~/grupos.ldif
-
-```
-
-### Crear usuarios LDAP
-
-Primero, genera contraseñas encriptadas para los usuarios:
-
-```bash
-slappasswd -s "password123"
-
-```
-
-Anota el hash resultante para usarlo en el siguiente archivo.
-
-Crea un archivo para los usuarios:
-
-```bash
-nano ~/usuarios.ldif
-
-```
-
-Con el siguiente contenido (reemplazando {HASH} con el hash que generaste):
-
-```ldif
-dn: uid=admin,ou=usuarios,dc=correos,dc=com
-objectClass: inetOrgPerson
-objectClass: posixAccount
-objectClass: shadowAccount
-uid: admin
-sn: Admin
-givenName: Admin
-cn: Admin User
-displayName: Admin User
-uidNumber: 1000
-gidNumber: 1000
-userPassword: {HASH}
-loginShell: /bin/bash
-homeDirectory: /home/admin
-mail: admin@correos.com
-
-dn: uid=developer,ou=usuarios,dc=correos,dc=com
-objectClass: inetOrgPerson
-objectClass: posixAccount
-objectClass: shadowAccount
-uid: developer
-sn: Developer
-givenName: Dev
-cn: Dev User
-displayName: Developer User
-uidNumber: 1001
-gidNumber: 1001
-userPassword: {HASH}
-loginShell: /bin/bash
-homeDirectory: /home/developer
-mail: developer@correos.com
-
-dn: uid=user,ou=usuarios,dc=correos,dc=com
-objectClass: inetOrgPerson
-objectClass: posixAccount
-objectClass: shadowAccount
-uid: user
-sn: User
-givenName: Normal
-cn: Normal User
-displayName: Normal User
-uidNumber: 1002
-gidNumber: 1002
-userPassword: {HASH}
-loginShell: /bin/bash
-homeDirectory: /home/user
-mail: user@correos.com
-
-```
-
-Aplica los cambios:
-
-```bash
-ldapadd -x -D cn=admin,dc=correos,dc=com -W -f ~/usuarios.ldif
-
-```
-
-### Asociar usuarios a grupos
-
-Crea un archivo para las membresías:
-
-```bash
-nano ~/miembros.ldif
-
-```
-
-Con el siguiente contenido:
-
-```ldif
-dn: cn=administradores,ou=grupos,dc=correos,dc=com
-changetype: modify
-add: memberUid
-memberUid: admin
-
-dn: cn=desarrolladores,ou=grupos,dc=correos,dc=com
-changetype: modify
-add: memberUid
-memberUid: developer
-
-dn: cn=usuarios,ou=grupos,dc=correos,dc=com
-changetype: modify
-add: memberUid
-memberUid: user
-
-```
-
-Aplica los cambios:
-
-```bash
-ldapmodify -x -D cn=admin,dc=correos,dc=com -W -f ~/miembros.ldif
-
-```
-
-### Verificar la estructura LDAP
-
-```bash
-ldapsearch -x -H ldap://localhost -b dc=correos,dc=com -D "cn=admin,dc=correos,dc=com" -W
-
-```
-
-## 4. Instalación y configuración de Keycloak
-
-### Descargar e instalar Keycloak
-
-```bash
-# Crear directorio para Keycloak
-mkdir -p ~/keycloak
-cd ~/keycloak
-
-# Descargar la última versión de Keycloak
-wget https://github.com/keycloak/keycloak/releases/download/21.1.1/keycloak-21.1.1.tar.gz
-
-# Extraer el archivo
-tar -xvzf keycloak-21.1.1.tar.gz
-
-# Mover a una ubicación más adecuada
-sudo mv keycloak-21.1.1 /opt/keycloak
-
-# Crear un usuario para Keycloak
-sudo useradd -r -s /sbin/nologin keycloak
-
-# Asignar permisos
-sudo chown -R keycloak:keycloak /opt/keycloak
-
-```
-
-### Configurar usuario administrador inicial para Keycloak
-
-Crea un archivo de propiedades:
-
-```bash
-sudo nano /opt/keycloak/conf/keycloak.conf
-
-```
-
-Agrega estas líneas:
-
-```
-# Configuración básica
-http-port=8080
-https-port=8443
-hostname=localhost
-
-# Configuración de administrador inicial
-http-enabled=true
-
-```
-
-### Iniciar Keycloak en modo desarrollo
-
-```bash
-cd /opt/keycloak
-export KEYCLOAK_ADMIN=admin
-export KEYCLOAK_ADMIN_PASSWORD=admin
-sudo -u keycloak bin/kc.sh start-dev
-
-```
-
-Esto iniciará Keycloak con un usuario administrador "admin" y contraseña "admin".
-
-### Configurar Keycloak como servicio
-
-En otra terminal, crea un archivo de servicio systemd:
-
-```bash
-sudo nano /etc/systemd/system/keycloak.service
-
-```
-
-Con el siguiente contenido:
-
-```ini
-[Unit]
-Description=Keycloak Application Server
-After=network.target
-
-[Service]
-Type=idle
-User=keycloak
-Group=keycloak
-Environment="KEYCLOAK_ADMIN=admin"
-Environment="KEYCLOAK_ADMIN_PASSWORD=admin"
-ExecStart=/opt/keycloak/bin/kc.sh start-dev
-TimeoutStartSec=600
-TimeoutStopSec=600
-
-[Install]
-WantedBy=multi-user.target
-
-```
-
-En el futuro, podrás habilitar e iniciar el servicio con:
-
-```bash
-sudo systemctl daemon-reload
-sudo systemctl enable keycloak
-sudo systemctl start keycloak
-
-```
-
-## 5. Configuración de Keycloak en la interfaz web
-
-Ahora puedes acceder a la consola de administración de Keycloak en http://localhost:8080/admin/ e iniciar sesión con:
-
-- Usuario: `admin`
-- Contraseña: `admin`
-
-### Crear un nuevo Reino (Realm)
-
-1. Haz clic en el menú desplegable superior izquierdo que dice "master"
-2. Selecciona "Create Realm"
-3. Ingresa el nombre: `angular-app`
-4. Haz clic en "Create"
-
-### Configurar la Federación de Usuarios LDAP
-
-1. En el menú lateral izquierdo, selecciona "User Federation"
-
-2. Haz clic en "Add provider" → "ldap"
-
-3. Completa los siguientes campos:
-
- - Console Display Name: `LDAP`
- - Vendor: `Other`
- - Connection URL: `ldap://localhost:389`
- - Enable StartTLS: `OFF`
- - Bind Type: `simple`
- - Bind DN: `cn=admin,dc=correos,dc=com`
- - Bind Credential: (la contraseña de admin LDAP)
- - Edit Mode: `WRITABLE`
- - Users DN: `ou=usuarios,dc=correos,dc=com`
- - Username LDAP attribute: `uid`
- - RDN LDAP attribute: `uid`
- - UUID LDAP attribute: `entryUUID`
- - User Object Classes: `inetOrgPerson, posixAccount`
- - Custom User LDAP Filter: (dejar en blanco)
- - Search Scope: `One Level`
-4. Haz clic en "Test connection" y "Test authentication" para verificar
-
-5. Guarda la configuración
-
-6. En la pantalla del proveedor LDAP, ve a la pestaña "Synchronization"
-
-7. Haz clic en "Sync all users"
-
-
-### Configurar el Mapeo de Grupos LDAP
-
-1. En la pantalla del proveedor LDAP, ve a la pestaña "Mappers"
-2. Haz clic en "Create"
-3. Completa:
- - Name: `group-mapper`
- - Mapper Type: `group-ldap-mapper`
- - LDAP Groups DN: `ou=grupos,dc=correos,dc=com`
- - Group Object Classes: `posixGroup`
- - Membership LDAP Attribute: `memberUid`
- - Group Name LDAP Attribute: `cn`
- - User Roles Retrieve Strategy: `LOAD_GROUPS_BY_MEMBER_ATTRIBUTE`
- - Member-Of LDAP Attribute: `memberOf`
- - Mapped Group Attributes: (dejar en blanco)
- - Drop non-existing groups during sync: `ON`
-4. Haz clic en "Save"
-5. En la pantalla del mapper, haz clic en "Sync LDAP Groups to Keycloak"
-
-### Crear un Cliente para Angular
-
-1. En el menú lateral izquierdo, selecciona "Clients"
-
-2. Haz clic en "Create client"
-
-3. Completa:
-
- - Client type: `OpenID Connect`
- - Client ID: `angular-app`
- - Name: `Angular Application`
- - Haz clic en "Next"
-4. En la siguiente pantalla:
-
- - Client authentication: `ON`
- - Authorization: `OFF`
- - Haz clic en "Next"
-5. En la siguiente pantalla:
-
- - Root URL: `http://localhost:4200`
- - Home URL: `/`
- - Valid redirect URIs: `http://localhost:4200/*`
- - Web origins: `http://localhost:4200`
- - Haz clic en "Save"
-6. En la pestaña "Credentials" del cliente, copia el "Client secret" (lo necesitarás para la aplicación Angular)
-
-
-## 6. Configuración de una aplicación Angular
-
-Primero, instala Node.js y npm:
-
-```bash
-curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -
-sudo apt-get install -y nodejs
-
-```
-
-### Crear una aplicación Angular
-
-```bash
-# Instalar Angular CLI
-npm install -g @angular/cli
-
-# Crear una nueva aplicación
-ng new angular-keycloak-app
-cd angular-keycloak-app
-
-# Instalar la biblioteca para integrar Keycloak
-npm install keycloak-angular keycloak-js
-
-```
-
-### Configurar Keycloak en Angular
-
-Crea un archivo de configuración en `src/assets/keycloak.json`:
-
-```json
-{
- "realm": "angular-app",
- "auth-server-url": "http://localhost:8080/",
- "resource": "angular-app",
- "credentials": {
- "secret": "TU_CLIENT_SECRET_AQUÍ"
- }
-}
-
-```
-
-Modifica el archivo `src/app/app.module.ts`:
-
-```typescript
-import { NgModule, APP_INITIALIZER } from '@angular/core';
-import { BrowserModule } from '@angular/platform-browser';
-import { KeycloakAngularModule, KeycloakService } from 'keycloak-angular';
-import { AppRoutingModule } from './app-routing.module';
-import { AppComponent } from './app.component';
-
-function initializeKeycloak(keycloak: KeycloakService) {
- return () =>
- keycloak.init({
- config: {
- url: 'http://localhost:8080',
- realm: 'angular-app',
- clientId: 'angular-app'
- },
- initOptions: {
- onLoad: 'check-sso',
- silentCheckSsoRedirectUri:
- window.location.origin + '/assets/silent-check-sso.html'
- }
- });
-}
-
-@NgModule({
- declarations: [
- AppComponent
- ],
- imports: [
- BrowserModule,
- AppRoutingModule,
- KeycloakAngularModule
- ],
- providers: [
- {
- provide: APP_INITIALIZER,
- useFactory: initializeKeycloak,
- multi: true,
- deps: [KeycloakService]
- }
- ],
- bootstrap: [AppComponent]
-})
-export class AppModule { }
-
-```
-
-Crea un archivo `src/assets/silent-check-sso.html`:
-
-```html
-
-
-
-
-
-
-```
-
-### Crear un servicio de autenticación
-
-```bash
-ng generate service auth
-
-```
-
-Edita `src/app/auth.service.ts`:
-
-```typescript
-import { Injectable } from '@angular/core';
-import { KeycloakService } from 'keycloak-angular';
-import { KeycloakProfile } from 'keycloak-js';
-
-@Injectable({
- providedIn: 'root'
-})
-export class AuthService {
-
- constructor(private keycloak: KeycloakService) { }
-
- public getLoggedUser(): Promise {
- return this.keycloak.loadUserProfile();
- }
-
- public login(): void {
- this.keycloak.login();
- }
-
- public logout(): void {
- this.keycloak.logout();
- }
-
- public isLoggedIn(): Promise {
- return this.keycloak.isLoggedIn();
- }
-
- public getRoles(): string[] {
- return this.keycloak.getUserRoles();
- }
-}
-
-```
-
-### Crear un guardia para rutas protegidas
-
-```bash
-ng generate guard auth
-
-```
-
-Edita `src/app/auth.guard.ts`:
-
-```typescript
-import { Injectable } from '@angular/core';
-import { ActivatedRouteSnapshot, RouterStateSnapshot, Router, UrlTree } from '@angular/router';
-import { KeycloakAuthGuard, KeycloakService } from 'keycloak-angular';
-
-@Injectable({
- providedIn: 'root'
-})
-export class AuthGuard extends KeycloakAuthGuard {
-
- constructor(
- protected override readonly router: Router,
- protected readonly keycloak: KeycloakService
- ) {
- super(router, keycloak);
- }
-
- public async isAccessAllowed(
- route: ActivatedRouteSnapshot,
- state: RouterStateSnapshot
- ): Promise {
-
- // Verifica si el usuario está autenticado
- if (!this.authenticated) {
- await this.keycloak.login({
- redirectUri: window.location.origin + state.url,
- });
- return false;
- }
-
- // Obtiene los roles requeridos desde la ruta
- const requiredRoles = route.data['roles'];
-
- // Permite el acceso si no hay roles requeridos
- if (!requiredRoles || requiredRoles.length === 0) {
- return true;
- }
-
- // Verifica si el usuario tiene al menos uno de los roles requeridos
- return requiredRoles.some((role: string) => this.roles.includes(role));
- }
-}
-
-```
-
-### Configurar las rutas con protección
-
-Modifica `src/app/app-routing.module.ts`:
-
-```typescript
-import { NgModule } from '@angular/core';
-import { RouterModule, Routes } from '@angular/router';
-import { AuthGuard } from './auth.guard';
-import { AppComponent } from './app.component';
-
-const routes: Routes = [
- {
- path: 'admin',
- component: AppComponent,
- canActivate: [AuthGuard],
- data: { roles: ['administradores'] }
- },
- {
- path: 'developer',
- component: AppComponent,
- canActivate: [AuthGuard],
- data: { roles: ['desarrolladores'] }
- },
- {
- path: 'user',
- component: AppComponent,
- canActivate: [AuthGuard],
- data: { roles: ['usuarios'] }
- },
- {
- path: '',
- component: AppComponent
- }
-];
-
-@NgModule({
- imports: [RouterModule.forRoot(routes)],
- exports: [RouterModule]
-})
-export class AppRoutingModule { }
-
-```
-
-### Modificar el componente principal
-
-Edita `src/app/app.component.ts`:
-
-```typescript
-import { Component, OnInit } from '@angular/core';
-import { AuthService } from './auth.service';
-import { KeycloakProfile } from 'keycloak-js';
-
-@Component({
- selector: 'app-root',
- templateUrl: './app.component.html',
- styleUrls: ['./app.component.css']
-})
-export class AppComponent implements OnInit {
- title = 'angular-keycloak-app';
- isLoggedIn = false;
- userProfile: KeycloakProfile | null = null;
- userRoles: string[] = [];
-
- constructor(private authService: AuthService) {}
-
- async ngOnInit() {
- this.isLoggedIn = await this.authService.isLoggedIn();
-
- if (this.isLoggedIn) {
- this.userProfile = await this.authService.getLoggedUser();
- this.userRoles = this.authService.getRoles();
- }
- }
-
- login() {
- this.authService.login();
- }
-
- logout() {
- this.authService.logout();
- }
-}
-
-```
-
-Edita `src/app/app.component.html`:
-
-```html
-
-
-
-
-
-
-
-
Bienvenido, {{ userProfile?.firstName }} {{ userProfile?.lastName }}
-
Email: {{ userProfile?.email }}
-
Nombre de Usuario: {{ userProfile?.username }}
-
-
Tus Roles:
-
-
-
Cerrar Sesión
-
-
-
- Por favor inicia sesión para acceder al sistema
- Iniciar Sesión
-
-
-
-
-
-
-
-```
-
-Agrega Bootstrap a `src/index.html`:
-
-```html
-
-
-
-
-
-```
-
-### Ejecutar la aplicación
-
-```bash
-ng serve
-
-```
-
-Abre http://localhost:4200 en tu navegador.
-
-## 7. Arquitectura del sistema
-
-La arquitectura del sistema está compuesta por los siguientes componentes:
-
-- **OpenLDAP**: Directorio de usuarios y grupos (puerto 389)
-- **phpLDAPadmin**: Interfaz gráfica para administrar LDAP (puerto 80)
-- **Keycloak**: Servidor de autenticación y autorización (puerto 8080)
-- **Aplicación Angular**: Cliente que utiliza el SSO de Keycloak (puerto 4200)
-
-La estructura del directorio LDAP para correos.com es:
-
-- dc=correos,dc=com (raíz del directorio)
- - ou=usuarios (unidad organizativa para usuarios)
- - uid=admin (usuario administrador)
- - uid=developer (usuario desarrollador)
- - uid=user (usuario normal)
- - ou=grupos (unidad organizativa para grupos)
- - cn=administradores (grupo de administradores)
- - cn=desarrolladores (grupo de desarrolladores)
- - cn=usuarios (grupo de usuarios)
-
-## 8. Resolución de problemas comunes
-
-### Problema: "ldap_bind: Invalid credentials (49)"
-
-Este error ocurre cuando intentas conectarte a LDAP con credenciales incorrectas. Para resolverlo:
-
-1. Verifica que estás usando el dominio correcto: `dc=correos,dc=com`
-2. Asegúrate de usar el DN correcto: `cn=admin,dc=correos,dc=com`
-3. Comprueba que la contraseña de administrador es correcta
-
-Si olvidaste la contraseña, puedes restablecerla:
-
-```bash
-sudo dpkg-reconfigure slapd
-
-```
-
-O también:
-
-```bash
-sudo slappasswd
-# Copia el hash generado
-sudo nano /etc/ldap/slapd.d/cn=config/olcDatabase={1}mdb.ldif
-# Busca olcRootPW y reemplaza el valor con el nuevo hash
-sudo systemctl restart slapd
-
-```
-
-### Problema: No puedes conectarte a LDAP en absoluto
-
-```bash
-# Verifica que el servicio esté ejecutándose
-sudo systemctl status slapd
-
-# Reinicia el servicio si es necesario
-sudo systemctl restart slapd
-
-# Verifica que el puerto esté abierto
-sudo netstat -tulpn | grep 389
-
-```
-
-### Problema: Keycloak no se inicia
-
-```bash
-# Verifica los logs
-sudo journalctl -u keycloak
-
-# Asegúrate de que Java esté instalado correctamente
-java -version
-
-# Verifica los permisos
-sudo ls -la /opt/keycloak
-
-```
-
-### Problema: La aplicación Angular no puede conectarse a Keycloak
-
-1. Verifica que Keycloak esté en ejecución
-2. Comprueba la URL del servidor Keycloak en la configuración de Angular
-3. Asegúrate de que el cliente esté correctamente configurado en Keycloak
-4. Verifica el Client Secret
-5. Comprueba las URLs de redirección
-
-## 9. Verificación y prueba del sistema
-
-### Verificar que Keycloak esté sincronizando correctamente con LDAP
-
-1. Inicia sesión en la consola de administración de Keycloak (http://localhost:8080/admin/)
-2. Navega a "Users" en el reino "angular-app"
-3. Deberías ver los usuarios de LDAP: admin, developer y user
-4. Navega a "Groups" y verifica que los grupos de LDAP estén presentes
-
-### Probar la aplicación Angular
-
-1. Asegúrate de que Keycloak esté ejecutándose
-2. Inicia la aplicación Angular con `ng serve`
-3. Navega a http://localhost:4200
-4. Haz clic en "Iniciar Sesión"
-5. Deberías ser redirigido a la pantalla de inicio de sesión de Keycloak
-6. Inicia sesión con uno de los usuarios LDAP:
- - Usuario: admin, Contraseña: password123
- - Usuario: developer, Contraseña: password123
- - Usuario: user, Contraseña: password123
-7. Después de iniciar sesión, serás redirigido de vuelta a la aplicación
-8. La aplicación mostrará tu perfil y roles
-
-## 10. Resumen
-
-Esta configuración proporciona un sistema completo de gestión de identidades y acceso con:
-
-- **Autenticación centralizada**: Los usuarios solo necesitan recordar un conjunto de credenciales
-- **Gestión de usuarios unificada**: Todos los usuarios se administran en LDAP
-- **Control de acceso basado en roles**: Las rutas y funcionalidades pueden ser protegidas según los roles del usuario
-- **Experiencia de inicio de sesión único (SSO)**: Una vez autenticado, el usuario puede acceder a todas las aplicaciones integradas
-
-Este sistema es adecuado para entornos empresariales donde se requiere un control de acceso detallado y una gestión centralizada de usuarios.
-
-La implementación es nativa en un sistema Ubuntu, lo que la hace ideal para despliegues en servidores VPS o entornos similares sin necesidad de contenedores.
diff --git a/tutorial-keycloak-completo.md b/tutorial-keycloak-completo.md
new file mode 100644
index 0000000..efba6fc
--- /dev/null
+++ b/tutorial-keycloak-completo.md
@@ -0,0 +1,1476 @@
+# Tutorial Completo: Implementación de Keycloak con LDAP e Integración con Angular
+
+Este tutorial comprensivo explica cómo configurar un sistema de autenticación completo utilizando Keycloak como proveedor de identidad, OpenLDAP como directorio de usuarios, y cómo integrar este sistema con una aplicación Angular moderna. La guía cubre desde la configuración del entorno básico hasta las técnicas más avanzadas de integración con Angular 19.
+
+## Índice
+
+1. [Preparación del entorno](#1-preparación-del-entorno)
+2. [Instalación y configuración de OpenLDAP](#2-instalación-y-configuración-de-openldap)
+3. [Crear estructura LDAP para usuarios y grupos](#3-crear-estructura-ldap-para-usuarios-y-grupos)
+4. [Instalación y configuración de Keycloak](#4-instalación-y-configuración-de-keycloak)
+5. [Configuración de Keycloak en la interfaz web](#5-configuración-de-keycloak-en-la-interfaz-web)
+6. [Integración con Angular: Enfoque básico](#6-integración-con-angular-enfoque-básico)
+7. [Integración con Angular 19: Enfoque moderno](#7-integración-con-angular-19-enfoque-moderno)
+8. [Servicios de autenticación avanzados](#8-servicios-de-autenticación-avanzados)
+9. [Guardias de ruta e interceptores HTTP](#9-guardias-de-ruta-e-interceptores-http)
+10. [Componentes de UI para login/logout](#10-componentes-de-ui-para-loginlogout)
+11. [Arquitectura del sistema](#11-arquitectura-del-sistema)
+12. [Resolución de problemas comunes](#12-resolución-de-problemas-comunes)
+13. [Verificación y prueba del sistema](#13-verificación-y-prueba-del-sistema)
+14. [Recursos adicionales](#14-recursos-adicionales)
+15. [Resumen](#15-resumen)
+
+## 1. Preparación del entorno
+
+### Requisitos del sistema
+
+Para seguir este tutorial, necesitarás:
+
+- Un sistema Ubuntu Server o una distribución similar de Linux
+- Acceso de root o privilegios sudo
+- Node.js (versión 18.x o superior)
+- Angular CLI (versión 19.x o compatible)
+- Java JDK (OpenJDK 17 o superior)
+
+### Actualización del sistema e instalación de Java
+
+Primero, actualiza el sistema e instala Java:
+
+```bash
+sudo apt update
+sudo apt upgrade -y
+sudo apt install openjdk-17-jdk -y
+```
+
+Verifica la instalación de Java:
+
+```bash
+java -version
+```
+
+### Instalación de Node.js y Angular CLI
+
+Para el desarrollo de aplicaciones Angular, instala Node.js y Angular CLI:
+
+```bash
+# Instalar Node.js
+curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -
+sudo apt-get install -y nodejs
+
+# Verificar la instalación
+node -v
+npm -v
+
+# Instalar Angular CLI
+npm install -g @angular/cli
+
+# Verificar la instalación
+ng version
+```
+
+## 2. Instalación y configuración de OpenLDAP
+
+### Instalar OpenLDAP y utilidades
+
+```bash
+sudo apt install slapd ldap-utils -y
+```
+
+Durante la instalación, se te pedirá configurar una contraseña de administrador para LDAP.
+
+### Reconfigurar LDAP con el dominio correcto
+
+```bash
+sudo dpkg-reconfigure slapd
+```
+
+En la configuración:
+
+1. "¿Omitir configuración del servidor LDAP?" → No
+2. "Nombre de dominio DNS:" → **correos.com**
+3. "Nombre de la organización:" → Correos Org
+4. "Contraseña de administrador:" → [tu contraseña segura]
+5. "Confirmar contraseña:" → [repetir la contraseña]
+6. "Motor de base de datos:" → MDB
+7. "¿Quiere que se elimine la base de datos cuando se purgue slapd?" → No
+8. "¿Mover la base de datos antigua?" → Sí
+
+### Verificar que LDAP se esté ejecutando correctamente
+
+```bash
+sudo systemctl status slapd
+```
+
+### Comprobar la conexión LDAP básica
+
+```bash
+ldapsearch -x -H ldap://localhost -b dc=correos,dc=com -D "cn=admin,dc=correos,dc=com" -W
+```
+
+### Instalar phpLDAPadmin para la gestión gráfica
+
+```bash
+sudo apt install phpldapadmin -y
+```
+
+### Configurar phpLDAPadmin
+
+Edita el archivo de configuración:
+
+```bash
+sudo nano /etc/phpldapadmin/config.php
+```
+
+Busca y modifica las siguientes líneas:
+
+```php
+$servers->setValue('server','host','127.0.0.1');
+$servers->setValue('server','base',array('dc=correos,dc=com'));
+$servers->setValue('login','bind_id','cn=admin,dc=correos,dc=com');
+```
+
+Y cambia esta línea:
+
+```php
+$servers->setValue('login','anon_bind',true);
+```
+
+por:
+
+```php
+$servers->setValue('login','anon_bind',false);
+```
+
+Reinicia el servidor web:
+
+```bash
+sudo systemctl restart apache2
+```
+
+## 3. Crear estructura LDAP para usuarios y grupos
+
+### Crear unidades organizativas
+
+Crea un archivo para las unidades organizativas:
+
+```bash
+nano ~/ou.ldif
+```
+
+Con el siguiente contenido:
+
+```ldif
+dn: ou=grupos,dc=correos,dc=com
+objectClass: organizationalUnit
+ou: grupos
+
+dn: ou=usuarios,dc=correos,dc=com
+objectClass: organizationalUnit
+ou: usuarios
+```
+
+Aplica los cambios:
+
+```bash
+ldapadd -x -D cn=admin,dc=correos,dc=com -W -f ~/ou.ldif
+```
+
+### Crear grupos LDAP
+
+Crea un archivo para los grupos:
+
+```bash
+nano ~/grupos.ldif
+```
+
+Con el siguiente contenido:
+
+```ldif
+dn: cn=administradores,ou=grupos,dc=correos,dc=com
+objectClass: posixGroup
+cn: administradores
+gidNumber: 1000
+
+dn: cn=desarrolladores,ou=grupos,dc=correos,dc=com
+objectClass: posixGroup
+cn: desarrolladores
+gidNumber: 1001
+
+dn: cn=usuarios,ou=grupos,dc=correos,dc=com
+objectClass: posixGroup
+cn: usuarios
+gidNumber: 1002
+```
+
+Aplica los cambios:
+
+```bash
+ldapadd -x -D cn=admin,dc=correos,dc=com -W -f ~/grupos.ldif
+```
+
+### Crear usuarios LDAP
+
+Primero, genera contraseñas encriptadas para los usuarios:
+
+```bash
+slappasswd -s "password123"
+```
+
+Anota el hash resultante para usarlo en el siguiente archivo.
+
+Crea un archivo para los usuarios:
+
+```bash
+nano ~/usuarios.ldif
+```
+
+Con el siguiente contenido (reemplazando {HASH} con el hash que generaste):
+
+```ldif
+dn: uid=admin,ou=usuarios,dc=correos,dc=com
+objectClass: inetOrgPerson
+objectClass: posixAccount
+objectClass: shadowAccount
+uid: admin
+sn: Admin
+givenName: Admin
+cn: Admin User
+displayName: Admin User
+uidNumber: 1000
+gidNumber: 1000
+userPassword: {HASH}
+loginShell: /bin/bash
+homeDirectory: /home/admin
+mail: admin@correos.com
+
+dn: uid=developer,ou=usuarios,dc=correos,dc=com
+objectClass: inetOrgPerson
+objectClass: posixAccount
+objectClass: shadowAccount
+uid: developer
+sn: Developer
+givenName: Dev
+cn: Dev User
+displayName: Developer User
+uidNumber: 1001
+gidNumber: 1001
+userPassword: {HASH}
+loginShell: /bin/bash
+homeDirectory: /home/developer
+mail: developer@correos.com
+
+dn: uid=user,ou=usuarios,dc=correos,dc=com
+objectClass: inetOrgPerson
+objectClass: posixAccount
+objectClass: shadowAccount
+uid: user
+sn: User
+givenName: Normal
+cn: Normal User
+displayName: Normal User
+uidNumber: 1002
+gidNumber: 1002
+userPassword: {HASH}
+loginShell: /bin/bash
+homeDirectory: /home/user
+mail: user@correos.com
+```
+
+Aplica los cambios:
+
+```bash
+ldapadd -x -D cn=admin,dc=correos,dc=com -W -f ~/usuarios.ldif
+```
+
+### Asociar usuarios a grupos
+
+Crea un archivo para las membresías:
+
+```bash
+nano ~/miembros.ldif
+```
+
+Con el siguiente contenido:
+
+```ldif
+dn: cn=administradores,ou=grupos,dc=correos,dc=com
+changetype: modify
+add: memberUid
+memberUid: admin
+
+dn: cn=desarrolladores,ou=grupos,dc=correos,dc=com
+changetype: modify
+add: memberUid
+memberUid: developer
+
+dn: cn=usuarios,ou=grupos,dc=correos,dc=com
+changetype: modify
+add: memberUid
+memberUid: user
+```
+
+Aplica los cambios:
+
+```bash
+ldapmodify -x -D cn=admin,dc=correos,dc=com -W -f ~/miembros.ldif
+```
+
+### Verificar la estructura LDAP
+
+```bash
+ldapsearch -x -H ldap://localhost -b dc=correos,dc=com -D "cn=admin,dc=correos,dc=com" -W
+```
+
+## 4. Instalación y configuración de Keycloak
+
+### Descargar e instalar Keycloak
+
+```bash
+# Crear directorio para Keycloak
+mkdir -p ~/keycloak
+cd ~/keycloak
+
+# Descargar la última versión de Keycloak
+wget https://github.com/keycloak/keycloak/releases/download/26.2.4/keycloak-26.2.4.tar.gz
+
+# Extraer el archivo
+tar -xvzf keycloak-26.2.4.tar.gz
+
+# Mover a una ubicación más adecuada
+sudo mv keycloak-26.2.4 /opt/keycloak
+
+# Crear un usuario para Keycloak
+sudo useradd -r -s /sbin/nologin keycloak
+
+# Asignar permisos
+sudo chown -R keycloak:keycloak /opt/keycloak
+```
+
+### Configurar usuario administrador inicial para Keycloak
+
+Crea un archivo de propiedades:
+
+```bash
+sudo nano /opt/keycloak/conf/keycloak.conf
+```
+
+Agrega estas líneas:
+
+```
+# Configuración básica
+http-port=8080
+https-port=8443
+hostname=localhost
+
+# Configuración de administrador inicial
+http-enabled=true
+```
+
+### Iniciar Keycloak en modo desarrollo
+
+```bash
+cd /opt/keycloak
+export KEYCLOAK_ADMIN=admin
+export KEYCLOAK_ADMIN_PASSWORD=admin
+sudo -u keycloak bin/kc.sh start-dev
+```
+
+Esto iniciará Keycloak con un usuario administrador "admin" y contraseña "admin".
+
+### Configurar Keycloak como servicio
+
+En otra terminal, crea un archivo de servicio systemd:
+
+```bash
+sudo nano /etc/systemd/system/keycloak.service
+```
+
+Con el siguiente contenido:
+
+```ini
+[Unit]
+Description=Keycloak Application Server
+After=network.target
+
+[Service]
+Type=idle
+User=keycloak
+Group=keycloak
+Environment="KEYCLOAK_ADMIN=admin"
+Environment="KEYCLOAK_ADMIN_PASSWORD=admin"
+ExecStart=/opt/keycloak/bin/kc.sh start-dev
+TimeoutStartSec=600
+TimeoutStopSec=600
+
+[Install]
+WantedBy=multi-user.target
+```
+
+Habilita e inicia el servicio:
+
+```bash
+sudo systemctl daemon-reload
+sudo systemctl enable keycloak
+sudo systemctl start keycloak
+```
+
+## 5. Configuración de Keycloak en la interfaz web
+
+Ahora puedes acceder a la consola de administración de Keycloak en http://localhost:8080/admin/ e iniciar sesión con:
+
+- Usuario: `admin`
+- Contraseña: `admin`
+
+### Crear un nuevo Reino (Realm)
+
+1. Haz clic en el menú desplegable superior izquierdo que dice "master"
+2. Selecciona "Create Realm"
+3. Ingresa el nombre: `angular-app`
+4. Haz clic en "Create"
+
+### Configurar la Federación de Usuarios LDAP
+
+1. En el menú lateral izquierdo, selecciona "User Federation"
+
+2. Haz clic en "Add provider" → "ldap"
+
+3. Completa los siguientes campos:
+
+ - Console Display Name: `LDAP`
+ - Vendor: `Other`
+ - Connection URL: `ldap://localhost:389`
+ - Enable StartTLS: `OFF`
+ - Bind Type: `simple`
+ - Bind DN: `cn=admin,dc=correos,dc=com`
+ - Bind Credential: (la contraseña de admin LDAP)
+ - Edit Mode: `WRITABLE`
+ - Users DN: `ou=usuarios,dc=correos,dc=com`
+ - Username LDAP attribute: `uid`
+ - RDN LDAP attribute: `uid`
+ - UUID LDAP attribute: `entryUUID`
+ - User Object Classes: `inetOrgPerson, posixAccount`
+ - Custom User LDAP Filter: (dejar en blanco)
+ - Search Scope: `One Level`
+4. Haz clic en "Test connection" y "Test authentication" para verificar
+
+5. Guarda la configuración
+
+6. En la pantalla del proveedor LDAP, ve a la pestaña "Synchronization"
+
+7. Haz clic en "Sync all users"
+
+
+### Configurar el Mapeo de Grupos LDAP
+
+1. En la pantalla del proveedor LDAP, ve a la pestaña "Mappers"
+2. Haz clic en "Create"
+3. Completa:
+ - Name: `group-mapper`
+ - Mapper Type: `group-ldap-mapper`
+ - LDAP Groups DN: `ou=grupos,dc=correos,dc=com`
+ - Group Object Classes: `posixGroup`
+ - Membership LDAP Attribute: `memberUid`
+ - Group Name LDAP Attribute: `cn`
+ - User Roles Retrieve Strategy: `LOAD_GROUPS_BY_MEMBER_ATTRIBUTE`
+ - Member-Of LDAP Attribute: `memberOf`
+ - Mapped Group Attributes: (dejar en blanco)
+ - Drop non-existing groups during sync: `ON`
+4. Haz clic en "Save"
+5. En la pantalla del mapper, haz clic en "Sync LDAP Groups to Keycloak"
+
+### Crear un Cliente para Angular
+
+1. En el menú lateral izquierdo, selecciona "Clients"
+
+2. Haz clic en "Create client"
+
+3. Completa:
+
+ - Client type: `OpenID Connect`
+ - Client ID: `angular-app`
+ - Name: `Angular Application`
+ - Haz clic en "Next"
+4. En la siguiente pantalla (para aplicaciones SPA modernas):
+
+ - Client authentication: `OFF` (para aplicaciones SPA)
+ - Authorization: `OFF`
+ - Haz clic en "Next"
+5. En la siguiente pantalla:
+
+ - Root URL: `http://localhost:4200`
+ - Home URL: `/`
+ - Valid redirect URIs: `http://localhost:4200/*`
+ - Web origins: `http://localhost:4200` (o usar `+` para permitir todos los orígenes durante desarrollo)
+ - Haz clic en "Save"
+
+Para aplicaciones que no son SPA, puedes habilitar "Client authentication" y obtener un client secret que deberás usar en la configuración.
+
+## 6. Integración con Angular: Enfoque básico
+
+Para las versiones más antiguas de Angular, vamos a utilizar el enfoque tradicional de integración con Keycloak.
+
+### Crear una aplicación Angular
+
+```bash
+# Crear una nueva aplicación
+ng new angular-keycloak-app
+cd angular-keycloak-app
+
+# Instalar la biblioteca para integrar Keycloak
+npm install keycloak-angular keycloak-js
+```
+
+### Configurar Keycloak en Angular
+
+Crea un archivo de configuración en `src/assets/keycloak.json`:
+
+```json
+{
+ "realm": "angular-app",
+ "auth-server-url": "http://localhost:8080/",
+ "resource": "angular-app",
+ "public-client": true
+}
+```
+
+Si tu cliente en Keycloak tiene autenticación habilitada, deberás agregar el campo credentials con el client secret:
+
+```json
+{
+ "realm": "angular-app",
+ "auth-server-url": "http://localhost:8080/",
+ "resource": "angular-app",
+ "credentials": {
+ "secret": "TU_CLIENT_SECRET_AQUÍ"
+ }
+}
+```
+
+Crea un archivo `src/assets/silent-check-sso.html`:
+
+```html
+
+
+
+
+
+```
+
+### Configurar el módulo principal (Angular tradicional)
+
+Modifica el archivo `src/app/app.module.ts`:
+
+```typescript
+import { NgModule, APP_INITIALIZER } from '@angular/core';
+import { BrowserModule } from '@angular/platform-browser';
+import { KeycloakAngularModule, KeycloakService } from 'keycloak-angular';
+import { AppRoutingModule } from './app-routing.module';
+import { AppComponent } from './app.component';
+
+function initializeKeycloak(keycloak: KeycloakService) {
+ return () =>
+ keycloak.init({
+ config: {
+ url: 'http://localhost:8080',
+ realm: 'angular-app',
+ clientId: 'angular-app'
+ },
+ initOptions: {
+ onLoad: 'check-sso',
+ silentCheckSsoRedirectUri:
+ window.location.origin + '/assets/silent-check-sso.html'
+ }
+ });
+}
+
+@NgModule({
+ declarations: [
+ AppComponent
+ ],
+ imports: [
+ BrowserModule,
+ AppRoutingModule,
+ KeycloakAngularModule
+ ],
+ providers: [
+ {
+ provide: APP_INITIALIZER,
+ useFactory: initializeKeycloak,
+ multi: true,
+ deps: [KeycloakService]
+ }
+ ],
+ bootstrap: [AppComponent]
+})
+export class AppModule { }
+```
+
+### Crear un servicio de autenticación básico
+
+```bash
+ng generate service auth
+```
+
+Edita `src/app/auth.service.ts`:
+
+```typescript
+import { Injectable } from '@angular/core';
+import { KeycloakService } from 'keycloak-angular';
+import { KeycloakProfile } from 'keycloak-js';
+
+@Injectable({
+ providedIn: 'root'
+})
+export class AuthService {
+
+ constructor(private keycloak: KeycloakService) { }
+
+ public getLoggedUser(): Promise {
+ return this.keycloak.loadUserProfile();
+ }
+
+ public login(): void {
+ this.keycloak.login();
+ }
+
+ public logout(): void {
+ this.keycloak.logout();
+ }
+
+ public isLoggedIn(): Promise {
+ return this.keycloak.isLoggedIn();
+ }
+
+ public getRoles(): string[] {
+ return this.keycloak.getUserRoles();
+ }
+}
+```
+
+### Crear un guardia para rutas protegidas (Angular tradicional)
+
+```bash
+ng generate guard auth
+```
+
+Edita `src/app/auth.guard.ts`:
+
+```typescript
+import { Injectable } from '@angular/core';
+import { ActivatedRouteSnapshot, RouterStateSnapshot, Router, UrlTree } from '@angular/router';
+import { KeycloakAuthGuard, KeycloakService } from 'keycloak-angular';
+
+@Injectable({
+ providedIn: 'root'
+})
+export class AuthGuard extends KeycloakAuthGuard {
+
+ constructor(
+ protected override readonly router: Router,
+ protected readonly keycloak: KeycloakService
+ ) {
+ super(router, keycloak);
+ }
+
+ public async isAccessAllowed(
+ route: ActivatedRouteSnapshot,
+ state: RouterStateSnapshot
+ ): Promise {
+
+ // Verifica si el usuario está autenticado
+ if (!this.authenticated) {
+ await this.keycloak.login({
+ redirectUri: window.location.origin + state.url,
+ });
+ return false;
+ }
+
+ // Obtiene los roles requeridos desde la ruta
+ const requiredRoles = route.data['roles'];
+
+ // Permite el acceso si no hay roles requeridos
+ if (!requiredRoles || requiredRoles.length === 0) {
+ return true;
+ }
+
+ // Verifica si el usuario tiene al menos uno de los roles requeridos
+ return requiredRoles.some((role: string) => this.roles.includes(role));
+ }
+}
+```
+
+### Configurar las rutas con protección
+
+Modifica `src/app/app-routing.module.ts`:
+
+```typescript
+import { NgModule } from '@angular/core';
+import { RouterModule, Routes } from '@angular/router';
+import { AuthGuard } from './auth.guard';
+import { AppComponent } from './app.component';
+
+const routes: Routes = [
+ {
+ path: 'admin',
+ component: AppComponent,
+ canActivate: [AuthGuard],
+ data: { roles: ['administradores'] }
+ },
+ {
+ path: 'developer',
+ component: AppComponent,
+ canActivate: [AuthGuard],
+ data: { roles: ['desarrolladores'] }
+ },
+ {
+ path: 'user',
+ component: AppComponent,
+ canActivate: [AuthGuard],
+ data: { roles: ['usuarios'] }
+ },
+ {
+ path: '',
+ component: AppComponent
+ }
+];
+
+@NgModule({
+ imports: [RouterModule.forRoot(routes)],
+ exports: [RouterModule]
+})
+export class AppRoutingModule { }
+```
+
+## 7. Integración con Angular 19: Enfoque moderno
+
+Para Angular 19 y versiones más recientes, utilizaremos el enfoque moderno que aprovecha características como componentes independientes y el sistema de inyección de dependencias mejorado.
+
+### Configuración en app.config.ts (Angular 19)
+
+Configura Keycloak en el archivo `src/app/app.config.ts`:
+
+```typescript
+import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core';
+import { provideRouter, withPreloading, PreloadAllModules } from '@angular/router';
+import { provideHttpClient, withFetch, withInterceptors } from '@angular/common/http';
+import { routes } from './app.routes';
+import { provideAnimations } from '@angular/platform-browser/animations';
+import {
+ provideKeycloak,
+ createInterceptorCondition,
+ IncludeBearerTokenCondition,
+ INCLUDE_BEARER_TOKEN_INTERCEPTOR_CONFIG,
+ includeBearerTokenInterceptor
+} from 'keycloak-angular';
+
+// Define condiciones para incluir el token en las peticiones
+const localhostCondition = createInterceptorCondition({
+ urlPattern: /^(http:\/\/localhost)(\/.*)?$/i, // URLs que comienzan con http://localhost
+ bearerPrefix: 'Bearer'
+});
+
+// Condición para APIs
+const apiCondition = createInterceptorCondition({
+ urlPattern: /^(\/api)(\/.*)?$/i, // URLs que comienzan con /api
+ bearerPrefix: 'Bearer'
+});
+
+export const appConfig: ApplicationConfig = {
+ providers: [
+ provideZoneChangeDetection({ eventCoalescing: true }),
+ provideRouter(
+ routes,
+ withPreloading(PreloadAllModules)
+ ),
+ provideAnimations(),
+ // Usar el interceptor de Keycloak para adjuntar tokens de autenticación
+ provideHttpClient(
+ withFetch(),
+ withInterceptors([includeBearerTokenInterceptor])
+ ),
+ // Configuración para Keycloak
+ provideKeycloak({
+ config: {
+ url: 'http://localhost:8080', // URL del servidor Keycloak
+ realm: 'angular-app', // Nombre del realm
+ clientId: 'angular-app' // ID del cliente
+ },
+ initOptions: {
+ onLoad: 'check-sso', // Opciones: 'login-required' o 'check-sso'
+ silentCheckSsoRedirectUri: `${window.location.origin}/silent-check-sso.html`,
+ checkLoginIframe: false,
+ pkceMethod: 'S256' // Mejora la seguridad
+ },
+ // Configurar el interceptor especificando las URLs donde incluir el token
+ providers: [
+ {
+ provide: INCLUDE_BEARER_TOKEN_INTERCEPTOR_CONFIG,
+ useValue: [localhostCondition, apiCondition]
+ }
+ ]
+ })
+ ]
+};
+```
+
+## 8. Servicios de autenticación avanzados
+
+Con Angular 19, podemos crear un servicio de autenticación más avanzado utilizando signals y effects.
+
+```typescript
+import { Injectable, inject, effect, signal } from '@angular/core';
+import { HttpClient } from '@angular/common/http';
+import { BehaviorSubject, Observable, from } from 'rxjs';
+import { KEYCLOAK_EVENT_SIGNAL, KeycloakEventType } from 'keycloak-angular';
+import { Router } from '@angular/router';
+import Keycloak from 'keycloak-js';
+
+@Injectable({
+ providedIn: 'root'
+})
+export class AuthService {
+ // Inyectar Keycloak directamente
+ private keycloak = inject(Keycloak);
+ private keycloakEvents = inject(KEYCLOAK_EVENT_SIGNAL);
+ private router = inject(Router);
+
+ // Estado del usuario
+ private userSubject = new BehaviorSubject(null);
+ public user$ = this.userSubject.asObservable();
+
+ // Estado de autenticación como signal
+ public isAuthenticated = signal(false);
+
+ constructor(private http: HttpClient) {
+ // Verificar estado inicial
+ this.checkInitialAuthState();
+
+ // Configurar manejadores de eventos usando Angular effects
+ effect(() => {
+ const event = this.keycloakEvents();
+ if (!event) return;
+
+ console.log('Keycloak event:', event.type);
+
+ // Autenticación exitosa
+ if (event.type === KeycloakEventType.AuthSuccess) {
+ this.isAuthenticated.set(true);
+ this.loadUserInfo();
+ }
+
+ // Cierre de sesión
+ if (event.type === KeycloakEventType.AuthLogout) {
+ this.isAuthenticated.set(false);
+ this.userSubject.next(null);
+ this.router.navigate(['/login']);
+ }
+
+ // Error de autenticación
+ if (event.type === KeycloakEventType.AuthError) {
+ console.error('Authentication error:', event);
+ this.isAuthenticated.set(false);
+ this.userSubject.next(null);
+ }
+
+ // Expiración del token
+ if (event.type === KeycloakEventType.TokenExpired) {
+ console.log('Token expired, refreshing...');
+ this.updateToken();
+ }
+ });
+ }
+
+ private async checkInitialAuthState(): Promise {
+ try {
+ const isLoggedIn = await this.keycloak.authenticated;
+ this.isAuthenticated.set(isLoggedIn);
+
+ if (isLoggedIn) {
+ await this.loadUserInfo();
+ }
+ } catch (error) {
+ console.error('Error checking initial auth state:', error);
+ this.isAuthenticated.set(false);
+ }
+ }
+
+ private async loadUserInfo(): Promise {
+ try {
+ const isLoggedIn = await this.keycloak.authenticated;
+
+ if (!isLoggedIn) {
+ this.userSubject.next(null);
+ return;
+ }
+
+ const userProfile = await this.keycloak.loadUserProfile();
+ const isAdmin = this.keycloak.hasRealmRole('admin');
+
+ // Obtener roles del usuario
+ const realmRoles = this.keycloak.realmAccess?.roles || [];
+ const resourceRoles = this.keycloak.resourceAccess || {};
+
+ const user = {
+ id: userProfile.id,
+ username: userProfile.username,
+ name: `${userProfile.firstName || ''} ${userProfile.lastName || ''}`.trim(),
+ email: userProfile.email,
+ role: isAdmin ? 'admin' : 'user',
+ roles: {
+ realm: realmRoles,
+ resource: resourceRoles
+ },
+ isAdmin: isAdmin
+ };
+
+ this.userSubject.next(user);
+ } catch (error) {
+ console.error('Error loading user profile:', error);
+ this.userSubject.next(null);
+ }
+ }
+
+ login(redirectUri?: string): Promise {
+ return this.keycloak.login({
+ redirectUri: redirectUri || window.location.origin
+ });
+ }
+
+ logout(): Promise {
+ return this.keycloak.logout({
+ redirectUri: window.location.origin
+ });
+ }
+
+ isLoggedIn(): Observable {
+ try {
+ return from(Promise.resolve(this.keycloak.authenticated || false));
+ } catch (error) {
+ console.error('Error checking authentication:', error);
+ return from(Promise.resolve(false));
+ }
+ }
+
+ getToken(): Promise {
+ try {
+ return Promise.resolve(this.keycloak.token || '');
+ } catch (error) {
+ console.error('Error getting token:', error);
+ return Promise.resolve('');
+ }
+ }
+
+ async updateToken(minValidity = 30): Promise {
+ try {
+ return await this.keycloak.updateToken(minValidity);
+ } catch (error) {
+ console.error('Error refreshing token:', error);
+ await this.login();
+ return false;
+ }
+ }
+
+ getCurrentUser(): any {
+ return this.userSubject.value;
+ }
+
+ // Verificar si el usuario tiene un rol específico
+ hasRole(role: string): boolean {
+ return this.keycloak.hasRealmRole(role);
+ }
+
+ // Verificar si el usuario tiene alguno de los roles especificados
+ hasAnyRole(roles: string[]): boolean {
+ for (const role of roles) {
+ if (this.keycloak.hasRealmRole(role)) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
+```
+
+## 9. Guardias de ruta e interceptores HTTP
+
+### Guardia de ruta funcional (Angular 19)
+
+```typescript
+import { inject } from '@angular/core';
+import { CanActivateFn, Router } from '@angular/router';
+import Keycloak from 'keycloak-js';
+import { ActivatedRouteSnapshot, RouterStateSnapshot, UrlTree } from '@angular/router';
+import { createAuthGuard, AuthGuardData } from 'keycloak-angular';
+
+// Implementación directa
+export const authGuard: CanActivateFn = async (
+ route: ActivatedRouteSnapshot,
+ state: RouterStateSnapshot
+): Promise => {
+ const keycloak = inject(Keycloak);
+ const router = inject(Router);
+
+ try {
+ // Verificar si el usuario está autenticado
+ const authenticated = await keycloak.authenticated;
+
+ if (authenticated) {
+ // Verificar roles si están especificados en la ruta
+ const requiredRoles = route.data['roles'] as string[];
+
+ if (requiredRoles && requiredRoles.length > 0) {
+ // Comprobar si el usuario tiene los roles requeridos
+ const hasRequiredRole = requiredRoles.some(role =>
+ keycloak.hasRealmRole(role)
+ );
+
+ if (!hasRequiredRole) {
+ console.log('Usuario no tiene los roles requeridos');
+ return router.createUrlTree(['/unauthorized']);
+ }
+ }
+
+ return true;
+ }
+
+ // Si no está autenticado, redirigir a la página de login
+ console.log('Usuario no autenticado, redirigiendo a login');
+ return router.createUrlTree(['/login'], {
+ queryParams: { returnUrl: state.url !== '/' ? state.url : '/inicio' }
+ });
+ } catch (error) {
+ console.error('Error al verificar autenticación:', error);
+ return router.createUrlTree(['/login']);
+ }
+};
+
+// Alternativa usando el helper de keycloak-angular
+const isAccessAllowed = async (
+ route: ActivatedRouteSnapshot,
+ state: RouterStateSnapshot,
+ authData: AuthGuardData
+): Promise => {
+ const { authenticated, grantedRoles } = authData;
+ const router = inject(Router);
+
+ if (authenticated) {
+ const requiredRoles = route.data['roles'] as string[];
+
+ if (!requiredRoles || requiredRoles.length === 0) {
+ return true;
+ }
+
+ const hasRequiredRole = requiredRoles.some(role =>
+ grantedRoles.realmRoles.includes(role)
+ );
+
+ if (hasRequiredRole) {
+ return true;
+ }
+
+ return router.createUrlTree(['/unauthorized']);
+ }
+
+ return router.createUrlTree(['/login'], { queryParams: { returnUrl: state.url } });
+};
+
+// Helper para crear el guardia (opcional)
+export const authGuardWithHelper = createAuthGuard(isAccessAllowed);
+```
+
+### Interceptor HTTP para manejo de errores
+
+```typescript
+import { inject } from '@angular/core';
+import {
+ HttpRequest,
+ HttpHandlerFn,
+ HttpInterceptorFn,
+ HttpErrorResponse
+} from '@angular/common/http';
+import { Observable, throwError, from, switchMap } from 'rxjs';
+import { catchError } from 'rxjs/operators';
+import Keycloak from 'keycloak-js';
+import { Router } from '@angular/router';
+
+/**
+ * Este interceptor complementa al incluido en app.config.ts
+ * Proporciona funcionalidad adicional para manejo de errores
+ */
+export const authInterceptor: HttpInterceptorFn = (
+ request: HttpRequest,
+ next: HttpHandlerFn
+): Observable => {
+ const keycloak = inject(Keycloak);
+ const router = inject(Router);
+
+ // Manejar la petición con gestión de errores de autenticación
+ return next(request).pipe(
+ catchError((error: HttpErrorResponse) => {
+ // Manejar errores 401 Unauthorized
+ if (error.status === 401) {
+ console.log('Error 401, refrescando token o redirigiendo a login');
+
+ // Intentar refrescar el token primero
+ return from(keycloak.updateToken(30)).pipe(
+ switchMap(refreshed => {
+ if (refreshed) {
+ // Token refrescado, reintentar la petición
+ return next(request);
+ } else {
+ // No se pudo refrescar el token, redirigir a login
+ keycloak.login();
+ return throwError(() => error);
+ }
+ }),
+ catchError(refreshError => {
+ console.error('Error al refrescar token:', refreshError);
+ // Redirigir a login en caso de error
+ router.navigate(['/login']);
+ return throwError(() => error);
+ })
+ );
+ }
+
+ // Para otros errores, simplemente pasarlos
+ return throwError(() => error);
+ })
+ );
+};
+```
+
+## 10. Componentes de UI para login/logout
+
+### Componente de login (Angular 19)
+
+```typescript
+import { Component, OnInit, inject } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { Router, ActivatedRoute } from '@angular/router';
+import { AuthService } from '../../services/auth.service';
+import { take } from 'rxjs/operators';
+
+@Component({
+ selector: 'app-login',
+ standalone: true,
+ imports: [CommonModule],
+ templateUrl: './login.component.html',
+ styleUrl: './login.component.scss'
+})
+export class LoginComponent implements OnInit {
+ private authService = inject(AuthService);
+ private route = inject(ActivatedRoute);
+ private router = inject(Router);
+
+ loading: boolean = false;
+ returnUrl: string = '';
+
+ ngOnInit() {
+ // Obtener el returnUrl de los query params
+ this.returnUrl = this.route.snapshot.queryParams['returnUrl'] || '/inicio';
+
+ // Verificar si ya está autenticado y redirigir
+ this.authService.isLoggedIn().subscribe({
+ next: (isLoggedIn) => {
+ if (isLoggedIn) {
+ console.log('Usuario ya autenticado, redirigiendo a:', this.returnUrl);
+ // Comprobar si la URL de retorno es válida
+ const effectiveReturnUrl = this.returnUrl === '/' ? '/inicio' : this.returnUrl;
+ // Redirigir
+ this.router.navigate([effectiveReturnUrl], { replaceUrl: true });
+ }
+ },
+ error: (error) => console.error('Error al verificar autenticación:', error)
+ });
+ }
+
+ async onLogin() {
+ this.loading = true;
+
+ try {
+ // Comprobar si la URL de retorno es válida
+ const effectiveReturnUrl = this.returnUrl === '/' ? '/inicio' : this.returnUrl;
+
+ // Construir la redirectUri
+ const redirectUri = window.location.origin + effectiveReturnUrl;
+ console.log('Iniciando login con redirectUri:', redirectUri);
+
+ // Iniciar el flujo de autenticación
+ await this.authService.login(redirectUri);
+ // Keycloak se encargará de la redirección
+ } catch (error) {
+ console.error('Error al iniciar sesión:', error);
+ this.loading = false;
+ }
+ }
+}
+```
+
+### Plantilla HTML para el componente de login
+
+```html
+
+
+
Iniciar Sesión
+
Por favor, inicia sesión para acceder al sistema.
+
+
+ {{ loading ? 'Cargando...' : 'Iniciar Sesión con Keycloak' }}
+
+
+
+```
+
+### Componente principal con información de usuario (común para ambos enfoques)
+
+Edita `src/app/app.component.ts`:
+
+```typescript
+import { Component, OnInit } from '@angular/core';
+import { AuthService } from './auth.service';
+import { KeycloakProfile } from 'keycloak-js';
+
+@Component({
+ selector: 'app-root',
+ templateUrl: './app.component.html',
+ styleUrls: ['./app.component.css']
+})
+export class AppComponent implements OnInit {
+ title = 'angular-keycloak-app';
+ isLoggedIn = false;
+ userProfile: KeycloakProfile | null = null;
+ userRoles: string[] = [];
+
+ constructor(private authService: AuthService) {}
+
+ async ngOnInit() {
+ this.isLoggedIn = await this.authService.isLoggedIn();
+
+ if (this.isLoggedIn) {
+ this.userProfile = await this.authService.getLoggedUser();
+ this.userRoles = this.authService.getRoles();
+ }
+ }
+
+ login() {
+ this.authService.login();
+ }
+
+ logout() {
+ this.authService.logout();
+ }
+}
+```
+
+Edita `src/app/app.component.html`:
+
+```html
+
+
+
+
+
+
+
+
Bienvenido, {{ userProfile?.firstName }} {{ userProfile?.lastName }}
+
Email: {{ userProfile?.email }}
+
Nombre de Usuario: {{ userProfile?.username }}
+
+
Tus Roles:
+
+
+
Cerrar Sesión
+
+
+
+ Por favor inicia sesión para acceder al sistema
+ Iniciar Sesión
+
+
+
+
+
+
+```
+
+## 11. Arquitectura del sistema
+
+La arquitectura del sistema está compuesta por los siguientes componentes:
+
+- **OpenLDAP**: Directorio de usuarios y grupos (puerto 389)
+- **phpLDAPadmin**: Interfaz gráfica para administrar LDAP (puerto 80)
+- **Keycloak**: Servidor de autenticación y autorización (puerto 8080)
+- **Aplicación Angular**: Cliente que utiliza el SSO de Keycloak (puerto 4200)
+
+La estructura del directorio LDAP para correos.com es:
+
+- dc=correos,dc=com (raíz del directorio)
+ - ou=usuarios (unidad organizativa para usuarios)
+ - uid=admin (usuario administrador)
+ - uid=developer (usuario desarrollador)
+ - uid=user (usuario normal)
+ - ou=grupos (unidad organizativa para grupos)
+ - cn=administradores (grupo de administradores)
+ - cn=desarrolladores (grupo de desarrolladores)
+ - cn=usuarios (grupo de usuarios)
+
+### Flujo de autenticación
+
+El flujo de autenticación funciona de la siguiente manera:
+
+1. El usuario accede a la aplicación Angular
+2. Si el usuario no ha iniciado sesión, es redirigido a Keycloak
+3. Keycloak autentica al usuario contra LDAP
+4. Si la autenticación es exitosa, Keycloak redirige al usuario de vuelta a la aplicación con un token JWT
+5. La aplicación Angular verifica el token y permite el acceso
+6. Los interceptores HTTP añaden automáticamente el token a las peticiones API
+7. Las rutas protegidas verifican los roles del usuario antes de permitir el acceso
+
+## 12. Resolución de problemas comunes
+
+### Problema: "ldap_bind: Invalid credentials (49)"
+
+Este error ocurre cuando intentas conectarte a LDAP con credenciales incorrectas. Para resolverlo:
+
+1. Verifica que estás usando el dominio correcto: `dc=correos,dc=com`
+2. Asegúrate de usar el DN correcto: `cn=admin,dc=correos,dc=com`
+3. Comprueba que la contraseña de administrador es correcta
+
+Si olvidaste la contraseña, puedes restablecerla:
+
+```bash
+sudo dpkg-reconfigure slapd
+```
+
+O también:
+
+```bash
+sudo slappasswd
+# Copia el hash generado
+sudo nano /etc/ldap/slapd.d/cn=config/olcDatabase={1}mdb.ldif
+# Busca olcRootPW y reemplaza el valor con el nuevo hash
+sudo systemctl restart slapd
+```
+
+### Problema: No puedes conectarte a LDAP en absoluto
+
+```bash
+# Verifica que el servicio esté ejecutándose
+sudo systemctl status slapd
+
+# Reinicia el servicio si es necesario
+sudo systemctl restart slapd
+
+# Verifica que el puerto esté abierto
+sudo netstat -tulpn | grep 389
+```
+
+### Problema: Keycloak no se inicia
+
+```bash
+# Verifica los logs
+sudo journalctl -u keycloak
+
+# Asegúrate de que Java esté instalado correctamente
+java -version
+
+# Verifica los permisos
+sudo ls -la /opt/keycloak
+```
+
+### Problema: Error 401 Unauthorized al autenticar con Keycloak
+
+Este error suele ocurrir cuando hay problemas con la configuración del cliente en Keycloak:
+
+1. Verifica que el realm y el clientId sean correctos en la configuración de Angular
+2. Asegúrate de que el cliente en Keycloak tenga la configuración correcta:
+ - Web Origins: debe incluir `http://localhost:4200` o `+` para desarrollo
+ - Valid redirect URIs: debe incluir `http://localhost:4200/*`
+ - Client authentication debe estar OFF para aplicaciones SPA
+ - Access Type debe ser "public"
+
+### Problema: Redireccionamiento circular
+
+Si ves redirecciones constantes entre tu aplicación y Keycloak:
+
+1. Verifica la lógica en el componente app.component.ts y login.component.ts
+2. Asegúrate de que no haya múltiples redirecciones activándose a la vez
+3. Usa el parámetro `replaceUrl: true` en las navegaciones para evitar acumular entradas en el historial
+4. Maneja correctamente las rutas especiales como '/' redirigiendo a una ruta válida como '/inicio'
+
+### Problema: "Can't resolve 'keycloak-angular'"
+
+Si encuentras este error al compilar:
+
+1. Asegúrate de haber instalado correctamente las dependencias:
+ ```bash
+ npm install keycloak-angular keycloak-js
+ ```
+2. Verifica que las versiones sean compatibles con tu versión de Angular
+3. Limpia la caché de npm y reinstala:
+ ```bash
+ npm cache clean --force
+ rm -rf node_modules
+ npm install
+ ```
+
+### Problema: El token no se adjunta a las peticiones HTTP
+
+1. Verifica que estés utilizando el interceptor correcto
+2. Asegúrate de que las condiciones de URL para el token sean correctas
+3. Revisa si estás usando `provideHttpClient` con `withInterceptors([includeBearerTokenInterceptor])`
+4. Comprueba los patrones en `createInterceptorCondition` para asegurarte de que coincidan con tus URLs
+
+## 13. Verificación y prueba del sistema
+
+### Verificar que Keycloak esté sincronizando correctamente con LDAP
+
+1. Inicia sesión en la consola de administración de Keycloak (http://localhost:8080/admin/)
+2. Navega a "Users" en el reino "angular-app"
+3. Deberías ver los usuarios de LDAP: admin, developer y user
+4. Navega a "Groups" y verifica que los grupos de LDAP estén presentes
+
+### Probar la aplicación Angular
+
+1. Asegúrate de que Keycloak esté ejecutándose
+2. Inicia la aplicación Angular con `ng serve`
+3. Navega a http://localhost:4200
+4. Haz clic en "Iniciar Sesión"
+5. Deberías ser redirigido a la pantalla de inicio de sesión de Keycloak
+6. Inicia sesión con uno de los usuarios LDAP:
+ - Usuario: admin, Contraseña: password123
+ - Usuario: developer, Contraseña: password123
+ - Usuario: user, Contraseña: password123
+7. Después de iniciar sesión, serás redirigido de vuelta a la aplicación
+8. La aplicación mostrará tu perfil y roles
+
+## 14. Recursos adicionales
+
+- [Documentación oficial de Keycloak](https://www.keycloak.org/documentation)
+- [Documentación oficial de OpenLDAP](https://www.openldap.org/doc/)
+- [Documentación oficial de keycloak-angular](https://github.com/mauriciovigolo/keycloak-angular)
+- [Guía de migración para Keycloak-Angular v19](https://www.keycloak.org/securing-apps/v19.0.2/angular)
+- [Ejemplos de implementación en GitHub](https://github.com/mauriciovigolo/keycloak-angular/tree/main/examples/standalone-app)
+
+## 15. Resumen
+
+Esta configuración proporciona un sistema completo de gestión de identidades y acceso con:
+
+- **Autenticación centralizada**: Los usuarios solo necesitan recordar un conjunto de credenciales
+- **Gestión de usuarios unificada**: Todos los usuarios se administran en LDAP
+- **Control de acceso basado en roles**: Las rutas y funcionalidades pueden ser protegidas según los roles del usuario
+- **Experiencia de inicio de sesión único (SSO)**: Una vez autenticado, el usuario puede acceder a todas las aplicaciones integradas
+- **Soporte para versiones modernas de Angular**: Incluye configuraciones para Angular tradicional y Angular 19 con standalone components y signals
+
+Este sistema es adecuado para entornos empresariales donde se requiere un control de acceso detallado y una gestión centralizada de usuarios.
+
+La implementación es nativa en un sistema Ubuntu, lo que la hace ideal para despliegues en servidores VPS o entornos similares sin necesidad de contenedores. También funciona perfectamente en entornos de desarrollo local.
\ No newline at end of file