2022-06-29 19:46:41 -04:00
|
|
|
|
using System.Security.Claims;
|
2023-11-20 15:55:31 +01:00
|
|
|
|
using Bit.Core;
|
2023-11-23 07:07:37 +10:00
|
|
|
|
using Bit.Core.AdminConsole.Services;
|
2023-06-26 20:17:39 -04:00
|
|
|
|
using Bit.Core.Auth.Repositories;
|
2023-04-14 13:25:56 -04:00
|
|
|
|
using Bit.Core.Auth.Services;
|
2021-02-04 12:54:21 -06:00
|
|
|
|
using Bit.Core.Context;
|
2022-01-11 10:40:51 +01:00
|
|
|
|
using Bit.Core.Entities;
|
2017-01-18 00:14:28 -05:00
|
|
|
|
using Bit.Core.Repositories;
|
2017-05-26 00:50:27 -04:00
|
|
|
|
using Bit.Core.Services;
|
2021-02-22 15:35:16 -06:00
|
|
|
|
using Bit.Core.Settings;
|
2021-08-11 06:21:46 +10:00
|
|
|
|
using Bit.Core.Utilities;
|
2023-11-20 16:32:23 -05:00
|
|
|
|
using Duende.IdentityServer.Models;
|
|
|
|
|
|
using Duende.IdentityServer.Validation;
|
2017-01-11 00:34:16 -05:00
|
|
|
|
using Microsoft.AspNetCore.Identity;
|
|
|
|
|
|
|
2024-10-24 10:41:25 -07:00
|
|
|
|
namespace Bit.Identity.IdentityServer.RequestValidators;
|
2022-08-29 16:06:55 -04:00
|
|
|
|
|
2017-05-05 16:11:50 -04:00
|
|
|
|
public class ResourceOwnerPasswordValidator : BaseRequestValidator<ResourceOwnerPasswordValidationContext>,
|
2020-07-16 08:01:39 -04:00
|
|
|
|
IResourceOwnerPasswordValidator
|
2017-01-11 00:34:16 -05:00
|
|
|
|
{
|
2020-07-16 08:01:39 -04:00
|
|
|
|
private UserManager<User> _userManager;
|
2021-05-28 16:04:58 -04:00
|
|
|
|
private readonly ICurrentContext _currentContext;
|
2021-06-16 12:47:41 -04:00
|
|
|
|
private readonly ICaptchaValidationService _captchaValidationService;
|
2022-09-26 13:21:13 -04:00
|
|
|
|
private readonly IAuthRequestRepository _authRequestRepository;
|
2024-10-10 17:26:17 -07:00
|
|
|
|
private readonly IDeviceValidator _deviceValidator;
|
2020-07-16 08:01:39 -04:00
|
|
|
|
public ResourceOwnerPasswordValidator(
|
2017-01-25 22:31:14 -05:00
|
|
|
|
UserManager<User> userManager,
|
2017-12-01 10:07:14 -05:00
|
|
|
|
IUserService userService,
|
2018-04-03 14:31:33 -04:00
|
|
|
|
IEventService eventService,
|
2024-10-10 17:26:17 -07:00
|
|
|
|
IDeviceValidator deviceValidator,
|
2024-10-24 10:41:25 -07:00
|
|
|
|
ITwoFactorAuthenticationValidator twoFactorAuthenticationValidator,
|
2018-04-03 14:31:33 -04:00
|
|
|
|
IOrganizationUserRepository organizationUserRepository,
|
2019-01-24 22:37:49 -05:00
|
|
|
|
IMailService mailService,
|
2020-07-16 08:01:39 -04:00
|
|
|
|
ILogger<ResourceOwnerPasswordValidator> logger,
|
2021-02-04 12:54:21 -06:00
|
|
|
|
ICurrentContext currentContext,
|
2020-07-16 08:01:39 -04:00
|
|
|
|
GlobalSettings globalSettings,
|
2022-03-02 15:45:00 -06:00
|
|
|
|
ICaptchaValidationService captchaValidationService,
|
2022-09-26 13:21:13 -04:00
|
|
|
|
IAuthRequestRepository authRequestRepository,
|
2023-04-17 07:35:47 -07:00
|
|
|
|
IUserRepository userRepository,
|
2023-05-04 15:12:03 -04:00
|
|
|
|
IPolicyService policyService,
|
2023-06-26 20:17:39 -04:00
|
|
|
|
IFeatureService featureService,
|
2023-09-09 14:35:08 -07:00
|
|
|
|
ISsoConfigRepository ssoConfigRepository,
|
2023-11-20 15:55:31 +01:00
|
|
|
|
IUserDecryptionOptionsBuilder userDecryptionOptionsBuilder)
|
2024-10-24 10:41:25 -07:00
|
|
|
|
: base(
|
|
|
|
|
|
userManager,
|
|
|
|
|
|
userService,
|
|
|
|
|
|
eventService,
|
|
|
|
|
|
deviceValidator,
|
|
|
|
|
|
twoFactorAuthenticationValidator,
|
|
|
|
|
|
organizationUserRepository,
|
|
|
|
|
|
mailService,
|
|
|
|
|
|
logger,
|
|
|
|
|
|
currentContext,
|
|
|
|
|
|
globalSettings,
|
|
|
|
|
|
userRepository,
|
|
|
|
|
|
policyService,
|
|
|
|
|
|
featureService,
|
|
|
|
|
|
ssoConfigRepository,
|
|
|
|
|
|
userDecryptionOptionsBuilder)
|
2022-08-29 16:06:55 -04:00
|
|
|
|
{
|
2021-05-28 16:04:58 -04:00
|
|
|
|
_userManager = userManager;
|
|
|
|
|
|
_currentContext = currentContext;
|
|
|
|
|
|
_captchaValidationService = captchaValidationService;
|
2022-09-26 13:21:13 -04:00
|
|
|
|
_authRequestRepository = authRequestRepository;
|
2024-10-10 17:26:17 -07:00
|
|
|
|
_deviceValidator = deviceValidator;
|
2022-08-29 16:06:55 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-07-16 08:01:39 -04:00
|
|
|
|
public async Task ValidateAsync(ResourceOwnerPasswordValidationContext context)
|
2017-01-11 00:34:16 -05:00
|
|
|
|
{
|
2021-06-16 12:47:41 -04:00
|
|
|
|
if (!AuthEmailHeaderIsValid(context))
|
2017-01-11 00:34:16 -05:00
|
|
|
|
{
|
2021-05-28 16:04:58 -04:00
|
|
|
|
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant,
|
|
|
|
|
|
"Auth-Email header invalid.");
|
2022-08-29 16:06:55 -04:00
|
|
|
|
return;
|
2017-01-11 00:34:16 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var user = await _userManager.FindByEmailAsync(context.UserName.ToLowerInvariant());
|
|
|
|
|
|
var validatorContext = new CustomValidatorRequestContext
|
|
|
|
|
|
{
|
2022-05-09 12:25:13 -04:00
|
|
|
|
User = user,
|
2024-10-10 17:26:17 -07:00
|
|
|
|
KnownDevice = await _deviceValidator.KnownDeviceAsync(user, context.Request),
|
2022-08-29 16:06:55 -04:00
|
|
|
|
};
|
2021-09-30 11:24:29 -04:00
|
|
|
|
string bypassToken = null;
|
|
|
|
|
|
if (!validatorContext.KnownDevice &&
|
2021-07-21 13:42:06 -05:00
|
|
|
|
_captchaValidationService.RequireCaptchaValidation(_currentContext, user))
|
2022-08-29 16:06:55 -04:00
|
|
|
|
{
|
2021-07-21 13:42:06 -05:00
|
|
|
|
var captchaResponse = context.Request.Raw["captchaResponse"]?.ToString();
|
2021-06-16 12:47:41 -04:00
|
|
|
|
|
2021-09-30 11:24:29 -04:00
|
|
|
|
if (string.IsNullOrWhiteSpace(captchaResponse))
|
2022-08-29 15:53:48 -04:00
|
|
|
|
{
|
2020-10-26 11:56:16 -05:00
|
|
|
|
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, "Captcha required.",
|
2022-05-09 12:25:13 -04:00
|
|
|
|
new Dictionary<string, object>
|
2022-08-29 15:53:48 -04:00
|
|
|
|
{
|
2021-07-22 12:29:06 -05:00
|
|
|
|
{ _captchaValidationService.SiteKeyResponseKeyName, _captchaValidationService.SiteKey },
|
2022-08-29 15:53:48 -04:00
|
|
|
|
});
|
2021-07-21 13:42:06 -05:00
|
|
|
|
return;
|
2022-08-29 15:53:48 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
2022-05-09 12:25:13 -04:00
|
|
|
|
validatorContext.CaptchaResponse = await _captchaValidationService.ValidateCaptchaResponseAsync(
|
|
|
|
|
|
captchaResponse, _currentContext.IpAddress, user);
|
|
|
|
|
|
if (!validatorContext.CaptchaResponse.Success)
|
2022-08-29 15:53:48 -04:00
|
|
|
|
{
|
2022-05-09 12:25:13 -04:00
|
|
|
|
await BuildErrorResultAsync("Captcha is invalid. Please refresh and try again", false, context, null);
|
|
|
|
|
|
return;
|
2021-07-21 13:42:06 -05:00
|
|
|
|
}
|
|
|
|
|
|
bypassToken = _captchaValidationService.GenerateCaptchaBypassToken(user);
|
2020-07-16 08:01:39 -04:00
|
|
|
|
}
|
2017-01-18 00:14:28 -05:00
|
|
|
|
|
2022-05-09 12:25:13 -04:00
|
|
|
|
await ValidateAsync(context, context.Request, validatorContext);
|
|
|
|
|
|
if (context.Result.CustomResponse != null && bypassToken != null)
|
2020-07-16 08:01:39 -04:00
|
|
|
|
{
|
2022-05-09 12:25:13 -04:00
|
|
|
|
context.Result.CustomResponse["CaptchaBypassToken"] = bypassToken;
|
2017-01-18 00:14:28 -05:00
|
|
|
|
}
|
2022-08-29 16:06:55 -04:00
|
|
|
|
}
|
2017-01-18 00:14:28 -05:00
|
|
|
|
|
2021-11-09 16:37:32 +01:00
|
|
|
|
protected async override Task<bool> ValidateContextAsync(ResourceOwnerPasswordValidationContext context,
|
2020-07-16 08:01:39 -04:00
|
|
|
|
CustomValidatorRequestContext validatorContext)
|
2022-08-29 16:06:55 -04:00
|
|
|
|
{
|
2020-07-16 08:01:39 -04:00
|
|
|
|
if (string.IsNullOrWhiteSpace(context.UserName) || validatorContext.User == null)
|
2017-01-21 23:12:28 -05:00
|
|
|
|
{
|
2021-11-09 16:37:32 +01:00
|
|
|
|
return false;
|
2017-01-21 23:12:28 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
2022-09-26 13:21:13 -04:00
|
|
|
|
var authRequestId = context.Request.Raw["AuthRequest"]?.ToString()?.ToLowerInvariant();
|
|
|
|
|
|
if (!string.IsNullOrWhiteSpace(authRequestId) && Guid.TryParse(authRequestId, out var authRequestGuid))
|
2017-01-25 00:28:18 -05:00
|
|
|
|
{
|
2022-09-26 13:21:13 -04:00
|
|
|
|
var authRequest = await _authRequestRepository.GetByIdAsync(authRequestGuid);
|
|
|
|
|
|
if (authRequest != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
var requestAge = DateTime.UtcNow - authRequest.CreationDate;
|
2022-10-18 14:50:48 -04:00
|
|
|
|
if (requestAge < TimeSpan.FromHours(1) &&
|
2022-09-26 13:21:13 -04:00
|
|
|
|
CoreHelpers.FixedTimeEquals(authRequest.AccessCode, context.Password))
|
|
|
|
|
|
{
|
|
|
|
|
|
authRequest.AuthenticationDate = DateTime.UtcNow;
|
|
|
|
|
|
await _authRequestRepository.ReplaceAsync(authRequest);
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2017-01-25 00:28:18 -05:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
2022-10-18 14:50:48 -04:00
|
|
|
|
|
|
|
|
|
|
if (!await _userService.CheckPasswordAsync(validatorContext.User, context.Password))
|
2022-09-26 13:21:13 -04:00
|
|
|
|
{
|
2022-10-18 14:50:48 -04:00
|
|
|
|
return false;
|
2022-09-26 13:21:13 -04:00
|
|
|
|
}
|
2022-10-18 14:50:48 -04:00
|
|
|
|
return true;
|
2022-08-29 16:06:55 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-07-16 08:01:39 -04:00
|
|
|
|
protected override Task SetSuccessResult(ResourceOwnerPasswordValidationContext context, User user,
|
|
|
|
|
|
List<Claim> claims, Dictionary<string, object> customResponse)
|
2022-08-29 16:06:55 -04:00
|
|
|
|
{
|
2021-11-09 16:37:32 +01:00
|
|
|
|
context.Result = new GrantValidationResult(user.Id.ToString(), "Application",
|
2023-11-20 15:55:31 +01:00
|
|
|
|
identityProvider: Constants.IdentityProvider,
|
2017-02-11 23:00:55 -05:00
|
|
|
|
claims: claims.Count > 0 ? claims : null,
|
2017-02-21 22:52:02 -05:00
|
|
|
|
customResponse: customResponse);
|
2021-11-09 16:37:32 +01:00
|
|
|
|
return Task.CompletedTask;
|
2022-08-29 16:06:55 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-07-16 08:01:39 -04:00
|
|
|
|
protected override void SetTwoFactorResult(ResourceOwnerPasswordValidationContext context,
|
|
|
|
|
|
Dictionary<string, object> customResponse)
|
2022-08-29 16:06:55 -04:00
|
|
|
|
{
|
2017-01-25 00:28:18 -05:00
|
|
|
|
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, "Two factor required.",
|
|
|
|
|
|
customResponse);
|
2022-08-29 16:06:55 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-07-16 08:01:39 -04:00
|
|
|
|
protected override void SetSsoResult(ResourceOwnerPasswordValidationContext context,
|
|
|
|
|
|
Dictionary<string, object> customResponse)
|
2022-08-29 14:53:16 -04:00
|
|
|
|
{
|
2020-10-26 11:56:16 -05:00
|
|
|
|
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, "Sso authentication required.",
|
|
|
|
|
|
customResponse);
|
2022-08-29 16:06:55 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-07-16 08:01:39 -04:00
|
|
|
|
protected override void SetErrorResult(ResourceOwnerPasswordValidationContext context,
|
|
|
|
|
|
Dictionary<string, object> customResponse)
|
2022-08-29 16:06:55 -04:00
|
|
|
|
{
|
2020-07-16 08:01:39 -04:00
|
|
|
|
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, customResponse: customResponse);
|
2020-10-26 11:56:16 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-06-26 20:17:39 -04:00
|
|
|
|
protected override ClaimsPrincipal GetSubject(ResourceOwnerPasswordValidationContext context)
|
|
|
|
|
|
{
|
|
|
|
|
|
return context.Result.Subject;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-07-16 08:01:39 -04:00
|
|
|
|
private bool AuthEmailHeaderIsValid(ResourceOwnerPasswordValidationContext context)
|
2022-08-29 16:06:55 -04:00
|
|
|
|
{
|
2020-07-16 08:01:39 -04:00
|
|
|
|
if (!_currentContext.HttpContext.Request.Headers.ContainsKey("Auth-Email"))
|
2017-01-18 00:14:28 -05:00
|
|
|
|
{
|
2020-07-16 08:01:39 -04:00
|
|
|
|
return false;
|
2017-01-11 00:34:16 -05:00
|
|
|
|
}
|
2021-08-11 06:21:46 +10:00
|
|
|
|
else
|
|
|
|
|
|
{
|
2022-08-29 16:06:55 -04:00
|
|
|
|
try
|
2021-08-11 06:21:46 +10:00
|
|
|
|
{
|
|
|
|
|
|
var authEmailHeader = _currentContext.HttpContext.Request.Headers["Auth-Email"];
|
|
|
|
|
|
var authEmailDecoded = CoreHelpers.Base64UrlDecodeString(authEmailHeader);
|
|
|
|
|
|
|
|
|
|
|
|
if (authEmailDecoded != context.UserName)
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (System.Exception e) when (e is System.InvalidOperationException || e is System.FormatException)
|
2022-08-29 16:06:55 -04:00
|
|
|
|
{
|
2021-08-11 06:21:46 +10:00
|
|
|
|
// Invalid B64 encoding
|
2022-05-09 12:25:13 -04:00
|
|
|
|
return false;
|
2022-08-29 16:06:55 -04:00
|
|
|
|
}
|
2022-08-29 15:53:48 -04:00
|
|
|
|
}
|
2022-08-29 16:06:55 -04:00
|
|
|
|
|
2022-05-09 12:25:13 -04:00
|
|
|
|
return true;
|
2017-01-11 00:34:16 -05:00
|
|
|
|
}
|
|
|
|
|
|
}
|