Programmer's Picnic
Angular 0 to Infinity Series
Angular Lesson

Authentication in Angular 0 to Infinity

Authentication means proving who the user is. In Angular, authentication usually includes login forms, token handling, protected routes, HTTP interceptors, logout, and sometimes role-based access.

This lesson begins from a fake login system so that the idea becomes clear. Then it moves to real API login, JWT (JSON Web Token), route guards, interceptors, roles, and a complete mini project.

Login Logout Route guards JWT Interceptors Roles

Login → Token → Protected Pages

Authentication controls who can enter which part of your Angular app.

1 User enters email and password.
2 Server returns a token.
3 Angular stores token safely enough for demo use.
4 Guards protect routes.

1. What is Authentication?

Authentication answers this question: “Who are you?”

Authorization answers another question: “What are you allowed to do?”

Authentication

The user logs in with email, password, Google login, phone OTP, or another identity method.

Authorization

The app checks whether the logged-in user is allowed to open a page or perform an action.

Term Meaning Angular Example
Login User proves identity. Email and password form.
Token A small proof returned by server after login. JWT (JSON Web Token).
Guard Protects routes from unauthorized access. canActivate guard.
Interceptor Adds token to HTTP requests automatically. Authorization: Bearer token.
Role User category or permission level. admin, student, teacher.
Very important: Angular runs in the browser. Browser code can be inspected by users. Therefore, real security must be enforced by the backend server, not only by Angular.
Google AdSense area. Use your ad unit slot: 7782388330

2. Angular Setup

Create a fresh Angular project.

Terminal
npm install -g @angular/cli

ng new angular-auth-demo

cd angular-auth-demo

ng serve
Checkpoint 1: Type ng serve. Open http://localhost:4200. You should see the Angular application running.

Open the project in Visual Studio Code.

Terminal
code .
Checkpoint 2: These files should exist:
src/app/app.component.ts, src/app/app.component.html, src/app/app.routes.ts, src/app/app.config.ts.

3. The Authentication Flow

A common Angular authentication flow looks like this:

  1. User opens the login page.
  2. User enters email and password.
  3. Angular sends the login data to the backend API.
  4. Backend verifies the credentials.
  5. Backend returns a token and user information.
  6. Angular stores the token.
  7. Angular uses a guard to protect private pages.
  8. Angular uses an interceptor to attach the token to future API requests.
  9. User logs out and Angular removes the token.
Learning strategy: First build a fake login system. Then replace the fake login with a real API call. This makes authentication much easier to understand.

4. Fake Login First

A fake login does not call a real server. It checks one email and password locally. This is not real security, but it teaches the Angular flow clearly.

Fake credentials for learning
Email:    student@example.com
Password: 123456

We will create:

  • AuthService for login, logout, and checking login status.
  • LoginComponent for the login form.
  • DashboardComponent as a protected page.
  • AuthGuard to stop direct access to dashboard.

5. Create Auth Service

A service is the best place to keep authentication logic.

Terminal
ng generate service services/auth
Checkpoint 3: This file should be created:
src/app/services/auth.service.ts
src/app/services/auth.service.ts
import { Injectable } from '@angular/core';

export interface LoginRequest {
  email: string;
  password: string;
}

export interface AuthUser {
  email: string;
  name: string;
  role: 'student' | 'teacher' | 'admin';
}

@Injectable({
  providedIn: 'root'
})
export class AuthService {
  private tokenKey = 'pp_auth_token';
  private userKey = 'pp_auth_user';

  login(credentials: LoginRequest): boolean {
    if (
      credentials.email === 'student@example.com' &&
      credentials.password === '123456'
    ) {
      const fakeToken = 'fake-jwt-token-for-learning';

      const user: AuthUser = {
        email: credentials.email,
        name: 'Student User',
        role: 'student'
      };

      localStorage.setItem(this.tokenKey, fakeToken);
      localStorage.setItem(this.userKey, JSON.stringify(user));

      return true;
    }

    return false;
  }

  logout(): void {
    localStorage.removeItem(this.tokenKey);
    localStorage.removeItem(this.userKey);
  }

