Mejora de la autenticación y la gestión de tokens
Mejora la lógica de actualización de tokens teniendo en cuenta la actividad del usuario y las configuraciones del entorno. Elimina el obsoleto AuthService e integra el registro de actividad para una mejor supervisión. Actualiza las configuraciones del entorno para gestionar los tiempos de inactividad y el comportamiento de renovación de tokens. Aborda los posibles problemas de renovación de tokens relacionados con los usuarios inactivos y mejora la seguridad aplicando políticas estrictas de renovación de tokens. Traducción realizada con la versión gratuita del traductor DeepL.com
This commit is contained in:
parent
8a1434e553
commit
f2ce7327d8
@ -40,6 +40,8 @@ function addToken(request: HttpRequest<unknown>, token: string): HttpRequest<unk
|
||||
|
||||
// Función para manejar errores 401 (token expirado)
|
||||
function handle401Error(req: HttpRequest<unknown>, next: HttpHandlerFn, authService: DirectAuthService) {
|
||||
// Intentar refrescar el token solo si el usuario está activo
|
||||
// El método refreshToken() ya maneja el caso de inactividad internamente
|
||||
return authService.refreshToken().pipe(
|
||||
switchMap((token: any) => {
|
||||
return next(addToken(req, token.access_token));
|
||||
|
||||
@ -1,69 +0,0 @@
|
||||
// This file is kept for reference only and is not used in the application.
|
||||
// The application now uses DirectAuthService instead.
|
||||
|
||||
import { Injectable, signal } from '@angular/core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { BehaviorSubject, Observable, from } from 'rxjs';
|
||||
import { Router } from '@angular/router';
|
||||
import { MessageService } from 'primeng/api';
|
||||
import { environment } from '../../environments/environment';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class AuthService {
|
||||
// User state
|
||||
private userSubject = new BehaviorSubject<any>(null);
|
||||
public user$ = this.userSubject.asObservable();
|
||||
|
||||
// Authentication state as a signal
|
||||
public isAuthenticated = signal<boolean>(false);
|
||||
|
||||
constructor(
|
||||
private http: HttpClient,
|
||||
private router: Router,
|
||||
private messageService: MessageService
|
||||
) {}
|
||||
|
||||
async login(redirectUri?: string): Promise<void> {
|
||||
console.warn('AuthService is deprecated. Use DirectAuthService instead.');
|
||||
return Promise.reject('AuthService is deprecated. Use DirectAuthService instead.');
|
||||
}
|
||||
|
||||
async logout(): Promise<void> {
|
||||
console.warn('AuthService is deprecated. Use DirectAuthService instead.');
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
isLoggedIn(): Observable<boolean> {
|
||||
console.warn('AuthService is deprecated. Use DirectAuthService instead.');
|
||||
return from(Promise.resolve(false));
|
||||
}
|
||||
|
||||
getToken(): Promise<string> {
|
||||
console.warn('AuthService is deprecated. Use DirectAuthService instead.');
|
||||
return Promise.resolve('');
|
||||
}
|
||||
|
||||
async updateToken(): Promise<boolean> {
|
||||
console.warn('AuthService is deprecated. Use DirectAuthService instead.');
|
||||
return false;
|
||||
}
|
||||
|
||||
getCurrentUser(): any {
|
||||
console.warn('AuthService is deprecated. Use DirectAuthService instead.');
|
||||
return null;
|
||||
}
|
||||
|
||||
// Check if user has a specific role
|
||||
hasRole(role: string): boolean {
|
||||
console.warn('AuthService is deprecated. Use DirectAuthService instead.');
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if user has any of the specified roles
|
||||
hasAnyRole(roles: string[]): boolean {
|
||||
console.warn('AuthService is deprecated. Use DirectAuthService instead.');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -32,16 +32,36 @@ export class DirectAuthService {
|
||||
// Idle detection
|
||||
private userActivity: any = null;
|
||||
private userInactive = signal<boolean>(false);
|
||||
private lastActivityTime: number = Date.now();
|
||||
|
||||
// Percentage for inactivity timeout (90% of token lifetime)
|
||||
private readonly INACTIVITY_PERCENTAGE = 0.9;
|
||||
// Configuraciones de inactividad desde environment
|
||||
private readonly INACTIVITY_PERCENTAGE: number;
|
||||
private readonly MIN_INACTIVITY_TIME: number;
|
||||
private readonly STRICT_TOKEN_RENEWAL: boolean;
|
||||
private readonly ENABLE_ACTIVITY_LOGS: boolean;
|
||||
private readonly LOG_INTERVAL: number;
|
||||
|
||||
// Temporizador para logs de actividad
|
||||
private activityLogTimer: any = null;
|
||||
|
||||
constructor(private http: HttpClient) {
|
||||
// Inicializar configuraciones desde environment
|
||||
this.INACTIVITY_PERCENTAGE = environment.auth.inactivityPercentage;
|
||||
this.MIN_INACTIVITY_TIME = environment.auth.minInactivityTime;
|
||||
this.STRICT_TOKEN_RENEWAL = environment.auth.strictTokenRenewal;
|
||||
this.ENABLE_ACTIVITY_LOGS = environment.auth.enableActivityLogs;
|
||||
this.LOG_INTERVAL = environment.auth.logInterval;
|
||||
|
||||
// Intentar cargar el token del almacenamiento local al iniciar
|
||||
this.loadTokenFromStorage();
|
||||
|
||||
// Iniciar monitoreo de actividad
|
||||
this.setupActivityMonitoring();
|
||||
|
||||
// Iniciar logs de actividad si están habilitados
|
||||
if (this.ENABLE_ACTIVITY_LOGS && this.LOG_INTERVAL > 0) {
|
||||
this.startActivityLogging();
|
||||
}
|
||||
}
|
||||
|
||||
// Cargar token del almacenamiento local
|
||||
@ -53,25 +73,47 @@ export class DirectAuthService {
|
||||
|
||||
// Verificar si el token ha expirado
|
||||
if (this.isTokenExpired()) {
|
||||
// Si tiene refresh token, intentar renovar
|
||||
if (this.tokenInfo.refresh_token) {
|
||||
// Token expirado al cargar
|
||||
// Si tiene refresh token y (no está en modo estricto o el usuario ha estado activo), intentar renovar
|
||||
if (this.tokenInfo.refresh_token && (!this.STRICT_TOKEN_RENEWAL || this.isUserActive())) {
|
||||
// Usuario activo o no en modo estricto, intentando renovar token
|
||||
this.refreshToken().subscribe();
|
||||
} else {
|
||||
// Usuario inactivo o sin refresh token, cerrando sesión
|
||||
this.logout();
|
||||
}
|
||||
} else {
|
||||
// Decodificar info del usuario desde el token
|
||||
this.setUserFromToken(this.tokenInfo.access_token);
|
||||
// Configurar temporizador para renovación de token
|
||||
this.startRefreshTokenTimer();
|
||||
// Solo si el usuario está activo (en modo estricto) o siempre (en modo no estricto)
|
||||
if (!this.STRICT_TOKEN_RENEWAL || this.isUserActive()) {
|
||||
this.startRefreshTokenTimer();
|
||||
}
|
||||
|
||||
// Iniciar logs de actividad si están habilitados
|
||||
if (this.ENABLE_ACTIVITY_LOGS && this.LOG_INTERVAL > 0) {
|
||||
console.log('[Auth] Iniciando logs al cargar token existente');
|
||||
this.startActivityLogging();
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Error al cargar token:', e);
|
||||
// Error al cargar token
|
||||
this.logout();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Comprueba si el usuario ha estado activo dentro del período de inactividad
|
||||
private isUserActive(): boolean {
|
||||
const currentTime = Date.now();
|
||||
const inactiveTime = currentTime - this.lastActivityTime;
|
||||
|
||||
// El usuario se considera activo si no ha estado inactivo por más tiempo que
|
||||
// el porcentaje configurado del tiempo de vida del token o el tiempo mínimo
|
||||
return inactiveTime < this.MIN_INACTIVITY_TIME;
|
||||
}
|
||||
|
||||
// Login directo con credenciales
|
||||
public login(username: string, password: string): Observable<any> {
|
||||
const headers = new HttpHeaders({
|
||||
@ -99,9 +141,18 @@ export class DirectAuthService {
|
||||
|
||||
// Reiniciar detección de inactividad
|
||||
this.resetInactivity();
|
||||
|
||||
// Registrar tiempo de actividad
|
||||
this.lastActivityTime = Date.now();
|
||||
|
||||
// Iniciar logs de actividad después del login si están habilitados
|
||||
if (this.ENABLE_ACTIVITY_LOGS && this.LOG_INTERVAL > 0) {
|
||||
console.log('[Auth] Iniciando logs después del login');
|
||||
this.startActivityLogging();
|
||||
}
|
||||
}),
|
||||
catchError(error => {
|
||||
console.error('Error de autenticación:', error);
|
||||
// Error de autenticación
|
||||
return throwError(() => new Error('Credenciales incorrectas o error de servidor'));
|
||||
})
|
||||
);
|
||||
@ -115,6 +166,9 @@ export class DirectAuthService {
|
||||
// Detener monitoreo de actividad
|
||||
this.stopActivityMonitoring();
|
||||
|
||||
// Detener logs de actividad
|
||||
this.stopActivityLogging();
|
||||
|
||||
// Limpiar datos de sesión
|
||||
localStorage.removeItem('keycloak_token');
|
||||
this.tokenInfo = null;
|
||||
@ -124,15 +178,43 @@ export class DirectAuthService {
|
||||
this.router.navigate(['/login']);
|
||||
}
|
||||
|
||||
// Iniciar logs de actividad en intervalos regulares
|
||||
private startActivityLogging(): void {
|
||||
// Detener cualquier temporizador existente
|
||||
this.stopActivityLogging();
|
||||
|
||||
// Crear nuevo temporizador para logs
|
||||
this.activityLogTimer = setInterval(() => {
|
||||
const currentTime = Date.now();
|
||||
const inactiveTime = currentTime - this.lastActivityTime;
|
||||
const inactiveSeconds = Math.round(inactiveTime / 1000);
|
||||
|
||||
// Calcular tiempo hasta expiración del token
|
||||
const tokenExpirationTime = this.getTokenExpirationTime();
|
||||
const expirationSeconds = Math.round(tokenExpirationTime / 1000);
|
||||
|
||||
console.log(`[Auth] Inactividad: ${inactiveSeconds}s | Expiración token: ${expirationSeconds}s | Inactivo: ${this.userInactive()} | Umbral: ${Math.round(this.MIN_INACTIVITY_TIME/1000)}s`);
|
||||
}, this.LOG_INTERVAL);
|
||||
}
|
||||
|
||||
// Detener logs de actividad
|
||||
private stopActivityLogging(): void {
|
||||
if (this.activityLogTimer) {
|
||||
clearInterval(this.activityLogTimer);
|
||||
this.activityLogTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Renovar token usando refresh token
|
||||
public refreshToken(): Observable<any> {
|
||||
if (!this.tokenInfo?.refresh_token) {
|
||||
return throwError(() => new Error('No hay refresh token disponible'));
|
||||
}
|
||||
|
||||
// No refrescar token si el usuario está inactivo
|
||||
if (this.userInactive()) {
|
||||
console.log('Usuario inactivo, no se renovará el token');
|
||||
// Verificar si el usuario ha estado inactivo por más tiempo que el umbral configurado
|
||||
// Si STRICT_TOKEN_RENEWAL está activado, verificar la actividad del usuario
|
||||
if (this.STRICT_TOKEN_RENEWAL && !this.isUserActive()) {
|
||||
// Usuario inactivo por más tiempo que el permitido, no se renovará el token
|
||||
this.logout();
|
||||
return throwError(() => new Error('Usuario inactivo'));
|
||||
}
|
||||
@ -146,9 +228,12 @@ export class DirectAuthService {
|
||||
.set('grant_type', 'refresh_token')
|
||||
.set('refresh_token', this.tokenInfo.refresh_token);
|
||||
|
||||
// Intentando renovar el token
|
||||
|
||||
return this.http.post<any>(this.tokenEndpoint, params.toString(), { headers })
|
||||
.pipe(
|
||||
tap(newTokenInfo => {
|
||||
// Token renovado exitosamente
|
||||
// Actualizar información del token
|
||||
this.tokenInfo = newTokenInfo;
|
||||
localStorage.setItem('keycloak_token', JSON.stringify(newTokenInfo));
|
||||
@ -161,9 +246,15 @@ export class DirectAuthService {
|
||||
|
||||
// Reiniciar detección de inactividad
|
||||
this.resetInactivity();
|
||||
|
||||
// Reiniciar logs si están habilitados
|
||||
if (this.ENABLE_ACTIVITY_LOGS && this.LOG_INTERVAL > 0) {
|
||||
console.log('[Auth] Reiniciando logs después de renovación de token');
|
||||
this.startActivityLogging();
|
||||
}
|
||||
}),
|
||||
catchError(error => {
|
||||
console.error('Error al renovar token:', error);
|
||||
// Error al renovar token
|
||||
// Si falla la renovación, forzar cierre de sesión
|
||||
this.logout();
|
||||
return throwError(() => new Error('Error al renovar la sesión'));
|
||||
@ -204,7 +295,7 @@ export class DirectAuthService {
|
||||
|
||||
return currentTime >= expirationTime;
|
||||
} catch (e) {
|
||||
console.error('Error al verificar expiración del token:', e);
|
||||
// Error al verificar expiración del token
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -231,7 +322,7 @@ export class DirectAuthService {
|
||||
|
||||
this.userInfo.set(user);
|
||||
} catch (e) {
|
||||
console.error('Error al decodificar token:', e);
|
||||
// Error al decodificar token
|
||||
this.userInfo.set(null);
|
||||
}
|
||||
}
|
||||
@ -256,11 +347,19 @@ export class DirectAuthService {
|
||||
const timeToExpiry = expirationTime - currentTime;
|
||||
const refreshTime = timeToExpiry * 0.7;
|
||||
|
||||
// Calcular tiempos para la renovación del token
|
||||
|
||||
this.refreshTokenTimeout = setTimeout(() => {
|
||||
this.refreshToken().subscribe();
|
||||
// Si STRICT_TOKEN_RENEWAL está activado, verificar la actividad del usuario
|
||||
if (!this.STRICT_TOKEN_RENEWAL || this.isUserActive()) {
|
||||
this.refreshToken().subscribe();
|
||||
} else {
|
||||
// En modo estricto, si el usuario está inactivo, cerrar sesión
|
||||
this.logout();
|
||||
}
|
||||
}, refreshTime);
|
||||
} catch (e) {
|
||||
console.error('Error al configurar renovación de token:', e);
|
||||
// Error al configurar renovación de token
|
||||
}
|
||||
}
|
||||
|
||||
@ -300,7 +399,7 @@ export class DirectAuthService {
|
||||
|
||||
return Math.max(0, timeRemaining);
|
||||
} catch (error) {
|
||||
console.error('Error al obtener tiempo de expiración del token:', error);
|
||||
// Error al obtener tiempo de expiración del token
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@ -333,8 +432,13 @@ export class DirectAuthService {
|
||||
|
||||
// Reiniciar timer de inactividad basado en un porcentaje de la expiración del token
|
||||
private resetInactivity(): void {
|
||||
// Registrar que el usuario está activo
|
||||
this.lastActivityTime = Date.now();
|
||||
|
||||
// Marca el usuario como activo
|
||||
this.userInactive.set(false);
|
||||
|
||||
// Limpiar cualquier temporizador existente
|
||||
clearTimeout(this.userActivity);
|
||||
|
||||
// Obtener el tiempo de expiración del token
|
||||
@ -345,9 +449,15 @@ export class DirectAuthService {
|
||||
}
|
||||
|
||||
// Calcular el tiempo de inactividad como el porcentaje configurado del tiempo de expiración
|
||||
const inactivityTime = tokenExpirationTime * this.INACTIVITY_PERCENTAGE;
|
||||
const inactivityTime = Math.min(
|
||||
tokenExpirationTime * this.INACTIVITY_PERCENTAGE,
|
||||
this.MIN_INACTIVITY_TIME
|
||||
);
|
||||
|
||||
// Configurar timer de inactividad
|
||||
|
||||
this.userActivity = setTimeout(() => {
|
||||
// Umbral de inactividad alcanzado
|
||||
this.userInactive.set(true);
|
||||
}, inactivityTime);
|
||||
}
|
||||
|
||||
@ -10,5 +10,17 @@ export const environment = {
|
||||
},
|
||||
api: {
|
||||
baseUrl: '/api'
|
||||
},
|
||||
auth: {
|
||||
// Tiempo mínimo de inactividad en milisegundos (5 minutos para producción)
|
||||
minInactivityTime: 5 * 60 * 1000,
|
||||
// Porcentaje del tiempo de vida del token para considerar inactividad (90%)
|
||||
inactivityPercentage: 0.9,
|
||||
// Estricto: true = no renovar si inactivo, false = renovar siempre
|
||||
strictTokenRenewal: true,
|
||||
// Intervalo de log en milisegundos (desactivado en producción)
|
||||
logInterval: 0,
|
||||
// Deshabilitar logs de inactividad en producción
|
||||
enableActivityLogs: false
|
||||
}
|
||||
};
|
||||
@ -10,5 +10,17 @@ export const environment = {
|
||||
},
|
||||
api: {
|
||||
baseUrl: '/api'
|
||||
},
|
||||
auth: {
|
||||
// Tiempo mínimo de inactividad en milisegundos (30 segundos para desarrollo)
|
||||
minInactivityTime: 30 * 1000,
|
||||
// Porcentaje del tiempo de vida del token para considerar inactividad (90%)
|
||||
inactivityPercentage: 0.9,
|
||||
// Estricto: true = no renovar si inactivo, false = renovar siempre
|
||||
strictTokenRenewal: true,
|
||||
// Intervalo de log en milisegundos (1 segundo)
|
||||
logInterval: 2000,
|
||||
// Habilitar logs de inactividad
|
||||
enableActivityLogs: true
|
||||
}
|
||||
};
|
||||
@ -468,39 +468,60 @@ sudo useradd -r -s /sbin/nologin keycloak
|
||||
# Asignar permisos
|
||||
sudo chown -R keycloak:keycloak /opt/keycloak
|
||||
```
|
||||
|
||||
### Configurar usuario administrador inicial para Keycloak
|
||||
## Configurar usuario administrador inicial para Keycloak
|
||||
|
||||
Crea un archivo de propiedades:
|
||||
|
||||
```bash
|
||||
sudo nano /opt/keycloak/conf/keycloak.conf
|
||||
|
||||
```
|
||||
|
||||
Agrega estas líneas:
|
||||
|
||||
> En mi caso esta máquina tiene la IP `192.168.1.27`, ajustar según sea necesario en una red real para la administración remota.
|
||||
|
||||
```
|
||||
# Configuración básica
|
||||
hostname=192.168.1.27
|
||||
http-port=8080
|
||||
https-port=8443
|
||||
hostname=localhost
|
||||
http-enabled=true
|
||||
|
||||
# Configuración de administrador inicial
|
||||
http-enabled=true
|
||||
```
|
||||

|
||||
|
||||
### Iniciar Keycloak en modo desarrollo
|
||||
```
|
||||
|
||||

|
||||
|
||||
## Crear un usuario administrador
|
||||
|
||||
```bash
|
||||
cd /opt/keycloak
|
||||
export KEYCLOAK_ADMIN=admin
|
||||
export KEYCLOAK_ADMIN_PASSWORD=admin
|
||||
sudo -u keycloak bin/kc.sh start-dev
|
||||
sudo -u keycloak /opt/keycloak/bin/kc.sh bootstrap-admin user --username admin
|
||||
|
||||
```
|
||||
|
||||
En este comando preguntará para crear la contraseña del usuario "admin" que usaremos como administrador.
|
||||
|
||||
### Configurar Keycloak como servicio (opcional)
|
||||
## Iniciar Keycloak en modo desarrollo
|
||||
|
||||
Sin hacer cd, corremos el siguiente comando:
|
||||
|
||||
```bash
|
||||
sudo -u keycloak bin/kc.sh start-dev
|
||||
|
||||
```
|
||||
|
||||
## Iniciar Keycloak en modo productivo
|
||||
|
||||
Similar al anterior, sin embargo se le quita la flag `-dev` únicamente:
|
||||
|
||||
```bash
|
||||
sudo -u keycloak bin/kc.sh start
|
||||
```
|
||||
|
||||
## Configurar Keycloak como servicio productivo
|
||||
|
||||
En otra terminal, crea un archivo de servicio systemd:
|
||||
|
||||
@ -519,9 +540,7 @@ After=network.target
|
||||
Type=idle
|
||||
User=keycloak
|
||||
Group=keycloak
|
||||
Environment="KEYCLOAK_ADMIN=admin"
|
||||
Environment="KEYCLOAK_ADMIN_PASSWORD=admin"
|
||||
ExecStart=/opt/keycloak/bin/kc.sh start-dev
|
||||
ExecStart=/opt/keycloak/bin/kc.sh start
|
||||
TimeoutStartSec=600
|
||||
TimeoutStopSec=600
|
||||
|
||||
@ -537,77 +556,96 @@ sudo systemctl enable keycloak
|
||||
sudo systemctl start keycloak
|
||||
```
|
||||
|
||||
## 5. Configuración de Keycloak en la interfaz web
|
||||
Para ver el estado del servicio únicamente corre el comando:
|
||||
|
||||
```bash
|
||||
sudo systemctl status keycloak
|
||||
```
|
||||
|
||||
> **Nota**: si quieres ver el log del servicio usa el comando `sudo journalctl -u keycloak -f`
|
||||
|
||||
## 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`
|
||||
- Usuario: `admin`
|
||||
- Contraseña: la que configuraste anteriormente
|
||||
|
||||
Esto iniciará Keycloak con un usuario administrador "admin" y contraseña "admin" tambien pedira cambiar la contraseña
|
||||

|
||||
> No necesariamente es ese login siempre, todo depende de qué usuario crearon anteriormente y su contraseña.
|
||||
|
||||
### Crear un nuevo Reino (Realm)
|
||||

|
||||
|
||||
## Crear un nuevo Reino (Realm)
|
||||
|
||||
1. Haz clic en el menú desplegable superior izquierdo que dice "Manage realm"
|
||||
2. Selecciona "Create Realm"
|
||||
3. Ingresa el nombre: `angular-app`
|
||||
4. Haz clic en "Create"
|
||||
|
||||
1. Haz clic en el menú desplegable superior izquierdo que dice "Manage realm"
|
||||
2. Selecciona "Create Realm"
|
||||
3. Ingresa el nombre: `angular-app`
|
||||
4. Haz clic en "Create"
|
||||

|
||||
|
||||
### Configurar la Federación de Usuarios LDAP
|
||||
## Configurar la Federación de Usuarios LDAP
|
||||
|
||||
1. En el menú lateral izquierdo, selecciona "User Federation"
|
||||
|
||||
2. Haz clic en "Add Ldap provider"
|
||||

|
||||
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)
|
||||
1. En el menú lateral izquierdo, selecciona "User Federation"
|
||||
|
||||
2. Haz clic en "Add Ldap provider"
|
||||
|
||||

|
||||
|
||||
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
|
||||
|
||||
|
||||

|
||||
- 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
|
||||
|
||||
> mas abajo hay mas configuraciones pero se quedan en su valor por defecto,
|
||||
> despues de guardar regresara a la pestaña anterior donde tendremos que precionar en la configuracion recien creada
|
||||
> Más abajo hay más configuraciones pero se quedan con su valor por defecto. Después de guardar regresará a la pestaña anterior donde tendremos que presionar en la configuración recién creada.
|
||||
|
||||

|
||||
|
||||
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
|
||||
6. En la pantalla del proveedor LDAP, ve a la pestaña "Synchronization"
|
||||
|
||||
1. En la pantalla del proveedor LDAP, ve a la pestaña "Mappers"
|
||||

|
||||
2. Haz clic en "Add mapper"
|
||||

|
||||
3.# Configuración Corregida del Mapeo de Grupos LDAP en Keycloak
|
||||
|
||||
3. En la pantalla del proveedor LDAP, ve a la pestaña "Mappers"
|
||||
|
||||

|
||||
|
||||
4. Haz clic en "Add mapper"
|
||||
7. Haz clic en "Sync all users"
|
||||
|
||||
5. Completa los siguientes campos exactamente en este orden:
|
||||
|
||||
## Configurar el Mapeo de Grupos LDAP
|
||||
|
||||
1. En la pantalla del proveedor LDAP, ve a la pestaña "Mappers"
|
||||
|
||||

|
||||
|
||||
2. Haz clic en "Add mapper"
|
||||
|
||||

|
||||
|
||||
|
||||
## Configuración del Mapeo de Grupos LDAP en Keycloak
|
||||
|
||||
3. Completa los siguientes campos exactamente en este orden:
|
||||
|
||||
- **Name**: `group-mapper`
|
||||
- **Mapper type**: `group-ldap-mapper`
|
||||
@ -618,7 +656,7 @@ Esto iniciará Keycloak con un usuario administrador "admin" y contraseña "admi
|
||||
- **Preserve Group Inheritance**: `Off` _(¡IMPORTANTE! Debe estar desactivado para funcionar con Membership Attribute Type: UID)_
|
||||
- **Ignore Missing Groups**: `Off`
|
||||
- **Membership LDAP Attribute**: `memberUid`
|
||||
- **Membership Attribute Type**: `UID` _(¡IMPORTANTE! Debe ser UID, no DN )_
|
||||
- **Membership Attribute Type**: `UID` _(¡IMPORTANTE! Debe ser UID, no DN)_
|
||||
- **Membership User LDAP Attribute**: `uid`
|
||||
- **LDAP Filter**: (dejar en blanco)
|
||||
- **Mode**: `LDAP_ONLY`
|
||||
@ -627,46 +665,51 @@ Esto iniciará Keycloak con un usuario administrador "admin" y contraseña "admi
|
||||
- **Mapped Group Attributes**: (dejar en blanco)
|
||||
- **Drop non-existing groups during sync**: `ON`
|
||||
- **Groups Path**: `/`
|
||||
6. Haz clic en "Save"
|
||||
4. Haz clic en "Save"
|
||||
|
||||
|
||||
> **Nota importante**: Esta configuración está optimizada para tu estructura LDAP donde se utiliza `posixGroup` con atributos `memberUid` simples (no DNs). El punto crítico para evitar errores es asegurarte de que **"Preserve Group Inheritance" esté desactivado (Off)** cuando usas el tipo de membresía UID.
|
||||
|
||||
> **Nota importante**: Esta configuración está optimizada para tu estructura LDAP donde se utiliza `posixGroup` con atributos `memberUid` simples (no DNs). El punto crítico para evitar el error es asegurarte de que **"Preserve Group Inheritance" esté desactivado (Off)** cuando usas el tipo de membresía UID.
|
||||

|
||||
|
||||
|
||||
> al igual que con el provedor ldap , al guardar se regresara a la pantalla anterior , donde tendremos que hacer click nuevamente en el mapper
|
||||
|
||||
7. En la pantalla del mapper, haz clic en "Sync LDAP Groups to Keycloak"
|
||||
> Al igual que con el proveedor LDAP, al guardar se regresará a la pantalla anterior, donde tendremos que hacer clic nuevamente en el mapper.
|
||||
|
||||
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"
|
||||
|
||||

|
||||
|
||||
### 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.
|
||||
> **Nota**: 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
|
||||
|
||||
@ -1761,3 +1804,4 @@ Con la configuración y conocimientos adquiridos en este tutorial, estarás bien
|
||||
---
|
||||
|
||||
*Nota final: Recuerda que la seguridad es un proceso continuo, no un estado. Mantén todos los componentes actualizados y revisa regularmente las configuraciones y políticas de seguridad para adaptarte a nuevas amenazas y requisitos.*
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user