How to implement authentication in Angular
Authentication provides secure user access control, protecting routes and resources from unauthorized users while managing login sessions and tokens. As the creator of CoreUI, a widely used open-source UI library, I’ve implemented authentication systems in enterprise Angular applications throughout my 12 years of frontend development since 2014. The most effective approach is creating an authentication service with route guards to protect routes and HTTP interceptors for token management. This method centralizes authentication logic, provides reusable guards, and automatically handles token injection for API requests.
Create authentication service with login, logout, and token management functionality.
// src/app/services/auth.service.ts
import { Injectable } from '@angular/core'
import { HttpClient } from '@angular/common/http'
import { BehaviorSubject, Observable, tap } from 'rxjs'
import { Router } from '@angular/router'
interface User {
id: string
email: string
name: string
}
interface AuthResponse {
user: User
token: string
}
@Injectable({
providedIn: 'root'
})
export class AuthService {
private currentUserSubject = new BehaviorSubject<User | null>(null)
public currentUser$ = this.currentUserSubject.asObservable()
private readonly TOKEN_KEY = 'auth_token'
private readonly USER_KEY = 'current_user'
constructor(
private http: HttpClient,
private router: Router
) {
this.loadStoredUser()
}
private loadStoredUser(): void {
const storedUser = localStorage.getItem(this.USER_KEY)
if (storedUser) {
this.currentUserSubject.next(JSON.parse(storedUser))
}
}
login(email: string, password: string): Observable<AuthResponse> {
return this.http.post<AuthResponse>('/api/auth/login', { email, password })
.pipe(
tap(response => {
this.setSession(response)
})
)
}
logout(): void {
localStorage.removeItem(this.TOKEN_KEY)
localStorage.removeItem(this.USER_KEY)
this.currentUserSubject.next(null)
this.router.navigate(['/login'])
}
private setSession(authResponse: AuthResponse): void {
localStorage.setItem(this.TOKEN_KEY, authResponse.token)
localStorage.setItem(this.USER_KEY, JSON.stringify(authResponse.user))
this.currentUserSubject.next(authResponse.user)
}
getToken(): string | null {
return localStorage.getItem(this.TOKEN_KEY)
}
isAuthenticated(): boolean {
const token = this.getToken()
return token !== null
}
getCurrentUser(): User | null {
return this.currentUserSubject.value
}
}
Create authentication guard to protect routes:
// src/app/guards/auth.guard.ts
import { Injectable } from '@angular/core'
import { Router, CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router'
import { AuthService } from '../services/auth.service'
@Injectable({
providedIn: 'root'
})
export class AuthGuard implements CanActivate {
constructor(
private authService: AuthService,
private router: Router
) {}
canActivate(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot
): boolean {
if (this.authService.isAuthenticated()) {
return true
}
this.router.navigate(['/login'], {
queryParams: { returnUrl: state.url }
})
return false
}
}
Create login component:
// src/app/components/login/login.component.ts
import { Component } from '@angular/core'
import { FormBuilder, FormGroup, Validators, ReactiveFormsModule } from '@angular/forms'
import { Router, ActivatedRoute } from '@angular/router'
import { AuthService } from '../../services/auth.service'
import { CommonModule } from '@angular/common'
@Component({
selector: 'app-login',
standalone: true,
imports: [CommonModule, ReactiveFormsModule],
template: `
<div class='login-container'>
<form [formGroup]='loginForm' (ngSubmit)='onSubmit()'>
<h2>Login</h2>
<div class='form-group'>
<label for='email'>Email</label>
<input
id='email'
type='email'
formControlName='email'
[class.error]='email?.invalid && email?.touched'
/>
<div *ngIf='email?.invalid && email?.touched' class='error-message'>
<span *ngIf='email?.errors?.["required"]'>Email is required</span>
<span *ngIf='email?.errors?.["email"]'>Invalid email format</span>
</div>
</div>
<div class='form-group'>
<label for='password'>Password</label>
<input
id='password'
type='password'
formControlName='password'
[class.error]='password?.invalid && password?.touched'
/>
<div *ngIf='password?.invalid && password?.touched' class='error-message'>
<span *ngIf='password?.errors?.["required"]'>Password is required</span>
<span *ngIf='password?.errors?.["minlength"]'>
Password must be at least 6 characters
</span>
</div>
</div>
<div *ngIf='errorMessage' class='alert alert-error'>
{{ errorMessage }}
</div>
<button
type='submit'
[disabled]='loginForm.invalid || isLoading'
>
{{ isLoading ? 'Logging in...' : 'Login' }}
</button>
</form>
</div>
`,
styles: [`
.login-container {
max-width: 400px;
margin: 2rem auto;
padding: 2rem;
border: 1px solid #ddd;
border-radius: 8px;
}
.form-group {
margin-bottom: 1rem;
}
label {
display: block;
margin-bottom: 0.5rem;
}
input {
width: 100%;
padding: 0.5rem;
border: 1px solid #ddd;
border-radius: 4px;
}
input.error {
border-color: #dc3545;
}
.error-message {
color: #dc3545;
font-size: 0.875rem;
margin-top: 0.25rem;
}
button {
width: 100%;
padding: 0.75rem;
background: #007bff;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
button:disabled {
opacity: 0.6;
cursor: not-allowed;
}
`]
})
export class LoginComponent {
loginForm: FormGroup
isLoading = false
errorMessage = ''
constructor(
private fb: FormBuilder,
private authService: AuthService,
private router: Router,
private route: ActivatedRoute
) {
this.loginForm = this.fb.group({
email: ['', [Validators.required, Validators.email]],
password: ['', [Validators.required, Validators.minLength(6)]]
})
}
get email() {
return this.loginForm.get('email')
}
get password() {
return this.loginForm.get('password')
}
onSubmit(): void {
if (this.loginForm.invalid) {
return
}
this.isLoading = true
this.errorMessage = ''
const { email, password } = this.loginForm.value
this.authService.login(email, password).subscribe({
next: () => {
const returnUrl = this.route.snapshot.queryParams['returnUrl'] || '/dashboard'
this.router.navigate([returnUrl])
},
error: (error) => {
this.errorMessage = error.error?.message || 'Login failed. Please try again.'
this.isLoading = false
},
complete: () => {
this.isLoading = false
}
})
}
}
Configure protected routes:
// src/app/app.routes.ts
import { Routes } from '@angular/router'
import { LoginComponent } from './components/login/login.component'
import { DashboardComponent } from './components/dashboard/dashboard.component'
import { AuthGuard } from './guards/auth.guard'
export const routes: Routes = [
{ path: 'login', component: LoginComponent },
{
path: 'dashboard',
component: DashboardComponent,
canActivate: [AuthGuard]
},
{
path: 'profile',
loadComponent: () => import('./components/profile/profile.component')
.then(m => m.ProfileComponent),
canActivate: [AuthGuard]
},
{ path: '', redirectTo: '/dashboard', pathMatch: 'full' },
{ path: '**', redirectTo: '/dashboard' }
]
Here the AuthService manages authentication state using BehaviorSubject for reactive user updates across components. The login method sends credentials to API and stores token and user data in localStorage. The isAuthenticated method checks token existence for route guard decisions. The AuthGuard implements CanActivate interface to protect routes from unauthorized access. The guard redirects unauthenticated users to login with returnUrl query parameter for post-login navigation. The LoginComponent uses reactive forms with validators for email format and password length. The form submission disables button during loading and displays error messages from API responses.
Best Practice Note:
This is the authentication pattern we use in CoreUI for Angular admin templates with protected routes and centralized auth management. Implement HTTP interceptor to automatically attach tokens to API requests and handle 401 responses for token expiration, add refresh token mechanism for seamless session renewal without requiring re-login, and store sensitive tokens in httpOnly cookies instead of localStorage when working with same-origin APIs for enhanced XSS protection.