  isLoggedIn(): boolean {
    return !!localStorage.getItem(this.tokenKey);
  }

  getToken(): string | null {
    return localStorage.getItem(this.tokenKey);
  }

  getUser(): AuthUser | null {
    const userJson = localStorage.getItem(this.userKey);

    if (!userJson) {
      return null;
    }

    return JSON.parse(userJson) as AuthUser;
  }

  getRole(): string | null {
    return this.getUser()?.role || null;
  }
}
What is happening here? If the email and password match, we store a fake token and fake user object. Later, route guards will check whether this token exists.

6. Create Login Component

Terminal
ng generate component pages/login

ng generate component pages/dashboard

ng generate component pages/home
Checkpoint 4: These folders should be created:
src/app/pages/login, src/app/pages/dashboard, src/app/pages/home.

Login Component Logic

src/app/pages/login/login.component.ts
import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { Router, RouterLink } from '@angular/router';
import { AuthService, LoginRequest } from '../../services/auth.service';

@Component({
  selector: 'app-login',
  imports: [FormsModule, RouterLink],
  templateUrl: './login.component.html',
  styleUrl: './login.component.css'
})
export class LoginComponent {
  credentials: LoginRequest = {
    email: '',
    password: ''
  };

  errorMessage = '';

  constructor(
    private authService: AuthService,
    private router: Router
  ) {}

  login() {
    this.errorMessage = '';

    const success = this.authService.login(this.credentials);

    if (success) {
      this.router.navigate(['/dashboard']);
    } else {
      this.errorMessage = 'Invalid email or password.';
    }
  }
}

Login Component HTML

src/app/pages/login/login.component.html
<main class="auth-page">
  <section class="auth-card">
    <h1>Login</h1>
    <p>Use the demo account to enter the dashboard.</p>

    <div class="demo-box">
      <strong>Demo Email:</strong> student@example.com<br />
      <strong>Demo Password:</strong> 123456
    </div>

    <form (ngSubmit)="login()">
      <label>
        Email
        <input
          type="email"
          name="email"
          [(ngModel)]="credentials.email"
          required
          placeholder="student@example.com"
        />
      </label>

      <label>
        Password
        <input
          type="password"
          name="password"
          [(ngModel)]="credentials.password"
          required
          placeholder="123456"
        />
      </label>

      @if (errorMessage) {
        <p class="error">{{ errorMessage }}</p>
      }

      <button type="submit">Login</button>
    </form>

    <p>
      <a routerLink="/">Back to home</a>
    </p>
  </section>
</main>

Login Component CSS

src/app/pages/login/login.component.css
.auth-page {
  min-height: 100vh;
  display: grid;
  place-items: center;
  background: #fff7ed;
  padding: 24px;
}

.auth-card {
  width: min(460px, 100%);
  background: white;
  border-radius: 24px;
  padding: 28px;
  box-shadow: 0 18px 40px rgba(100, 50, 0, 0.14);
  border: 1px solid #ffd0a1;
}

h1 {
  margin-top: 0;
  color: #9a3412;
}

label {
  display: block;
  margin: 16px 0;
  font-weight: bold;
}

input {
  display: block;
  width: 100%;
  margin-top: 8px;
  padding: 13px;
  border-radius: 12px;
  border: 1px solid #d6b17d;
  font-size: 16px;
}

button {
  width: 100%;
  padding: 13px;
  border: none;
  border-radius: 999px;
  background: #ff9933;
  color: #1f1300;
  font-weight: bold;
  cursor: pointer;
  font-size: 16px;
}

.demo-box {
  background: #fff3e3;
  padding: 14px;
  border-radius: 14px;
  border: 1px solid #ffd0a1;
}

.error {
  color: white;
  background: #b91c1c;
  padding: 10px;
  border-radius: 10px;
}

7. Set Up Routing

Routing decides which component appears for which URL.

src/app/app.routes.ts
import { Routes } from '@angular/router';
import { HomeComponent } from './pages/home/home.component';
import { LoginComponent } from './pages/login/login.component';
import { DashboardComponent } from './pages/dashboard/dashboard.component';

