1
0
mirror of https://github.com/bitwarden/server.git synced 2025-04-06 05:28:15 -05:00

feat : add feature flag to grant validator;

fix : authed user flag stays in sessions for 5 minutes to account for 2FA
This commit is contained in:
Ike Kottlowski 2025-03-21 10:50:04 -04:00
parent 77206b12a9
commit ac8bf0f3dc
No known key found for this signature in database
GPG Key ID: C86308E3DCA6D76F
2 changed files with 16 additions and 10 deletions

View File

@ -197,7 +197,11 @@ public class OpaqueKeyExchangeService : IOpaqueKeyExchangeService
await _distributedCache.SetAsync( await _distributedCache.SetAsync(
string.Format(LOGIN_SESSION_KEY, sessionId), string.Format(LOGIN_SESSION_KEY, sessionId),
Encoding.ASCII.GetBytes(JsonSerializer.Serialize(loginSession)), Encoding.ASCII.GetBytes(JsonSerializer.Serialize(loginSession)),
_distributedCacheEntryOptions); new DistributedCacheEntryOptions()
{
// Our login sessions are 5 minutes long so if a user needs to accomplish 2FA this ensures the user has time to do so.
AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(5)
});
return true; return true;
} }

View File

@ -1,7 +1,6 @@
using System.Security.Claims; using System.Security.Claims;
using Bit.Core; using Bit.Core;
using Bit.Core.AdminConsole.Services; using Bit.Core.AdminConsole.Services;
using Bit.Core.Auth.Models.Business.Tokenables;
using Bit.Core.Auth.Repositories; using Bit.Core.Auth.Repositories;
using Bit.Core.Auth.Services; using Bit.Core.Auth.Services;
using Bit.Core.Context; using Bit.Core.Context;
@ -9,7 +8,6 @@ using Bit.Core.Entities;
using Bit.Core.Repositories; using Bit.Core.Repositories;
using Bit.Core.Services; using Bit.Core.Services;
using Bit.Core.Settings; using Bit.Core.Settings;
using Bit.Core.Tokens;
using Duende.IdentityServer.Models; using Duende.IdentityServer.Models;
using Duende.IdentityServer.Validation; using Duende.IdentityServer.Validation;
using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Identity;
@ -19,8 +17,8 @@ namespace Bit.Identity.IdentityServer.RequestValidators;
public class OpaqueKeyExchangeGrantValidator : BaseRequestValidator<ExtensionGrantValidationContext>, IExtensionGrantValidator public class OpaqueKeyExchangeGrantValidator : BaseRequestValidator<ExtensionGrantValidationContext>, IExtensionGrantValidator
{ {
public const string GrantType = "opaque-ke"; public const string GrantType = "opaque-ke";
private IUserRepository userRepository; private readonly IOpaqueKeyExchangeService _opaqueKeyExchangeService;
private IOpaqueKeyExchangeService opaqueKeyExchangeService; private readonly IFeatureService _featureService;
public OpaqueKeyExchangeGrantValidator( public OpaqueKeyExchangeGrantValidator(
UserManager<User> userManager, UserManager<User> userManager,
@ -36,7 +34,6 @@ public class OpaqueKeyExchangeGrantValidator : BaseRequestValidator<ExtensionGra
ISsoConfigRepository ssoConfigRepository, ISsoConfigRepository ssoConfigRepository,
IUserRepository userRepository, IUserRepository userRepository,
IPolicyService policyService, IPolicyService policyService,
IDataProtectorTokenFactory<WebAuthnLoginAssertionOptionsTokenable> assertionOptionsDataProtector,
IFeatureService featureService, IFeatureService featureService,
IUserDecryptionOptionsBuilder userDecryptionOptionsBuilder, IUserDecryptionOptionsBuilder userDecryptionOptionsBuilder,
IOpaqueKeyExchangeService opaqueKeyExchangeService) IOpaqueKeyExchangeService opaqueKeyExchangeService)
@ -57,14 +54,19 @@ public class OpaqueKeyExchangeGrantValidator : BaseRequestValidator<ExtensionGra
ssoConfigRepository, ssoConfigRepository,
userDecryptionOptionsBuilder) userDecryptionOptionsBuilder)
{ {
this.userRepository = userRepository; _opaqueKeyExchangeService = opaqueKeyExchangeService;
this.opaqueKeyExchangeService = opaqueKeyExchangeService;
} }
string IExtensionGrantValidator.GrantType => "opaque-ke"; string IExtensionGrantValidator.GrantType => "opaque-ke";
public async Task ValidateAsync(ExtensionGrantValidationContext context) public async Task ValidateAsync(ExtensionGrantValidationContext context)
{ {
if (!_featureService.IsEnabled(FeatureFlagKeys.OpaqueKeyExchange))
{
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant);
return;
}
var sessionId = context.Request.Raw.Get("sessionId"); var sessionId = context.Request.Raw.Get("sessionId");
if (string.IsNullOrWhiteSpace(sessionId)) if (string.IsNullOrWhiteSpace(sessionId))
{ {
@ -72,7 +74,7 @@ public class OpaqueKeyExchangeGrantValidator : BaseRequestValidator<ExtensionGra
return; return;
} }
var user = await opaqueKeyExchangeService.GetUserForAuthenticatedSession(Guid.Parse(sessionId)); var user = await _opaqueKeyExchangeService.GetUserForAuthenticatedSession(Guid.Parse(sessionId));
if (user == null) if (user == null)
{ {
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant); context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant);
@ -104,7 +106,7 @@ public class OpaqueKeyExchangeGrantValidator : BaseRequestValidator<ExtensionGra
identityProvider: Constants.IdentityProvider, identityProvider: Constants.IdentityProvider,
claims: claims.Count > 0 ? claims : null, claims: claims.Count > 0 ? claims : null,
customResponse: customResponse); customResponse: customResponse);
await opaqueKeyExchangeService.ClearAuthenticationSession(Guid.Parse(context.Request.Raw.Get("sessionId"))); await _opaqueKeyExchangeService.ClearAuthenticationSession(Guid.Parse(context.Request.Raw.Get("sessionId")));
} }
protected override ClaimsPrincipal GetSubject(ExtensionGrantValidationContext context) protected override ClaimsPrincipal GetSubject(ExtensionGrantValidationContext context)