Files
server/src/Core/IdentityServer/ResourceOwnerPasswordValidator.cs

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

173 lines
6.6 KiB
C#
Raw Normal View History

using System.Security.Claims;
using Bit.Core.Context;
using Bit.Core.Entities;
2018-04-03 14:31:33 -04:00
using Bit.Core.Identity;
using Bit.Core.Repositories;
using Bit.Core.Services;
using Bit.Core.Settings;
using Bit.Core.Utilities;
using IdentityServer4.Models;
using IdentityServer4.Validation;
using Microsoft.AspNetCore.Identity;
2019-11-22 07:30:32 -05:00
using Microsoft.Extensions.Logging;
2017-05-05 16:11:50 -04:00
namespace Bit.Core.IdentityServer;
2022-08-29 16:06:55 -04:00
2017-05-05 16:11:50 -04:00
public class ResourceOwnerPasswordValidator : BaseRequestValidator<ResourceOwnerPasswordValidationContext>,
IResourceOwnerPasswordValidator
{
private UserManager<User> _userManager;
2017-06-20 14:50:12 -04:00
private readonly IUserService _userService;
private readonly ICurrentContext _currentContext;
private readonly ICaptchaValidationService _captchaValidationService;
public ResourceOwnerPasswordValidator(
UserManager<User> userManager,
IDeviceRepository deviceRepository,
2017-06-20 14:50:12 -04:00
IDeviceService deviceService,
2017-12-01 10:07:14 -05:00
IUserService userService,
2018-04-03 14:31:33 -04:00
IEventService eventService,
IOrganizationDuoWebTokenProvider organizationDuoWebTokenProvider,
2018-04-03 14:31:33 -04:00
IOrganizationRepository organizationRepository,
IOrganizationUserRepository organizationUserRepository,
IApplicationCacheService applicationCacheService,
IMailService mailService,
ILogger<ResourceOwnerPasswordValidator> logger,
ICurrentContext currentContext,
GlobalSettings globalSettings,
IPolicyRepository policyRepository,
ICaptchaValidationService captchaValidationService,
IUserRepository userRepository)
: base(userManager, deviceRepository, deviceService, userService, eventService,
organizationDuoWebTokenProvider, organizationRepository, organizationUserRepository,
applicationCacheService, mailService, logger, currentContext, globalSettings, policyRepository,
userRepository, captchaValidationService)
2022-08-29 16:06:55 -04:00
{
_userManager = userManager;
_userService = userService;
_currentContext = currentContext;
_captchaValidationService = captchaValidationService;
2022-08-29 16:06:55 -04:00
}
public async Task ValidateAsync(ResourceOwnerPasswordValidationContext context)
{
if (!AuthEmailHeaderIsValid(context))
{
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant,
"Auth-Email header invalid.");
2022-08-29 16:06:55 -04:00
return;
}
var user = await _userManager.FindByEmailAsync(context.UserName.ToLowerInvariant());
var validatorContext = new CustomValidatorRequestContext
{
User = user,
KnownDevice = await KnownDeviceAsync(user, context.Request)
2022-08-29 16:06:55 -04:00
};
string bypassToken = null;
if (!validatorContext.KnownDevice &&
_captchaValidationService.RequireCaptchaValidation(_currentContext, user))
2022-08-29 16:06:55 -04:00
{
var captchaResponse = context.Request.Raw["captchaResponse"]?.ToString();
if (string.IsNullOrWhiteSpace(captchaResponse))
{
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, "Captcha required.",
new Dictionary<string, object>
{
{ _captchaValidationService.SiteKeyResponseKeyName, _captchaValidationService.SiteKey },
});
return;
}
validatorContext.CaptchaResponse = await _captchaValidationService.ValidateCaptchaResponseAsync(
captchaResponse, _currentContext.IpAddress, user);
if (!validatorContext.CaptchaResponse.Success)
{
await BuildErrorResultAsync("Captcha is invalid. Please refresh and try again", false, context, null);
return;
}
bypassToken = _captchaValidationService.GenerateCaptchaBypassToken(user);
}
await ValidateAsync(context, context.Request, validatorContext);
if (context.Result.CustomResponse != null && bypassToken != null)
{
context.Result.CustomResponse["CaptchaBypassToken"] = bypassToken;
}
2022-08-29 16:06:55 -04:00
}
protected async override Task<bool> ValidateContextAsync(ResourceOwnerPasswordValidationContext context,
CustomValidatorRequestContext validatorContext)
2022-08-29 16:06:55 -04:00
{
if (string.IsNullOrWhiteSpace(context.UserName) || validatorContext.User == null)
{
return false;
}
if (!await _userService.CheckPasswordAsync(validatorContext.User, context.Password))
2017-01-25 00:28:18 -05:00
{
return false;
}
return true;
2022-08-29 16:06:55 -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
{
context.Result = new GrantValidationResult(user.Id.ToString(), "Application",
identityProvider: "bitwarden",
claims: claims.Count > 0 ? claims : null,
customResponse: customResponse);
return Task.CompletedTask;
2022-08-29 16:06:55 -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
}
protected override void SetSsoResult(ResourceOwnerPasswordValidationContext context,
Dictionary<string, object> customResponse)
2022-08-29 14:53:16 -04:00
{
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, "Sso authentication required.",
customResponse);
2022-08-29 16:06:55 -04:00
}
protected override void SetErrorResult(ResourceOwnerPasswordValidationContext context,
Dictionary<string, object> customResponse)
2022-08-29 16:06:55 -04:00
{
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, customResponse: customResponse);
}
private bool AuthEmailHeaderIsValid(ResourceOwnerPasswordValidationContext context)
2022-08-29 16:06:55 -04:00
{
if (!_currentContext.HttpContext.Request.Headers.ContainsKey("Auth-Email"))
{
return false;
}
else
{
2022-08-29 16:06:55 -04:00
try
{
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
{
// Invalid B64 encoding
return false;
2022-08-29 16:06:55 -04:00
}
}
2022-08-29 16:06:55 -04:00
return true;
}
}