export const routes: Routes = [
  {
    path: '',
    component: HomeComponent
  },
  {
    path: 'login',
    component: LoginComponent
  },
  {
    path: 'dashboard',
    component: DashboardComponent
  },
  {
    path: '**',
    redirectTo: ''
  }
];

App Component HTML

src/app/app.component.html
<router-outlet></router-outlet>

App Component TypeScript

src/app/app.component.ts
import { Component } from '@angular/core';
import { RouterOutlet } from '@angular/router';

@Component({
  selector: 'app-root',
  imports: [RouterOutlet],
  templateUrl: './app.component.html',
  styleUrl: './app.component.css'
})
export class AppComponent {}
Checkpoint 5: Open http://localhost:4200/login. You should see the login page. Open http://localhost:4200/dashboard. At this stage, it will still open because the guard is not added yet.

8. Protect Routes with an Auth Guard

A guard prevents unauthorized users from entering protected pages.

Terminal
ng generate guard guards/auth
Checkpoint 6: A guard file should be created inside:
src/app/guards
src/app/guards/auth.guard.ts
import { inject } from '@angular/core';
import { CanActivateFn, Router } from '@angular/router';
import { AuthService } from '../services/auth.service';

export const authGuard: CanActivateFn = () => {
  const authService = inject(AuthService);
  const router = inject(Router);

  if (authService.isLoggedIn()) {
    return true;
  }

  return router.createUrlTree(['/login']);
};

Add Guard to Dashboard Route

src/app/app.routes.ts
import { Routes } from '@angular/router';
import { HomeComponent } from './pages/home/home.component';
import { LoginComponent } from './pages/login/login.component';
import { DashboardComponent } from './pages/dashboard/dashboard.component';
import { authGuard } from './guards/auth.guard';

export const routes: Routes = [
  {
    path: '',
    component: HomeComponent
  },
  {
    path: 'login',
    component: LoginComponent
  },
  {
    path: 'dashboard',
    component: DashboardComponent,
    canActivate: [authGuard]
  },
  {
    path: '**',
    redirectTo: ''
  }
];
Checkpoint 7: Open http://localhost:4200/dashboard without logging in. You should be redirected to /login.

9. Dashboard and Logout

After login, the user should see a private page and a logout button.

src/app/pages/dashboard/dashboard.component.ts
import { Component } from '@angular/core';
import { Router } from '@angular/router';
import { AuthService, AuthUser } from '../../services/auth.service';

@Component({
  selector: 'app-dashboard',
  imports: [],
  templateUrl: './dashboard.component.html',
  styleUrl: './dashboard.component.css'
})
export class DashboardComponent {
  user: AuthUser | null = null;

  constructor(
    private authService: AuthService,
    private router: Router
  ) {
    this.user = this.authService.getUser();
  }

  logout() {
    this.authService.logout();
    this.router.navigate(['/login']);
  }
}
src/app/pages/dashboard/dashboard.component.html
<main class="dashboard">
  <nav class="topbar">
    <strong>Private Dashboard</strong>
    <button (click)="logout()">Logout</button>
  </nav>

  <section class="card">
    <h1>Welcome, {{ user?.name }}</h1>
    <p>Email: {{ user?.email }}</p>
    <p>Role: {{ user?.role }}</p>
  </section>
</main>
src/app/pages/dashboard/dashboard.component.css
.dashboard {
  min-height: 100vh;
  background: #fff7ed;
  padding: 24px;
  font-family: Arial, sans-serif;
}

.topbar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  background: #2a1704;
  color: white;
  padding: 16px 20px;
  border-radius: 18px;
}

button {
  border: none;
  border-radius: 999px;
  padding: 10px 16px;
  background: #ff9933;
  color: #1f1300;
  font-weight: bold;
  cursor: pointer;
}

.card {
  margin-top: 24px;
  background: white;
  border-radius: 24px;
  padding: 28px;
  border: 1px solid #ffd0a1;
  box-shadow: 0 18px 40px rgba(100, 50, 0, 0.12);
}
Checkpoint 8: Login with student@example.com and 123456. You should reach the dashboard. Click logout. You should return to the login page.

10. JWT: JSON Web Token

JWT means JSON Web Token. A JWT is a compact token usually returned by the backend after successful login.

