mirror of
https://github.com/bitwarden/server.git
synced 2025-01-22 21:51:22 +01:00
[PM-6196] Cleanup distributed cache for identity (#3704)
* cleanup distributed cache for identity * removed unused using * use persistent IDistributedCache
This commit is contained in:
parent
b1967aa8a7
commit
6174df0874
@ -65,7 +65,7 @@ public class Startup
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Authentication
|
// Authentication
|
||||||
services.AddDistributedIdentityServices(globalSettings);
|
services.AddDistributedIdentityServices();
|
||||||
services.AddAuthentication()
|
services.AddAuthentication()
|
||||||
.AddCookie(AuthenticationSchemes.BitwardenExternalCookieAuthenticationScheme);
|
.AddCookie(AuthenticationSchemes.BitwardenExternalCookieAuthenticationScheme);
|
||||||
services.AddSsoServices(globalSettings);
|
services.AddSsoServices(globalSettings);
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
using Bit.Core.Settings;
|
using Duende.IdentityServer.Configuration;
|
||||||
using Duende.IdentityServer.Configuration;
|
|
||||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.DataProtection;
|
||||||
using Microsoft.Extensions.Caching.StackExchangeRedis;
|
using Microsoft.Extensions.Caching.Distributed;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
namespace Bit.Core.IdentityServer;
|
namespace Bit.Core.IdentityServer;
|
||||||
@ -10,15 +10,18 @@ namespace Bit.Core.IdentityServer;
|
|||||||
public class ConfigureOpenIdConnectDistributedOptions : IPostConfigureOptions<CookieAuthenticationOptions>
|
public class ConfigureOpenIdConnectDistributedOptions : IPostConfigureOptions<CookieAuthenticationOptions>
|
||||||
{
|
{
|
||||||
private readonly IdentityServerOptions _idsrv;
|
private readonly IdentityServerOptions _idsrv;
|
||||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
private readonly IDistributedCache _distributedCache;
|
||||||
private readonly GlobalSettings _globalSettings;
|
private readonly IDataProtectionProvider _dataProtectionProvider;
|
||||||
|
|
||||||
public ConfigureOpenIdConnectDistributedOptions(IHttpContextAccessor httpContextAccessor, GlobalSettings globalSettings,
|
public ConfigureOpenIdConnectDistributedOptions(
|
||||||
|
[FromKeyedServices("persistent")]
|
||||||
|
IDistributedCache distributedCache,
|
||||||
|
IDataProtectionProvider dataProtectionProvider,
|
||||||
IdentityServerOptions idsrv)
|
IdentityServerOptions idsrv)
|
||||||
{
|
{
|
||||||
_httpContextAccessor = httpContextAccessor ?? throw new ArgumentNullException(nameof(httpContextAccessor));
|
|
||||||
_globalSettings = globalSettings;
|
|
||||||
_idsrv = idsrv;
|
_idsrv = idsrv;
|
||||||
|
_distributedCache = distributedCache;
|
||||||
|
_dataProtectionProvider = dataProtectionProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void PostConfigure(string name, CookieAuthenticationOptions options)
|
public void PostConfigure(string name, CookieAuthenticationOptions options)
|
||||||
@ -34,19 +37,7 @@ public class ConfigureOpenIdConnectDistributedOptions : IPostConfigureOptions<Co
|
|||||||
options.Cookie.Name = AuthenticationSchemes.BitwardenExternalCookieAuthenticationScheme;
|
options.Cookie.Name = AuthenticationSchemes.BitwardenExternalCookieAuthenticationScheme;
|
||||||
options.Cookie.IsEssential = true;
|
options.Cookie.IsEssential = true;
|
||||||
options.Cookie.SameSite = _idsrv.Authentication.CookieSameSiteMode;
|
options.Cookie.SameSite = _idsrv.Authentication.CookieSameSiteMode;
|
||||||
options.TicketDataFormat = new DistributedCacheTicketDataFormatter(_httpContextAccessor, name);
|
options.TicketDataFormat = new DistributedCacheTicketDataFormatter(_distributedCache, _dataProtectionProvider, name);
|
||||||
|
options.SessionStore = new DistributedCacheTicketStore(_distributedCache);
|
||||||
if (string.IsNullOrWhiteSpace(_globalSettings.IdentityServer?.RedisConnectionString))
|
|
||||||
{
|
|
||||||
options.SessionStore = new MemoryCacheTicketStore();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var redisOptions = new RedisCacheOptions
|
|
||||||
{
|
|
||||||
Configuration = _globalSettings.IdentityServer.RedisConnectionString,
|
|
||||||
};
|
|
||||||
options.SessionStore = new RedisCacheTicketStore(redisOptions);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -58,7 +58,7 @@ public class DistributedCacheCookieManager : ICookieManager
|
|||||||
}
|
}
|
||||||
|
|
||||||
private IDistributedCache GetCache(HttpContext context) =>
|
private IDistributedCache GetCache(HttpContext context) =>
|
||||||
context.RequestServices.GetRequiredService<IDistributedCache>();
|
context.RequestServices.GetRequiredKeyedService<IDistributedCache>("persistent");
|
||||||
|
|
||||||
private string GetKey(string key, string id) => $"{CacheKeyPrefix}-{key}-{id}";
|
private string GetKey(string key, string id) => $"{CacheKeyPrefix}-{key}-{id}";
|
||||||
|
|
||||||
|
@ -1,32 +1,32 @@
|
|||||||
using Microsoft.AspNetCore.Authentication;
|
using Microsoft.AspNetCore.Authentication;
|
||||||
using Microsoft.AspNetCore.DataProtection;
|
using Microsoft.AspNetCore.DataProtection;
|
||||||
using Microsoft.AspNetCore.Http;
|
|
||||||
using Microsoft.Extensions.Caching.Distributed;
|
using Microsoft.Extensions.Caching.Distributed;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
|
|
||||||
namespace Bit.Core.IdentityServer;
|
namespace Bit.Core.IdentityServer;
|
||||||
|
|
||||||
public class DistributedCacheTicketDataFormatter : ISecureDataFormat<AuthenticationTicket>
|
public class DistributedCacheTicketDataFormatter : ISecureDataFormat<AuthenticationTicket>
|
||||||
{
|
{
|
||||||
private readonly IHttpContextAccessor _httpContext;
|
private const string CacheKeyPrefix = "ticket-data";
|
||||||
private readonly string _name;
|
|
||||||
|
|
||||||
public DistributedCacheTicketDataFormatter(IHttpContextAccessor httpContext, string name)
|
private readonly IDistributedCache _distributedCache;
|
||||||
|
private readonly IDataProtector _dataProtector;
|
||||||
|
private readonly string _prefix;
|
||||||
|
|
||||||
|
public DistributedCacheTicketDataFormatter(
|
||||||
|
IDistributedCache distributedCache,
|
||||||
|
IDataProtectionProvider dataProtectionProvider,
|
||||||
|
string name)
|
||||||
{
|
{
|
||||||
_httpContext = httpContext;
|
_distributedCache = distributedCache;
|
||||||
_name = name;
|
_dataProtector = dataProtectionProvider.CreateProtector(CacheKeyPrefix, name);
|
||||||
|
_prefix = $"{CacheKeyPrefix}-{name}";
|
||||||
}
|
}
|
||||||
|
|
||||||
private string CacheKeyPrefix => "ticket-data";
|
|
||||||
private IDistributedCache Cache => _httpContext.HttpContext.RequestServices.GetRequiredService<IDistributedCache>();
|
|
||||||
private IDataProtector Protector => _httpContext.HttpContext.RequestServices.GetRequiredService<IDataProtectionProvider>()
|
|
||||||
.CreateProtector(CacheKeyPrefix, _name);
|
|
||||||
|
|
||||||
public string Protect(AuthenticationTicket data) => Protect(data, null);
|
public string Protect(AuthenticationTicket data) => Protect(data, null);
|
||||||
public string Protect(AuthenticationTicket data, string purpose)
|
public string Protect(AuthenticationTicket data, string purpose)
|
||||||
{
|
{
|
||||||
var key = Guid.NewGuid().ToString();
|
var key = Guid.NewGuid().ToString();
|
||||||
var cacheKey = $"{CacheKeyPrefix}-{_name}-{purpose}-{key}";
|
var cacheKey = $"{_prefix}-{purpose}-{key}";
|
||||||
|
|
||||||
var expiresUtc = data.Properties.ExpiresUtc ??
|
var expiresUtc = data.Properties.ExpiresUtc ??
|
||||||
DateTimeOffset.UtcNow.AddMinutes(15);
|
DateTimeOffset.UtcNow.AddMinutes(15);
|
||||||
@ -35,9 +35,9 @@ public class DistributedCacheTicketDataFormatter : ISecureDataFormat<Authenticat
|
|||||||
options.SetAbsoluteExpiration(expiresUtc);
|
options.SetAbsoluteExpiration(expiresUtc);
|
||||||
|
|
||||||
var ticket = TicketSerializer.Default.Serialize(data);
|
var ticket = TicketSerializer.Default.Serialize(data);
|
||||||
Cache.Set(cacheKey, ticket, options);
|
_distributedCache.Set(cacheKey, ticket, options);
|
||||||
|
|
||||||
return Protector.Protect(key);
|
return _dataProtector.Protect(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
public AuthenticationTicket Unprotect(string protectedText) => Unprotect(protectedText, null);
|
public AuthenticationTicket Unprotect(string protectedText) => Unprotect(protectedText, null);
|
||||||
@ -49,9 +49,9 @@ public class DistributedCacheTicketDataFormatter : ISecureDataFormat<Authenticat
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Decrypt the key and retrieve the data from the cache.
|
// Decrypt the key and retrieve the data from the cache.
|
||||||
var key = Protector.Unprotect(protectedText);
|
var key = _dataProtector.Unprotect(protectedText);
|
||||||
var cacheKey = $"{CacheKeyPrefix}-{_name}-{purpose}-{key}";
|
var cacheKey = $"{_prefix}-{purpose}-{key}";
|
||||||
var ticket = Cache.Get(cacheKey);
|
var ticket = _distributedCache.Get(cacheKey);
|
||||||
|
|
||||||
if (ticket == null)
|
if (ticket == null)
|
||||||
{
|
{
|
||||||
|
@ -1,23 +1,22 @@
|
|||||||
using Microsoft.AspNetCore.Authentication;
|
using Microsoft.AspNetCore.Authentication;
|
||||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||||
using Microsoft.Extensions.Caching.Distributed;
|
using Microsoft.Extensions.Caching.Distributed;
|
||||||
using Microsoft.Extensions.Caching.StackExchangeRedis;
|
|
||||||
|
|
||||||
namespace Bit.Core.IdentityServer;
|
namespace Bit.Core.IdentityServer;
|
||||||
|
|
||||||
public class RedisCacheTicketStore : ITicketStore
|
public class DistributedCacheTicketStore : ITicketStore
|
||||||
{
|
{
|
||||||
private const string _keyPrefix = "auth-";
|
private const string KeyPrefix = "auth-";
|
||||||
private readonly IDistributedCache _cache;
|
private readonly IDistributedCache _cache;
|
||||||
|
|
||||||
public RedisCacheTicketStore(RedisCacheOptions options)
|
public DistributedCacheTicketStore(IDistributedCache distributedCache)
|
||||||
{
|
{
|
||||||
_cache = new RedisCache(options);
|
_cache = distributedCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<string> StoreAsync(AuthenticationTicket ticket)
|
public async Task<string> StoreAsync(AuthenticationTicket ticket)
|
||||||
{
|
{
|
||||||
var key = $"{_keyPrefix}{Guid.NewGuid()}";
|
var key = $"{KeyPrefix}{Guid.NewGuid()}";
|
||||||
await RenewAsync(key, ticket);
|
await RenewAsync(key, ticket);
|
||||||
|
|
||||||
return key;
|
return key;
|
@ -1,53 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Authentication;
|
|
||||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
|
||||||
using Microsoft.Extensions.Caching.Memory;
|
|
||||||
|
|
||||||
namespace Bit.Core.IdentityServer;
|
|
||||||
|
|
||||||
public class MemoryCacheTicketStore : ITicketStore
|
|
||||||
{
|
|
||||||
private const string _keyPrefix = "auth-";
|
|
||||||
private readonly IMemoryCache _cache;
|
|
||||||
|
|
||||||
public MemoryCacheTicketStore()
|
|
||||||
{
|
|
||||||
_cache = new MemoryCache(new MemoryCacheOptions());
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<string> StoreAsync(AuthenticationTicket ticket)
|
|
||||||
{
|
|
||||||
var key = $"{_keyPrefix}{Guid.NewGuid()}";
|
|
||||||
await RenewAsync(key, ticket);
|
|
||||||
return key;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task RenewAsync(string key, AuthenticationTicket ticket)
|
|
||||||
{
|
|
||||||
var options = new MemoryCacheEntryOptions();
|
|
||||||
var expiresUtc = ticket.Properties.ExpiresUtc;
|
|
||||||
if (expiresUtc.HasValue)
|
|
||||||
{
|
|
||||||
options.SetAbsoluteExpiration(expiresUtc.Value);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
options.SetSlidingExpiration(TimeSpan.FromMinutes(15));
|
|
||||||
}
|
|
||||||
|
|
||||||
_cache.Set(key, ticket, options);
|
|
||||||
|
|
||||||
return Task.FromResult(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<AuthenticationTicket> RetrieveAsync(string key)
|
|
||||||
{
|
|
||||||
_cache.TryGetValue(key, out AuthenticationTicket ticket);
|
|
||||||
return Task.FromResult(ticket);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task RemoveAsync(string key)
|
|
||||||
{
|
|
||||||
_cache.Remove(key);
|
|
||||||
return Task.FromResult(0);
|
|
||||||
}
|
|
||||||
}
|
|
@ -46,6 +46,7 @@ public class CustomTokenRequestValidator : BaseRequestValidator<CustomTokenReque
|
|||||||
IPolicyService policyService,
|
IPolicyService policyService,
|
||||||
IDataProtectorTokenFactory<SsoEmail2faSessionTokenable> tokenDataFactory,
|
IDataProtectorTokenFactory<SsoEmail2faSessionTokenable> tokenDataFactory,
|
||||||
IFeatureService featureService,
|
IFeatureService featureService,
|
||||||
|
[FromKeyedServices("persistent")]
|
||||||
IDistributedCache distributedCache,
|
IDistributedCache distributedCache,
|
||||||
IUserDecryptionOptionsBuilder userDecryptionOptionsBuilder)
|
IUserDecryptionOptionsBuilder userDecryptionOptionsBuilder)
|
||||||
: base(userManager, deviceRepository, deviceService, userService, eventService,
|
: base(userManager, deviceRepository, deviceService, userService, eventService,
|
||||||
|
@ -49,6 +49,7 @@ public class ResourceOwnerPasswordValidator : BaseRequestValidator<ResourceOwner
|
|||||||
IDataProtectorTokenFactory<SsoEmail2faSessionTokenable> tokenDataFactory,
|
IDataProtectorTokenFactory<SsoEmail2faSessionTokenable> tokenDataFactory,
|
||||||
IFeatureService featureService,
|
IFeatureService featureService,
|
||||||
ISsoConfigRepository ssoConfigRepository,
|
ISsoConfigRepository ssoConfigRepository,
|
||||||
|
[FromKeyedServices("persistent")]
|
||||||
IDistributedCache distributedCache,
|
IDistributedCache distributedCache,
|
||||||
IUserDecryptionOptionsBuilder userDecryptionOptionsBuilder)
|
IUserDecryptionOptionsBuilder userDecryptionOptionsBuilder)
|
||||||
: base(userManager, deviceRepository, deviceService, userService, eventService,
|
: base(userManager, deviceRepository, deviceService, userService, eventService,
|
||||||
|
@ -50,6 +50,7 @@ public class WebAuthnGrantValidator : BaseRequestValidator<ExtensionGrantValidat
|
|||||||
IDataProtectorTokenFactory<SsoEmail2faSessionTokenable> tokenDataFactory,
|
IDataProtectorTokenFactory<SsoEmail2faSessionTokenable> tokenDataFactory,
|
||||||
IDataProtectorTokenFactory<WebAuthnLoginAssertionOptionsTokenable> assertionOptionsDataProtector,
|
IDataProtectorTokenFactory<WebAuthnLoginAssertionOptionsTokenable> assertionOptionsDataProtector,
|
||||||
IFeatureService featureService,
|
IFeatureService featureService,
|
||||||
|
[FromKeyedServices("persistent")]
|
||||||
IDistributedCache distributedCache,
|
IDistributedCache distributedCache,
|
||||||
IUserDecryptionOptionsBuilder userDecryptionOptionsBuilder,
|
IUserDecryptionOptionsBuilder userDecryptionOptionsBuilder,
|
||||||
IAssertWebAuthnLoginCredentialCommand assertWebAuthnLoginCredentialCommand
|
IAssertWebAuthnLoginCredentialCommand assertWebAuthnLoginCredentialCommand
|
||||||
|
@ -91,7 +91,7 @@ public class Startup
|
|||||||
|
|
||||||
// Authentication
|
// Authentication
|
||||||
services
|
services
|
||||||
.AddDistributedIdentityServices(globalSettings)
|
.AddDistributedIdentityServices()
|
||||||
.AddAuthentication()
|
.AddAuthentication()
|
||||||
.AddCookie(AuthenticationSchemes.BitwardenExternalCookieAuthenticationScheme)
|
.AddCookie(AuthenticationSchemes.BitwardenExternalCookieAuthenticationScheme)
|
||||||
.AddOpenIdConnect("sso", "Single Sign On", options =>
|
.AddOpenIdConnect("sso", "Single Sign On", options =>
|
||||||
|
@ -35,7 +35,6 @@ using Bit.Core.Vault.Services;
|
|||||||
using Bit.Infrastructure.Dapper;
|
using Bit.Infrastructure.Dapper;
|
||||||
using Bit.Infrastructure.EntityFramework;
|
using Bit.Infrastructure.EntityFramework;
|
||||||
using DnsClient;
|
using DnsClient;
|
||||||
using Duende.IdentityServer.Configuration;
|
|
||||||
using IdentityModel;
|
using IdentityModel;
|
||||||
using LaunchDarkly.Sdk.Server;
|
using LaunchDarkly.Sdk.Server;
|
||||||
using LaunchDarkly.Sdk.Server.Interfaces;
|
using LaunchDarkly.Sdk.Server.Interfaces;
|
||||||
@ -632,18 +631,13 @@ public static class ServiceCollectionExtensions
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IServiceCollection AddDistributedIdentityServices(this IServiceCollection services, GlobalSettings globalSettings)
|
public static IServiceCollection AddDistributedIdentityServices(this IServiceCollection services)
|
||||||
{
|
{
|
||||||
services.AddOidcStateDataFormatterCache();
|
services.AddOidcStateDataFormatterCache();
|
||||||
services.AddSession();
|
services.AddSession();
|
||||||
services.ConfigureApplicationCookie(configure => configure.CookieManager = new DistributedCacheCookieManager());
|
services.ConfigureApplicationCookie(configure => configure.CookieManager = new DistributedCacheCookieManager());
|
||||||
services.ConfigureExternalCookie(configure => configure.CookieManager = new DistributedCacheCookieManager());
|
services.ConfigureExternalCookie(configure => configure.CookieManager = new DistributedCacheCookieManager());
|
||||||
services.AddSingleton<IPostConfigureOptions<CookieAuthenticationOptions>>(
|
services.AddSingleton<IPostConfigureOptions<CookieAuthenticationOptions>, ConfigureOpenIdConnectDistributedOptions>();
|
||||||
svcs => new ConfigureOpenIdConnectDistributedOptions(
|
|
||||||
svcs.GetRequiredService<IHttpContextAccessor>(),
|
|
||||||
globalSettings,
|
|
||||||
svcs.GetRequiredService<IdentityServerOptions>())
|
|
||||||
);
|
|
||||||
|
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user