VSATutorialSeptember 2026 · 8 min read

VSA Todo App — Part 18: Authorization per Slice — JWT, Cookie Auth, Roles & Policies

TL;DR

Part 18 secures VSA slices. The Blazor CRM uses JWT Bearer authentication on Minimal API endpoint groups; the MVC Project Manager uses cookie authentication on MVC controllers. We cover per-group RequireAuthorization with roles, policy-based authorization, how roles flow through handlers, and protecting both API endpoints and Razor views within the same feature folder.

Part 18 of 20 in the VSA Todo App Tutorial Series|← Previous: Part 17|Next: Part 19 →

Help Us Grow

Love this tutorial? Explore our ready-to-use enterprise starter kits built with VSA in .NET 10.

Security in VSA follows the same principle as everything else: authorization is declared where the feature is. The endpoints file declares what roles can call the API; the controller declares what roles can view the pages; the handlers don't worry about authentication because the framework enforces it before a handler ever runs. This part shows the two production approaches — JWT for the Blazor CRM and cookie auth for the MVC Project Manager — and how roles and policies fit into each VSA slice.

Both applications use ASP.NET Core Identity with role-based authorization. The difference is the authentication scheme: the Blazor CRM issues JWT bearer tokens (ideal for its Blazor WebAssembly and API-driven architecture), while the MVC Project Manager uses cookie authentication (the natural fit for server-rendered Razor pages). Both schemes plug into the same authorization pipeline, so roles and policies work identically.

JWT Authorization at the Endpoint Group Level (Blazor CRM)

The Blazor CRM secures the entire Todo API in one place — on the MapGroup. Every endpoint under /api/todo inherits the JWT requirement automatically:

var group = app.MapGroup("/api/todo").WithTags("Todos")
    .RequireAuthorization(policy => policy
        .AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme)
        .RequireAuthenticatedUser());

This is VSA security in its purest form: the authorization rule lives in the feature's TodoEndpoint.cs file, right next to the endpoints it protects. Adding a new endpoint to the group automatically secures it — you can't accidentally forget to authorize a new route. The RequireAuthenticatedUser() is the baseline; role requirements layer on top.

Role-Based Endpoint Requirements

When an endpoint needs specific roles, add RequireAuthorization with role names directly on the endpoint. The MVC Project Manager restricts its Todo API to Admin and Member roles:

group.MapGet("/", async (IMediator mediator) =>
{
    var result = await mediator.Send(new GetTodoListQuery());
    return Results.Ok(result);
})
.RequireAuthorization(new AuthorizeAttribute
{
    Roles = $"{ApplicationRoles.AdminConst},{ApplicationRoles.MemberConst}"
});

Placing the role requirement on the endpoint keeps the authorization rule visible at the point of exposure. If the business rule changes — say, only Admins can export — you edit the export endpoint's attribute, not the handler. The handler stays authorization-agnostic, which keeps it testable without authentication setup.

Cookie Authentication on MVC Controllers (MVC Project Manager)

The MVC Project Manager's TodoController uses cookie auth with [Authorize(Roles)] to protect the Razor pages:

[Area("Main")]
[Authorize(Roles = $"{ApplicationRoles.AdminConst},{ApplicationRoles.MemberConst}")]
public class TodoController : Controller
{
    public IActionResult Index() => View();
    public IActionResult Create() => View();
    public IActionResult Edit(string id) => View();
    public IActionResult Detail(string id) => View();
}

The class-level [Authorize(Roles)] protects all four actions — Index, Create, Edit, Detail. Unauthenticated users are redirected to the login page (cookie auth's default behavior), while the browser cookie is sent automatically with every request. The controller and the endpoint file sit side-by-side in the same feature folder, so the complete authorization story for the Todo feature is visible in two adjacent files.

Policy-Based Authorization

For more complex rules, policy-based authorization beats role strings. Define policies once in Program.cs and reference them by name in any slice:

builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("TodoAdmin", policy =>
        policy.RequireRole(ApplicationRoles.AdminConst));
    options.AddPolicy("CanManageTodos", policy =>
        policy.RequireAssertion(ctx =>
            ctx.User.IsInRole(ApplicationRoles.AdminConst) ||
            ctx.User.HasClaim("permission", "todo.manage")));
});