A JWT has three parts:

JWT shape
header.payload.signature

Header

Information about token type and signing algorithm.

Payload

User-related claims such as id, email, role, and expiry time.

Signature

Server-side proof that the token was not changed.

Do not store secrets in JWT payload. The payload can be decoded by the browser. A JWT is signed, not automatically encrypted.

Example Decoding Function for Learning

decode-token.helper.ts
export function decodeJwtPayload(token: string): any {
  const parts = token.split('.');

  if (parts.length !== 3) {
    return null;
  }

  const payload = parts[1];
  const decodedPayload = atob(payload);

  return JSON.parse(decodedPayload);
}
In real production apps, token validation must happen on the backend. Frontend decoding is useful for displaying information, not for final security decisions.

11. Real API Login Pattern

In a real app, login is usually an HTTP POST request.

Enable HttpClient

src/app/app.config.ts
import { ApplicationConfig } from '@angular/core';
import { provideRouter } from '@angular/router';
import { provideHttpClient, withInterceptors } from '@angular/common/http';

import { routes } from './app.routes';

export const appConfig: ApplicationConfig = {
  providers: [
    provideRouter(routes),
    provideHttpClient()
  ]
};

Auth Models

src/app/models/auth.models.ts
export interface LoginRequest {
  email: string;
  password: string;
}

export interface LoginResponse {
  token: string;
  user: {
    id: number;
    name: string;
    email: string;
    role: 'student' | 'teacher' | 'admin';
  };
}

Real API Auth Service Pattern

src/app/services/auth.service.ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { tap } from 'rxjs';
import { LoginRequest, LoginResponse } from '../models/auth.models';

@Injectable({
  providedIn: 'root'
})
export class AuthService {
  private apiUrl = 'https://example.com/api/auth';
  private tokenKey = 'pp_auth_token';
  private userKey = 'pp_auth_user';

  constructor(private http: HttpClient) {}

  login(credentials: LoginRequest) {
    return this.http
      .post<LoginResponse>(`${this.apiUrl}/login`, credentials)
      .pipe(
        tap(response => {
          localStorage.setItem(this.tokenKey, response.token);
          localStorage.setItem(this.userKey, JSON.stringify(response.user));
        })
      );
  }

  logout() {
    localStorage.removeItem(this.tokenKey);
    localStorage.removeItem(this.userKey);
  }

  isLoggedIn(): boolean {
    return !!this.getToken();
  }

  getToken(): string | null {
    return localStorage.getItem(this.tokenKey);
  }

  getUser() {
    const userJson = localStorage.getItem(this.userKey);
    return userJson ? JSON.parse(userJson) : null;
  }
}

Login Component with API

login.component.ts
login() {
  this.errorMessage = '';

  this.authService.login(this.credentials)
    .subscribe({
      next: () => {
        this.router.navigate(['/dashboard']);
      },
      error: error => {
        console.error(error);
        this.errorMessage = 'Login failed. Please check your email and password.';
      }
    });
}
Checkpoint 9: The fake login version works without a backend. The real API version needs a working backend login URL.

12. Add Token Automatically with an HTTP Interceptor

After login, protected API requests usually need an authorization header.

Authorization header
Authorization: Bearer YOUR_TOKEN_HERE

Instead of manually adding this header everywhere, use an interceptor.

Terminal
ng generate interceptor interceptors/auth
src/app/interceptors/auth.interceptor.ts
import { HttpInterceptorFn } from '@angular/common/http';
import { inject } from '@angular/core';
import { AuthService } from '../services/auth.service';

export const authInterceptor: HttpInterceptorFn = (request, next) => {
  const authService = inject(AuthService);
  const token = authService.getToken();

  if (!token) {
    return next(request);
  }

  const authRequest = request.clone({
    setHeaders: {
      Authorization: `Bearer ${token}`
    }
  });

  return next(authRequest);
};

Register the Interceptor

src/app/app.config.ts
import { ApplicationConfig } from '@angular/core';
import { provideRouter } from '@angular/router';
import { provideHttpClient, withInterceptors } from '@angular/common/http';

