Secure your Angular application with a complete JWT authentication flow — login, token storage, protected routes, and automatic token refresh.
// services/auth.service.ts
@Injectable({ providedIn: 'root' })
export class AuthService {
private http = inject(HttpClient);
private router = inject(Router);
private _token = signal<string | null>(localStorage.getItem('token'));
private _user = signal<User | null>(JSON.parse(localStorage.getItem('user') ?? 'null'));
readonly token = this._token.asReadonly();
readonly user = this._user.asReadonly();
readonly isLoggedIn = computed(() => !!this._token());
login(email: string, password: string): Observable<void> {
return this.http.post<{ token: string; user: User }>('/api/login', { email, password }).pipe(
tap(({ token, user }) => {
this._token.set(token);
this._user.set(user);
localStorage.setItem('token', token);
localStorage.setItem('user', JSON.stringify(user));
}),
map(() => void 0)
);
}
logout(): void {
this._token.set(null);
this._user.set(null);
localStorage.removeItem('token');
localStorage.removeItem('user');
this.router.navigate(['/login']);
}
}
// guards/auth.guard.ts
export const authGuard: CanActivateFn = () => {
const auth = inject(AuthService);
const router = inject(Router);
return auth.isLoggedIn() ? true : router.createUrlTree(['/login']);
};
export const routes: Routes = [
{ path: 'login', loadComponent: () => import('./login/login.component').then(m => m.LoginComponent) },
{ path: 'dashboard', loadComponent: () => import('./dashboard/dashboard.component').then(m => m.DashboardComponent),
canActivate: [authGuard] },
];
@Component({ standalone: true, imports: [ReactiveFormsModule], template: `...` })
export class LoginComponent {
private auth = inject(AuthService);
private router = inject(Router);
form = inject(FormBuilder).group({
email: ['', [Validators.required, Validators.email]],
password: ['', Validators.required],
});
onSubmit(): void {
if (this.form.invalid) return;
const { email, password } = this.form.value;
this.auth.login(email!, password!).subscribe({
next: () => this.router.navigate(['/dashboard']),
error: err => console.error(err),
});
}
}
All Comments