You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

多租户Azure AD认证问题:.NET 7 Web App无法验证外部租户用户

问题:实现支持任意Azure AD租户用户的.NET 7 Web应用SSO认证

问题背景

我有一个.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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 06:14:52