Catalogskills.sh · auth0

auth0-aspnetcore-authentication

Use when adding cookie-based login, logout, or user profile to an ASP.NET Core MVC, Razor Pages, or Blazor Server web app. Integrates Auth0.AspNetCore.Authentication — use even if the user says "add login to my .NET web app" without naming the package.

Review advisedVerified hub309 installs

Savant verdict: Review advised

Security analysis or source-hub review identified patterns requiring attention.

Live evaluation

Not evaluated yet. Workspaces can request a live evaluation.

Security assessment

Scan pending.

Structure

  • The source hub's security checks flagged this skillSnyk: warn (Risk: MEDIUM · 1 issue)

SKILL.md

---
name: auth0-aspnetcore-authentication
description: >
  Use when adding cookie-based login, logout, or user profile to an ASP.NET Core MVC, Razor Pages, or Blazor Server web app. Integrates Auth0.AspNetCore.Authentication — use even if the user says "add login to my .NET web app" without naming the package.
license: Apache-2.0
metadata:
  author: Auth0 <support@auth0.com>
  version: '1.0.0'
  openclaw:
    emoji: "\U0001F510"
    homepage: https://github.com/auth0/agent-skills
---

# Auth0 ASP.NET Core Web App Integration

Add login, logout, and user profile to an ASP.NET Core MVC, Razor Pages, or Blazor Server application using `Auth0.AspNetCore.Authentication`.

---

## Prerequisites

- ASP.NET Core application (.NET 8 or higher)
- Auth0 Regular Web Application configured (not an API - must be an Application)
- If you don't have Auth0 set up yet, use the `auth0-quickstart` skill first

## When NOT to Use

- **ASP.NET Core Web APIs with JWT Bearer validation** - Use `auth0-aspnetcore-api` for JWT-protected REST APIs
- **Blazor WebAssembly** - Requires OIDC client-side auth; see the Auth0 Blazor WebAssembly quickstart
- **Single Page Applications** - Use `auth0-react`, `auth0-vue`, or `auth0-angular` for client-side auth
- **Next.js applications** - Use `auth0-nextjs` which handles both client and server
- **Python web apps** - Use `auth0-flask` for Flask or see the Django quickstart

---

## Quick Start Workflow

### 1. Install SDK

```bash
dotnet add package Auth0.AspNetCore.Authentication
```

### 2. Configure Credentials

Add Auth0 settings to `appsettings.json`:

```json
{
  "Auth0": {
    "Domain": "your-tenant.us.auth0.com",
    "ClientId": "your_client_id",
    "ClientSecret": "your_client_secret"
  }
}
```

**For local development**, keep secrets out of source control - use `dotnet user-secrets` to avoid committing `ClientSecret`:

```bash
dotnet user-secrets set "Auth0:Domain" "your-tenant.us.auth0.com"
dotnet user-secrets set "Auth0:ClientId" "your_client_id"
dotnet user-secrets set "Auth0:ClientSecret" "your_client_secret"
```

`Auth0:Domain` is your tenant domain (without `https://`). `Auth0:ClientId` and `Auth0:ClientSecret` come from your Auth0 Application settings.

### 3. Configure Auth0 Dashboard

In your Auth0 Application settings:
- **Allowed Callback URLs**: `http://localhost:5000/callback`
- **Allowed Logout URLs**: `http://localhost:5000`
- **Allowed Web Origins**: `http://localhost:5000`

### 4. Register Auth0 in Program.cs

```csharp
using Auth0.AspNetCore.Authentication;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddAuth0WebAppAuthentication(options =>
{
    options.Domain = builder.Configuration["Auth0:Domain"];
    options.ClientId = builder.Configuration["Auth0:ClientId"];
    options.ClientSecret = builder.Configuration["Auth0:ClientSecret"];
});

builder.Services.AddControllersWithViews();

var app = builder.Build();

// Standard middleware...
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();

app.UseAuthentication();    // Must come before UseAuthorization
app.UseAuthorization();     // Critical: order matters

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

app.Run();
```

**Critical:** `UseAuthentication()` must come before `UseAuthorization()`. Reversing these causes silent auth failures where protected routes are never challenged.

### 5. Create AccountController

