Tengo una pregunta.
¿Es posible usar múltiples guardias de autenticación en una ruta (en mi caso, autenticación básica y ldap). La ruta debe ser autenticada cuando un guardia fue exitoso.
Solución del problema
Puede crear una protección abstracta y pasar instancias o referencias allí, y devolver verdadero desde esta protección si alguna de las protecciones aprobadas devolvió verdadero.
Imaginemos que tienes 2 guardias: BasicGuard
y LdapGuard
. Y tienes un controlador UserController
con ruta @Get()
, que debe ser protegido por estos guardias.
Entonces, podemos crear una guardia abstracta MultipleAuthorizeGuard
con el siguiente código:
@Injectable()
export class MultipleAuthorizeGuard implements CanActivate {
constructor(private readonly reflector: Reflector, private readonly moduleRef: ModuleRef) {}
public canActivate(context: ExecutionContext): Observable<boolean> {
const allowedGuards = this.reflector.get<Type<CanActivate>[]>('multipleGuardsReferences', context.getHandler()) || [];
const guards = allowedGuards.map((guardReference) => this.moduleRef.get<CanActivate>(guardReference));
if (guards.length === 0) {
return of(true);
}
if (guards.length === 1) {
return guards[0].canActivate(context) as Observable<boolean>;
}
const checks$: Observable<boolean>[] = guards.map((guard) =>
(guard.canActivate(context) as Observable<boolean>).pipe(
catchError((err) => {
if (err instanceof UnauthorizedException) {
return of(false);
}
throw err;
}),
),
);
return forkJoin(checks$).pipe(map((results: boolean[]) => any(identity, results)));
}
}
Como puede ver, este guardia no contiene ninguna referencia a un guardia en particular, sino que solo acepta la lista de referencias. En mi ejemplo, todos los guardias regresan Observable
, por lo que uso forkJoin
para ejecutar múltiples solicitudes. Pero, por supuesto, también se puede adoptar para Promises.
Para evitar iniciar MultipleAuthorizeGuard
en el controlador y pasar las dependencias necesarias manualmente, le dejo esta tarea a Nest.js y paso las referencias a través de un decorador personalizado.MultipleGuardsReferences
export const MultipleGuardsReferences = (...guards: Type<CanActivate>[]) =>
SetMetadata('multipleGuardsReferences', guards);
Entonces, en el controlador podemos tener el siguiente código:
@Get()
@MultipleGuardsReferences(BasicGuard, LdapGuard)
@UseGuards(MultipleAuthorizeGuard)
public getUser(): Observable<User> {
return this.userService.getUser();
}
No hay comentarios:
Publicar un comentario