Going DeeperPro· 45 min read

Authentication & Protecting Routes

A login flow: send credentials, store the returned token, attach it to requests, and guard the pages that need a logged-in user.

What you will learn

  • Outline a JWT login flow end to end
  • Store and read a token with a service
  • Protect pages by combining the auth service with a guard

What “logging in” really means

Authentication answers one question: who is this user, and are they allowed in? The modern web way uses a token — usually a JWT (JSON Web Token), a long signed string the server gives you after a correct login. From then on, your app sends that token with each request to prove who you are. The server trusts the token instead of asking for the password every time.

Here is the whole flow in order. Every step maps to a piece of code below:

  1. The user types email and password into a login form.
  2. Your app POSTs those credentials to the server’s login API.
  3. If they are correct, the server replies with a token.
  4. Your app stores the token (commonly in localStorage) and marks the user as logged in.
  5. For later requests, an interceptor attaches the token so the server knows who is calling.
  6. A route guard uses the auth service to block pages when no valid token is present.
  7. On logout, you delete the stored token.

An auth service holds the token

Put all the login logic in one AuthService: it calls the login API, stores the token, tells the rest of the app whether the user is logged in, and logs out.

The AuthService: login, store, check, logout
// auth.service.ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { tap } from 'rxjs/operators';

@Injectable({ providedIn: 'root' })
export class AuthService {
  constructor(private http: HttpClient) {}

  login(email: string, password: string) {
    return this.http
      .post<{ token: string }>('https://example.com/api/login',
        { email, password })
      .pipe(tap(res => localStorage.setItem('token', res.token)));
  }

  getToken(): string | null {
    return localStorage.getItem('token');
  }

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

  logout() {
    localStorage.removeItem('token');
  }
}

Note: Output: (No visible output by itself — this is the logic layer.) login POSTs the credentials and, when the token comes back, tap saves it to localStorage without disturbing the stream. isLoggedIn simply checks whether a token exists, and logout removes it. (localStorage is the browser’s small key-value store that survives page reloads.)

A login form that calls the service

The component subscribes to login(...) and, on success, navigates into the app.

The login component reacts to success or failure
// login.component.ts
export class LoginComponent {
  email = '';
  password = '';

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

  submit() {
    this.auth.login(this.email, this.password)
      .subscribe({
        next: () => this.router.navigate(['/dashboard']),
        error: () => alert('Wrong email or password')
      });
  }
}

Note: Output: Correct login → you land on /dashboard. Wrong login → an alert appears. The subscribe object has a next (runs on success) and an error (runs on failure). On success we send the user to a protected page.

Guard the protected pages

Reuse the guard idea from the routing unit, now backed by the real auth service. Combine it with the interceptor from the last lesson, which attaches the token to every request.

The guard asks the auth service if the user is in
// auth.guard.ts
import { inject } from '@angular/core';
import { Router } from '@angular/router';
import { AuthService } from './auth.service';

export const authGuard = () => {
  const auth = inject(AuthService);
  const router = inject(Router);
  if (auth.isLoggedIn()) return true;
  router.navigate(['/login']);
  return false;
};

Note: Output: Logged in → protected routes open normally. Logged out → any guarded route bounces you to /login. The three pieces — service, interceptor, guard — work together: the service holds the token, the interceptor sends it, and the guard checks it.

Watch out: Never trust the front end alone. A guard only hides pages in the browser; the server must verify the token on every protected API call. Front-end checks are for user experience, not security.

Tip: Tokens can expire. A common next step is a refresh token flow: when a request returns 401, your error interceptor quietly asks for a new token and retries — so the user is not logged out mid-session.

Q. In a JWT login flow, what does the app do with the token the server returns?

Answer: After login the app stores the token and sends it with subsequent requests (typically via an interceptor) so the server can identify the user without re-checking the password each time.

✍️ Practice

  1. Add a logout() button that calls the service and routes back to /login.
  2. Add an isLoggedIn() check that shows “Login” or “Logout” in the nav bar.

🏠 Homework

  1. Build a mini login flow: a login form, an AuthService storing a fake token, an interceptor attaching it, and a guard protecting a /profile route.
Want to learn this with a mentor?

CodingClave runs guided, project-based training (28-day, 45-day & 6-month batches).

Explore Training →