```csharp
using Auth0.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

public class AccountController : Controller
{
    public async Task Login(string returnUrl = "/")
    {
        var authenticationProperties = new LoginAuthenticationPropertiesBuilder()
            .WithRedirectUri(returnUrl)
            .Build();

        await HttpContext.ChallengeAsync(Auth0Constants.AuthenticationScheme, authenticationProperties);
    }

    [Authorize]
    public async Task Logout()
    {
        var authenticationProperties = new LogoutAuthenticationPropertiesBuilder()
            .WithRedirectUri(Url.Action("Index", "Home"))
            .Build();

        await HttpContext.SignOutAsync(Auth0Constants.AuthenticationScheme, authenticationProperties);
        await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
    }

    [Authorize]
    public IActionResult Profile()
    {
        return View();
    }
}
```

`Login` does not need `[Authorize]` - it is the entry point for unauthenticated users. `Logout` requires `[Authorize]` to ensure the sign-out only fires for authenticated sessions. **Always call both `SignOutAsync` methods** - signing out of only the Auth0 scheme leaves a local cookie; signing out of only the cookie scheme skips the Auth0 logout URL.

### 6. Create Profile View

Create `Views/Account/Profile.cshtml`:

```html
@{
    ViewData["Title"] = "User Profile";
}

<div class="row">
    <div class="col-md-2">
        <img src="@User.FindFirst(c => c.Type == "picture")?.Value"
             alt="Profile picture" class="img-fluid rounded-circle" />
    </div>
    <div class="col-md-10">
        <h3>@User.Identity.Name</h3>
        <p><strong>Email:</strong>
           @User.FindFirst(c => c.Type == System.Security.Claims.ClaimTypes.Email)?.Value</p>
        <p><strong>User ID:</strong>
           @User.FindFirst(c => c.Type == System.Security.Claims.ClaimTypes.NameIdentifier)?.Value</p>
    </div>
</div>

<h4 class="mt-4">Claims</h4>
<table class="table">
    <thead><tr><th>Claim Type</th><th>Claim Value</th></tr></thead>
    <tbody>
        @foreach (var claim in User.Claims)
        {
            <tr><td>@claim.Type</td><td>@claim.Value</td></tr>
        }
    </tbody>
</table>
```

### 7. Update Navigation (_Layout.cshtml)

Add login/logout/profile links to your nav bar inside `_Layout.cshtml`:

```html
@if (User.Identity.IsAuthenticated)
{
    <li class="nav-item">
        <a class="nav-link text-dark" asp-controller="Account" asp-action="Profile">@User.Identity.Name</a>
    </li>
    <li class="nav-item">
        <a class="nav-link text-dark" asp-controller="Account" asp-action="Logout">Logout</a>
    </li>
}
else
{
    <li class="nav-item">
        <a class="nav-link text-dark" asp-controller="Account" asp-action="Login">Login</a>
    </li>
}
```

### 8. Test the App

```bash
dotnet run
```

Visit `http://localhost:5000` and click Login to start the Auth0 login flow.

---

## Blazor Server Variant

For Blazor Server apps, use Razor Pages as auth endpoints - Blazor components cannot perform the HTTP redirects required by OAuth challenges.

### Additional Program.cs Setup

```csharp
using Auth0.AspNetCore.Authentication;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddAuth0WebAppAuthentication(options =>
{
    options.Domain = builder.Configuration["Auth0:Domain"];
    options.ClientId = builder.Configuration["Auth0:ClientId"];
    options.ClientSecret = builder.Configuration["Auth0:ClientSecret"];
});

builder.Services.AddRazorComponents()
    .AddInteractiveServerComponents();

builder.Services.AddCascadingAuthenticationState();  // Required for Blazor auth state
builder.Services.AddRazorPages();                     // Required for auth endpoints

var app = builder.Build();

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

app.MapRazorPages();
app.MapRazorComponents<App>()
    .AddInteractiveServerRenderMode();

app.Run();
```

### Login Razor Page (Pages/Login.cshtml.cs)

```csharp
using Auth0.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;

public class LoginModel : PageModel
{
    public async Task OnGet(string returnUrl = "/")
    {
        var authenticationProperties = new LoginAuthenticationPropertiesBuilder()
            .WithRedirectUri(returnUrl)
            .Build();

        await HttpContext.ChallengeAsync(Auth0Constants.AuthenticationScheme, authenticationProperties);
    }
}
```

### Logout Razor Page (Pages/Logout.cshtml.cs)

