环境:
基本令牌发行和验证在我们的环境中运行良好。我现在正在尝试启用无提示刷新技术(如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)的例程返回false,该错误被转换为401。我可以在失败的{{1 }}请求GET
。
我尝试指定的WWW-Authenticate: error="invalid_token"
小于每个this的IdentityTokenLifetime
,但没有成功。
这是两个请求的日志:
AccessTokenLifetime
问题:
如何在无提示刷新期间向UserInfo端点发送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
请求以验证到GET
的身份?
更新:
添加两个请求的所有标头的屏幕截图以及生成的浏览器cookie,以调查@Anders的答案。
答案 0 :(得分:2)
对/connect/userinfo
的请求由IdentityServer域/路径中的会话认证cookie进行认证。
我的猜测是该cookie已正确包含在OPTIONS请求中,但未包含在后续的GET请求中。您可以通过查看请求在浏览器开发工具中进行验证。
如果我的猜测正确,则原因可能是同一网站属性。默认情况下,ASP.NET Core(IdentityServer4使用)中的所有身份验证cookie都具有相同的site属性,以防止“跨站点请求伪造”攻击。
根据information I can find,AJAX Get请求中不允许使用带有samesite = lax的cookie。但是,如果OPTIONS请求中允许,则找不到任何内容。您可以验证(使用浏览器开发工具)从第一个请求到/connect/authorize
的响应中,cookie头中是否存在相同的网站设置。
该设置本身位于对Cookie
的调用的cookie选项的AddCookie()
部分中。 MS文档说它默认为lax
。
另一方面,Idsrv4存储库中有一个GitHub thread,表示他们已将Idsrv4会话cookie的默认值更改为“ none”。
我显然在这里有点猜测,但是验证我上面概述的假设应该相当简单。
答案 1 :(得分:2)
好的,我想我知道发生了什么。您发出了无提示刷新令牌请求(这是我从重定向到oidc-silent-refresh.html所看到的成功),并且第一个ProfileService的“ IsActiveAsync”被调用(因为它需要通过配置文件服务来生成令牌)静默刷新)。您能够看到“ HttpContext.User”,因为打开了一个iframe,该iframe允许发送所有必要的cookie。
用户信息预检请求甚至没有进入ProfileService,正如您从仅显示“调用IdentityServer端点:IdentityServer4.Endpoints.UserInfoEndpoint for / connect / userinfo”的日志中看到的那样。另外,UserInfoEndpoint阻止任何选项请求通过(您可以在'ProcessAsync'方法here的开头验证其为真)。
现在,当您发出实际的用户信息请求时,您正在尝试从HttpContext获取信息(通常使用cookie中的信息填充该信息),但是由于请求中未发送任何cookie,因此无法访问。该请求是在{get3son}方法(来自oidc-client-js库)中通过here发出的,您可以看到请求中未发送任何cookie(onCredentials属性将在(如果有的话)。
那么我们如何获得有关用户的必要信息?要获取此信息,我们可以参考传递到ProfileService的“ IsActiveAsync”中的“ context”,其中包含由访问令牌声明填充的主体(此过程可以在“ ValidateAccessTokenAsync”方法here中看到)。