我正在使用一个角度SPA,该角度SPA通过使用identity server 4和oidc client js实现身份验证。
在静默访问令牌续订级别无法正常工作。预期的行为是访问令牌的自动更新,这要归功于iframe调用/connect/authorize
端点的幕后功夫。该调用将身份服务器身份验证cookie与HTTP请求一起发送,从而使身份服务器知道用户会话仍然有效,并且能够发出新的新访问令牌而无需 >要求用户再次交互式登录。到目前为止,我很确定我的理解很好。
这是棘手的部分:我希望身份服务器身份验证cookie的滑动期限应为,,以便每次调用{{1 }}端点。换句话说,我希望用户第一次登录后不需要其他交互式登录,因为每次时,用户会话的到期日期都会自动自动向前移动静默续订iframe需要新的访问令牌。
为了获得此行为,我在身份服务器级别设置了以下配置。
这是客户端配置(请注意,访问令牌的生存期为2分钟= 120秒):
/connect/authorize
这是 new Client
{
ClientId = "web-client",
ClientName = "SPA web client",
AllowedGrantTypes = GrantTypes.Code,
RequireClientSecret = false,
RequirePkce = true,
RequireConsent = false,
AccessTokenLifetime = 120,
RedirectUris = { "https://localhost:4200/assets/signin-callback.html", "https://localhost:4200/assets/silent-callback.html" },
PostLogoutRedirectUris = { "https://localhost:4200/signout-callback" },
AllowedCorsOrigins = { "https://localhost:4200" },
AllowedScopes =
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
IdentityServerConstants.StandardScopes.Email,
"dataset",
"exercise",
"user-permissions"
}
}
,我在其中添加了所有身份服务器配置。请注意,cookie的生存期设置为15分钟,并且cookie的滑动期限是必需的:
ConfigureServices
在阅读this github issue之后,我已将呼叫添加到 public void ConfigureServices(IServiceCollection services)
{
services.Configure<RequestLoggingOptions>(o =>
{
o.EnrichDiagnosticContext = (diagnosticContext, httpContext) =>
{
diagnosticContext.Set("RemoteIpAddress", httpContext.Connection.RemoteIpAddress.MapToIPv4());
};
});
services.AddControllersWithViews();
var migrationsAssembly = GetRunningAssemblyName();
var connectionString = this.Configuration.GetConnectionString(IdentityServerDatabaseConnectionString);
var identityServerBuilder = services.AddIdentityServer(options =>
{
options.Authentication.CookieLifetime = TimeSpan.FromMinutes(15);
options.Authentication.CookieSlidingExpiration = true;
})
.AddTestUsers(TestData.Users)
.AddConfigurationStore(options =>
{
options.ConfigureDbContext = dbContextBuilder =>
dbContextBuilder.UseSqlServer(
connectionString,
sqlServerOptionsBuilder => sqlServerOptionsBuilder.MigrationsAssembly(migrationsAssembly)
);
})
.AddOperationalStore(options =>
{
options.ConfigureDbContext = dbContextBuilder =>
dbContextBuilder.UseSqlServer(
connectionString,
sqlServerOptionsBuilder => sqlServerOptionsBuilder.MigrationsAssembly(migrationsAssembly)
);
});
services.AddAuthentication(x => x.DefaultAuthenticateScheme = IdentityServer4.IdentityServerConstants.DefaultCookieAuthenticationScheme);
identityServerBuilder.AddDeveloperSigningCredential();
}
。根据我的理解,此调用是多余的,因为通过使用常量services.AddAuthentication(x => x.DefaultAuthenticateScheme = IdentityServer4.IdentityServerConstants.DefaultCookieAuthenticationScheme);
作为身份验证方案名称,对services.AddIdentityServer
的调用应该已经将cookie身份验证设置为默认身份验证方案。
通过使用此身份服务器配置,silen访问令牌续订不按我期望的方式工作。
访问令牌被静默更新14次,然后第十五次尝试更新访问令牌失败,并显示消息IdentityServer4.IdentityServerConstants.DefaultCookieAuthenticationScheme
。
这基本上意味着身份验证cookie的滑动过期无效,因为我的身份验证cookie的生命周期为15分钟,SPA客户端的访问令牌的生命周期为2分钟,并且oidc客户端js库正在执行静默刷新周期每分钟一次(访问令牌在其到期时间之前60秒更新一次,因此,根据我的设置,每分钟完成一次无提示更新)。在第十五次尝试更新访问令牌时,身份验证cookie最终到期,并且身份服务器授权端点将错误响应返回到SilentRenewService._tokenExpiring: Error from signinSilent: login_required
静态页面。
这些是我的控制台日志(请注意,静音更新已按预期进行了14次):
这些是身份服务器编写的服务器端日志,该日志确认用户会话在第15次尝试时已过期:
这些是身份服务器在成功尝试更新访问令牌(最初14次尝试更新访问令牌中的一个)期间调用https://localhost:4200/assets/silent-callback.html
端点时由响应服务器返回的响应标头)。请注意,有一个响应标头为/connect/authorize
cookie设置了新值:
这些是身份服务器在尝试更新访问令牌失败(第十五次尝试更新访问令牌)期间调用idsrv
端点时由身份服务器返回的响应标头。请注意,/connect/authorize
cookie无效,因为它的到期日期设置为2019年的过去日期:
我是否丢失有关静默访问令牌续订和身份验证Cookie滑动过期之间的关系的任何信息?
这是预期的行为吗?
是否有一种方法可以使静默访问令牌续期工作而无需,而无需新的用户登录交互?
更新2020年9月16日
我终于设法解决了这个问题。
此修复程序是将idsrv.session
nuget软件包更新为最新的可用版本(截至今天为IdentityServer4.EntityFramework
)。
所有详细信息都报告为in my own github issue on the oidc-client-js github repository。
总而言之,cookie滑动过期的异常行为的根本原因是此identity server bug,由4.1.0
nuget软件包的4.1.0
版本修复,如{{ 3}}。
答案 0 :(得分:0)
这是我在您的request上的配置:
public void ConfigureServices(IServiceCollection services)
{
services.AddHealthChecks();
services.AddControllersWithViews();
services.AddCustomOptions(Configuration);
services.AddCustomDbContext(Configuration)
.ResolveDependencies();
services.AddIdentityServer(options =>
{
options.Authentication.CookieLifetime = AccountOptions.RememberMeLoginDuration;
options.Events.RaiseSuccessEvents = true;
options.Events.RaiseFailureEvents = true;
options.Events.RaiseErrorEvents = true;
})
.AddProfileService<ProfileService>()
.AddSigningCertificate(Configuration)
.AddInMemoryClients(Configuration.GetSection("IdentityServer:Clients"))
.AddInMemoryIdentityResources(Resources.GetIdentityResources())
.AddInMemoryApiResources(Resources.GetApis());
var externalProviders = Configuration.GetSection(nameof(ApplicationOptions.ExternalProviders))
.Get<ExternalProvidersOptions>();
services.AddWindowsIdentityProvider(externalProviders.UseWindows);
services.AddLocalization(options => options.ResourcesPath = Constants.Resources);
services.AddMvc()
.AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix)
.AddDataAnnotationsLocalization();
services.AddMvcCore()
.AddCustomCors();
}
此外,以下是appsettings中的客户端配置:
{
"Enabled": true,
"ClientId": "dashboard",
"ClientName": "Web Client",
"ClientSecrets": [ { "Value": "K7gNU3sdo+OL0wNhqoVWhr3g6s1xYv72ol/pe/Unols=" } ],
"AllowedGrantTypes": [ "implicit", "authorization_code" ],
"AllowedScopes": [ "openid", "email", "profile", "role" ],
"AllowOfflineAccess": true,
"AllowAccessTokensViaBrowser": true,
"AllowedCorsOrigins": [
"http://localhost:7004"
],
"RedirectUris": [
"http://localhost:7004/callback",
"http://localhost:7004/refreshtoken"
],
"PostLogoutRedirectUris": [
"http://localhost:7004"
],
"AccessTokenLifetime": 3600,
"RequireConsent": false
}