Introduction to Authentication Authorization in Angular

1. Introduction

In modern web applications, managing who can access your app and what they can do is crucial. This involves two fundamental concepts:

  • Authentication: Verifying a user’s identity, usually by validating credentials like username and password.
  • Authorization: Determining whether a user has permission to perform a specific action or access certain resources.

In Angular applications, authentication often relies on JWT (JSON Web Tokens), while authorization is implemented using role-based access control (RBAC). Proper implementation of these mechanisms ensures that both frontend routes and backend APIs remain secure.

This post provides a comprehensive overview of authentication and authorization in Angular, including login flow, JWT usage, route protection, role-based access, and best practices.

2. Understanding Authentication

Authentication is the process of verifying the identity of a user. Without authentication, you cannot control access to sensitive parts of your application.

Common Authentication Methods

  1. Username and Password: Traditional method where users enter credentials, validated on the backend.
  2. OAuth2 / OpenID Connect: Uses third-party providers like Google or Facebook for login.
  3. JWT-Based Authentication: Popular in SPAs for its stateless and scalable nature.

In Angular, the login process typically involves sending credentials to a backend API and receiving a JWT token in response. The token is stored in the client (usually in localStorage or sessionStorage) and attached to subsequent API requests for verification.


3. Understanding Authorization

Authorization determines what the user is allowed to do once authenticated. It is typically role-based:

  • Admin: Full access to all resources.
  • Editor: Can modify certain data.
  • Viewer: Can only view data, not modify.

Authorization is enforced both on the frontend (to hide or show UI elements and protect routes) and the backend (to secure APIs).


4. Setting Up Angular Authentication

Step 1 – Creating the AuthService

Angular services handle authentication logic such as login, logout, and token management.

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { tap } from 'rxjs/operators';
import { Observable } from 'rxjs';

@Injectable({ providedIn: 'root' })
export class AuthService {
  private token: string | null = null;

  constructor(private http: HttpClient) {}

  login(username: string, password: string): Observable<{ token: string }> {
return this.http.post&lt;{ token: string }&gt;('/api/login', { username, password })
  .pipe(
    tap(res =&gt; this.token = res.token)
  );
} logout(): void {
this.token = null;
localStorage.removeItem('token');
} getToken(): string | null {
return this.token || localStorage.getItem('token');
} isLoggedIn(): boolean {
return !!this.getToken();
} }

Explanation:

  • login() sends credentials to the backend and stores the returned JWT.
  • logout() clears the token.
  • getToken() retrieves the stored token.
  • isLoggedIn() returns whether the user is authenticated.

Step 2 – Login Component

The login component collects user credentials and calls the AuthService.

import { Component } from '@angular/core';
import { AuthService } from './auth.service';
import { Router } from '@angular/router';

