← Back to Articles         
How would you protect an Admin route?

How would you protect an Admin route?

Published on 02 Oct 2026     7 min read Angular
Scenario Based Questions

For an Angular + ASP.NET Core Web API application, protect the Admin area at both layers:

Angular route guard
      +
ASP.NET Core authorization

The Angular guard improves navigation/UX, but the ASP.NET Core API must enforce the actual security boundary.

1. How would you protect an Admin route?

What is an Admin route guard?

Suppose Angular has:

/login
/products
/admin

Only users with the Admin role should navigate to:

/admin

In modern standalone Angular, we can use a functional CanActivateFn guard.


JWT example

Suppose ASP.NET Core issues a JWT containing:

{
  "sub": "123",
  "name": "Syed",
  "role": "Admin"
}

Angular receives the JWT after login.


Auth Service

auth.service.ts

import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root'
})
export class AuthService {

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

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

  hasRole(role: string): boolean {

    const token = this.getToken();

    if (!token) {
      return false;
    }

    const payload = JSON.parse(
      atob(token.split('.')[1])
    );

    return payload.role === role;
  }
}

For a simple demo, this reads the JWT payload.

Decoding a JWT on the client does not prove that it is trustworthy. Angular uses the claim only to control UI/navigation. ASP.NET Core must validate the JWT signature and authorization claims.


Create Admin Guard

admin.guard.ts

import { inject } from '@angular/core';

import {
  CanActivateFn,
  Router
} from '@angular/router';

import { AuthService } from './auth.service';

