Help us improve
Share bugs, ideas, or general feedback.
From auth0
Adds login, logout, and user profile to ASP.NET Core MVC, Razor Pages, or Blazor Server apps using Auth0.AspNetCore.Authentication with cookie-based authentication.
npx claudepluginhub auth0/agent-skills --plugin auth0How this skill is triggered — by the user, by Claude, or both
Slash command
/auth0:auth0-aspnetcore-authenticationThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Add login, logout, and user profile to an ASP.NET Core MVC, Razor Pages, or Blazor Server application using `Auth0.AspNetCore.Authentication`.
Adding auth to Blazor. AuthorizeView, CascadingAuthenticationState, Identity UI, per-model flows.
Secures ASP.NET Core Web API endpoints with JWT Bearer token validation and Auth0 integration. Handles DPoP proof-of-possession binding.
Guides ASP.NET Core authentication and authorization with JWT bearer tokens, OpenID Connect, ASP.NET Identity, policies, roles, claims, and API keys. For login, endpoint protection, and auth rules.
Share bugs, ideas, or general feedback.
Add login, logout, and user profile to an ASP.NET Core MVC, Razor Pages, or Blazor Server application using Auth0.AspNetCore.Authentication.
auth0-quickstart skill firstauth0-aspnetcore-api for JWT-protected REST APIsauth0-react, auth0-vue, or auth0-angular for client-side authauth0-nextjs which handles both client and serverauth0-flask for Flask or see the Django quickstartdotnet add package Auth0.AspNetCore.Authentication
Add Auth0 settings to appsettings.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:
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.
In your Auth0 Application settings:
http://localhost:5000/callbackhttp://localhost:5000http://localhost:5000using 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.
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.
Create Views/Account/Profile.cshtml:
@{
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>
Add login/logout/profile links to your nav bar inside _Layout.cshtml:
@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>
}
dotnet run
Visit http://localhost:5000 and click Login to start the Auth0 login flow.
For Blazor Server apps, use Razor Pages as auth endpoints - Blazor components cannot perform the HTTP redirects required by OAuth challenges.
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();
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);
}
}
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);
}
}
@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>
@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>
Wrap the Router in CascadingAuthenticationState to enable authorization throughout the component tree:
<CascadingAuthenticationState>
<Router AppAssembly="typeof(Program).Assembly">
<Found Context="routeData">
<AuthorizeRouteView RouteData="routeData" DefaultLayout="typeof(Layout.MainLayout)" />
<FocusOnNavigate RouteData="routeData" Selector="h1" />
</Found>
</Router>
</CascadingAuthenticationState>
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.
| 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 |
| 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 |
auth0-aspnetcore-api - For ASP.NET Core Web APIs with JWT Bearer token validationauth0-express - For server-rendered Express web apps with login/logout sessionsauth0-flask - For Flask web applications with session-based authSDK registration:
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:
var props = new LoginAuthenticationPropertiesBuilder().WithRedirectUri(returnUrl).Build();
await HttpContext.ChallengeAsync(Auth0Constants.AuthenticationScheme, props);
Logout action (always call both):
var props = new LogoutAuthenticationPropertiesBuilder().WithRedirectUri(Url.Action("Index", "Home")).Build();
await HttpContext.SignOutAsync(Auth0Constants.AuthenticationScheme, props);
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
Route protection:
[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 IDAuth0:ClientSecret - Application client secret (use user-secrets in development)