OIDC关联在Microsoft Teams身份验证弹出窗口中失败(浏览器中没有问题) [英] OIDC correlation failed in Microsoft Teams authentication popup (no problems in browser)

查看:116
本文介绍了OIDC关联在Microsoft Teams身份验证弹出窗口中失败(浏览器中没有问题)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

使用带有.NET Core 3.1的ASP.NET Core.
Microsoft.AspNetCore.Authentication.OpenIdConnect 处理的OIDC身份验证流程.
开始出现错误后,实际上已经将上述名称空间包含在我的项目中,因此我可以设置断点并轻松检查数据.

Using ASP.NET Core w/ .NET Core 3.1.
OIDC authentication flow handled by Microsoft.AspNetCore.Authentication.OpenIdConnect.
After I started getting the error, I have actually included the above namespace into my project so I can set breakpoints and inspect data easily.

根据此文档: https://developer.microsoft.com/zh-cn/office/blogs/authentication-in-microsoft-teams-apps-tabs/我想要实现的目标应该是可能的.

According to this document: https://developer.microsoft.com/en-us/office/blogs/authentication-in-microsoft-teams-apps-tabs/ what I'm trying to achieve should be possible.

假设我们已经在Microsoft Teams中配置了一个选项卡,该选项卡托管在ASP.NET Core MVC应用程序中,位于 https://localhost:60151 (不是通过IIS Express,而是自托管的).MS Teams应用程序可以使用 ngrok 访问我们的应用程序,该应用程序是使用命令行启动的:

Let's say we have configured a tab in Microsoft Teams which is hosted in our ASP.NET Core MVC application at https://localhost:60151 (not via IIS Express, but self-hosted). The MS Teams application can access our application using ngrok, which is started using the command line:

./ngrok http https://localhost:60151

此应用程序具有如下定义的TabController:

This application has a TabController defined like this:

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授权页面.

When redirected to /tab/tabauthstart, the [Authorize] attribute will make sure the OIDC challenge handler will pick up the request and redirect to the configured IdentityServer authorize page.

说到OIDC处理程序,它是在Startup.cs中这样配置的:

Speaking of OIDC handler, it is configured in Startup.cs like this:

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 方法,如下所示:

and then we have a Configure method like this:

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挑战处理程序,该处理程序准备身份验证属性,将现成和相关cookie写入Response.Cookies集合.

In IdentityServer, assume the client is configured correctly.
So when we start our application and go to a tab in Microsoft Teams app, we see a button saying "authenticate". Click of that button triggers the OIDC challenge handler which prepares the authentication properties, writes nonce and correlation cookies to the Response.Cookies collection.

生成相关性ID后,我们具有以下Request参数:

After generating the correlation Id, we have the following Request parameters:

  • 方案: https
  • 主机: [assigned-subdomain] .ngrok.io
  • 路径:/tab/tabauthstart

Set-Cookie 响应标头包含以下内容:

The Set-Cookie response header contains the following:

.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登录页面.

After that is done, we are redirected to the IdSrv sign in page.

我们在此处输入了登录详细信息并完成了登录过程,这使我们返回到OIDC处理程序,该处理程序随后检查相关cookie的存在.但是,相关性cookie不存在,因此抛出异常,提示相关性失败".

There we input our sign in details and finish the sign in process, which brings us back to our OIDC handler, which then checks for the existence of the correlation cookie. However, the correlation cookie doesn't exist and thus, the exception is thrown saying "Correlation failed".

以下是相关性验证之前的请求参数:

These are the request parameters just before the correlation is validated:

  • 方案: https
  • 主机: [assigned-subdomain] .ngrok.io
  • 路径:/signin-oidc

cookie集合为空.为什么?

为了使事情变得更加有趣,当我们打开浏览器时,导航至 https://[assigned-subdomain] .ngrok.io/tab/index 并通过单击按钮开始身份验证,该过程成功完成,我们最终被重定向到/tab/tabAuthEnd ,顺便说一下,其视图如下:

To make things even more interesting, when we open a browser, navigate to https://[assigned-subdomain].ngrok.io/tab/index and start the authentication by clicking the button, the process completes successfully and we are finally redirected to /tab/tabAuthEnd, whose view, by the way, looks like this:

@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的任何线索?

So... any clue why the OIDC cookies are not saved when redirecting to the IdSrv login page?

推荐答案

您将看到Set-Cookie响应标头以"secure; samesite = none;"结尾.并且Teams基于不允许的Chrome版本,并且没有存储cookie,从而导致此问题.

You will see that the Set-Cookie response header ends with "secure; samesite=none;" and Teams is based on a Chrome version that does not allow that, and no cookies are stored, causing this problem.

您还将看到将SameSite设置为Lax或Strict不会更改Set-Cookie标头.您将必须在Startup类(在aspnetcore中)中对此进行管理,如下所示:

You will also see that setting SameSite to Lax or Strict will not change the Set-Cookie header. You will have to manage this in the Startup class (in aspnetcore) like this:

private void CheckSameSite(HttpContext httpContext, CookieOptions options)
{
    if (options.SameSite == SameSiteMode.None)
    {
        var userAgent = httpContext.Request.Headers["User-Agent"].ToString();
        // TODO: Use your User Agent library of choice here.
        if (/* UserAgent doesn’t support new behavior */)
        {
               // For .NET Core < 3.1 set SameSite = (SameSiteMode)(-1)
               options.SameSite = SameSiteMode.Unspecified;
         }
    }
}

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<CookiePolicyOptions>(options =>
    {
        options.MinimumSameSitePolicy = SameSiteMode.Unspecified;
        options.OnAppendCookie = cookieContext => 
            CheckSameSite(cookieContext.Context, cookieContext.CookieOptions);
        options.OnDeleteCookie = cookieContext => 
            CheckSameSite(cookieContext.Context, cookieContext.CookieOptions);
    });
}

public void Configure(IApplicationBuilder app)
{
    app.UseCookiePolicy(); // Before UseAuthentication or anything else that writes cookies.
    app.UseAuthentication();
    // …
}

在此检查:如果(/* UserAgent不支持新行为*/)

In this is check: if (/* UserAgent doesn’t support new behavior */)

...您检查User-Agent标头.像fon实例一样,如果其中包含团队"或更具体的内容.

...you check on the User-Agent header. Like fon instance if it contains "Teams" or more specific.

Microsoft Teams Teams通过此用户代理标识:Mozilla/5.0(Windows NT 10.0; Win64; x64)AppleWebKit/537.36(KHTML,like Gecko)Teams/1.3.00.362 Chrome/66.0.3359.181 Electron/3.1.13 Safari/537.36

Microsoft Teams Teams identifies by this User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Teams/1.3.00.362 Chrome/66.0.3359.181 Electron/3.1.13 Safari/537.36

其来源位于: https://devblogs.microsoft.com/aspnet/upcoming-samesite-cookie-changes-in-asp-net-and-asp-net-core/

这篇关于OIDC关联在Microsoft Teams身份验证弹出窗口中失败(浏览器中没有问题)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