mcpField
A
auth0-winforms

auth0-winforms

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

关于 auth0-winforms

面向 .NET Windows Forms 应用的 Auth0 OIDC 登录与退出集成技能,指导编码 Agent 完成配置、WebView2 集成、回调设置和构建验证。

主要功能

  1. 01通过 WebView2 登录 Auth0
  2. 02OIDC 授权码流程与 PKCE
  3. 03集成登录和退出
  4. 04回调 URL 配置指导
  5. 05构建与测试检查清单

适用场景

  1. 01为 WinForms 应用添加 Auth0 身份验证
  2. 02配置登录和退出处理程序
  3. 03为 Auth0 原生应用设置回调 URL

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/auth0/agent-skills --skill auth0-winforms

安装后在 Claude Code 中输入/auth0-winforms确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
api.md
integration.md
setup.md

详细介绍

Auth0 WinForms 集成

为 Windows Forms(WinForms)桌面应用添加 Auth0 身份验证。此技能集成了 Auth0.OidcClient.WinForms NuGet 包,该包使用 WebView2 在弹出窗口中显示 Auth0 登录页面,以便通过 PKCE 安全地执行基于 OIDC 的登录和登出流程。

SDK 通过在 WinForms Form 中承载的 WebView2 控件内打开 Auth0 Universal Login 页面来执行 WebAuth(使用 PKCE 的 OIDC 授权码流程)。与需要在 Info.plist 或 AndroidManifest.xml 中注册 URL 方案的原生移动 SDK 不同,WinForms SDK 完全通过 WebView2 导航事件处理回调,无需自定义 URL 类型或协议处理程序。

Agent 指令: 在提供 SDK 设置说明之前,运行以下命令,获取最新发布版本:

gh api repos/auth0/auth0-oidc-client-net/releases --jq '[.[] | select(.tag_name | startswith("winforms-")) | .tag_name | ltrimstr("winforms-")] | .[0]'

在所有依赖项行中使用返回的版本,而不是下文中任何硬编码的版本。

前提条件

  • .NET 6.0 SDK 或更高版本(WinForms 需要 Windows)
  • Visual Studio 2022 或 JetBrains Rider,并安装 .NET 桌面开发工作负载
  • Windows 10 或更高版本
  • Microsoft Edge WebView2 Runtime(Windows 11 已包含,Windows 10 可安装)
  • 已配置 Native 应用程序的 Auth0 账户
  • Node.js 20+(用于 Auth0 CLI 自动设置)

不适用场景

使用场景推荐技能
.NET MAUI 跨平台应用auth0-maui
WPF 桌面应用auth0-wpf
ASP.NET Core 服务端 Web 应用auth0-aspnetcore-authentication
ASP.NET Core Web API(JWT 验证)auth0-aspnetcore-api
仅适用于 iOS 的 Swift 应用auth0-swift
仅适用于 Android 的 Kotlin 应用auth0-android

快速入门工作流

Agent 指令: 开始之前,检查用户的项目:

  1. 从 .csproj 文件的 TargetFramework 中确定 .NET 版本
  2. 检查代码库中是否已有身份验证提供程序——搜索现有的 Auth0Client 或 WebViewBrowser 用法,如有则复用
  3. 记录项目的命名空间和目录约定
  1. 安装 SDK:dotnet add package Auth0.OidcClient.WinForms
  2. 配置 Auth0:参阅设置指南,了解自动或手动配置方式。
  3. 集成身份验证:添加 Auth0Client 初始化,并将登录/登出操作绑定到按钮点击处理程序。
  4. 构建并验证:dotnet build

Agent 指令: 编写 Auth0Client 配置时:

  • 使用本技能集成指南中的确切代码模式。
  • SDK 使用 WebView2 在弹出窗口中显示登录页面——无需自定义浏览器设置。
  • 默认回调 URL 为 https://{yourDomain}/mobile——必须将其添加到 Auth0 Dashboard 的 Allowed Callback URLs 和 Allowed Logout URLs 中。
  • 与使用 https://{domain}/ios/{bundleId}/callback 或类似平台专用模式的原生移动 SDK 不同,WinForms 使用更简单的 https://{domain}/mobile 回调格式。

编写配置和代码后,验证构建是否成功:

dotnet build

如果构建失败,请尝试修复问题。连续失败 5-6 次后,请向用户寻求帮助。

回调 URL 配置

WinForms SDK 默认使用 https://{yourDomain}/mobile 作为回调 URL。这与移动原生 SDK 不同:

  • 移动 SDK 使用平台专属的回调,例如 https://{domain}/ios/{bundleId}/callback 或 https://{domain}/android/{packageName}/callback
  • WPF/WinForms 使用通用的 https://{yourDomain}/mobile 回调

回调由 WebView2 控件的 NavigationStarting 事件拦截 — 无需注册系统级 URL 方案。你不需要配置 Info.plist、AndroidManifest.xml 或 Windows 协议处理程序。

在 Auth0 Dashboard 中配置:

  • Allowed Callback URLs:https://{yourDomain}/mobile
  • Allowed Logout URLs:https://{yourDomain}/mobile

完成条件

  • 已安装 Auth0.OidcClient.WinForms 包
  • 已使用 Domain 和 ClientId 配置 Auth0Client
  • 登录/注销流程正常工作(WebView2 弹出窗口打开并进行身份验证)
  • 登录后可以访问用户个人资料声明
  • 已在 Auth0 Dashboard 中注册回调 URL https://{yourDomain}/mobile
  • 构建成功且没有错误
  • 已在真实设备上测试(实体 Windows 计算机,而不仅是远程桌面)

详细文档

  • 设置指南 — Auth0 租户配置、SDK 安装、回调 URL 设置
  • 集成模式 — 登录/注销流程、令牌刷新、用户个人资料、错误处理
  • API 参考与测试 — 完整的 Auth0ClientOptions 参考、声明、测试清单、故障排除

常见错误

错误修复方法
Auth0 Dashboard 中的应用类型未设置为 Native在 Dashboard 设置中将应用类型改为“Native”
Auth0 Dashboard 中缺少回调 URL将 https://{yourDomain}/mobile 添加到 Allowed Callback URLs 和 Allowed Logout URLs
在 Domain 配置中使用 https:// 前缀Domain 应仅包含主机名(例如 tenant.auth0.com,而不是 https://tenant.auth0.com)
未安装 WebView2 Runtime在 Windows 10 上安装 Microsoft Edge WebView2 Runtime(Windows 11 已包含)
刷新令牌时未请求 offline_access 范围将 offline_access 添加到 Auth0ClientOptions 中的 Scope
在代码中存储 ClientSecret原生应用不会使用 Client Secret — 请将其移除
尝试在注册表/清单中注册 URL 方案WinForms 在进程内使用 WebView2 — 无需注册 URL 方案(与需要 AppxManifest 的 MAUI 不同)

测试说明

Agent 指令: 提醒用户在实体设备上测试。某些 WebView2 行为(弹出窗口、证书处理)在远程桌面或虚拟机环境中可能与实体 Windows 计算机有所不同。发布前,请在真实硬件上测试完整的登录 → WebView2 → 回调 → 令牌流程。

测试清单:

  • 登录流程:点击登录 → WebView2 弹出窗口打开 → 完成身份验证 → 弹出窗口关闭 → 显示用户信息
  • 注销流程:点击注销 → WebView2 弹出窗口打开 → 清除会话 → 弹出窗口关闭
  • 令牌刷新:使用已存储的刷新令牌调用 RefreshTokenAsync 可正常工作
  • 取消:用户关闭 WebView2 窗体 → 应用妥善处理 UserCancel
  • 实体设备:在真实 Windows 计算机上测试(而不仅是虚拟环境)
  • 多次登录:确认注销后仍可正常登录(没有残留状态)

相关技能

  • auth0-wpf — WPF 桌面应用
  • auth0-maui — .NET MAUI 跨平台应用
  • auth0-aspnetcore-authentication — ASP.NET Core 服务端 Web 应用
  • auth0-aspnetcore-api — 使用 JWT 验证的 ASP.NET Core Web API

快速参考

using Auth0.OidcClient;
using System.Diagnostics;

// Initialize client
var client = new Auth0Client(new Auth0ClientOptions
{
    Domain = "{yourDomain}",
    ClientId = "{yourClientId}",
    Scope = "openid profile email offline_access"
});

// Login — opens WebView2 popup form (WebAuth flow with PKCE)
var loginResult = await client.LoginAsync();
if (loginResult.IsError == false)
{
    var user = loginResult.User;
    var name = user.FindFirst(c => c.Type == "name")?.Value;
    var email = user.FindFirst(c => c.Type == "email")?.Value;
    var picture = user.FindFirst(c => c.Type == "picture")?.Value;

    Debug.WriteLine($"name: {name}");
    Debug.WriteLine($"email: {email}");

    foreach (var claim in loginResult.User.Claims)
    {
        Debug.WriteLine($"{claim.Type} = {claim.Value}");
    }
}

// Logout
await client.LogoutAsync();

// Refresh token (requires offline_access scope)
var refreshToken = loginResult.RefreshToken;
var refreshResult = await client.RefreshTokenAsync(refreshToken);
if (refreshResult.IsError == false)
{
    var newAccessToken = refreshResult.AccessToken;
}
Form1.cs(WinForms 完整示例)
using Auth0.OidcClient;
using System.Diagnostics;

namespace MyApp;

public partial class Form1 : Form
{
    private Auth0Client _client;
    private Button loginButton;
    private Button logoutButton;

    public Form1()
    {
        InitializeComponent();

        _client = new Auth0Client(new Auth0ClientOptions
        {
            Domain = "{yourDomain}",
            ClientId = "{yourClientId}",
            Scope = "openid profile email offline_access"
        });

        loginButton = new Button
        {
            Text = "Log In",
            Width = 120,
            Height = 40,
            Left = (ClientSize.Width - 120) / 2,
            Top = (ClientSize.Height - 40) / 2
        };
        loginButton.Click += loginButton_Click;
        Controls.Add(loginButton);

        logoutButton = new Button
        {
            Text = "Log Out",
            Width = 120,
            Height = 40,
            Left = (ClientSize.Width - 120) / 2,
            Top = (ClientSize.Height - 40) / 2 + 50
        };
        logoutButton.Click += logoutButton_Click;
        Controls.Add(logoutButton);
    }

    private async void loginButton_Click(object sender, EventArgs e)
    {
        var loginResult = await _client.LoginAsync();

        if (loginResult.IsError)
        {
            Debug.WriteLine($"Error: {loginResult.Error}");
            Debug.WriteLine($"Description: {loginResult.ErrorDescription}");
            return;
        }

        var user = loginResult.User;
        var name = user.FindFirst(c => c.Type == "name")?.Value;
        var email = user.FindFirst(c => c.Type == "email")?.Value;
        var picture = user.FindFirst(c => c.Type == "picture")?.Value;

        Debug.WriteLine($"name: {name}");
        Debug.WriteLine($"email: {email}");

        foreach (var claim in loginResult.User.Claims)
        {
            Debug.WriteLine($"{claim.Type} = {claim.Value}");
        }
    }

    private async void logoutButton_Click(object sender, EventArgs e)
    {
        await _client.LogoutAsync();
    }
}

References

评论

支持 Markdown