【发布时间】:2025-12-25 06:10:06
【问题描述】:
使用带有 .NET Core 3.1 的 ASP.NET Core。
由Microsoft.AspNetCore.Authentication.OpenIdConnect 处理的 OIDC 身份验证流程。
在我开始收到错误后,我实际上已将上述命名空间包含到我的项目中,这样我就可以轻松设置断点和检查数据。
根据本文档:https://developer.microsoft.com/en-us/office/blogs/authentication-in-microsoft-teams-apps-tabs/ 我想要实现的目标应该是可能的。
假设我们在 Microsoft Teams 中配置了一个选项卡,该选项卡托管在我们的 ASP.NET Core MVC 应用程序中,地址为 https://localhost:60151(不是通过 IIS Express,而是自托管)。 MS Teams 应用程序可以使用 ngrok 访问我们的应用程序,它是使用命令行启动的:
./ngrok http https://localhost:60151
这个应用程序有一个 TabController 定义如下:
public class TabController : Controller
{
public IActionResult Index()
{
return View();
}
[Authorize]
public IActionResult TabAuthStart()
{
return RedirectToAction(nameof(TabAuthEnd), new { serializedClaims = string.Join("; ", User.Claims.Select(x => $"{x.Type}: {x.Value}")) });
}
// for simplicity, let's assume no one navigates to this action
// except when redirected from TabAuthStart after the authentication flow completes
public IActionResult TabAuthEnd(string serializedClaims)
{
return View(model: serializedClaims);
}
}
让索引视图定义如下:
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>MS Teams Tab</title>
<script src="https://statics.teams.microsoft.com/sdk/v1.4.2/js/MicrosoftTeams.min.js" crossorigin="anonymous"></script>
<script>
// Call the initialize API first
microsoftTeams.initialize();
function authenticate() {
microsoftTeams.authentication.authenticate({
url: window.location.origin + "/tab/tabauthstart",
successCallback: function (result) {
// do something on success
},
failureCallback: function (reason) {
// do something on failure
}
});
}
</script>
</head>
<body>
@if (!User.Identity.IsAuthenticated)
{
<button onclick="authenticate()">authenticate</button>
}
else
{
<p>Hello, @User.Identity.Name</p>
}
</body>
</html>
当重定向到 /tab/tabauthstart 时,[Authorize] 属性将确保 OIDC 质询处理程序将接收请求并重定向到配置的 IdentityServer 授权页面。
说到 OIDC 处理器,它在 Startup.cs 中是这样配置的:
services.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie(options =>
{
options.ExpireTimeSpan = TimeSpan.FromMinutes(60);
options.Cookie.Name = "mvchybridautorefresh";
})
.AddOpenIdConnect(options =>
{
options.Authority = "https://localhost:44333/"; // The local IdentityServer instance
options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.ClientId = "msteams";
options.ResponseType = "code id_token"; // Hybrid flow
options.Scope.Clear();
options.Scope.Add("openid");
options.Scope.Add("profile");
options.Scope.Add("offline_access");
options.ClaimActions.MapAllExcept("iss", "nbf", "exp", "aud", "nonce", "iat", "c_hash");
options.GetClaimsFromUserInfoEndpoint = true;
options.SaveTokens = true;
// The following were added in despair. However, they don't have any effect on the process.
options.CorrelationCookie.Path = null;
options.CorrelationCookie.SameSite = Microsoft.AspNetCore.Http.SameSiteMode.None;
options.CorrelationCookie.SecurePolicy = Microsoft.AspNetCore.Http.CookieSecurePolicy.Always;
options.CorrelationCookie.HttpOnly = false;
});
然后我们有一个像这样的Configure 方法:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseDefaultFiles();
app.UseStaticFiles();
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}"
);
});
}
在 IdentityServer 中,假设客户端配置正确。
因此,当我们启动我们的应用程序并转到 Microsoft Teams 应用程序中的一个选项卡时,我们会看到一个显示“身份验证”的按钮。单击该按钮会触发 OIDC 质询处理程序,该处理程序准备身份验证属性,将 nonce 和相关 cookie 写入 Response.Cookies 集合。
生成关联Id后,我们有如下Request参数:
- 方案:https
- 主机:[assigned-subdomain].ngrok.io
- 路径:/tab/tabauthstart
Set-Cookie 响应标头包含以下内容:
.AspNetCore.OpenIdConnect.Nonce.blabla; expires=Tue, 21 Jan 2020 20:54:28 GMT; path=/signin-oidc; secure; samesite=none; httponly,
.AspNetCore.Correlation.OpenIdConnect.blabla; expires=Tue, 21 Jan 2020 20:58:57 GMT; path=/signin-oidc; secure; samesite=none
完成后,我们将被重定向到 IdSrv 登录页面。
我们在此处输入登录详细信息并完成登录过程,这会将我们带回 OIDC 处理程序,然后检查相关 cookie 是否存在。但是,关联 cookie 不存在,因此会引发异常,提示“关联失败”。
这些是验证相关性之前的请求参数:
- 方案:https
- 主机:[assigned-subdomain].ngrok.io
- 路径:/signin-oidc
cookies 集合为空。为什么?
为了让事情变得更有趣,当我们打开浏览器时,导航到 https://[assigned-subdomain].ngrok.io/tab/index 并通过单击按钮开始身份验证,该过程成功完成,我们最终被重定向到 /tab/tabAuthEnd,顺便说一下,它的视图如下所示:
@model string
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Authentication successful</title>
<script src="https://statics.teams.microsoft.com/sdk/v1.4.2/js/MicrosoftTeams.min.js" crossorigin="anonymous"></script>
<script>
// Call the initialize API first
microsoftTeams.initialize();
microsoftTeams.authentication.notifySuccess(@Model);
</script>
</head>
<body>
<p>Redirecting back..</p>
</body>
</html>
所以...任何线索为什么在重定向到 IdSrv 登录页面时没有保存 OIDC cookie?
【问题讨论】:
-
为铬引入了新的samesite policy。我建议你看看这个sample code 这可以帮助你实现身份验证选项卡。
-
@Trinetra-MSFT 如果您仔细查看上述 Startup.cs 中的 OIDC 配置,您会发现 CorrelationCookie 明确配置了
SameSite.None和Secure。此外,您可以看到Set-Cookie响应标头具有secure; samesite=none;用于关联和随机数 cookie。我错过了你说的什么吗?此外,您链接到的示例不处理基于 Web 的身份验证,而是基于机器人框架。该链接对我有什么帮助?抱歉,如果我遗漏了一些明显的东西。
标签: asp.net-core openid-connect microsoft-teams