@laqus/platform-auth-angular
v1.0.1
Published
Biblioteca Angular para autenticação e autorização com o Laqus Platform
Readme
Laqus Platform Auth Angular
Este guia explica como integrar e utilizar as bibliotecas @laqus/platform-auth-angular e @laqus/auth-permissions em aplicações Angular.
Instalação
# Instalar a biblioteca de autenticação para Angular
npm install @laqus/platform-auth-angular --save
# Instalar a biblioteca de permissões
npm install @laqus/auth-permissions --saveConfiguração Básica
1. Configurar o Módulo de Autenticação
No seu app.module.ts ou em um módulo específico de autenticação:
import { NgModule } from '@angular/core';
import { PlatformAuthModule } from '@laqus/platform-auth-angular';
import { AUTH_CONFIG } from '@laqus/platform-auth-angular';
@NgModule({
imports: [
// ... outros módulos
PlatformAuthModule
],
providers: [
{
provide: AUTH_CONFIG,
useValue: {
authMiddlewareApiUrlBase: 'https://sua-api-auth.com',
cache: {
authCacheKeysPrefix: 'app_auth_',
expirationTimeInMs: 1800000 // 30 minutos em milissegundos (opcional, padrão: 30 minutos)
}
}
}
]
})
export class AppModule { }2. Inicialização Automática
A biblioteca @laqus/platform-auth-angular inicializa automaticamente o serviço de autenticação quando é carregada, verificando se existe um token salvo no localStorage.
Uso do AuthService
Login
import { Component } from '@angular/core';
import { AuthService } from '@laqus/platform-auth-angular';
import { Router } from '@angular/router';
@Component({
selector: 'app-login',
template: `...`
})
export class LoginComponent {
constructor(
private authService: AuthService,
private router: Router
) {}
login(username: string, password: string) {
this.authService.loginWithCredentials(username, password).subscribe({
next: (result) => {
if (result.success) {
this.router.navigate(['/dashboard']);
}
},
error: (error) => {
console.error('Login failed', error);
// Tratar erro de login
}
});
}
}Verificar Autenticação
import { Component, OnInit } from '@angular/core';
import { AuthService } from '@laqus/platform-auth-angular';
import { Router } from '@angular/router';
@Component({
selector: 'app-navbar',
template: `
<nav>
<div *ngIf="isAuthenticated">
Bem-vindo, {{ username }}
<button (click)="logout()">Sair</button>
</div>
<div *ngIf="!isAuthenticated">
<a routerLink="/login">Login</a>
</div>
</nav>
`
})
export class NavbarComponent implements OnInit {
isAuthenticated = false;
username = '';
constructor(
private authService: AuthService,
private router: Router
) {}
ngOnInit() {
// Verificar status de autenticação inicial
this.authService.checkAuthStatus().subscribe();
// Inscrever-se para mudanças no status de autenticação
this.authService.isAuthenticated$.subscribe(isAuth => {
this.isAuthenticated = isAuth;
});
// Inscrever-se para mudanças no usuário atual
this.authService.currentUser$.subscribe(user => {
this.username = user?.name || '';
});
}
logout(): void {
this.authService.logout().subscribe(() => {
this.router.navigate(['/login']);
});
}
}Verificação de Permissões
Usando o AuthService para Verificar Permissões
import { Component, OnInit } from '@angular/core';
import { AuthService } from '@laqus/platform-auth-angular';
import { IResourceAction } from '@laqus/platform-auth-angular';
import { PlatformActions } from '@laqus/auth-permissions';
@Component({
selector: 'app-user-management',
template: `
<div *ngIf="canListUsers">
<!-- Conteúdo visível apenas para usuários com permissão -->
<button *ngIf="canCreateUsers" (click)="createUser()">Novo Usuário</button>
</div>
`
})
export class UserManagementComponent implements OnInit {
canListUsers = false;
canCreateUsers = false;
constructor(private authService: AuthService) {}
ngOnInit() {
// Verificar permissões específicas
this.canListUsers = this.authService.hasAction(
PlatformActions.PlatformManagement.Tenant.User.Users.List
);
this.canCreateUsers = this.authService.hasAction(
PlatformActions.PlatformManagement.Tenant.User.Users.Create
);
// Verificar múltiplas permissões
const hasAllPermissions = this.authService.hasAllActions([
PlatformActions.PlatformManagement.Tenant.User.Users.List,
PlatformActions.PlatformManagement.Tenant.User.Users.Create
]);
// Verificar se tem pelo menos uma permissão
const hasAnyPermission = this.authService.hasAnyAction([
PlatformActions.PlatformManagement.Tenant.User.Users.List,
PlatformActions.PlatformManagement.Tenant.User.Users.Create
]);
}
}Usando Diretivas de Permissão
Para usar as diretivas de permissão, importe o PlatformAuthModule no seu módulo:
import { NgModule } from '@angular/core';
import { PlatformAuthModule } from '@laqus/platform-auth-angular';
@NgModule({
imports: [
// ... outros módulos
PlatformAuthModule
]
})
export class FeatureModule { }Então, use a diretiva hasAction nos seus templates:
<!-- Elemento visível apenas se o usuário tiver a permissão específica -->
<button *hasAction="PlatformActions.PlatformManagement.Tenant.User.Users.Create">
Criar Usuário
</button>
<!-- Elemento visível apenas se o usuário tiver todas as permissões -->
<div *hasAllActions="[listAction, createAction, deleteAction]">
Acesso completo
</div>
<!-- Elemento visível se o usuário tiver pelo menos uma das permissões -->
<div *hasAnyAction="[listAction, createAction]">
Acesso parcial
</div>Proteção de Rotas
Use o ActionAuthGuard para proteger rotas com base em permissões:
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { ActionAuthGuard } from '@laqus/platform-auth-angular';
import { PlatformActions } from '@laqus/auth-permissions';
const routes: Routes = [
{
path: 'users',
component: UserListComponent,
canActivate: [ActionAuthGuard],
data: {
requiredAction: PlatformActions.PlatformManagement.Tenant.User.Users.List
}
},
{
path: 'users/create',
component: UserCreateComponent,
canActivate: [ActionAuthGuard],
data: {
requiredAction: PlatformActions.PlatformManagement.Tenant.User.Users.Create
}
}
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class UsersRoutingModule { }Estrutura de Permissões
A biblioteca @laqus/auth-permissions fornece uma estrutura tipada para as permissões do sistema:
import { PlatformActions } from '@laqus/auth-permissions';
// Exemplos de permissões disponíveis
const userListAction = PlatformActions.PlatformManagement.Tenant.User.Users.List;
const userCreateAction = PlatformActions.PlatformManagement.Tenant.User.Users.Create;
const userUnassignAction = PlatformActions.PlatformManagement.Tenant.User.Users.Unassign;
const allUserActions = PlatformActions.PlatformManagement.Tenant.User.Users.All;Boas Práticas
Inicialização Automática: A biblioteca gerencia automaticamente o token no localStorage.
Verificação de Autenticação: Use
authService.isAuthenticated()para verificar se o usuário está autenticado.Verificação de Permissões: Use os métodos
hasAction(),hasAllActions()ouhasAnyAction()para verificar permissões.Diretivas: Use as diretivas
*hasAction,*hasAllActionse*hasAnyActionpara controle de acesso no template.Proteção de Rotas: Use o
ActionAuthGuardpara proteger rotas com base em permissões.
Integração com Auth Middleware API Diretamente
Se sua aplicação já realiza autenticação diretamente com o endpoint /api/v1/auth/user/sessions/credentials/login do Auth Middleware API, você pode integrar facilmente com a biblioteca para usar os recursos de autorização. Este endpoint estabelece uma sessão de usuário que é utilizada pela biblioteca.
1. Autenticação Direta com Auth Middleware API
import { HttpClient } from '@angular/common/http';
import { AuthService } from '@laqus/platform-auth-angular';
import { Router } from '@angular/router';
@Component({...})
export class LoginComponent {
constructor(
private http: HttpClient,
private authService: AuthService,
private router: Router
) {}
async login(username: string, password: string) {
try {
// Chamada direta ao endpoint do Auth Middleware API que estabelece a sessão
const response = await this.http.post<{
success: boolean;
// outros campos...
}>('https://sua-api.com/api/v1/auth/user/sessions/credentials/login', {
username,
password
}, {
withCredentials: true // Importante para estabelecer a sessão
}).toPromise();
if (response && response.success) {
// A sessão já foi estabelecida pelo endpoint
// Apenas inicialize o AuthService para carregar as permissões
await this.authService.initialize();
this.router.navigate(['/dashboard']);
return true;
}
return false;
} catch (error) {
console.error('Falha no login:', error);
return false;
}
}
}2. Sistema de Autenticação Baseado em Sessão
A biblioteca @laqus/platform-auth-angular utiliza autenticação baseada em sessão, de forma idêntica ao projeto portal-monorepo:
Autenticação via Sessão
O endpoint /api/v1/auth/user/sessions/credentials/login do auth-middleware-api estabelece uma sessão de usuário e define um cookie seguro (__Secure-lqsid) no navegador. Este cookie tem a flag HttpOnly e é gerenciado automaticamente pelo navegador e pelo servidor.
Fluxo de Autenticação e Carregamento de Permissões
A biblioteca utiliza um fluxo em duas etapas para autenticação e carregamento de permissões:
Verificação de Status de Autenticação:
// Verificar status de autenticação this.authMiddlewareApiService.getUserSessionStatus().pipe( switchMap((status: AuthStatus) => { // Se autenticado e tem token, busca as permissões completas if (status.authenticated && status.session?.accessToken) { return this.authMiddlewareApiService.getUserSelf(status.session.accessToken); } return of(null); }) );Carregamento de Permissões:
// Carregar permissões do usuário usando o token de acesso this.authMiddlewareApiService.getUserSelf(token).pipe( map(userSelfData => { // Define os dados de permissão no serviço de autorização this.authorizationContextService.setPermissionData(userSelfData); this.permissionsLoaded = true; }) );
Nota: A biblioteca não manipula diretamente os cookies de sessão, pois eles têm a flag
HttpOnlye não são acessíveis via JavaScript. O navegador envia automaticamente os cookies de sessão em requisições para o mesmo domínio quando a opçãowithCredentials: trueé usada.
Cache de Permissões
A biblioteca também armazena as permissões do usuário em cache para evitar requisições desnecessárias:
// Verificar cache primeiro
const cachedPermissions = this.cacheService.get<IUserSelfDto>('user_permissions');
if (cachedPermissions) {
// Usar permissões do cache
this.authorizationContextService.setPermissionData(cachedPermissions);
this.currentUserSubject.next(cachedPermissions);
this.permissionsLoaded = true;
return;
}
// Se não estiver em cache, buscar da API e armazenar
// A sessão é enviada automaticamente via cookies
const status = await firstValueFrom(this.http.get<AuthStatus>(`${apiUrl}/api/v1/auth/user/sessions/status`, {
withCredentials: true
}));
// Processar dados e armazenar em cache
if (status.user) {
this.cacheService.set('user_permissions', status.user);
}Configuração do Cache
Você pode configurar o prefixo usado para as chaves de cache no localStorage e o tempo de expiração do cache:
@NgModule({
imports: [PlatformAuthModule],
providers: [
{
provide: AUTH_CONFIG,
useValue: {
authMiddlewareApiUrlBase: 'https://sua-api.com',
cache: {
authCacheKeysPrefix: 'minha_app_auth_', // Prefixo personalizado
expirationTimeInMs: 1800000 // 30 minutos em milissegundos (opcional)
}
}
}
]
})
export class AppModule { }Limpeza de Cache
Quando o usuário faz logout, todo o cache é limpo:
clearAuth(): void {
this._token = null;
this.permissionsLoaded = false;
this.currentUserSubject.next(null);
this.cacheService.clear(); // Limpa todo o cache
this.removeStoredToken(); // Remove o token
}3. Como o initialize() Funciona
O método initialize() do AuthService verifica o status de autenticação e carrega as permissões do usuário:
public async initialize(): Promise<boolean> {
try {
// Verifica o status de autenticação e carrega as permissões
const status = await firstValueFrom(this.checkAuthStatus(true));
return status.authenticated;
} catch (error) {
console.error('Failed to initialize auth service', error);
return false;
}
}Internamente, o método checkAuthStatus realiza o fluxo completo de verificação de autenticação e carregamento de permissões descrito anteriormente.
Arquitetura de Serviços
A biblioteca utiliza uma arquitetura de serviços bem definida para separar responsabilidades:
AuthMiddlewareApiService
Responsável por todas as chamadas de API para o middleware de autenticação:
@Injectable({ providedIn: 'root' })
export class AuthMiddlewareApiService {
// Verifica o status da sessão do usuário
getUserSessionStatus(): Observable<AuthStatus> { ... }
// Obtém os dados do usuário e suas permissões
getUserSelf(token: string): Observable<IUserSelfDto> { ... }
// Realiza o login do usuário usando credenciais
loginWithCredentials(email: string, password: string): Observable<{ success: boolean }> { ... }
// Realiza o logout do usuário
logout(): Observable<{ success: boolean }> { ... }
// Atualiza o token de acesso
refreshToken(): Observable<{ success: boolean }> { ... }
}AuthService
Gerencia o estado de autenticação e autorização da aplicação:
@Injectable({ providedIn: 'root' })
export class AuthService {
// Observables para o estado de autenticação
public readonly currentUser$ = this.currentUserSubject.asObservable();
public readonly isAuthenticated$ = this.isAuthenticatedSubject.asObservable();
public readonly currentSessionStatus$ = this.currentSessionStatusSubject.asObservable();
// Verifica o status de autenticação
public checkAuthStatus(forceRefresh: boolean = false): Observable<AuthStatus> { ... }
// Inicializa o serviço
public async initialize(): Promise<boolean> { ... }
// Verifica se o usuário está autenticado
isAuthenticated(): boolean { ... }
// Verifica permissões
hasAction(action: IResourceAction): boolean { ... }
hasAllActions(actions: IResourceAction[]): boolean { ... }
hasAnyAction(actions: IResourceAction[]): boolean { ... }
}AuthorizationContextService
Responsável por gerenciar o contexto de autorização e verificar permissões:
@Injectable({ providedIn: 'root' })
export class AuthorizationContextService {
// Define os dados de permissões
setPermissionData(data: any): void { ... }
// Verifica permissões
hasAction(action: IResourceAction): boolean { ... }
hasAllRequiredActions(actions: IResourceAction[]): boolean { ... }
hasAnyOfRequiredActions(actions: IResourceAction[]): boolean { ... }
}Solução de Problemas
Token Expirado
A biblioteca verifica automaticamente se o token está expirado. Se estiver, o método isAuthenticated() retornará false.
Permissões Não Carregadas
Se as permissões não estiverem carregadas, os métodos de verificação de permissão retornarão false e exibirão um aviso no console:
hasAction(action: IResourceAction): boolean {
if (!this.isAuthenticated() || !this.permissionsLoaded) {
console.warn('User not authenticated or permissions not loaded yet');
return false;
}
return this.authorizationContextService.hasAction(action);
}Atualização de Permissões
Para forçar uma atualização das permissões (por exemplo, após uma alteração de perfil):
// Forçar atualização do status e permissões
authService.checkAuthStatus(true).subscribe();
// Ou limpar autenticação e redirecionar para login
authService.clearAuth();
router.navigate(['/login']);