```csharp
using Auth0.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;

public class LogoutModel : PageModel
{
    public async Task OnGet()
    {
        var authenticationProperties = new LogoutAuthenticationPropertiesBuilder()
            .WithRedirectUri(Url.Content("~/"))
            .Build();

        await HttpContext.SignOutAsync(Auth0Constants.AuthenticationScheme, authenticationProperties);
        await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
    }
}
```

### Profile Component (Components/Pages/Profile.razor)

```razor
@page "/profile"
@attribute [Authorize]
@using System.Security.Claims

<h1>Profile</h1>

<AuthorizeView>
    <Authorized>
        <div class="row">
            <div class="col-2">
                <img src="@context.User.FindFirst("picture")?.Value"
                     alt="Profile" class="img-fluid rounded-circle" />
            </div>
            <div class="col-10">
                <h3>@context.User.Identity?.Name</h3>
                <p><strong>Email:</strong> @context.User.FindFirst(ClaimTypes.Email)?.Value</p>
            </div>
        </div>

        <h4 class="mt-4">Claims</h4>
        <table class="table">
            <thead><tr><th>Type</th><th>Value</th></tr></thead>
            <tbody>
                @foreach (var claim in context.User.Claims)
                {
                    <tr><td>@claim.Type</td><td>@claim.Value</td></tr>
                }
            </tbody>
        </table>
    </Authorized>
</AuthorizeView>
```

### Update MainLayout.razor Navigation

```razor
@using Microsoft.AspNetCore.Components.Authorization

<AuthorizeView>
    <Authorized>
        <a href="/profile">@context.User.Identity?.Name</a>
        <a href="/Logout">Logout</a>
    </Authorized>
    <NotAuthorized>
        <a href="/Login">Login</a>
    </NotAuthorized>
</AuthorizeView>
```

### Routes.razor

Wrap the `Router` in `CascadingAuthenticationState` to enable authorization throughout the component tree:

```razor
<CascadingAuthenticationState>
    <Router AppAssembly="typeof(Program).Assembly">
        <Found Context="routeData">
            <AuthorizeRouteView RouteData="routeData" DefaultLayout="typeof(Layout.MainLayout)" />
            <FocusOnNavigate RouteData="routeData" Selector="h1" />
        </Found>
    </Router>
</CascadingAuthenticationState>
```

---

## Razor Pages Variant

For Razor Pages apps (without Blazor), use `AddRazorPages()` instead of `AddControllersWithViews()` in `Program.cs`. Auth endpoints are the same Login/Logout page models shown in the Blazor Server section. Replace navigation in `_Layout.cshtml` using the same `User.Identity.IsAuthenticated` check shown in the MVC section.

---

## Common Mistakes

| Mistake | Fix |
|---------|-----|
| Hardcoding `Domain`, `ClientId`, or `ClientSecret` in source | Read from configuration - use `builder.Configuration["Auth0:Domain"]`; never embed credentials |
| Committing `ClientSecret` to source control | Use `dotnet user-secrets` or environment variables for the client secret - never commit it |
| `UseAuthorization()` before `UseAuthentication()` | Must call `UseAuthentication()` first - wrong order causes auth to never fire |
| Signing out of only one scheme | Always call both `SignOutAsync(Auth0Constants.AuthenticationScheme)` and `SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme)` |
| Adding `[Authorize]` to the `Login` action | `Login` must be accessible to unauthenticated users - only apply `[Authorize]` to `Logout` and `Profile` |
| Not configuring Callback URLs in Auth0 Dashboard | Must add `http://localhost:5000/callback` to Allowed Callback URLs |
| Passing `Domain` with `https://` prefix | `Domain` should be the bare domain, e.g., `my-tenant.us.auth0.com`, not `https://my-tenant.us.auth0.com` |
| Not adding `AddCascadingAuthenticationState()` in Blazor | Required for Blazor Server - without it, `AuthorizeView` and `[Authorize]` attributes have no auth context |
| Using Blazor components for login/logout redirects | Blazor components cannot perform HTTP redirects - use Razor Pages (`/Login`, `/Logout`) for auth endpoints |
| Not adding `AddRazorPages()` and `MapRazorPages()` in Blazor | Login and Logout Razor Pages won't be routed without these registrations |
| Using `Auth0.AspNetCore.Authentication.Api` for web apps | That package is for JWT-protected APIs - use `Auth0.AspNetCore.Authentication` for session-based web apps |
| Using `AddJwtBearer` instead of `AddAuth0WebAppAuthentication` | `AddJwtBearer` is for stateless API auth - session-based web apps require `AddAuth0WebAppAuthentication` |
| Not creating `Views/Account/` directory for Profile view | MVC requires the directory to exist before creating the view |

