mirror of
https://github.com/bitwarden/server.git
synced 2025-07-04 01:22:50 -05: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:
@ -1,8 +1,8 @@
|
||||
using Bit.Core.Settings;
|
||||
using Duende.IdentityServer.Configuration;
|
||||
using Duende.IdentityServer.Configuration;
|
||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Caching.StackExchangeRedis;
|
||||
using Microsoft.AspNetCore.DataProtection;
|
||||
using Microsoft.Extensions.Caching.Distributed;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace Bit.Core.IdentityServer;
|
||||
@ -10,15 +10,18 @@ namespace Bit.Core.IdentityServer;
|
||||
public class ConfigureOpenIdConnectDistributedOptions : IPostConfigureOptions<CookieAuthenticationOptions>
|
||||
{
|
||||
private readonly IdentityServerOptions _idsrv;
|
||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly IDistributedCache _distributedCache;
|
||||
private readonly IDataProtectionProvider _dataProtectionProvider;
|
||||
|
||||
public ConfigureOpenIdConnectDistributedOptions(IHttpContextAccessor httpContextAccessor, GlobalSettings globalSettings,
|
||||
public ConfigureOpenIdConnectDistributedOptions(
|
||||
[FromKeyedServices("persistent")]
|
||||
IDistributedCache distributedCache,
|
||||
IDataProtectionProvider dataProtectionProvider,
|
||||
IdentityServerOptions idsrv)
|
||||
{
|
||||
_httpContextAccessor = httpContextAccessor ?? throw new ArgumentNullException(nameof(httpContextAccessor));
|
||||
_globalSettings = globalSettings;
|
||||
_idsrv = idsrv;
|
||||
_distributedCache = distributedCache;
|
||||
_dataProtectionProvider = dataProtectionProvider;
|
||||
}
|
||||
|
||||
public void PostConfigure(string name, CookieAuthenticationOptions options)
|
||||
@ -34,19 +37,7 @@ public class ConfigureOpenIdConnectDistributedOptions : IPostConfigureOptions<Co
|
||||
options.Cookie.Name = AuthenticationSchemes.BitwardenExternalCookieAuthenticationScheme;
|
||||
options.Cookie.IsEssential = true;
|
||||
options.Cookie.SameSite = _idsrv.Authentication.CookieSameSiteMode;
|
||||
options.TicketDataFormat = new DistributedCacheTicketDataFormatter(_httpContextAccessor, name);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(_globalSettings.IdentityServer?.RedisConnectionString))
|
||||
{
|
||||
options.SessionStore = new MemoryCacheTicketStore();
|
||||
}
|
||||
else
|
||||
{
|
||||
var redisOptions = new RedisCacheOptions
|
||||
{
|
||||
Configuration = _globalSettings.IdentityServer.RedisConnectionString,
|
||||
};
|
||||
options.SessionStore = new RedisCacheTicketStore(redisOptions);
|
||||
}
|
||||
options.TicketDataFormat = new DistributedCacheTicketDataFormatter(_distributedCache, _dataProtectionProvider, name);
|
||||
options.SessionStore = new DistributedCacheTicketStore(_distributedCache);
|
||||
}
|
||||
}
|
||||
|
@ -58,7 +58,7 @@ public class DistributedCacheCookieManager : ICookieManager
|
||||
}
|
||||
|
||||
private IDistributedCache GetCache(HttpContext context) =>
|
||||
context.RequestServices.GetRequiredService<IDistributedCache>();
|
||||
context.RequestServices.GetRequiredKeyedService<IDistributedCache>("persistent");
|
||||
|
||||
private string GetKey(string key, string id) => $"{CacheKeyPrefix}-{key}-{id}";
|
||||
|
||||
|
@ -1,32 +1,32 @@
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.DataProtection;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Caching.Distributed;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Bit.Core.IdentityServer;
|
||||
|
||||
public class DistributedCacheTicketDataFormatter : ISecureDataFormat<AuthenticationTicket>
|
||||
{
|
||||
private readonly IHttpContextAccessor _httpContext;
|
||||
private readonly string _name;
|
||||
private const string CacheKeyPrefix = "ticket-data";
|
||||
|
||||
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;
|
||||
_name = name;
|
||||
_distributedCache = distributedCache;
|
||||
_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, string purpose)
|
||||
{
|
||||
var key = Guid.NewGuid().ToString();
|
||||
var cacheKey = $"{CacheKeyPrefix}-{_name}-{purpose}-{key}";
|
||||
var cacheKey = $"{_prefix}-{purpose}-{key}";
|
||||
|
||||
var expiresUtc = data.Properties.ExpiresUtc ??
|
||||
DateTimeOffset.UtcNow.AddMinutes(15);
|
||||
@ -35,9 +35,9 @@ public class DistributedCacheTicketDataFormatter : ISecureDataFormat<Authenticat
|
||||
options.SetAbsoluteExpiration(expiresUtc);
|
||||
|
||||
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);
|
||||
@ -49,9 +49,9 @@ public class DistributedCacheTicketDataFormatter : ISecureDataFormat<Authenticat
|
||||
}
|
||||
|
||||
// Decrypt the key and retrieve the data from the cache.
|
||||
var key = Protector.Unprotect(protectedText);
|
||||
var cacheKey = $"{CacheKeyPrefix}-{_name}-{purpose}-{key}";
|
||||
var ticket = Cache.Get(cacheKey);
|
||||
var key = _dataProtector.Unprotect(protectedText);
|
||||
var cacheKey = $"{_prefix}-{purpose}-{key}";
|
||||
var ticket = _distributedCache.Get(cacheKey);
|
||||
|
||||
if (ticket == null)
|
||||
{
|
||||
|
@ -1,23 +1,22 @@
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Authentication.Cookies;
|
||||
using Microsoft.Extensions.Caching.Distributed;
|
||||
using Microsoft.Extensions.Caching.StackExchangeRedis;
|
||||
|
||||
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;
|
||||
|
||||
public RedisCacheTicketStore(RedisCacheOptions options)
|
||||
public DistributedCacheTicketStore(IDistributedCache distributedCache)
|
||||
{
|
||||
_cache = new RedisCache(options);
|
||||
_cache = distributedCache;
|
||||
}
|
||||
|
||||
public async Task<string> StoreAsync(AuthenticationTicket ticket)
|
||||
{
|
||||
var key = $"{_keyPrefix}{Guid.NewGuid()}";
|
||||
var key = $"{KeyPrefix}{Guid.NewGuid()}";
|
||||
await RenewAsync(key, ticket);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user