import { routes } from './app.routes';
import { authInterceptor } from './interceptors/auth.interceptor';

export const appConfig: ApplicationConfig = {
  providers: [
    provideRouter(routes),
    provideHttpClient(
      withInterceptors([authInterceptor])
    )
  ]
};
Checkpoint 10: After login, inspect API requests in browser DevTools Network tab. Protected requests should contain the Authorization header.

13. Role-Based Access

Sometimes all logged-in users can enter the dashboard, but only admins can enter the admin page.

Terminal
ng generate component pages/admin

ng generate guard guards/role

Role Guard

src/app/guards/role.guard.ts
import { inject } from '@angular/core';
import { CanActivateFn, Router } from '@angular/router';
import { AuthService } from '../services/auth.service';

export const roleGuard: CanActivateFn = (route) => {
  const authService = inject(AuthService);
  const router = inject(Router);

  const allowedRoles = route.data['roles'] as string[];
  const currentRole = authService.getRole();

  if (currentRole && allowedRoles.includes(currentRole)) {
    return true;
  }

  return router.createUrlTree(['/dashboard']);
};

Route with Role Guard

src/app/app.routes.ts
{
  path: 'admin',
  component: AdminComponent,
  canActivate: [authGuard, roleGuard],
  data: {
    roles: ['admin']
  }
}
Important: Role guards improve frontend user experience, but the backend must also check roles. A user can inspect or manipulate frontend code.

14. Refresh Token Idea

Many production systems use two tokens:

Access Token

Short-lived token used to access protected API routes.

Refresh Token

Longer-lived token used to request a new access token.

A simple production flow:

  1. User logs in.
  2. Server returns an access token and a refresh token.
  3. Angular uses the access token for API requests.
  4. When the access token expires, Angular asks for a new one using the refresh token.
  5. If refresh fails, Angular logs the user out.
Good production pattern: Many teams store refresh tokens in secure, HTTP-only cookies managed by the server. This reduces exposure to JavaScript-based token theft.

15. Token Storage: localStorage, sessionStorage, Cookies

Token storage is an important security decision.

Storage Advantage Risk / Limitation
localStorage Easy to use. Token survives browser restart. Accessible by JavaScript, so Cross-Site Scripting can be dangerous.
sessionStorage Clears when tab/session ends. Still accessible by JavaScript.
HTTP-only cookie Not directly readable by JavaScript. Needs careful backend setup, SameSite, CSRF protection, and secure cookie settings.
For beginner projects and local demos, localStorage is convenient. For serious production apps, discuss storage strategy with the backend and security requirements.

16. Mini Project: Complete Angular Authentication Demo

This mini project uses fake authentication so it can run without a backend.

Features

  • Home page.
  • Login page.
  • Dashboard page.
  • Admin page.
  • Fake token storage.
  • Route guard.
  • Role guard.
  • Logout.
  • Navbar that changes based on login status.

Step 1: Generate Files

Terminal
ng generate component pages/home
ng generate component pages/login
ng generate component pages/dashboard
ng generate component pages/admin

ng generate service services/auth

ng generate guard guards/auth
ng generate guard guards/role

Step 2: Auth Service

src/app/services/auth.service.ts
import { Injectable } from '@angular/core';

export interface LoginRequest {
  email: string;
  password: string;
}

export interface AuthUser {
  email: string;
  name: string;
  role: 'student' | 'admin';
}

@Injectable({
  providedIn: 'root'
})
export class AuthService {
  private tokenKey = 'pp_auth_token';
  private userKey = 'pp_auth_user';

  login(credentials: LoginRequest): boolean {
    const users = [
      {
        email: 'student@example.com',
        password: '123456',
        name: 'Student User',
        role: 'student' as const
      },
      {
        email: 'admin@example.com',
        password: 'admin123',
        name: 'Admin User',
        role: 'admin' as const
      }
    ];

    const foundUser = users.find(user =>
      user.email === credentials.email &&
      user.password === credentials.password
    );

    if (!foundUser) {
      return false;
    }

    const fakeToken = `fake-token-${foundUser.role}-${Date.now()}`;

    const userToStore: AuthUser = {
      email: foundUser.email,
      name: foundUser.name,
      role: foundUser.role
    };

    localStorage.setItem(this.tokenKey, fakeToken);
    localStorage.setItem(this.userKey, JSON.stringify(userToStore));

    return true;
  }

