关于 auth0-aspnetcore-authentication
面向 ASP.NET Core MVC、Razor Pages 和 Blazor Server 应用的 Auth0 集成技能,指导使用 Auth0.AspNetCore.Authentication 实现登录、登出和用户资料。内容涵盖会话认证配置、路由保护及常见集成问题。
主要功能
- 01配置 Auth0 会话认证
- 02实现登录与登出流程
- 03展示用户资料和声明
- 04保护需要认证的路由
- 05提供 Blazor Server 集成步骤
适用场景
- 01为 ASP.NET Core 网站添加 Auth0 登录
- 02为用户配置资料页面和导航
- 03在 Blazor Server 中接入认证端点
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ auth0/ agent-skills --skill auth0-aspnetcore-authentication安装后在 Claude Code 中输入/auth0-aspnetcore-authentication确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
references/api.mdintegration.mdsetup.md详细介绍
Auth0 ASP.NET Core Web App 集成
使用 Auth0.AspNetCore.Authentication 为 ASP.NET Core MVC、Razor Pages 或 Blazor Server 应用添加登录、登出和用户资料功能。
前提条件
- ASP.NET Core 应用(.NET 8 或更高版本)
- 已配置 Auth0 Regular Web Application(不是 API,必须是 Application)
- 如果尚未设置 Auth0,请先使用
auth0-quickstartskill
不适用场景
- 使用 JWT Bearer 验证的 ASP.NET Core Web API——JWT 保护的 REST API 请使用
auth0-aspnetcore-api - Blazor WebAssembly——需要 OIDC 客户端身份验证;请参阅 Auth0 Blazor WebAssembly 快速入门
- 单页应用——客户端身份验证请使用
auth0-react、auth0-vue或auth0-angular - Next.js 应用——使用
auth0-nextjs,它同时处理客户端和服务端 - Python Web 应用——Flask 请使用
auth0-flask,或参阅 Django 快速入门
快速入门工作流
1. 安装 SDK
dotnet add package Auth0.AspNetCore.Authentication
2. 配置凭据
将 Auth0 设置添加到 appsettings.json:
{
"Auth0": {
"Domain": "your-tenant.us.auth0.com",
"ClientId": "your_client_id",
"ClientSecret": "your_client_secret"
}
}
对于本地开发,请勿将机密信息加入源代码管理;使用 dotnet user-secrets,避免提交 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 是你的租户域名(不含 https://)。Auth0:ClientId 和 Auth0:ClientSecret 来自 Auth0 Application 设置。
3. 配置 Auth0 Dashboard
在 Auth0 Application 设置中:
- Allowed Callback URLs:
http://localhost:5000/callback - Allowed Logout URLs:
http://localhost:5000 - Allowed Web Origins:
http://localhost:5000
4. 在 Program.cs 中注册 Auth0
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();
重要:UseAuthentication() 必须位于 UseAuthorization() 之前。反过来会导致静默身份验证失败,受保护的路由不会触发身份验证质询。
5. 创建 AccountController
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 不需要 [Authorize]——它是未通过身份验证的用户的入口。Logout 需要 [Authorize],以确保只有经过身份验证的会话才会触发登出。始终调用这两个 SignOutAsync 方法——只退出 Auth0 方案会留下本地 Cookie;只退出 Cookie 方案则会跳过 Auth0 登出 URL。
6. 创建资料视图
创建 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>
7. 更新导航(_Layout.cshtml)
在 _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>
}
8. 测试应用
dotnet run
访问 http://localhost:5000 并点击 Login,开始 Auth0 登录流程。
Blazor Server 变体
对于 Blazor Server 应用,请使用 Razor Pages 作为身份验证端点——Blazor 组件无法执行 OAuth 质询所需的 HTTP 重定向。
额外的 Program.cs 设置
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();
登录 Razor Page(Pages/Login.cshtml.cs)
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);
}
}
登出 Razor Page(Pages/Logout.cshtml.cs)
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);
}
}
资料组件(Components/Pages/Profile.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>
更新 MainLayout.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
将 Router 包装在 CascadingAuthenticationState 中,以便在整个组件树中启用授权:
<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 变体
对于 Razor Pages 应用(不含 Blazor),在 Program.cs 中使用 AddRazorPages(),而不是 AddControllersWithViews()。身份验证端点与 Blazor Server 部分中展示的 Login/Logout 页面模型相同。将 _Layout.cshtml 中的导航替换为使用 MVC 部分所示的 User.Identity.IsAuthenticated 检查。
常见错误
| 错误 | 修正方法 |
|---|---|
在源代码中硬编码 Domain、ClientId 或 ClientSecret | 从配置中读取——使用 builder.Configuration["Auth0:Domain"];绝不要嵌入凭据 |
将 ClientSecret 提交到源代码管理中 | 使用 dotnet user-secrets 或环境变量存储客户端密钥——绝不要提交 |
在 UseAuthentication() 之前调用 UseAuthorization() | 必须先调用 UseAuthentication()——顺序错误会导致身份验证无法触发 |
| 只注销一种方案 | 始终同时调用 SignOutAsync(Auth0Constants.AuthenticationScheme) 和 SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme) |
为 Login 操作添加 [Authorize] | 未经身份验证的用户必须能够访问 Login——只为 Logout 和 Profile 应用 [Authorize] |
| 未在 Auth0 Dashboard 中配置 Callback URL | 必须将 http://localhost:5000/callback 添加到 Allowed Callback URLs |
为 Domain 传入带有 https:// 前缀的值 | Domain 应为纯域名,例如 my-tenant.us.auth0.com,而不是 https://my-tenant.us.auth0.com |
在 Blazor 中未添加 AddCascadingAuthenticationState() | Blazor Server 必需——缺少此项时,AuthorizeView 和 [Authorize] 属性将没有身份验证上下文 |
| 使用 Blazor 组件进行登录/注销重定向 | Blazor 组件无法执行 HTTP 重定向——使用 Razor Pages(/Login、/Logout)处理身份验证端点 |
在 Blazor 中未添加 AddRazorPages() 和 MapRazorPages() | 如果没有这些注册,Login 和 Logout Razor Pages 将无法路由 |
在 Web 应用中使用 Auth0.AspNetCore.Authentication.Api | 该程序包用于受 JWT 保护的 API——会话式 Web 应用应使用 Auth0.AspNetCore.Authentication |
使用 AddJwtBearer 而不是 AddAuth0WebAppAuthentication | AddJwtBearer 用于无状态 API 身份验证——会话式 Web 应用必须使用 AddAuth0WebAppAuthentication |
未为 Profile 视图创建 Views/Account/ 目录 | 创建视图之前,MVC 要求该目录已存在 |
关键 SDK 方法
| 方法/属性 | 用法 | 用途 |
|---|---|---|
AddAuth0WebAppAuthentication | builder.Services.AddAuth0WebAppAuthentication(options => { ... }) | 注册基于 Auth0 Cookie 的身份验证 |
LoginAuthenticationPropertiesBuilder | new LoginAuthenticationPropertiesBuilder().WithRedirectUri(url).Build() | 构建登录质询所需的属性 |
LogoutAuthenticationPropertiesBuilder | new LogoutAuthenticationPropertiesBuilder().WithRedirectUri(url).Build() | 构建注销重定向所需的属性 |
ChallengeAsync | await HttpContext.ChallengeAsync(Auth0Constants.AuthenticationScheme, props) | 发起 Auth0 Universal Login 重定向 |
SignOutAsync(Auth0) | await HttpContext.SignOutAsync(Auth0Constants.AuthenticationScheme, props) | 从 Auth0 注销并重定向到注销 URL |
SignOutAsync(Cookie) | await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme) | 清除本地会话 Cookie |
User.FindFirst | User.FindFirst(c => c.Type == "picture")?.Value | 在控制器/视图中访问单个用户声明 |
User.Identity.IsAuthenticated | @if (User.Identity.IsAuthenticated) | 在视图/布局中检查身份验证状态 |
[Authorize] | 控制器操作或 Razor 组件上的 [Authorize] 特性 | 保护要求身份验证的路由 |
AddCascadingAuthenticationState | builder.Services.AddCascadingAuthenticationState() | Blazor Server 身份验证状态传播所必需 |
相关技能
auth0-aspnetcore-api- 用于采用 JWT Bearer 令牌验证的 ASP.NET Core Web APIauth0-express- 用于带登录/注销会话的服务端渲染 Express Web 应用auth0-flask- 用于采用基于会话的身份验证的 Flask Web 应用
快速参考
SDK 注册:
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
});
登录操作:
var props = new LoginAuthenticationPropertiesBuilder().WithRedirectUri(returnUrl).Build();
await HttpContext.ChallengeAsync(Auth0Constants.AuthenticationScheme, props);
注销操作(始终同时调用两项):
var props = new LogoutAuthenticationPropertiesBuilder().WithRedirectUri(Url.Action("Index", "Home")).Build();
await HttpContext.SignOutAsync(Auth0Constants.AuthenticationScheme, props);
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
路由保护:
[Authorize]
public IActionResult Profile() { return View(); }
appsettings.json 配置项:
Auth0:Domain- Auth0 租户域名(例如,tenant.us.auth0.com)Auth0:ClientId- 应用程序客户端 IDAuth0:ClientSecret- 应用程序客户端密钥(开发时请使用 user-secrets)