Cómo proteger nuestras rutas con CanActivate
// Desde la terminal creamos servicio de token y escapamos los tests
ng g s services/token --skip-tests
// Este servicio es el que va a persistir el token y va a escoger la estrategia adecuada para guardar el token
(*) Solo en ésta clase vamos a guardar el token en el localStorage, en la siguiente clase vamos a ver cómo guardar el token de forma más segura en cookies.
- En el servicio de token, creamos los métodos para guardar, obtener y remover el token:
// token.service.ts
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class TokenService {
constructor() { }
saveToken(token: string) {
localStorage.setItem('token', token);
}
getToken() {
const token = localStorage.getItem('token');
return token;
}
removeToken() {
localStorage.removeItem('token');
}
}
- En nuestro servicio de auth, en el método login a través del operador tap de rxjs, recibimos la respuesta que es el token.
- Importamos el servicio de token y le pasamos el token.
- Para tipar la respuesta, creamos un auth.model.ts
// auth.service.ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { environment } from '@environments/environment';
import { switchMap, tap } from 'rxjs';
import { TokenService } from './token.service';
import { ResponseLogin } from '@models/auth.model';
@Injectable({
providedIn: 'root'
})
export class AuthService {
apiUrl = environment.API_URL;
constructor(
private http: HttpClient,
private tokenService: TokenService
) { }
login(email: string, password: string) {
return this.http.post<ResponseLogin>(`${this.apiUrl}/api/v1/auth/login`, {
email,
password
})
.pipe(
tap(response => {
this.tokenService.saveToken(response.access_token);
})
)
}
register(name: string, email: string, password: string) {
return this.http.post(`${this.apiUrl}/api/v1/auth/register`, {
name,
email,
password
});
}
registerAndLogin(name: string, email: string, password: string) {
return this.register(name, email, password)
.pipe(
switchMap(() => this.login(email, password))
)
}
isAvailable(email: string) {
return this.http.post<{ isAvailable: boolean }>(`${this.apiUrl}/api/v1/auth/is-available`, { email });
}
recovery(email: string) {
return this.http.post(`${this.apiUrl}/api/v1/auth/recovery`, { email });
}
changePassword(token: string, newPassword: string) {
return this.http.post(`${this.apiUrl}/api/v1/auth/change-password`, { token, newPassword })
}
}
- Creamos auth.model.ts para tipar la respuesta
// auth.model.ts
export interface ResponseLogin {
access_token: string;
refresh_token: string;
}
// Desde la terminal creamos un guardián para proteger nuestras rutas
ng g g guards/auth --skip-tests
// Seleccionamos el tipo CanActivate
- Importamos el Router y el TokenService y mediante una validación le decimos que si no hay token me redirija al login, y para mayor seguridad también retornamos false, y si hay token retornamos true, por lo tanto me habilita las rutas del módulo de layout.
// auth.guard.ts
import { Injectable } from '@angular/core';
import { Router } from '@angular/router';
import { ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot, UrlTree } from '@angular/router';
import { Observable } from 'rxjs';
import { TokenService } from '@services/token.service';
@Injectable({
providedIn: 'root'
})
export class AuthGuard implements CanActivate {
constructor(
private tokenService: TokenService,
private router: Router
){}
canActivate(): boolean {
const token = this.tokenService.getToken();
if (!token) {
this.router.navigate(['/login']);
return false;
}
return true
}
}
- Finalmente en el app.routing.module.ts, le decimos qué ruta queremos proteger si hay token, en este caso, en el path: ‘app’, añadimos el guardián que hemos creado.
// app.routing.module.ts
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { AuthGuard } from '@guards/auth.guard';
const routes: Routes = [
{
path: '',
loadChildren: () => import('./modules/auth/auth.module').then((m) => m.AuthModule),
},
{
path: 'app',
canActivate: [ AuthGuard ], // (*)
loadChildren: () => import('./modules/layout/layout.module').then((m) => m.LayoutModule),
},
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
// (*) Protegiendo la ruta app, por herencia se protegen también las rutas hijas.
¿Quieres ver más aportes, preguntas y respuestas de la comunidad?