ASP.NET Core站点实现Teams标签页与浏览器Azure AD双认证问询
实现ASP.NET Core站点在浏览器与Microsoft Teams标签页的双端兼容认证
核心思路
要同时支持浏览器端Azure AD OIDC认证和Teams标签页SSO认证,核心是区分请求来源,为两种场景分别配置认证逻辑:
- 普通浏览器:沿用原有Azure AD OIDC流程完成登录
- Teams标签页:接收前端通过
microsoftTeams.authentication.getAuthToken()获取的JWT令牌,后端验证令牌有效性
1. 后端认证配置(Program.cs)
保留原有OIDC认证的同时,添加JWT Bearer认证以支持Teams令牌验证,通过授权策略允许两种认证方式共存:
var builder = WebApplication.CreateBuilder(args); // 配置双认证方案:Cookie+OIDC(浏览器)、JWT Bearer(Teams) builder.Services.AddAuthentication(options => { options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme; }) .AddCookie() .AddOpenIdConnect(options => { // 绑定appsettings.json中的AzureAd配置 builder.Configuration.Bind("AzureAd", options); options.ResponseType = "code"; options.Scope.Add("openid"); options.Scope.Add("profile"); options.Scope.Add("email"); }) .AddJwtBearer("TeamsBearer", options => { options.Authority = $"{builder.Configuration["AzureAd:Instance"]}{builder.Configuration["AzureAd:TenantId"]}/v2.0"; options.Audience = builder.Configuration["AzureAd:ClientId"]; options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidIssuer = $"https://sts.windows.net/{builder.Configuration["AzureAd:TenantId"]}/", ValidateLifetime = true, ClockSkew = TimeSpan.FromMinutes(5) }; }); // 配置默认授权策略,允许两种认证方式 builder.Services.AddAuthorization(options => { options.DefaultPolicy = new AuthorizationPolicyBuilder( CookieAuthenticationDefaults.AuthenticationScheme, "TeamsBearer") .RequireAuthenticatedUser() .Build(); }); builder.Services.AddControllersWithViews(); var app = builder.Build(); // 中间件配置 if (!app.Environment.IsDevelopment()) { app.UseExceptionHandler("/Home/Error"); app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); app.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}"); app.Run();
2. 前端适配逻辑
页面加载时先判断是否处于Teams环境,自动切换认证方式:
<!-- 引入Teams SDK和JWT解析库 --> <script src="https://statics.teams.cdn.office.net/sdk/v1.11.0/js/MicrosoftTeams.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/jwt-decode@3.1.2/build/jwt-decode.min.js"></script> <script> // 检测当前环境是否为Teams function isInTeams() { return window.location.href.includes("teams.microsoft.com") || window.navigator.userAgent.includes("Teams"); } window.onload = function() { if (isInTeams()) { // Teams环境:使用SSO获取令牌 microsoftTeams.initialize(); const authTokenRequest = { successCallback: function(result) { const token = jwtDecode(result); document.getElementById("userAqui").innerHTML = `Welcome ${token.name}`; // 全局设置请求头,后续API请求携带令牌 axios.defaults.headers.common['Authorization'] = `Bearer ${result}`; }, failureCallback: function(error) { alert(`Failed to get Teams token: ${error}`); } }; microsoftTeams.authentication.getAuthToken(authTokenRequest); } else { // 普通浏览器:检查登录状态,未登录则跳转OIDC认证 fetch('/api/account/isauthenticated') .then(res => res.json()) .then(data => { if (!data.isAuthenticated) { window.location.href = "/signin-oidc"; } else { // 已登录,获取用户信息 fetch('/api/account/userinfo') .then(res => res.json()) .then(user => { document.getElementById("userAqui").innerHTML = `Welcome ${user.name}`; }); } }); } } </script>
3. 后端辅助API(AccountController)
用于浏览器端检查登录状态和获取用户信息:
[ApiController] [Route("api/[controller]")] public class AccountController : ControllerBase { [HttpGet("isauthenticated")] public IActionResult IsAuthenticated() { return Ok(new { isAuthenticated = User.Identity.IsAuthenticated }); } [HttpGet("userinfo")] public IActionResult GetUserInfo() { if (!User.Identity.IsAuthenticated) { return Unauthorized(); } var userInfo = new { name = User.Claims.FirstOrDefault(c => c.Type == "name")?.Value, email = User.Claims.FirstOrDefault(c => c.Type == "email")?.Value }; return Ok(userInfo); } }
4. 关键注意事项
- Teams应用注册配置:确保Azure AD应用已添加Teams平台,并且
API权限中添加了User.Read等必要权限,已授予管理员同意。 - 令牌验证一致性:JWT Bearer认证的
Audience和Issuer必须与应用注册信息完全匹配。 - Teams初始化时机:必须先调用
microsoftTeams.initialize()再请求令牌,否则会失败。
内容的提问来源于stack exchange,提问作者Ricardo Figueiredo
相关产品推荐
相关产品推荐

