Azure AD B2C登录时SignInManager.GetExternalLoginInfoAsync返回NULL问题
Azure AD B2C认证时SignInManager.GetExternalLoginInfoAsync()返回NULL的解决办法
问题背景
基于.NET 7的Web应用使用Microsoft.AspNetCore.Identity管理用户,通过Microsoft Identity Web和Microsoft Identity Web UI实现微软账号与Azure AD B2C双认证。微软账号认证正常,但Azure AD B2C认证时,SignInManager.GetExternalLoginInfoAsync()始终返回NULL,尝试设置IdentityConstants.ExternalScheme后问题仍存在。
现有配置
appsettings.json
{ "DetailedErrors": true, "Logging": { "LogLevel": { "Default": "Error", "Microsoft.AspNetCore": "Error" } }, "Microsoft": { "AppId": "<client-id>", "AppSecret": "<client-secret", "AuthEndPoint": "https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/authorize", "TokanEndPoint": "https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/token" }, "B2C": { "Instance": "https://<domain-id>.b2clogin.com", "ClientId": "<client-id>", "Domain": "<domain-id>.onmicrosoft.com", "SignedOutCallbackPath": "/signout/<b2c_policy_id>", "SignUpSignInPolicyId": "<b2c_policy_id>" }, "AllowedHosts": "*" }
Program.cs
using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; using Microsoft.Identity.Web; using Portal.Interface.Web.Data; WebApplicationBuilder builder = WebApplication.CreateBuilder(args); string connectionString = builder.Configuration.GetConnectionString("DefaultConnection") ?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found."); builder.Services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(connectionString)); builder.Services.AddDatabaseDeveloperPageExceptionFilter(); builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = false) .AddEntityFrameworkStores<ApplicationDbContext>(); builder.Services.AddAuthentication() .AddMicrosoftAccount("Microsoft", "Microsoft", micorosoftOptions => { micorosoftOptions.AuthorizationEndpoint = builder.Configuration.GetSection("Microsoft").GetValue<string>("AuthEndPoint"); micorosoftOptions.TokenEndpoint = builder.Configuration.GetSection("Microsoft").GetValue<string>("TokanEndPoint"); micorosoftOptions.ClientId = builder.Configuration.GetSection("Microsoft").GetValue<string>("AppId"); micorosoftOptions.ClientSecret = builder.Configuration.GetSection("Microsoft").GetValue<string>("AppSecret"); micorosoftOptions.AccessDeniedPath = "/Identity/Account/AccessDenied"; }) .AddMicrosoftIdentityWebApp(azureOptions => { azureOptions.SignInScheme = IdentityConstants.ExternalScheme; azureOptions.Instance = builder.Configuration.GetSection("B2C").GetValue<string>("Instance"); azureOptions.ClientId = builder.Configuration.GetSection("B2C").GetValue<string>("ClientId"); azureOptions.Domain = builder.Configuration.GetSection("B2C").GetValue<string>("Domain"); azureOptions.SignedOutCallbackPath = builder.Configuration.GetSection("B2C").GetValue<string>("SignedOutCallbackPath"); azureOptions.SignUpSignInPolicyId = builder.Configuration.GetSection("B2C").GetValue<string>("SignUpSignInPolicyId"); }); builder.Services.AddRazorPages(); builder.Services.AddAntiforgery(a => a.HeaderName = "XSRF-TOKEN"); WebApplication app = builder.Build(); if (app.Environment.IsDevelopment()) app.UseMigrationsEndPoint(); else { app.UseExceptionHandler("/Error"); app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); app.UseAuthorization(); app.MapRazorPages(); app.Run();
回调端点代码
public async Task<IActionResult> OnGetCallbackAsync(string returnUrl = null, string remoteError = null) { returnUrl = returnUrl ?? Url.Content("~/~"); if (remoteError != null) { ErrorMessage = $"Error from external provider: {remoteError}"; return RedirectToPage("./Login", new { ReturnUrl = returnUrl }); } var info = await _signInManager.GetExternalLoginInfoAsync(); //Returns null. if (info == null) { ErrorMessage = "Error loading external login information."; return RedirectToPage("./Login", new { ReturnUrl = returnUrl }); } //Code omitted... }
解决措施
1. 补全认证中间件并调整顺序
现有代码缺少app.UseAuthentication()中间件,这是触发认证流程的关键,必须放在UseAuthorization之前:
app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); // 新增该行,确保认证中间件优先执行 app.UseAuthentication(); app.UseAuthorization(); app.MapRazorPages(); app.Run();
2. 统一认证方案配置
显式指定默认认证方案,同时确保两个外部提供者都使用IdentityConstants.ExternalScheme作为登录方案,避免冲突:
builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = false) .AddEntityFrameworkStores<ApplicationDbContext>(); // 显式设置默认认证方案 builder.Services.AddAuthentication(options => { options.DefaultAuthenticateScheme = IdentityConstants.ApplicationScheme; options.DefaultSignInScheme = IdentityConstants.ApplicationScheme; options.DefaultChallengeScheme = IdentityConstants.ApplicationScheme; }) .AddMicrosoftAccount("Microsoft", "Microsoft", micorosoftOptions => { micorosoftOptions.AuthorizationEndpoint = builder.Configuration.GetSection("Microsoft").GetValue<string>("AuthEndPoint"); micorosoftOptions.TokenEndpoint = builder.Configuration.GetSection("Microsoft").GetValue<string>("TokanEndPoint"); micorosoftOptions.ClientId = builder.Configuration.GetSection("Microsoft").GetValue<string>("AppId"); micorosoftOptions.ClientSecret = builder.Configuration.GetSection("Microsoft").GetValue<string>("AppSecret"); micorosoftOptions.AccessDeniedPath = "/Identity/Account/AccessDenied"; // 给微软账号认证添加外部登录方案配置 micorosoftOptions.SignInScheme = IdentityConstants.ExternalScheme; }) .AddMicrosoftIdentityWebApp(azureOptions => { // 使用配置绑定简化代码,减少手动取值错误 builder.Configuration.Bind("B2C", azureOptions); azureOptions.SignInScheme = IdentityConstants.ExternalScheme; });
3. 验证Azure AD B2C门户配置
- 检查应用注册的回复URL是否与应用默认回调路径(
/signin-oidc)一致,若自定义回调路径需同步更新 - 确认注销URL与
SignedOutCallbackPath配置匹配
4. 开启调试日志排查细节
调整日志级别为Debug,查看认证流程中的详细错误:
"Logging": { "LogLevel": { "Default": "Debug", "Microsoft.AspNetCore.Authentication": "Debug", "Microsoft.Identity.Web": "Debug" } }
通过日志可查看外部认证是否成功获取令牌、Cookie生成与存储过程是否存在异常。
内容的提问来源于stack exchange,提问作者Razack Mahomed
相关产品推荐
相关产品推荐