  logout(): void {
    localStorage.removeItem(this.tokenKey);
    localStorage.removeItem(this.userKey);
  }

  isLoggedIn(): boolean {
    return !!localStorage.getItem(this.tokenKey);
  }

  getToken(): string | null {
    return localStorage.getItem(this.tokenKey);
  }

  getUser(): AuthUser | null {
    const userJson = localStorage.getItem(this.userKey);
    return userJson ? JSON.parse(userJson) as AuthUser : null;
  }

  getRole(): 'student' | 'admin' | null {
    return this.getUser()?.role || null;
  }
}

Step 3: Guards

src/app/guards/auth.guard.ts
import { inject } from '@angular/core';
import { CanActivateFn, Router } from '@angular/router';
import { AuthService } from '../services/auth.service';

export const authGuard: CanActivateFn = () => {
  const authService = inject(AuthService);
  const router = inject(Router);

  if (authService.isLoggedIn()) {
    return true;
  }

  return router.createUrlTree(['/login']);
};
src/app/guards/role.guard.ts
import { inject } from '@angular/core';
import { CanActivateFn, Router } from '@angular/router';
import { AuthService } from '../services/auth.service';

export const roleGuard: CanActivateFn = (route) => {
  const authService = inject(AuthService);
  const router = inject(Router);

  const allowedRoles = route.data['roles'] as string[];
  const role = authService.getRole();

  if (role && allowedRoles.includes(role)) {
    return true;
  }

  return router.createUrlTree(['/dashboard']);
};

Step 4: Routes

src/app/app.routes.ts
import { Routes } from '@angular/router';

import { HomeComponent } from './pages/home/home.component';
import { LoginComponent } from './pages/login/login.component';
import { DashboardComponent } from './pages/dashboard/dashboard.component';
import { AdminComponent } from './pages/admin/admin.component';

import { authGuard } from './guards/auth.guard';
import { roleGuard } from './guards/role.guard';

export const routes: Routes = [
  {
    path: '',
    component: HomeComponent
  },
  {
    path: 'login',
    component: LoginComponent
  },
  {
    path: 'dashboard',
    component: DashboardComponent,
    canActivate: [authGuard]
  },
  {
    path: 'admin',
    component: AdminComponent,
    canActivate: [authGuard, roleGuard],
    data: {
      roles: ['admin']
    }
  },
  {
    path: '**',
    redirectTo: ''
  }
];

Step 5: App Shell with Navbar

src/app/app.component.ts
import { Component } from '@angular/core';
import { Router, RouterLink, RouterOutlet } from '@angular/router';
import { AuthService } from './services/auth.service';

@Component({
  selector: 'app-root',
  imports: [RouterOutlet, RouterLink],
  templateUrl: './app.component.html',
  styleUrl: './app.component.css'
})
export class AppComponent {
  constructor(
    public authService: AuthService,
    private router: Router
  ) {}

  logout() {
    this.authService.logout();
    this.router.navigate(['/login']);
  }
}
src/app/app.component.html
<nav class="navbar">
  <a routerLink="/" class="brand">Angular Auth Demo</a>

  <div class="links">
    <a routerLink="/">Home</a>

    @if (authService.isLoggedIn()) {
      <a routerLink="/dashboard">Dashboard</a>

      @if (authService.getRole() === 'admin') {
        <a routerLink="/admin">Admin</a>
      }

      <button (click)="logout()">Logout</button>
    } @else {
      <a routerLink="/login">Login</a>
    }
  </div>
</nav>

<router-outlet></router-outlet>
src/app/app.component.css
.navbar {
  display: flex;
  justify-content: space-between;
  align-items: center;
  background: #2a1704;
  color: white;
  padding: 14px 24px;
  font-family: Arial, sans-serif;
}

.brand {
  color: #ffd49a;
  font-weight: bold;
  text-decoration: none;
}

.links {
  display: flex;
  align-items: center;
  gap: 14px;
}

