多租户Azure AD认证问题:.NET 7 Web App无法验证外部租户用户
问题背景
我有一个.NET 7.0 Web应用,希望通过SSO实现任意Azure AD租户用户的身份认证,认证后获取用户的邮箱地址和/或唯一用户ID。
已在Azure门户的Azure Active Directory中创建新的应用注册,关联了MPN发布者账户,尝试过「多租户」和「任何Microsoft账户」两种支持的账户类型。
自身租户用户可成功验证,但外部租户用户认证时抛出错误:
AADSTS50020: User account 'someuser@foreigndomain.com' from identity provider 'https://sts.windows.net/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/' does not exist in tenant 'mydomain.com' and cannot access the application 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'(myapp) in that tenant. The account needs to be added as an external user in the tenant first. Sign out and sign in again with a different Azure Active Directory user account.
不想在自身租户中添加外部用户(用户数量多且无法提前知晓),求实现支持外部域用户认证的方法。
当前代码配置
Program.cs
using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Authentication.OpenIdConnect; // nuget: Microsoft.Identity.Web using Microsoft.AspNetCore.Authorization; using Microsoft.Identity.Web; // nuget: Microsoft.Identity.Web using System.Security.Claims; var builder = WebApplication.CreateBuilder(args); var authenticationBuilder = builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme); authenticationBuilder.AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd")); builder.Services.AddAuthorization(); var app = builder.Build(); app.UseAuthentication(); app.MapGet("/", (ClaimsPrincipal user, HttpResponse response) => { response.ContentType = "text/html"; if (user.Identity?.IsAuthenticated ?? false) return $"Hello {user.GetDisplayName()}. <a href='/logout'>Logout</a>."; else return $"Hello. You are not authenticated. Please <a href='login'>log in</a>."; }); app.MapGet("/logout", async context => { await context.SignOutAsync(OpenIdConnectDefaults.AuthenticationScheme); await context.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme); }); app.MapGet("/login", [Authorize] (ClaimsPrincipal user, HttpResponse response) => response.Redirect("/")); app.UseRouting(); app.UseAuthorization(); app.Run();
appsettings.json
{ "AzureAd": { "Instance": "https://login.microsoftonline.com/", "Domain": "mydomain.com", "ClientId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", "TenantId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", "CallbackPath": "/signin-oidc" }, "AllowedHosts": "*" }
解决方案
1. 修正核心配置:租户ID设置
你的应用注册虽已选择多租户,但配置文件中TenantId仍指定了自身租户ID,这是导致外部用户无法认证的核心原因。对于多租户应用,需将TenantId改为以下值:
common:支持所有Azure AD租户用户+个人Microsoft账户(如Skype、Xbox)organizations:仅支持Azure AD租户用户(不含个人账户)
2. 更新appsettings.json
修改AzureAd配置段:
{ "AzureAd": { "Instance": "https://login.microsoftonline.com/", "Domain": "mydomain.com", "ClientId": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", "TenantId": "common", // 或 "organizations" "CallbackPath": "/signin-oidc" }, "AllowedHosts": "*" }
3. 优化认证配置(可选)
若需更精细控制多租户逻辑,可扩展OpenID Connect配置,确保使用Microsoft Identity Platform v2.0端点:
authenticationBuilder.AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd")) .EnableTokenAcquisitionToCallDownstreamApi() .AddInMemoryTokenCaches(); // 配置OpenID Connect选项 builder.Services.Configure<OpenIdConnectOptions>(OpenIdConnectDefaults.AuthenticationScheme, options => { options.Authority = options.Authority + "/v2.0/"; // 启用v2.0端点,增强多租户支持 options.TokenValidationParameters.IssuerValidator = AadIssuerValidator.GetIssuerValidator(options.Authority).Validate; });
4. 获取用户邮箱与唯一ID
认证成功后,可通过ClaimsPrincipal或Microsoft.Identity.Web扩展方法获取用户信息:
- 唯一用户ID:
user.GetObjectId()或user.FindFirstValue(ClaimTypes.NameIdentifier) - 邮箱地址:
user.GetEmail()或user.FindFirstValue(ClaimTypes.Email)
修改首页示例代码:
app.MapGet("/", (ClaimsPrincipal user, HttpResponse response) => { response.ContentType = "text/html"; if (user.Identity?.IsAuthenticated ?? false) { var userId = user.GetObjectId(); var email = user.GetEmail(); return $"Hello {user.GetDisplayName()}.<br>用户ID: {userId}<br>邮箱: {email}<br><a href='/logout'>Logout</a>."; } else { return $"Hello. You are not authenticated. Please <a href='login'>log in</a>."; } });
5. 确认应用注册的账户类型
确保Azure门户中应用注册的「支持的账户类型」与配置匹配:
- 选「任何组织目录中的账户(任何Azure AD目录 - 多租户)」对应
organizations - 选「任何组织目录中的账户和个人Microsoft账户」对应
common
内容的提问来源于stack exchange,提问作者nl-x

