Claude Cursor GitHub Copilot Skill

configure-auth

Add authentication and authorization to a Blazor Web App, accounting for the app's render mode. USE WHEN the user needs [Authorize] on pages, AuthorizeView, role or policy-based access, login/logout Identity pages, or AuthenticationStateProvider. Also USE WHEN auth state is null

LLM Mart · 0 points · 18 views 0 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download dotnet-skills-plugins_dotnet-blazor_skills_configure-auth-98f8485.zip · 2 KB
Part of dotnet/skills — 119 skills

Install

skills CLI npx skills add https://github.com/dotnet/skills/tree/main/plugins/dotnet-blazor/skills/configure-auth
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install dotnet-skills@llmmart
Git git clone https://github.com/dotnet/skills.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole dotnet/skills collection as a plugin from our marketplace. Git is the plain clone.

Skill manifest

Configure Auth

Step 1 — Read AGENTS.md

Read AGENTS.md at the workspace root for the project's interactivity mode and scope before making changes.

Step 2 — Register auth services in Program.cs

// Program.cs (server project)
builder.Services.AddCascadingAuthenticationState();
builder.Services.AddAuthorization();

For ASP.NET Core Identity add the Identity services:

builder.Services.AddAuthentication(options =>
{
    options.DefaultScheme = IdentityConstants.ApplicationScheme;
    options.DefaultSignInScheme = IdentityConstants.ExternalScheme;
})
.AddIdentityCookies();

builder.Services.AddIdentityCore<ApplicationUser>()
    .AddRoles<IdentityRole>()
    .AddEntityFrameworkStores<ApplicationDbContext>()
    .AddSignInManager()
    .AddDefaultTokenProviders();

Step 3 — Wire App.razor for auth and render mode

The App.razor component must use AuthorizeRouteView and conditionally apply the render mode so that pages excluded from interactive routing render statically.

<!DOCTYPE html>
<html>
<head>
    <HeadOutlet @rendermode="RenderModeForPage" />
</head>
<body>
    <Routes @rendermode="RenderModeForPage" />
    <script src="_framework/blazor.web.js"></script>
</body>
</html>

@code {
    [CascadingParameter]
    public HttpContext HttpContext { get; set; } = default!;

    private IComponentRenderMode? RenderModeForPage =>
        HttpContext.AcceptsInteractiveRouting()
            ? InteractiveServer   // replace with the app's render mode
            : null;
}

In Routes.razor (or wherever the router lives), use AuthorizeRouteView:

<Router AppAssembly="typeof(Program).Assembly">
    <Found Context="routeData">
        <AuthorizeRouteView RouteData="routeData"
                            DefaultLayout="typeof(Layout.MainLayout)">
            <NotAuthorized>
                @if (context.User.Identity?.IsAuthenticated != true)
                {
                    <RedirectToLogin />
                }
                else
                {
                    <p>You are not authorized to access this resource.</p>
                }
            </NotAuthorized>
        </AuthorizeRouteView>
        <FocusOnNavigate RouteData="routeData" Selector="h1" />
    </Found>
</Router>

Step 4 — Protect pages and components

[Authorize] attribute on pages

@page "/admin"
@attribute [Authorize]

With roles or policies:

@attribute [Authorize(Roles = "Admin")]
@attribute [Authorize(Policy = "RequireManager")]

AuthorizeView for conditional UI

<AuthorizeView>
    <Authorized>Welcome, @context.User.Identity?.Name!</Authorized>
    <NotAuthorized><a href="Account/Login">Log in</a></NotAuthorized>
</AuthorizeView>

Role/policy variants:

<AuthorizeView Roles="Admin,Manager">
    <Authorized>Admin content here</Authorized>
</AuthorizeView>

Access auth state in code

[CascadingParameter]
private Task<AuthenticationState>? AuthState { get; set; }

protected override async Task OnInitializedAsync()
{
    if (AuthState is not null)
    {
        var state = await AuthState;
        var isAdmin = state.User.IsInRole("Admin");
    }
}

Step 5 — Identity pages must stay static SSR

SignInManager and UserManager use HttpContext internally and throw in interactive components. Identity pages (login, register, manage) must render as static SSR.

In a globally interactive app, mark every Identity page:

@page "/Account/Login"
@attribute [ExcludeFromInteractiveRouting]

This forces a full-page navigation (exits the interactive circuit) so the page renders through the static SSR pipeline with a real HttpContext.

App.razor must use AcceptsInteractiveRouting() (Step 3) to return null for these pages — otherwise the framework still tries to render them interactively.

In a per-page app, Identity pages are static by default (no @rendermode directive), so [ExcludeFromInteractiveRouting] is not needed.

Step 6 — Auth state in WebAssembly / Auto mode

WebAssembly components run in the browser and have no HttpContext. Auth state must be serialized from the server during prerendering and deserialized on the client.