// Usage in a slice endpoint
group.MapPost("/", async (CreateTodoRequest request, IMediator mediator) => { ... })
    .RequireAuthorization("CanManageTodos");

Policies centralize the rule definition while leaving the enforcement point in the slice. The RequireAssertion form handles composite rules (role OR claim) that string concatenation can't express cleanly. This is the pattern to reach for as your authorization rules grow beyond simple role checks.

How Roles Flow Through Handlers

A common question in VSA is whether handlers should check roles. They shouldn't. Authorization runs as middleware before the request reaches the handler — the framework validates the JWT/cookie, verifies the role requirement, and rejects unauthorized requests with 401/403. By the time a handler executes, you can trust that the caller is authenticated and authorized. This separation means handlers are pure business logic, testable with a fake DbContext and no authentication infrastructure.

Protecting Both APIs and Views in One Slice

The MVC Project Manager's Todo feature demonstrates the complete picture: the controller protects the Razor views (Index, Create, Edit, Detail) with cookie auth, while the endpoint group protects the JSON API with role requirements. Both files live in the same feature folder, and both enforce the same roles. A developer auditing the Todo feature's security opens two files and sees the entire authorization surface. That's the VSA security contract: authorization is co-located with the feature, never scattered across a global security config.

Key Takeaways

  • RequireAuthorization on MapGroup secures every endpoint in a slice automatically — no missed routes
  • Role requirements belong on the endpoint (API) or controller (views), not in handlers
  • JWT suits API/Blazor architectures; cookie auth suits server-rendered MVC pages — both feed the same role pipeline
  • Policy-based authorization handles composite rules (role OR claim) that string concatenation can't express
  • Authorization middleware runs before handlers — handlers stay pure business logic and testable without auth setup

Frequently Asked Questions

Q: JWT or cookie auth for VSA?

Use JWT for API-heavy and Blazor/SPA architectures — tokens travel in headers, work cross-origin, and scale to mobile clients. Use cookie auth for server-rendered MVC pages — the browser manages the cookie automatically and login redirects are built-in. Both feed the same ASP.NET Core authorization pipeline.

Q: How to protect individual VSA endpoints?

Apply RequireAuthorization(new AuthorizeAttribute { Roles = "..." }) on the specific endpoint, or secure the whole group with MapGroup(...).RequireAuthorization(...). The rule lives in the feature's TodoEndpoint.cs file, co-located with the endpoints it protects.

Q: How do roles work in VSA?

Roles are seeded into ASP.NET Core Identity and assigned to users. The framework validates role claims from the JWT/cookie before the handler runs. Endpoints and controllers declare required roles via RequireAuthorization or [Authorize(Roles)]. Handlers never check roles — authorization happens upstream.

Q: Can I mix auth schemes in one VSA app?

Yes. Register both schemes with AddAuthentication().AddJwtBearer().AddCookie() and use AddAuthenticationSchemes(...) per endpoint group to select the scheme. The MVC Project Manager uses cookie for views; you could add JWT groups for API consumers in the same project.

Part 18 of 20 in the VSA Todo App Tutorial Series|← Previous: Part 17|Next: Part 19 →

Ready to study real VSA code?

Every Indotalent product is a complete .NET 10 application built with Vertical Slice Architecture. Complete source code — $21 each.

Explore Products

Looking for a ready-to-use traditional monolithic multilayered clean architecture?

Monolithic Clean Architecture — 1,300+ devs, 500+ forks. Clean Arch + CQRS + Repository Pattern. Free & open source for commercial use.

Star on GitHub