.NET 7 MAUI对接本地Duende IdentityServer测试认证问题求助
.NET 7 MAUI对接Duende IdentityServer本地测试问题解决方案
问题概述
开发.NET 7 MAUI应用对接本地Duende IdentityServer(v6.2.3)时,本地测试遇到三个核心问题:
- 使用ngrok转发IDP时,发现文档端点仍指向localhost,与ngrok域名不匹配,触发主机错误:
System.InvalidOperationException: 'Error loading discovery document: Endpoint is on a different host than authority: https://localhost:5001/.well-known/openid-configuration/jwks' - Android模拟器访问
https://10.0.2.2时,出现证书信任错误:System.InvalidOperationException: 'Error loading discovery document: Error connecting to https://10.0.2.2/.well-known/openid-configuration. java.security.cert.CertPathValidatorException: Trust anchor for certification path not found..' - IDP切换为HTTP模式后,客户端提示HTTPS强制要求:
System.InvalidOperationException: 'Error loading discovery document: Error connecting to http://10.0.2.2/.well-known/openid-configuration. HTTPS required.'
关键代码参考
IDP Program.cs
builder.Services.AddIdentityServer(options => { options.EmitStaticAudienceClaim = true; }) .AddInMemoryIdentityResources(Config.IdentityResources) .AddInMemoryApiScopes(Config.ApiScopes) .AddInMemoryClients(Config.Clients) .AddTestUsers(TestUsers.Users);
IDP Config类
using Duende.IdentityServer; using Duende.IdentityServer.Models; namespace MyApp.IDP; public static class Config { public static IEnumerable<IdentityResource> IdentityResources => new IdentityResource[] { new IdentityResources.OpenId(), new IdentityResources.Profile() }; public static IEnumerable<ApiScope> ApiScopes => new ApiScope[] { }; public static IEnumerable<Client> Clients => new Client[] { new Client() { ClientName = "My App Mobile", ClientId = "myappmobile.client", AllowedGrantTypes = GrantTypes.Code, RedirectUris = { "myapp://callback" }, PostLogoutRedirectUris = { "myapp://callback" }, AllowedScopes = new List<string> { IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants.StandardScopes.Profile } } }; }
MAUI客户端配置
var options = new OidcClientOptions { Authority = "https://10.0.2.2", ClientId = "myappmobile.client", RedirectUri = "myapp://callback", Browser = new MauiAuthenticationBrowser() }; builder.Services.AddSingleton(new OidcClient(options));
MauiAuthenticationBrowser类
using IdentityModel.Client; using IdentityModel.OidcClient.Browser; namespace MyFirstAuth; public class MauiAuthenticationBrowser : IdentityModel.OidcClient.Browser.IBrowser { public async Task<BrowserResult> InvokeAsync(BrowserOptions options, CancellationToken cancellationToken = default) { try { var result = await WebAuthenticator.Default.AuthenticateAsync( new Uri(options.StartUrl), new Uri(options.EndUrl)); var url = new RequestUrl("myapp://callback") .Create(new Parameters(result.Properties)); return new BrowserResult { Response = url, ResultType = BrowserResultType.Success }; } catch (TaskCanceledException) { return new BrowserResult { ResultType = BrowserResultType.UserCancel }; } } }
MAUI MainPage代码
using IdentityModel.OidcClient; namespace MyFirstAuth; public partial class MainPage { private readonly OidcClient _client; public MainPage(OidcClient client) { InitializeComponent(); _client = client; } private async void OnLoginClicked(object sender, EventArgs e) { var result = await _client.LoginAsync(); if (result.IsError) { editor.Text = result.Error; return; } editor.Text = "Success!"; } }
针对性解决方案
1. 解决ngrok主机不匹配问题
IdentityServer默认根据当前请求主机生成发现文档端点,ngrok转发时需强制指定PublicOrigin确保端点域名一致:
// 修改IDP Program.cs builder.Services.AddIdentityServer(options => { options.EmitStaticAudienceClaim = true; // 替换为你的ngrok域名,例如https://abc123.ngrok.io options.PublicOrigin = "https://your-ngrok-domain.ngrok.io"; }) .AddInMemoryIdentityResources(Config.IdentityResources) .AddInMemoryApiScopes(Config.ApiScopes) .AddInMemoryClients(Config.Clients) .AddTestUsers(TestUsers.Users); // 添加转发头中间件,确保IdentityServer识别外部请求 app.UseForwardedHeaders();
同时更新MAUI客户端的Authority为ngrok域名。
2. 解决Android本地证书信任问题
方案A:让Android信任开发证书(推荐测试用)
- 导出本地开发证书:从
%USERPROFILE%\.aspnet\https目录找到IDP项目的.pfx文件,导出为.crt格式。 - 将.crt文件放入MAUI项目的
Platforms/Android/Resources/raw目录(无则新建)。 - 在
Platforms/Android/MainApplication.cs中添加证书信任逻辑:
using Android.App; using Android.Runtime; using System.Security.Cryptography.X509Certificates; using Android.Net.Http; namespace YourAppNamespace; [Application] public class MainApplication : MauiApplication { public MainApplication(IntPtr handle, JniHandleOwnership ownership) : base(handle, ownership) { } protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); public override void OnCreate() { base.OnCreate(); InstallLocalCertificate(); } private void InstallLocalCertificate() { try { // 替换为你的证书文件名(不含.crt后缀) using var stream = Resources.OpenRawResource(Resource.Raw.your_local_cert); var certFactory = X509CertificateFactory.GetInstance("X.509"); var cert = certFactory.GenerateCertificate(stream); var keyStore = KeyStore.GetInstance(KeyStore.DefaultType); keyStore.Load(null); keyStore.SetCertificateEntry("local_dev_cert", cert); var trustManagerFactory = TrustManagerFactory.GetInstance(TrustManagerFactory.DefaultAlgorithm); trustManagerFactory.Init(keyStore); var sslContext = SSLContext.GetInstance("TLS"); sslContext.Init(null, trustManagerFactory.GetTrustManagers(), new Java.Security.SecureRandom()); HttpsURLConnection.DefaultSSLSocketFactory = sslContext.SocketFactory; HttpsURLConnection.DefaultHostnameVerifier = new AcceptAllHostnameVerifier(); } catch (Exception ex) { // 记录错误日志 } } } public class AcceptAllHostnameVerifier : Java.Lang.Object, IHostnameVerifier { public bool Verify(string hostname, SSLSession session) => true; }
方案B:绕过证书检查(仅临时测试)
为OidcClient配置自定义HttpHandler跳过证书验证:
// 修改MAUI客户端配置 var handler = new HttpClientHandler { ServerCertificateCustomValidationCallback = (msg, cert, chain, errors) => true }; var options = new OidcClientOptions { Authority = "https://10.0.2.2", ClientId = "myappmobile.client", RedirectUri = "myapp://callback", Browser = new MauiAuthenticationBrowser(), BackchannelHandler = handler }; builder.Services.AddSingleton(new OidcClient(options));
⚠️ 此方法仅适用于本地测试,生产环境绝对禁止使用。
3. 解决HTTP模式下HTTPS强制要求问题
要在测试环境启用HTTP,需同时修改IDP和客户端配置:
IDP端修改(Program.cs)
builder.Services.AddIdentityServer(options => { options.EmitStaticAudienceClaim = true; options.IssuerUri = "http://10.0.2.2:5000"; options.KeyManagement.Enabled = false; // 禁用密钥管理,避免HTTPS依赖 }) .AddInMemoryIdentityResources(Config.IdentityResources) .AddInMemoryApiScopes(Config.ApiScopes) .AddInMemoryClients(Config.Clients) .AddTestUsers(TestUsers.Users); // 监听HTTP端口 app.Run("http://0.0.0.0:5000");
MAUI客户端修改
var options = new OidcClientOptions { Authority = "http://10.0.2.2:5000", ClientId = "myappmobile.client", RedirectUri = "myapp://callback", Browser = new MauiAuthenticationBrowser(), Policy = new OidcClientPolicy { AllowUnsafeNonHttps = true // 允许HTTP权威,仅测试用 } };
⚠️ 生产环境必须使用HTTPS,此配置仅用于本地测试。
内容的提问来源于stack exchange,提问作者Dave Barnett
相关产品推荐
相关产品推荐

