静默刷新在 OPTIONS 预检上进行身份验证,但不在 GET 到 UserInfo 端点上进行身份验证

Silent refresh authenticates on OPTIONS preflight but not on GET to UserInfo endpoint

环境:

基本令牌发行和验证在我们的环境中运行良好。我现在正在尝试启用静默刷新技术(如 documented here)。在启用 automaticSilentRenew 和一个简短的 AccessTokenLifetime 后,我可以在我的浏览器控制台中看到静默请求如我所料。

我可以看到对 IS4 的 UserInfo 端点的两次后续调用(请参见下面的屏幕截图)。第一个是 CORS 预检 OPTIONS 请求。在我自定义实现 IProfileService.IsActiveAsync() 的断点处,我可以看到该请求成功通过了身份验证(通过检查 httpContext)。

public class ProfileService : IProfileService
{
    private readonly HttpContext _httpContext;

    public ProfileService(IHttpContextAccessor httpContextAccessor)
    {
        _httpContext = httpContextAccessor.HttpContext;
    }

    ...

    public async Task IsActiveAsync(IsActiveContext context)
    {
        var temp = _httpContext.User; // breakpoint here
        // call external API with _httpContext.User info to get isActive
    }
}

但是,对 UserInfo 端点的第二个请求 (GET) 未进行身份验证。我在 IProfileService.IsActiveAsync() 中的断点显示没有经过身份验证的用户,因此我的例程用于验证用户是否处于活动状态(调用另一个 API)returns false 转换为 401。我可以看到此 header 失败 GET 请求 WWW-Authenticate: error="invalid_token".

我尝试指定一个小于 thisAccessTokenLifetimeIdentityTokenLifetime 但没有成功。

这是两个请求的日志:

Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request starting HTTP/1.1 OPTIONS http://localhost:5000/connect/userinfo  
Microsoft.AspNetCore.Cors.Infrastructure.CorsService:Information: CORS policy execution successful.
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request finished in 8.5635ms 204 
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request starting HTTP/1.1 GET http://localhost:5000/connect/userinfo  
Microsoft.AspNetCore.Cors.Infrastructure.CorsService:Information: CORS policy execution successful.
Microsoft.AspNetCore.Cors.Infrastructure.CorsService:Information: CORS policy execution successful.
IdentityServer4.Hosting.IdentityServerMiddleware:Information: Invoking IdentityServer endpoint: IdentityServer4.Endpoints.UserInfoEndpoint for /connect/userinfo
IdentityServer4.Validation.TokenValidator:Error: User marked as not active: f84db3aa-57b8-48e4-9b59-6deee3d288ad
Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request finished in 94.7189ms 401

问题:

如何在静默刷新期间获得对 UserInfo 端点的 GET 请求以验证 HttpContext

更新:

添加两个请求的所有 header 的屏幕截图和生成的浏览器 cookie 以调查@Anders 的答案。

/connect/userinfo 的请求由 IdentityServer domain/path 中的 session 身份验证 cookie 进行身份验证。

我的猜测是 cookie 正确包含在 OPTIONS 请求中,但没有包含在后续的 GET 请求中。您可以通过查看请求在浏览器开发工具中验证这一点。

如果我的猜测是正确的,原因可能是 samesite 属性。 ASP.NET Core(IdentityServer4 使用)中的所有身份验证 cookie 默认情况下都具有 samesite 属性,以防止跨站点请求伪造攻击。

根据 information I can find AJAX Get 请求不允许使用 samesite=lax 的 cookie。但是,如果在 OPTIONS 请求中允许,我找不到任何东西。您可以验证(使用浏览器开发工具)在第一个请求 /connect/authorize.

的响应中,cookie header 中是否存在 samesite 设置

设置本身位于 AddCookie() 调用中 cookie 选项的 Cookie 部分。 MS 文档说它默认为 lax.

另一方面,Idsrv4 存储库中有一个 GitHub thread,表示他们已将 Idsrv4 session cookie 的默认值更改为 "none"。

我显然在这里有点猜测,但验证我上面概述的假设应该相当简单。

好的,我想我知道发生了什么。您发出静默刷新令牌请求(从我从重定向到 oidc-silent-refresh.html 中看到的成功)并且调用第一个 ProfileService 的 'IsActiveAsync'(因为它需要通过配置文件服务为静默刷新生成令牌)。您能够看到 'HttpContext.User' 因为打开了一个 iframe,它允许发送所有必要的 cookie。

用户信息预检请求甚至没有到达配置文件服务,正如您从日志中看到的那样,只显示了一次 'Invoking IdentityServer endpoint: IdentityServer4.Endpoints.UserInfoEndpoint for /connect/userinfo'。此外,UserInfoEndpoint 会阻止任何选项请求通过(您可以在 'ProcessAsync' 方法 here 的开头验证它是否为真)。

现在,当您发出实际的用户信息请求时,您正试图从 HttpContext 获取信息(通常使用来自 cookie 的信息填充),但它是不可访问的,因为请求中没有发送 cookie。在'getJson'方法(来自oidc-client-js库)中发出请求here,可以看到请求中没有发送cookie('withCredentials' 属性 将根据请求设置为真)。

那么我们如何获取用户的必要信息呢?要获取此信息,我们可以参考 'context' passed into the ProfileService's 'IsActiveAsync' that contains a principal populated by the access token claims (this process can be seen in the 'ValidateAccessTokenAsync' method here).