Server Program.cs:

builder.Services.AddAuthenticationStateSerialization();

Client .Client/Program.cs:

builder.Services.AddAuthenticationStateDeserialization();

Without these calls, Task<AuthenticationState> resolves to an anonymous user after WebAssembly takes over from prerendering.

AddAuthenticationStateSerialization accepts options to include role and claim data:

builder.Services.AddAuthenticationStateSerialization(options =>
    options.SerializeAllClaims = true);

Render Mode × Auth Matrix

Render mode HttpContext.User SignInManager Auth state source Key requirement
Static SSR Available Works Server pipeline Use middleware for redirects, <NotAuthorized> does NOT render
Server (interactive) NOT available Throws CascadingAuthenticationState Use [Authorize] + AuthorizeView, not HttpContext
WebAssembly NOT available Throws Serialized from server AddAuthenticationStateSerialization / Deserialization
Auto NOT available after WASM Throws Serialized from server Same as WebAssembly; register in both Program.cs files

Common Mistakes

Mistake Symptom Fix
Using HttpContext.User in interactive component Null or stale claims Use [CascadingParameter] Task<AuthenticationState>
SignInManager in interactive component InvalidOperationException Move to static SSR page with [ExcludeFromInteractiveRouting]
Missing AddAuthenticationStateSerialization Anonymous user after WASM loads Add to server Program.cs; add Deserialization to client Program.cs
<NotAuthorized> in static SSR layout Content never shown Static SSR uses middleware pipeline; redirect via LoginPath or RedirectToLogin component
Global interactivity without AcceptsInteractiveRouting Identity pages crash Add AcceptsInteractiveRouting() check in App.razor (Step 3)
Missing AddCascadingAuthenticationState() Task<AuthenticationState> is null Register in Program.cs (Step 2)
Files (skills)
  • SKILL.md 7 KB
    ---
    license: MIT
    name: configure-auth
    description: >
      Add authentication and authorization to a Blazor Web App, accounting for the app's render mode.
      USE WHEN the user needs [Authorize] on pages, AuthorizeView, role or policy-based access,
      login/logout Identity pages, or AuthenticationStateProvider.
      Also USE WHEN auth state is null after WebAssembly loads, SignInManager throws in an interactive
      component, <NotAuthorized> content never renders in static SSR, or HttpContext.User is null in
      an interactive component.
      DO NOT USE for general component authoring (see author-component), for prerendering concerns
      unrelated to auth (see support-prerendering), or for managing non-auth cascading state
      (see coordinate-components).
    ---
    
    # Configure Auth
    
    ## Step 1 — Read AGENTS.md
    
    Read `AGENTS.md` at the workspace root for the project's interactivity mode and scope before making changes.
    
    ## Step 2 — Register auth services in Program.cs
    
    ```csharp
    // Program.cs (server project)
    builder.Services.AddCascadingAuthenticationState();
    builder.Services.AddAuthorization();
    ```
    
    For ASP.NET Core Identity add the Identity services:
    
    ```csharp
    builder.Services.AddAuthentication(options =>
    {
        options.DefaultScheme = IdentityConstants.ApplicationScheme;
        options.DefaultSignInScheme = IdentityConstants.ExternalScheme;
    })
    .AddIdentityCookies();
    
    builder.Services.AddIdentityCore<ApplicationUser>()
        .AddRoles<IdentityRole>()
        .AddEntityFrameworkStores<ApplicationDbContext>()
        .AddSignInManager()
        .AddDefaultTokenProviders();
    ```
    
    ## Step 3 — Wire App.razor for auth and render mode
    
    The `App.razor` component must use `AuthorizeRouteView` and conditionally apply the render mode so that pages excluded from interactive routing render statically.
    
    ```razor
    <!DOCTYPE html>
    <html>
    <head>
        <HeadOutlet @rendermode="RenderModeForPage" />
    </head>
    <body>
        <Routes @rendermode="RenderModeForPage" />
        <script src="_framework/blazor.web.js"></script>
    </body>
    </html>
    
    @code {
        [CascadingParameter]
        public HttpContext HttpContext { get; set; } = default!;
    
        private IComponentRenderMode? RenderModeForPage =>
            HttpContext.AcceptsInteractiveRouting()
                ? InteractiveServer   // replace with the app's render mode
                : null;
    }
    ```
    
    In `Routes.razor` (or wherever the router lives), use `AuthorizeRouteView`:
    
    ```razor
    <Router AppAssembly="typeof(Program).Assembly">
        <Found Context="routeData">
            <AuthorizeRouteView RouteData="routeData"
                                DefaultLayout="typeof(Layout.MainLayout)">
                <NotAuthorized>
                    @if (context.User.Identity?.IsAuthenticated != true)
                    {
                        <RedirectToLogin />
                    }
                    else
                    {
                        <p>You are not authorized to access this resource.</p>
                    }
                </NotAuthorized>
            </AuthorizeRouteView>
            <FocusOnNavigate RouteData="routeData" Selector="h1" />
        </Found>
    </Router>
    ```
    
    ## Step 4 — Protect pages and components
    
    ### [Authorize] attribute on pages
    
    ```razor
    @page "/admin"
    @attribute [Authorize]
    ```
    
    With roles or policies:
    
    ```razor
    @attribute [Authorize(Roles = "Admin")]
    @attribute [Authorize(Policy = "RequireManager")]
    ```
    
    ### AuthorizeView for conditional UI
    
    ```razor
    <AuthorizeView>
        <Authorized>Welcome, @context.User.Identity?.Name!</Authorized>
        <NotAuthorized><a href="Account/Login">Log in</a></NotAuthorized>
    </AuthorizeView>
    ```
    
    Role/policy variants:
    
    ```razor
    <AuthorizeView Roles="Admin,Manager">
        <Authorized>Admin content here</Authorized>
    </AuthorizeView>
    ```
    
    ### Access auth state in code
    
    ```csharp
    [CascadingParameter]
    private Task<AuthenticationState>? AuthState { get; set; }
    
    protected override async Task OnInitializedAsync()
    {
        if (AuthState is not null)
        {
            var state = await AuthState;
            var isAdmin = state.User.IsInRole("Admin");
        }
    }
    ```
    
    ## Step 5 — Identity pages must stay static SSR
    
    `SignInManager` and `UserManager` use `HttpContext` internally and **throw in interactive components**. Identity pages (login, register, manage) must render as static SSR.
    
    In a **globally interactive** app, mark every Identity page:
    
    ```razor
    @page "/Account/Login"
    @attribute [ExcludeFromInteractiveRouting]
    ```
    
    This forces a full-page navigation (exits the interactive circuit) so the page renders through the static SSR pipeline with a real `HttpContext`.
    
    `App.razor` must use `AcceptsInteractiveRouting()` (Step 3) to return `null` for these pages — otherwise the framework still tries to render them interactively.
    
    In a **per-page** app, Identity pages are static by default (no `@rendermode` directive), so `[ExcludeFromInteractiveRouting]` is not needed.
    
    ## Step 6 — Auth state in WebAssembly / Auto mode
    
    WebAssembly components run in the browser and have no `HttpContext`. Auth state must be serialized from the server during prerendering and deserialized on the client.
    
    **Server `Program.cs`:**
    
    ```csharp
    builder.Services.AddAuthenticationStateSerialization();
    ```
    
    **Client `.Client/Program.cs`:**
    
    ```csharp
    builder.Services.AddAuthenticationStateDeserialization();
    ```
    
    Without these calls, `Task<AuthenticationState>` resolves to an anonymous user after WebAssembly takes over from prerendering.
    
    `AddAuthenticationStateSerialization` accepts options to include role and claim data:
    
    ```csharp
    builder.Services.AddAuthenticationStateSerialization(options =>
        options.SerializeAllClaims = true);
    ```
    
    ## Render Mode × Auth Matrix
    
    | Render mode | HttpContext.User | SignInManager | Auth state source | Key requirement |
    |---|---|---|---|---|
    | Static SSR | Available | Works | Server pipeline | Use middleware for redirects, `<NotAuthorized>` does NOT render |
    | Server (interactive) | NOT available | Throws | `CascadingAuthenticationState` | Use `[Authorize]` + `AuthorizeView`, not `HttpContext` |
    | WebAssembly | NOT available | Throws | Serialized from server | `AddAuthenticationStateSerialization` / `Deserialization` |
    | Auto | NOT available after WASM | Throws | Serialized from server | Same as WebAssembly; register in **both** Program.cs files |
    
    ## Common Mistakes
    
    | Mistake | Symptom | Fix |
    |---------|---------|-----|
    | Using `HttpContext.User` in interactive component | Null or stale claims | Use `[CascadingParameter] Task<AuthenticationState>` |
    | `SignInManager` in interactive component | `InvalidOperationException` | Move to static SSR page with `[ExcludeFromInteractiveRouting]` |
    | Missing `AddAuthenticationStateSerialization` | Anonymous user after WASM loads | Add to server Program.cs; add `Deserialization` to client Program.cs |
    | `<NotAuthorized>` in static SSR layout | Content never shown | Static SSR uses middleware pipeline; redirect via `LoginPath` or `RedirectToLogin` component |
    | Global interactivity without `AcceptsInteractiveRouting` | Identity pages crash | Add `AcceptsInteractiveRouting()` check in App.razor (Step 3) |
    | Missing `AddCascadingAuthenticationState()` | `Task<AuthenticationState>` is null | Register in Program.cs (Step 2) |
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related