mcpField
A
auth0-aspnetcore-authentication

auth0-aspnetcore-authentication

github.com/auth0安全55309 次安装1 天前更新
代码仓库

关于 auth0-aspnetcore-authentication

面向 ASP.NET Core MVC、Razor Pages 和 Blazor Server 应用的 Auth0 集成技能,指导使用 Auth0.AspNetCore.Authentication 实现登录、登出和用户资料。内容涵盖会话认证配置、路由保护及常见集成问题。

主要功能

  1. 01配置 Auth0 会话认证
  2. 02实现登录与登出流程
  3. 03展示用户资料和声明
  4. 04保护需要认证的路由
  5. 05提供 Blazor Server 集成步骤

适用场景

  1. 01为 ASP.NET Core 网站添加 Auth0 登录
  2. 02为用户配置资料页面和导航
  3. 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.md
integration.md
setup.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-quickstart skill

不适用场景

  • 使用 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 而不是 AddAuth0WebAppAuthenticationAddJwtBearer 用于无状态 API 身份验证——会话式 Web 应用必须使用 AddAuth0WebAppAuthentication
未为 Profile 视图创建 Views/Account/ 目录创建视图之前,MVC 要求该目录已存在

关键 SDK 方法

方法/属性用法用途
AddAuth0WebAppAuthenticationbuilder.Services.AddAuth0WebAppAuthentication(options => { ... })注册基于 Auth0 Cookie 的身份验证
LoginAuthenticationPropertiesBuildernew LoginAuthenticationPropertiesBuilder().WithRedirectUri(url).Build()构建登录质询所需的属性
LogoutAuthenticationPropertiesBuildernew LogoutAuthenticationPropertiesBuilder().WithRedirectUri(url).Build()构建注销重定向所需的属性
ChallengeAsyncawait 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.FindFirstUser.FindFirst(c => c.Type == "picture")?.Value在控制器/视图中访问单个用户声明
User.Identity.IsAuthenticated@if (User.Identity.IsAuthenticated)在视图/布局中检查身份验证状态
[Authorize]控制器操作或 Razor 组件上的 [Authorize] 特性保护要求身份验证的路由
AddCascadingAuthenticationStatebuilder.Services.AddCascadingAuthenticationState()Blazor Server 身份验证状态传播所必需

相关技能

  • auth0-aspnetcore-api - 用于采用 JWT Bearer 令牌验证的 ASP.NET Core Web API
  • auth0-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 - 应用程序客户端 ID
  • Auth0:ClientSecret - 应用程序客户端密钥(开发时请使用 user-secrets)

详细文档

  • 设置指南 - 自动化设置脚本、凭据配置、Auth0 CLI 用法
  • 集成指南 - 受保护的路由、调用 API、Blazor 模式、错误处理
  • API 参考 - 完整的 SDK 配置、构建器选项、声明参考

参考资料

评论

支持 Markdown