---

## Key SDK Methods

| Method/Property | Usage | Purpose |
|-----------------|-------|---------|
| `AddAuth0WebAppAuthentication` | `builder.Services.AddAuth0WebAppAuthentication(options => { ... })` | Registers Auth0 cookie-based authentication |
| `LoginAuthenticationPropertiesBuilder` | `new LoginAuthenticationPropertiesBuilder().WithRedirectUri(url).Build()` | Builds properties for the login challenge |
| `LogoutAuthenticationPropertiesBuilder` | `new LogoutAuthenticationPropertiesBuilder().WithRedirectUri(url).Build()` | Builds properties for the logout redirect |
| `ChallengeAsync` | `await HttpContext.ChallengeAsync(Auth0Constants.AuthenticationScheme, props)` | Initiates the Auth0 Universal Login redirect |
| `SignOutAsync` (Auth0) | `await HttpContext.SignOutAsync(Auth0Constants.AuthenticationScheme, props)` | Signs out of Auth0 and redirects to logout URL |
| `SignOutAsync` (Cookie) | `await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme)` | Clears the local session cookie |
| `User.FindFirst` | `User.FindFirst(c => c.Type == "picture")?.Value` | Accesses individual user claims in controllers/views |
| `User.Identity.IsAuthenticated` | `@if (User.Identity.IsAuthenticated)` | Checks authentication state in views/layouts |
| `[Authorize]` | `[Authorize]` attribute on controller action or Razor component | Protects routes requiring authentication |
| `AddCascadingAuthenticationState` | `builder.Services.AddCascadingAuthenticationState()` | Required for Blazor Server auth state propagation |

---

## Related Skills

- `auth0-aspnetcore-api` - For ASP.NET Core Web APIs with JWT Bearer token validation
- `auth0-express` - For server-rendered Express web apps with login/logout sessions
- `auth0-flask` - For Flask web applications with session-based auth

---

## Quick Reference

**SDK registration:**
```csharp
builder.Services.AddAuth0WebAppAuthentication(options =>
{
    options.Domain = builder.Configuration["Auth0:Domain"];        // required
    options.ClientId = builder.Configuration["Auth0:ClientId"];    // required
    options.ClientSecret = builder.Configuration["Auth0:ClientSecret"]; // required
});
```

**Login action:**
```csharp
var props = new LoginAuthenticationPropertiesBuilder().WithRedirectUri(returnUrl).Build();
await HttpContext.ChallengeAsync(Auth0Constants.AuthenticationScheme, props);
```

**Logout action (always call both):**
```csharp
var props = new LogoutAuthenticationPropertiesBuilder().WithRedirectUri(Url.Action("Index", "Home")).Build();
await HttpContext.SignOutAsync(Auth0Constants.AuthenticationScheme, props);
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
```

**Route protection:**
```csharp
[Authorize]
public IActionResult Profile() { return View(); }
```

**appsettings.json configuration keys:**
- `Auth0:Domain` - Auth0 tenant domain (e.g., `tenant.us.auth0.com`)
- `Auth0:ClientId` - Application client ID
- `Auth0:ClientSecret` - Application client secret (use user-secrets in development)

---

## Detailed Documentation

- **[Setup Guide](references/setup.md)** - Automated setup scripts, credential configuration, Auth0 CLI usage
- **[Integration Guide](references/integration.md)** - Protected routes, calling APIs, Blazor patterns, error handling
- **[API Reference](references/api.md)** - Complete SDK configuration, builder options, claims reference

---

## References

- [Auth0.AspNetCore.Authentication on NuGet](https://www.nuget.org/packages/Auth0.AspNetCore.Authentication)
- [Auth0 ASP.NET Core MVC Quickstart](https://auth0.com/docs/quickstart/webapp/aspnet-core)
- [Auth0 ASP.NET Core Blazor Server Quickstart](https://auth0.com/docs/quickstart/webapp/aspnet-core-blazor-server)
- [ASP.NET Core Documentation](https://learn.microsoft.com/en-us/aspnet/core)