export const adminGuard: CanActivateFn =
  (route, state) => {

    const authService = inject(AuthService);
    const router = inject(Router);

    if (!authService.isLoggedIn()) {

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

    if (!authService.hasRole('Admin')) {

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

    return true;
  };

Apply the Guard

app.routes.ts

import { Routes } from '@angular/router';
import { adminGuard } from './admin.guard';

export const routes: Routes = [

  {
    path: 'admin',

    canActivate: [
      adminGuard
    ],

    loadComponent: () =>
      import('./admin/admin')
        .then(m => m.Admin)
  }

];

Now:

User visits /admin
        ↓
adminGuard
        ↓
Is logged in?
   /          \
 No            Yes
 ↓              ↓
Login       Has Admin role?
             /        \
           No          Yes
           ↓            ↓
       Forbidden      Admin

Protect an Entire Admin Section

Suppose your routes are:

/admin
/admin/users
/admin/articles
/admin/settings

Instead of adding the guard repeatedly, protect the parent route.

export const routes: Routes = [

  {
    path: 'admin',

    canActivate: [
      adminGuard
    ],

    children: [

      {
        path: '',
        loadComponent: () =>
          import('./admin/dashboard')
            .then(m => m.Dashboard)
      },

      {
        path: 'users',
        loadComponent: () =>
          import('./admin/users')
            .then(m => m.Users)
      },

      {
        path: 'articles',
        loadComponent: () =>
          import('./admin/articles')
            .then(m => m.Articles)
      }

    ]
  }

];

You can also use canActivateChild when you specifically want a guard applied to child-route activation.


Important Security Point

An Angular route guard is not real API security.

A malicious user doesn't have to use your Angular application.

They can call:

DELETE /api/admin/users/10

using Postman, curl, JavaScript, or another HTTP client.

Therefore:

Angular Guard
     ↓
Protects client navigation

ASP.NET Core Authorization
     ↓
Protects server resources/data

That brings us to the second question.


2. How would you protect the corresponding Admin API endpoint?

ASP.NET Core should validate the JWT and then enforce an authorization rule such as the Admin role.

JWT Authentication

Program.cs

using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.Text;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();

builder.Services
    .AddAuthentication(
        JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        options.TokenValidationParameters =
            new TokenValidationParameters
            {
                ValidateIssuer = true,
                ValidateAudience = true,
                ValidateLifetime = true,
                ValidateIssuerSigningKey = true,

                ValidIssuer =
                    builder.Configuration["Jwt:Issuer"],

                ValidAudience =
                    builder.Configuration["Jwt:Audience"],

                IssuerSigningKey =
                    new SymmetricSecurityKey(
                        Encoding.UTF8.GetBytes(
                            builder.Configuration[
                                "Jwt:Key"]!))
            };
    });

builder.Services.AddAuthorization();

var app = builder.Build();

app.UseHttpsRedirection();

app.UseAuthentication();
app.UseAuthorization();

app.MapControllers();

app.Run();

The order is important:

app.UseAuthentication();
app.UseAuthorization();

Authentication determines:

Who is this user?

Authorization determines:

Is this authenticated user allowed to perform this operation?


Protect Admin Controller

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

[ApiController]
[Route("api/admin")]
[Authorize(Roles = "Admin")]
public class AdminController : ControllerBase
{
    [HttpGet("dashboard")]
    public IActionResult Dashboard()
    {
        return Ok(new
        {
            Message = "Welcome Admin"
        });
    }
}

The important part is:

[Authorize(Roles = "Admin")]

Now the API requires an authenticated user with the Admin role.


JWT Must Contain the Role Claim

When creating the JWT:

var claims = new List<Claim>
{
    new Claim(
        ClaimTypes.NameIdentifier,
        user.Id.ToString()),

    new Claim(
        ClaimTypes.Name,
        user.UserName),

    new Claim(
        ClaimTypes.Role,
        "Admin")
};

Then create the token from those claims.

Conceptually:

JWT

{
   UserId: 123,
   Name: "Syed",
   Role: "Admin"
}

ASP.NET Core validates the token and then:

[Authorize(Roles = "Admin")]

checks the role claim.


Protect Only Certain Methods

You don't necessarily have to protect the entire controller.

For example:

[ApiController]
[Route("api/articles")]
public class ArticlesController : ControllerBase
{
    [HttpGet]
    [AllowAnonymous]
    public IActionResult GetArticles()
    {
        return Ok();
    }

    [HttpPost]
    [Authorize(Roles = "Admin")]
    public IActionResult CreateArticle()
    {
        return Ok();
    }

    [HttpDelete("{id}")]
    [Authorize(Roles = "Admin")]
    public IActionResult DeleteArticle(Guid id)
    {
        return Ok();
    }
}

This is useful for something like a portfolio application:

GET /api/articles
        ↓
Public
        ↓
Anyone can read


POST /api/articles
        ↓
Admin only
        ↓
[Authorize(Roles = "Admin")]


DELETE /api/articles/{id}
        ↓
Admin only

Angular Calls the Protected Endpoint

Angular:

deleteArticle(id: string) {

  return this.http.delete(
    `https://localhost:7001/api/articles/${id}`
  );
}

Your JWT interceptor automatically adds:

Authorization: Bearer eyJ...

ASP.NET Core then performs:

Request
   ↓
JWT Authentication
   ↓
Is token valid?
   │
   ├── No → 401
   │
   └── Yes
        ↓
   Authorization
        ↓
Does user have Admin role?
   │
   ├── No → 403
   │
   └── Yes
        ↓
   Controller Action

401 vs 403

This is an important interview distinction.

Situation Result
No authentication credentials 401 Unauthorized
Invalid/expired access token 401 Unauthorized
Valid authenticated user but insufficient permission 403 Forbidden
Valid Admin user Request proceeds

For example:

No JWT
  ↓
401

Valid JWT
Role = User
  ↓
403

Valid JWT
Role = Admin
  ↓
200

Role-Based vs Policy-Based Authorization

For a simple Admin requirement:

[Authorize(Roles = "Admin")]

is perfectly reasonable.

For more complex enterprise rules, policy-based authorization is often preferable.

For example:

builder.Services.AddAuthorization(options =>
{
    options.AddPolicy(
        "AdminOnly",
        policy =>
        {
            policy.RequireRole("Admin");
        });
});

Then:

[Authorize(Policy = "AdminOnly")]
[HttpDelete("{id}")]
public IActionResult Delete(Guid id)
{
    return Ok();
}

A policy can later evolve beyond a simple role check:

options.AddPolicy(
    "CanDeleteArticle",
    policy =>
    {
        policy.RequireClaim(
            "permission",
            "articles.delete");
    });

Controller:

[Authorize(Policy = "CanDeleteArticle")]

This is useful when permissions become more granular than Admin versus User.


Complete Architecture

For an Angular + ASP.NET Core application:

                   LOGIN
                     │
                     ▼
              ASP.NET Core API
                     │
                Issues JWT
                     │
                     ▼
                  Angular
                     │
              Stores auth state
                     │
         ┌───────────┴───────────┐
         │                       │
         ▼                       ▼
   Angular Router            HttpClient
         │                       │
    Admin Guard             JWT Interceptor
         │                       │
   Check Admin role         Attach Bearer JWT
         │                       │
         ▼                       ▼
   /admin allowed       ASP.NET Core Web API
                                 │
                         Authenticate JWT
                                 │
                                 ▼
                       [Authorize(Roles =
                             "Admin")]
                                 │
                    ┌────────────┴────────────┐
                    │                         │
                 Allowed                   Denied
                    │                         │
                   200                    401 / 403

Key Points

  • Protect Angular Admin routes using a route guard such as CanActivateFn.
  • The Angular guard is primarily for client-side navigation and UX.
  • Never rely on an Angular guard to secure backend data.
  • Protect ASP.NET Core endpoints independently with [Authorize].
  • Use [Authorize(Roles = "Admin")] for straightforward role-based authorization.
  • The JWT must contain the appropriate role/permission claims.
  • ASP.NET Core must validate the JWT; Angular decoding the JWT is not a security check.
  • 401 means authentication is missing/invalid; 403 means the authenticated user lacks permission.
  • For complex applications, consider policy/permission-based authorization instead of hard-coding roles everywhere.

Interview Answers

Q1. How would you protect an Admin route in Angular?

I would create an Angular route guard, such as a functional CanActivateFn, that checks the user's authentication state and Admin role. If authorized it returns true; otherwise it returns a UrlTree to the login or forbidden page.

Q2. How would you protect the corresponding Admin API endpoint?

I would protect the ASP.NET Core endpoint using server-side authorization, for example:

[Authorize(Roles = "Admin")]

ASP.NET Core validates the JWT and its role claim before executing the endpoint. The Angular guard is only a client-side navigation control; the API authorization is the actual security enforcement.

← Back to Articles