auth0-winforms

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Auth0 WinForms Integration

Auth0 WinForms 集成

Add Auth0 authentication to Windows Forms (WinForms) desktop applications. This skill integrates the
Auth0.OidcClient.WinForms
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.
The 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:
bash
gh 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认证功能。此技能集成
Auth0.OidcClient.WinForms
NuGet包,该包使用WebView2在弹出窗口中显示Auth0登录页面,实现基于OIDC并采用PKCE的安全登录和登出流程。
该SDK通过在WinForms窗体中托管的WebView2控件打开Auth0通用登录页面,执行WebAuth(带PKCE的OIDC授权码流程)。与需要在Info.plist或AndroidManifest.xml中注册URL scheme的原生移动SDK不同,WinForms SDK完全在WebView2导航事件内处理回调——无需自定义URL类型或协议处理程序。
Agent指令: 在提供SDK设置说明前,请运行以下命令获取最新版本:
bash
gh 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 CaseRecommended Skill
.NET MAUI cross-platform app
auth0-maui
WPF desktop app
auth0-wpf
ASP.NET Core server-side web app
auth0-aspnetcore-authentication
ASP.NET Core Web API (JWT validation)
auth0-aspnetcore-api
iOS-only Swift app
auth0-swift
Android-only Kotlin app
auth0-android
使用场景推荐技能
.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

Quick Start Workflow

快速开始流程

Agent instruction: Before starting, examine the user's project:
  1. Identify the .NET version from the
    .csproj
    file (
    TargetFramework
    )
  2. Check if there is already an existing authentication provider in the codebase — search for existing
    Auth0Client
    or
    WebViewBrowser
    usage and reuse if found
  3. Note the project's namespace and directory conventions
  1. Install SDK:
    dotnet add package Auth0.OidcClient.WinForms
  2. Configure Auth0: See Setup Guide for automatic or manual configuration.
  3. Integrate authentication: Add
    Auth0Client
    initialization and wire login/logout to button click handlers.
  4. 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
    https://{yourDomain}/mobile
    — this must be added to Auth0 Dashboard Allowed Callback URLs and Allowed Logout URLs.
  • Unlike native mobile SDKs that use
    https://{domain}/ios/{bundleId}/callback
    or similar platform-specific patterns, WinForms uses the simpler
    https://{domain}/mobile
    callback format.
After writing configuration and code, verify the build succeeds:
bash
dotnet build
If the build fails, attempt to fix the issue. After 5-6 failed attempts, ask the user for help.
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控制台的“允许的回调URL”和“允许的登出URL”中。
  • 与使用
    https://{domain}/ios/{bundleId}/callback
    或类似平台特定模式的原生移动SDK不同,WinForms使用更简洁的
    https://{domain}/mobile
    回调格式。
编写完配置和代码后,验证构建是否成功:
bash
dotnet build
若构建失败,尝试修复问题。经过5-6次失败尝试后,请向用户寻求帮助。

Callback URL Configuration

回调URL配置

The WinForms SDK uses
https://{yourDomain}/mobile
as its default callback URL. This differs from mobile native SDKs:
  • Mobile SDKs use platform-specific callbacks like
    https://{domain}/ios/{bundleId}/callback
    or
    https://{domain}/android/{packageName}/callback
  • WPF/WinForms use the generic
    https://{yourDomain}/mobile
    callback
The callback is intercepted by the WebView2 control's
NavigationStarting
event — no system-level URL scheme registration is required. You do NOT need to configure Info.plist, AndroidManifest.xml, or Windows protocol handlers.
Configure in the Auth0 Dashboard:
  • Allowed Callback URLs:
    https://{yourDomain}/mobile
  • Allowed Logout URLs:
    https://{yourDomain}/mobile
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 scheme注册。您无需配置Info.plist、AndroidManifest.xml或Windows协议处理程序。
在Auth0控制台中配置:
  • 允许的回调URL
    https://{yourDomain}/mobile
  • 允许的登出URL
    https://{yourDomain}/mobile

Done When

完成标准

  • Auth0.OidcClient.WinForms
    package installed
  • Auth0Client
    configured with Domain and ClientId
  • Login/logout flow working (WebView2 popup opens for authentication)
  • User profile claims accessible after login
  • Callback URL
    https://{yourDomain}/mobile
    registered in Auth0 Dashboard
  • Build succeeds with no errors
  • Tested on real device (physical Windows machine, not just remote desktop)
  • 已安装
    Auth0.OidcClient.WinForms
  • 已使用Domain和ClientId配置
    Auth0Client
  • 登录/登出流程正常工作(WebView2弹出窗口用于认证)
  • 登录后可访问用户配置声明
  • 回调URL
    https://{yourDomain}/mobile
    已在Auth0控制台注册
  • 构建成功且无错误
  • 在真实设备上测试(物理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
    Auth0ClientOptions
    reference, claims, testing checklist, troubleshooting
  • 设置指南 —— Auth0租户配置、SDK安装、回调URL设置
  • 集成模式 —— 登录/登出流程、令牌刷新、用户配置、错误处理
  • API参考与测试 —— 完整的
    Auth0ClientOptions
    参考、声明、测试清单、故障排除

Common Mistakes

常见错误

MistakeFix
App type not set to Native in Auth0 DashboardChange application type to "Native" in Dashboard settings
Missing callback URL in Auth0 DashboardAdd
https://{yourDomain}/mobile
to both Allowed Callback URLs AND Allowed Logout URLs
Using
https://
prefix in Domain config
Domain should be hostname only (e.g.,
tenant.auth0.com
, not
https://tenant.auth0.com
)
WebView2 Runtime not installedInstall Microsoft Edge WebView2 Runtime on Windows 10 (included with Windows 11)
Not requesting
offline_access
scope for token refresh
Add
offline_access
to
Scope
in
Auth0ClientOptions
Storing ClientSecret in codeNative apps do NOT use a Client Secret — remove it
Trying to register URL scheme in registry/manifestWinForms uses WebView2 in-process — no URL scheme registration needed (unlike MAUI which needs AppxManifest)
错误修复方案
Auth0控制台中应用类型未设置为Native在控制台设置中将应用类型更改为“Native”
Auth0控制台中缺少回调URL
https://{yourDomain}/mobile
添加到“允许的回调URL”和“允许的登出URL”中
Domain配置中使用
https://
前缀
Domain应仅为主机名(例如
tenant.auth0.com
,而非
https://tenant.auth0.com
未安装WebView2 Runtime在Windows 10上安装Microsoft Edge WebView2 Runtime(Windows 11已预装)
未请求
offline_access
范围以进行令牌刷新
Auth0ClientOptions
Scope
中添加
offline_access
在代码中存储ClientSecret原生应用使用Client Secret——请移除它
尝试在注册表/清单中注册URL schemeWinForms使用进程内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:
    RefreshTokenAsync
    with stored refresh token works
  • Cancel: User closes WebView2 form → app handles
    UserCancel
    gracefully
  • 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();
    }
}

References

参考链接