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

如何为Teams个人标签的.NET Core项目配置Azure AD身份验证

解决Teams个人标签页中.NET Core MVC的身份验证问题

我来帮你捋清楚这个问题——Teams iframe环境下的身份验证确实有特殊限制,你遇到的核心问题是弹窗认证后的会话没有同步回主页面的iframe,导致后端的[Authorize]特性无法识别已登录状态。咱们一步步来修复:

1. 先调整Cookie配置(关键!)

Teams在iframe中运行时,浏览器对Cookie的SameSite和Secure属性有严格要求,你当前的配置缺少了强制Secure的设置,这会导致认证Cookie无法在iframe中正常传递。修改Startup.cs中的Cookie配置:

services.Configure<CookiePolicyOptions>(options =>
{
    options.CheckConsentNeeded = context => false;
    options.MinimumSameSitePolicy = SameSiteMode.None;
    options.Secure = CookieSecurePolicy.Always; // 必须设置,Teams环境全是HTTPS
});

// 补充Cookie认证的详细配置(AddAzureAD默认依赖Cookie方案)
services.AddAuthentication(AzureADDefaults.AuthenticationScheme)
    .AddAzureAD(options => Configuration.Bind("AzureAd", options))
    .AddCookie(options =>
    {
        options.Cookie.SameSite = SameSiteMode.None;
        options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
        options.Cookie.HttpOnly = true; // 防止XSS
        options.Events = new CookieAuthenticationEvents
        {
            // 可选:处理Cookie验证失败的情况
            OnRedirectToLogin = context =>
            {
                // 如果是Teams环境,不要直接跳转,而是触发弹窗认证
                if (context.Request.Headers.ContainsKey("X-Teams-Factor"))
                {
                    context.Response.StatusCode = 401;
                    return Task.CompletedTask;
                }
                context.Response.Redirect(context.RedirectUri);
                return Task.CompletedTask;
            }
        };
    });

2. 完善OpenID Connect的回调逻辑

需要在认证成功后把会话标识暂存到缓存,方便后续主页面同步状态:

services.Configure<OpenIdConnectOptions>(AzureADDefaults.OpenIdScheme, options =>
{
    options.Authority = options.Authority + "/v2.0/";
    options.TokenValidationParameters.ValidateIssuer = false;
    options.CallbackPath = "/signin-oidc"; // 和Azure AD注册的回调地址一致

    // 授权码接收后,把Token信息存入缓存
    options.Events.OnAuthorizationCodeReceived = async context =>
    {
        var cache = context.HttpContext.RequestServices.GetRequiredService<IDistributedCache>();
        // 用state作为缓存key,对应前端传递的会话标识
        await cache.SetStringAsync(
            context.ProtocolMessage.State,
            JsonSerializer.Serialize(context.TokenEndpointResponse),
            new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(5) }
        );
    };
});

// 别忘了添加缓存和HttpContext访问服务
services.AddHttpContextAccessor();
services.AddDistributedMemoryCache(); // 开发环境用,生产环境建议换Redis

3. 新增Teams认证回调页面

认证弹窗完成后,需要通过Teams SDK把会话标识传回主页面,新建一个TeamsAuthCallback.cshtml页面:

@{ Layout = null; }
<script src="https://statics.teams.cdn.office.net/sdk/v1.11.0/js/MicrosoftTeams.min.js"></script>
<script>
    microsoftTeams.initialize();
    // 从URL中获取state参数,这是我们之前存在缓存的会话标识
    const state = new URLSearchParams(window.location.search).get('state');
    // 通知主页面认证成功,传递state
    microsoftTeams.authentication.notifySuccess(state);
</script>

4. 修改前端JS的认证流程

之前的代码只拿到了token但没同步到后端会话,现在要在成功回调后调用后端接口完成登录:

function login(url) {
    hideProfileAndError();
    // 生成随机state标识本次认证会话
    const authState = Math.random().toString(36).slice(2) + Math.random().toString(36).slice(2);
    localStorage.setItem('tempAuthState', authState);
    
    // 构造带state和回调地址的认证URL
    const fullAuthUrl = `${url}?state=${authState}&redirect_uri=${encodeURIComponent('https://你的域名/TeamsAuthCallback')}`;
    
    microsoftTeams.authentication.authenticate({
        url: fullAuthUrl,
        width: 600,
        height: 535,
        successCallback: async function (result) {
            // result是回调页面传回的state
            const response = await fetch('/api/CompleteAuth', {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ state: result })
            });
            if (response.ok) {
                // 认证会话已同步,现在可以调用需要授权的接口
                getUserProfile();
            } else {
                handleAuthError('会话同步失败');
            }
        },
        failureCallback: function (reason) {
            console.log("Login failed: " + reason);
            handleAuthError(reason);
        }
    });
}

5. 新增完成认证的API控制器

这个控制器负责从缓存中取出Token信息,生成认证会话:

[ApiController]
[Route("api/[controller]")]
public class CompleteAuthController : ControllerBase
{
    private readonly IDistributedCache _cache;
    private readonly IHttpContextAccessor _httpContextAccessor;

    public CompleteAuthController(IDistributedCache cache, IHttpContextAccessor httpContextAccessor)
    {
        _cache = cache;
        _httpContextAccessor = httpContextAccessor;
    }

    [HttpPost]
    public async Task<IActionResult> Post([FromBody] AuthStateModel model)
    {
        // 从缓存中取出Token数据
        var tokenDataStr = await _cache.GetStringAsync(model.State);
        if (string.IsNullOrEmpty(tokenDataStr)) return BadRequest("无效的会话标识");
        
        var tokenData = JsonSerializer.Deserialize<TokenEndpointResponse>(tokenDataStr);
        // 解析ID Token生成用户身份
        var jwtHandler = new JwtSecurityTokenHandler();
        var jwtToken = jwtHandler.ReadJwtToken(tokenData.IdToken);
        var claimsIdentity = new ClaimsIdentity(jwtToken.Claims, AzureADDefaults.AuthenticationScheme);
        
        // 登录用户,设置认证Cookie
        await _httpContextAccessor.HttpContext.SignInAsync(
            AzureADDefaults.AuthenticationScheme,
            new ClaimsPrincipal(claimsIdentity),
            new AuthenticationProperties { IsPersistent = true, ExpiresUtc = DateTimeOffset.UtcNow.AddHours(1) }
        );
        
        // 清理缓存
        await _cache.RemoveAsync(model.State);
        return Ok();
    }
}

public class AuthStateModel { public string State { get; set; } }

6. 检查Azure AD应用注册配置

确保你在Azure AD中注册的应用包含以下两个回调地址:

  • https://你的域名/signin-oidc(OpenID Connect默认回调)
  • https://你的域名/TeamsAuthCallback(Teams弹窗回调)

另外,API权限要至少添加User.Read(委托权限),并完成管理员同意(如果是租户级应用)。

最后验证逻辑

现在的流程是:

  1. 主页面调用login,触发Teams认证弹窗
  2. 用户在弹窗中完成Azure AD登录,跳转到TeamsAuthCallback
  3. 回调页面通过Teams SDK把state传回主页面
  4. 主页面调用/api/CompleteAuth,后端从缓存取出Token并生成认证会话
  5. 后续请求携带认证Cookie,[Authorize]特性就能正常识别用户

这样就能解决iframe中无法传递认证会话的问题了,非Teams环境的逻辑不受影响,因为常规跳转认证会直接设置Cookie。

内容的提问来源于stack exchange,提问作者Ionut Capusneanu

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.08 21:48:13