a {
  color: white;
  text-decoration: none;
  font-weight: bold;
}

button {
  border: none;
  border-radius: 999px;
  padding: 9px 14px;
  background: #ff9933;
  color: #1f1300;
  font-weight: bold;
  cursor: pointer;
}

@media (max-width: 700px) {
  .navbar,
  .links {
    flex-direction: column;
    align-items: flex-start;
  }
}

Step 6: Home Page

src/app/pages/home/home.component.html
<main class="page">
  <section class="hero">
    <h1>Angular Authentication Demo</h1>
    <p>Learn login, logout, protected routes, and role-based access.</p>

    <div class="cards">
      <div class="card">
        <h2>Student Login</h2>
        <p>Email: student@example.com</p>
        <p>Password: 123456</p>
      </div>

      <div class="card">
        <h2>Admin Login</h2>
        <p>Email: admin@example.com</p>
        <p>Password: admin123</p>
      </div>
    </div>
  </section>
</main>
src/app/pages/home/home.component.css
.page {
  min-height: calc(100vh - 60px);
  background: #fff7ed;
  padding: 32px;
  font-family: Arial, sans-serif;
}

.hero {
  max-width: 1000px;
  margin: auto;
  padding: 40px;
  border-radius: 28px;
  background: linear-gradient(135deg, #ff9933, #ffd6a3);
  color: #2a1400;
}

h1 {
  font-size: 44px;
  margin-top: 0;
}

.cards {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 18px;
  margin-top: 24px;
}

.card {
  background: white;
  border-radius: 20px;
  padding: 20px;
  border: 1px solid #ffd0a1;
}

@media (max-width: 700px) {
  .cards {
    grid-template-columns: 1fr;
  }

  h1 {
    font-size: 32px;
  }
}

Step 7: Login Page

src/app/pages/login/login.component.ts
import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { Router } from '@angular/router';
import { AuthService, LoginRequest } from '../../services/auth.service';

@Component({
  selector: 'app-login',
  imports: [FormsModule],
  templateUrl: './login.component.html',
  styleUrl: './login.component.css'
})
export class LoginComponent {
  credentials: LoginRequest = {
    email: '',
    password: ''
  };

  errorMessage = '';

  constructor(
    private authService: AuthService,
    private router: Router
  ) {}

  login() {
    this.errorMessage = '';

    const success = this.authService.login(this.credentials);

    if (success) {
      this.router.navigate(['/dashboard']);
    } else {
      this.errorMessage = 'Invalid email or password.';
    }
  }
}
src/app/pages/login/login.component.html
<main class="auth-page">
  <section class="auth-card">
    <h1>Login</h1>

    <form (ngSubmit)="login()">
      <label>
        Email
        <input
          type="email"
          name="email"
          [(ngModel)]="credentials.email"
          required
        />
      </label>

      <label>
        Password
        <input
          type="password"
          name="password"
          [(ngModel)]="credentials.password"
          required
        />
      </label>

      @if (errorMessage) {
        <p class="error">{{ errorMessage }}</p>
      }

      <button type="submit">Login</button>
    </form>
  </section>
</main>
src/app/pages/login/login.component.css
.auth-page {
  min-height: calc(100vh - 60px);
  display: grid;
  place-items: center;
  background: #fff7ed;
  padding: 24px;
  font-family: Arial, sans-serif;
}

.auth-card {
  width: min(460px, 100%);
  background: white;
  border-radius: 24px;
  padding: 28px;
  border: 1px solid #ffd0a1;
  box-shadow: 0 18px 40px rgba(100, 50, 0, 0.14);
}

label {
  display: block;
  margin: 16px 0;
  font-weight: bold;
}

input {
  display: block;
  width: 100%;
  margin-top: 8px;
  padding: 13px;
  border-radius: 12px;
  border: 1px solid #d6b17d;
}

button {
  width: 100%;
  padding: 13px;
  border: none;
  border-radius: 999px;
  background: #ff9933;
  color: #1f1300;
  font-weight: bold;
  cursor: pointer;
}

.error {
  color: white;
  background: #b91c1c;
  padding: 10px;
  border-radius: 10px;
}

Step 8: Dashboard Page

src/app/pages/dashboard/dashboard.component.ts
import { Component } from '@angular/core';
import { AuthService } from '../../services/auth.service';

@Component({
  selector: 'app-dashboard',
  imports: [],
  templateUrl: './dashboard.component.html',
  styleUrl: './dashboard.component.css'
})
export class DashboardComponent {
  user = this.authService.getUser();

  constructor(private authService: AuthService) {}
}
src/app/pages/dashboard/dashboard.component.html
<main class="page">
  <section class="card">
    <h1>Dashboard</h1>
    <p>This is a protected page.</p>

    <h2>Logged-in User</h2>
    <p>Name: {{ user?.name }}</p>
    <p>Email: {{ user?.email }}</p>
    <p>Role: {{ user?.role }}</p>
  </section>
</main>
src/app/pages/dashboard/dashboard.component.css
.page {
  min-height: calc(100vh - 60px);
  background: #fff7ed;
  padding: 32px;
  font-family: Arial, sans-serif;
}

.card {
  max-width: 800px;
  margin: auto;
  background: white;
  border-radius: 24px;
  padding: 28px;
  border: 1px solid #ffd0a1;
  box-shadow: 0 18px 40px rgba(100, 50, 0, 0.12);
}

Step 9: Admin Page

src/app/pages/admin/admin.component.html
<main class="page">
  <section class="admin-card">
    <h1>Admin Panel</h1>
    <p>Only admin users can open this page.</p>

    <ul>
      <li>Manage users</li>
      <li>View reports</li>
      <li>Change application settings</li>
    </ul>
  </section>
</main>
src/app/pages/admin/admin.component.css
.page {
  min-height: calc(100vh - 60px);
  background: #fff7ed;
  padding: 32px;
  font-family: Arial, sans-serif;
}

.admin-card {
  max-width: 800px;
  margin: auto;
  background: white;
  border-radius: 24px;
  padding: 28px;
  border: 1px solid #ffd0a1;
  box-shadow: 0 18px 40px rgba(100, 50, 0, 0.12);
}

h1 {
  color: #b45309;
}
Final Project Checkpoint: Run ng serve. Open http://localhost:4200. Student login should open dashboard but not admin page. Admin login should open both dashboard and admin page.

17. Common Mistakes and Fixes

Mistake Reason Fix
Can't bind to ngModel FormsModule missing. Add imports: [FormsModule] in the component.
Dashboard opens without login Guard not added to route. Add canActivate: [authGuard].
Page stays logged in after logout Token was not removed. Remove token and user from storage in logout().
API says unauthorized Token not sent in request. Add an HTTP interceptor to attach the token.
Role guard does not work Route data or user role mismatch. Check data: { roles: [...] } and stored user role.
Frontend guard treated as full security Misunderstanding of browser apps. Always enforce security on the backend too.

18. Practice Tasks

  1. Create a login page with email and password.
  2. Create an auth service with login and logout methods.
  3. Store a fake token after login.
  4. Show logged-in user details on dashboard.
  5. Protect dashboard using an auth guard.
  6. Create an admin page.
  7. Protect admin page using a role guard.
  8. Add a navbar that changes after login.
  9. Add an HTTP interceptor that attaches a token.
  10. Replace fake login with a real backend API later.

19. Quick Quiz

1. What is authentication?
Authentication means proving who the user is.
2. What does JWT stand for?
JWT stands for JSON Web Token.
3. What does a route guard do?
A route guard controls whether a route can be opened.
4. What does an HTTP interceptor do in authentication?
It can add the authorization token to outgoing HTTP requests automatically.
5. Is Angular-only route protection enough for real security?
No. Real security must also be enforced by the backend server.

Final Summary

Angular authentication is not one thing. It is a complete system: login form, auth service, token handling, guards, interceptors, logout, and backend verification.

Need Angular Tool
Login form FormsModule and ngModel
Store login logic AuthService
Protect pages CanActivateFn route guard
Send token to API HTTP interceptor
Admin-only pages Role guard
End session logout() and remove token
Real security Backend validation and authorization

The next natural lesson is: Angular + Backend API Authentication 0 to Infinity.

0%