@Component({
  selector: 'app-login',
  template: `
&lt;form (submit)="login()"&gt;
  &lt;input type="text" &#91;(ngModel)]="username" name="username" placeholder="Username"&gt;
  &lt;input type="password" &#91;(ngModel)]="password" name="password" placeholder="Password"&gt;
  &lt;button type="submit"&gt;Login&lt;/button&gt;
&lt;/form&gt;
` }) export class LoginComponent { username = ''; password = ''; constructor(private authService: AuthService, private router: Router) {} login() {
this.authService.login(this.username, this.password).subscribe(() =&gt; {
  localStorage.setItem('token', this.authService.getToken()!);
  this.router.navigate(&#91;'/dashboard']);
});
} }

Explanation:

  • The component binds form fields to variables using ngModel.
  • On submit, it calls AuthService.login().
  • After login, the token is stored and the user is redirected.

5. Protecting Routes with Guards

Angular guards can prevent unauthorized access to routes.

CanActivate Guard Example

import { Injectable } from '@angular/core';
import { CanActivate, Router } from '@angular/router';
import { AuthService } from './auth.service';

@Injectable({ providedIn: 'root' })
export class AuthGuard implements CanActivate {
  constructor(private authService: AuthService, private router: Router) {}

  canActivate(): boolean {
if (this.authService.isLoggedIn()) {
  return true;
} else {
  this.router.navigate(&#91;'/login']);
  return false;
}
} }

Route Setup:

const routes = [
  { path: 'dashboard', component: DashboardComponent, canActivate: [AuthGuard] },
  { path: 'login', component: LoginComponent }
];

Explanation:

  • AuthGuard prevents access to /dashboard if the user is not logged in.
  • Unauthorized users are redirected to /login.

6. Role-Based Access Control (RBAC)

RBAC restricts access to features based on user roles.

Adding Roles to AuthService

getUserRole(): string {
  // Example: decode JWT or fetch role from API
  return 'admin'; // hardcoded for demonstration
}

RoleGuard Example

@Injectable({ providedIn: 'root' })
export class RoleGuard implements CanActivate {
  constructor(private authService: AuthService, private router: Router) {}

  canActivate(): boolean {
const role = this.authService.getUserRole();
if (role === 'admin') {
  return true;
} else {
  this.router.navigate(&#91;'/unauthorized']);
  return false;
}
} }

Route Example:

{ path: 'admin', component: AdminComponent, canActivate: [AuthGuard, RoleGuard] }

Explanation:

  • AuthGuard checks authentication.
  • RoleGuard checks authorization (role).

7. Protecting APIs on the Backend

Frontend guards alone are not enough. APIs must also validate JWT and roles.

Example: Node.js / Express

const express = require('express');
const jwt = require('jsonwebtoken');
const app = express();

function verifyToken(req, res, next) {
  const token = req.headers['authorization']?.split(' ')[1];
  if (!token) return res.status(401).send('Unauthorized');

  jwt.verify(token, 'secretKey', (err, decoded) => {
if (err) return res.status(403).send('Forbidden');
req.user = decoded;
next();
}); } function checkRole(role) { return (req, res, next) => {
if (req.user.role === role) next();
else res.status(403).send('Forbidden');
}; } app.get('/admin', verifyToken, checkRole('admin'), (req, res) => { res.send('Admin data'); });

Explanation:

  • verifyToken ensures the request has a valid JWT.
  • checkRole ensures the user has the required role.

8. Using HTTP Interceptors for JWT

Angular interceptors attach JWT to every outgoing request automatically.

import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpRequest, HttpHandler } from '@angular/common/http';

@Injectable()
export class JwtInterceptor implements HttpInterceptor {
  intercept(req: HttpRequest<any>, next: HttpHandler) {
const token = localStorage.getItem('token');
if (token) {
  req = req.clone({ setHeaders: { Authorization: Bearer ${token} } });
}
return next.handle(req);
} }

Explanation:

  • Ensures all API requests carry the JWT token.
  • Reduces the need to manually attach headers in every request.

9. Handling Token Expiration

JWT tokens have an expiration time. Angular should handle expired tokens gracefully.

Example

logoutIfExpired() {
  const token = this.getToken();
  if (token) {
const expiry = JSON.parse(atob(token.split('.')&#91;1])).exp;
if (Date.now() &gt;= expiry * 1000) this.logout();
} }

Explanation:

  • Decode JWT to get the expiration.
  • Logout if the token has expired.

10. Refresh Tokens

For long-lived sessions, implement refresh tokens. The backend issues a short-lived access token and a long-lived refresh token. When the access token expires, a request to refresh the token is made.

refreshToken(): Observable<{ token: string }> {
  return this.http.post<{ token: string }>('/api/refresh', { refreshToken: this.getRefreshToken() })
.pipe(tap(res =&gt; localStorage.setItem('token', res.token)));
}

11. Frontend UI Security

  • Hide UI elements based on roles:
<button *ngIf="authService.getUserRole() === 'admin'">Admin Panel</button>
  • Ensure guards complement UI-level restrictions.

12. Best Practices

  • Always validate JWT on the backend.
  • Use HTTPS to secure token transmission.
  • Implement refresh tokens for better UX.
  • Use guards to protect routes.
  • Keep authentication logic in a centralized service.
  • Avoid storing sensitive info in localStorage without encryption.
  • Test guards and API authorization thoroughly.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *