auth0-winforms
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseAuth0 WinForms Integration
Auth0 WinForms 集成
Add Auth0 authentication to Windows Forms (WinForms) desktop applications. This skill integrates the NuGet package which uses WebView2 to display the Auth0 login page in a popup form for secure OIDC-based login and logout flows with PKCE.
Auth0.OidcClient.WinFormsThe SDK performs WebAuth (OIDC authorization code flow with PKCE) by opening the Auth0 Universal Login page inside a WebView2 control hosted in a WinForms Form. Unlike native mobile SDKs that require URL scheme registration in Info.plist or AndroidManifest.xml, the WinForms SDK handles the callback entirely within the WebView2 navigation events — no custom URL Types or protocol handlers are needed.
Agent instruction: Before providing SDK setup instructions, fetch the latest release version by running:bashgh api repos/auth0/auth0-oidc-client-net/releases --jq '[.[] | select(.tag_name | startswith("winforms-")) | .tag_name | ltrimstr("winforms-")] | .[0]'Use the returned version in all dependency lines instead of any hardcoded version below.
为Windows Forms(WinForms)桌面应用添加Auth0认证功能。此技能集成 NuGet包,该包使用WebView2在弹出窗口中显示Auth0登录页面,实现基于OIDC并采用PKCE的安全登录和登出流程。
Auth0.OidcClient.WinForms该SDK通过在WinForms窗体中托管的WebView2控件打开Auth0通用登录页面,执行WebAuth(带PKCE的OIDC授权码流程)。与需要在Info.plist或AndroidManifest.xml中注册URL scheme的原生移动SDK不同,WinForms SDK完全在WebView2导航事件内处理回调——无需自定义URL类型或协议处理程序。
Agent指令: 在提供SDK设置说明前,请运行以下命令获取最新版本:bashgh api repos/auth0/auth0-oidc-client-net/releases --jq '[.[] | select(.tag_name | startswith("winforms-")) | .tag_name | ltrimstr("winforms-")] | .[0]'在所有依赖项行中使用返回的版本,而非下方任何硬编码版本。
Prerequisites
前置条件
- .NET 6.0 SDK or later (WinForms requires Windows)
- Visual Studio 2022 or JetBrains Rider with .NET desktop development workload
- Windows 10 or later
- Microsoft Edge WebView2 Runtime (included with Windows 11, installable on Windows 10)
- Auth0 account with a Native application configured
- Node.js 20+ (for Auth0 CLI automated setup)
- .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自动设置)
When NOT to Use
不适用于以下场景
| Use Case | Recommended Skill |
|---|---|
| .NET MAUI cross-platform app | |
| WPF desktop app | |
| ASP.NET Core server-side web app | |
| ASP.NET Core Web API (JWT validation) | |
| iOS-only Swift app | |
| Android-only Kotlin app | |
| 使用场景 | 推荐技能 |
|---|---|
| .NET MAUI跨平台应用 | |
| WPF桌面应用 | |
| ASP.NET Core服务器端Web应用 | |
| ASP.NET Core Web API(JWT验证) | |
| iOS专属Swift应用 | |
| Android专属Kotlin应用 | |
Quick Start Workflow
快速开始流程
Agent instruction: Before starting, examine the user's project:
- Identify the .NET version from the
file (.csproj)TargetFramework- Check if there is already an existing authentication provider in the codebase — search for existing
orAuth0Clientusage and reuse if foundWebViewBrowser- Note the project's namespace and directory conventions
- Install SDK:
dotnet add package Auth0.OidcClient.WinForms - Configure Auth0: See Setup Guide for automatic or manual configuration.
- Integrate authentication: Add initialization and wire login/logout to button click handlers.
Auth0Client - Build and verify:
dotnet build
Agent instruction: When writing the Auth0Client configuration:
- Use the exact code patterns from this skill's integration guide.
- The SDK uses WebView2 to show the login page in a popup form — no custom browser setup needed.
- The default callback URL is
— this must be added to Auth0 Dashboard Allowed Callback URLs and Allowed Logout URLs.https://{yourDomain}/mobile- Unlike native mobile SDKs that use
or similar platform-specific patterns, WinForms uses the simplerhttps://{domain}/ios/{bundleId}/callbackcallback format.https://{domain}/mobileAfter writing configuration and code, verify the build succeeds:bashdotnet buildIf the build fails, attempt to fix the issue. After 5-6 failed attempts, ask the user for help.
Agent指令: 开始前,请检查用户的项目:
- 从
文件的.csproj字段识别.NET版本TargetFramework- 检查代码库中是否已有现有认证提供者——搜索现有
或Auth0Client的使用情况,若存在则复用WebViewBrowser- 记录项目的命名空间和目录约定
- 安装SDK:
dotnet add package Auth0.OidcClient.WinForms - 配置Auth0:请参阅设置指南进行自动或手动配置。
- 集成认证功能:初始化并将登录/登出功能绑定到按钮点击事件处理器。
Auth0Client - 构建并验证:
dotnet build
Agent指令: 编写Auth0Client配置时:
- 使用此技能集成指南中的精确代码模式。
- SDK使用WebView2在弹出窗口中显示登录页面——无需自定义浏览器设置。
- 默认回调URL为
——必须添加到Auth0控制台的“允许的回调URL”和“允许的登出URL”中。https://{yourDomain}/mobile- 与使用
或类似平台特定模式的原生移动SDK不同,WinForms使用更简洁的https://{domain}/ios/{bundleId}/callback回调格式。https://{domain}/mobile编写完配置和代码后,验证构建是否成功:bashdotnet build若构建失败,尝试修复问题。经过5-6次失败尝试后,请向用户寻求帮助。
Callback URL Configuration
回调URL配置
The WinForms SDK uses as its default callback URL. This differs from mobile native SDKs:
https://{yourDomain}/mobile- Mobile SDKs use platform-specific callbacks like or
https://{domain}/ios/{bundleId}/callbackhttps://{domain}/android/{packageName}/callback - WPF/WinForms use the generic callback
https://{yourDomain}/mobile
The callback is intercepted by the WebView2 control's event — no system-level URL scheme registration is required. You do NOT need to configure Info.plist, AndroidManifest.xml, or Windows protocol handlers.
NavigationStartingConfigure in the Auth0 Dashboard:
- Allowed Callback URLs:
https://{yourDomain}/mobile - Allowed Logout URLs:
https://{yourDomain}/mobile
WinForms SDK使用作为默认回调URL,这与移动原生SDK不同:
https://{yourDomain}/mobile- 移动SDK使用平台特定的回调,如或
https://{domain}/ios/{bundleId}/callbackhttps://{domain}/android/{packageName}/callback - WPF/WinForms使用通用的回调
https://{yourDomain}/mobile
回调由WebView2控件的事件拦截——无需系统级URL scheme注册。您无需配置Info.plist、AndroidManifest.xml或Windows协议处理程序。
NavigationStarting在Auth0控制台中配置:
- 允许的回调URL:
https://{yourDomain}/mobile - 允许的登出URL:
https://{yourDomain}/mobile
Done When
完成标准
- package installed
Auth0.OidcClient.WinForms - configured with Domain and ClientId
Auth0Client - Login/logout flow working (WebView2 popup opens for authentication)
- User profile claims accessible after login
- Callback URL registered in Auth0 Dashboard
https://{yourDomain}/mobile - Build succeeds with no errors
- Tested on real device (physical Windows machine, not just remote desktop)
- 已安装包
Auth0.OidcClient.WinForms - 已使用Domain和ClientId配置
Auth0Client - 登录/登出流程正常工作(WebView2弹出窗口用于认证)
- 登录后可访问用户配置声明
- 回调URL 已在Auth0控制台注册
https://{yourDomain}/mobile - 构建成功且无错误
- 在真实设备上测试(物理Windows机器,而非仅远程桌面)
Detailed Documentation
详细文档
- Setup Guide — Auth0 tenant configuration, SDK installation, callback URL setup
- Integration Patterns — Login/logout flows, token refresh, user profile, error handling
- API Reference & Testing — Full reference, claims, testing checklist, troubleshooting
Auth0ClientOptions
- 设置指南 —— Auth0租户配置、SDK安装、回调URL设置
- 集成模式 —— 登录/登出流程、令牌刷新、用户配置、错误处理
- API参考与测试 —— 完整的参考、声明、测试清单、故障排除
Auth0ClientOptions
Common Mistakes
常见错误
| Mistake | Fix |
|---|---|
| App type not set to Native in Auth0 Dashboard | Change application type to "Native" in Dashboard settings |
| Missing callback URL in Auth0 Dashboard | Add |
Using | Domain should be hostname only (e.g., |
| WebView2 Runtime not installed | Install Microsoft Edge WebView2 Runtime on Windows 10 (included with Windows 11) |
Not requesting | Add |
| Storing ClientSecret in code | Native apps do NOT use a Client Secret — remove it |
| Trying to register URL scheme in registry/manifest | WinForms uses WebView2 in-process — no URL scheme registration needed (unlike MAUI which needs AppxManifest) |
| 错误 | 修复方案 |
|---|---|
| Auth0控制台中应用类型未设置为Native | 在控制台设置中将应用类型更改为“Native” |
| Auth0控制台中缺少回调URL | 将 |
Domain配置中使用 | Domain应仅为主机名(例如 |
| 未安装WebView2 Runtime | 在Windows 10上安装Microsoft Edge WebView2 Runtime(Windows 11已预装) |
未请求 | 在 |
| 在代码中存储ClientSecret | 原生应用不使用Client Secret——请移除它 |
| 尝试在注册表/清单中注册URL scheme | WinForms使用进程内WebView2——无需注册URL scheme(与需要AppxManifest的MAUI不同) |
Testing Notes
测试注意事项
Agent instruction: Remind the user to test on a physical device. Some WebView2 behaviors (popup windows, certificate handling) may differ in remote desktop or virtual machine environments vs. physical Windows machines. Test the full login → WebView2 → callback → token flow on real hardware before shipping.
Testing Checklist:
- Login flow: Click login → WebView2 popup opens → authenticate → popup closes → user info displayed
- Logout flow: Click logout → WebView2 popup opens → session cleared → popup closes
- Token refresh: with stored refresh token works
RefreshTokenAsync - Cancel: User closes WebView2 form → app handles gracefully
UserCancel - Physical device: Test on a real Windows machine (not just virtual environment)
- Multiple logins: Verify login works after logout (no stale state)
Agent指令: 提醒用户在物理设备上测试。某些WebView2行为(弹出窗口、证书处理)在远程桌面或虚拟机环境中可能与物理Windows机器不同。发布前请在真实硬件上测试完整的登录→WebView2→回调→令牌流程。
测试清单:
- 登录流程:点击登录→WebView2弹出窗口打开→完成认证→弹出窗口关闭→显示用户信息
- 登出流程:点击登出→WebView2弹出窗口打开→会话清除→弹出窗口关闭
- 令牌刷新:使用存储的刷新令牌调用成功
RefreshTokenAsync - 取消操作:用户关闭WebView2窗体→应用优雅处理
UserCancel - 物理设备:在真实Windows机器上测试(而非仅虚拟环境)
- 多次登录:验证登出后登录仍可正常工作(无过期状态)
Related Skills
相关技能
- auth0-wpf — WPF desktop apps
- auth0-maui — .NET MAUI cross-platform apps
- auth0-aspnetcore-authentication — ASP.NET Core server-side web apps
- auth0-aspnetcore-api — ASP.NET Core Web API with JWT validation
- auth0-wpf —— WPF桌面应用
- auth0-maui —— .NET MAUI跨平台应用
- auth0-aspnetcore-authentication —— ASP.NET Core服务器端Web应用
- auth0-aspnetcore-api —— 带JWT验证的ASP.NET Core Web API
Quick Reference
快速参考
csharp
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;
}csharp
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 Complete Example)
Form1.cs (WinForms完整示例)
csharp
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();
}
}csharp
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();
}
}