1
0
mirror of https://github.com/bitwarden/server.git synced 2025-07-02 00:22:50 -05:00

Feature/sync Enable hcaptcha on login (#1469)

* Share globalSettings hcaptcha public key with clients

* Require captcha valid only prior to two factor

users with two factor will have already solved captcha is necessary.
Users without two factor will have`TwoFactorVerified` set to false

* Do not require CaptchaResponse on two-factor requests

* Add option to always require captcha for testing purposes

* Allow for self-hosted instances if they want to use it

* Move refresh suggestion to correct error

* Expect lifetime in helper method

* Add captcha bypass token to successful captcha validations

* Remove twofactorValidated

* PR Feedback
This commit is contained in:
Matt Gibson
2021-07-21 13:42:06 -05:00
committed by GitHub
parent 259bf8d760
commit 8e1e2fa2fe
9 changed files with 67 additions and 15 deletions

View File

@ -21,7 +21,6 @@ namespace Bit.Core.IdentityServer
private readonly IUserService _userService;
private readonly ICurrentContext _currentContext;
private readonly ICaptchaValidationService _captchaValidationService;
public ResourceOwnerPasswordValidator(
UserManager<User> userManager,
IDeviceRepository deviceRepository,
@ -60,25 +59,36 @@ namespace Bit.Core.IdentityServer
// return;
//}
if (_captchaValidationService.ServiceEnabled && _currentContext.IsBot)
string bypassToken = null;
if (_captchaValidationService.ServiceEnabled && (_currentContext.IsBot || _captchaValidationService.RequireCaptcha))
{
var captchaResponse = context.Request.Raw["CaptchaResponse"]?.ToString();
var user = await _userManager.FindByEmailAsync(context.UserName.ToLowerInvariant());
var captchaResponse = context.Request.Raw["captchaResponse"]?.ToString();
if (string.IsNullOrWhiteSpace(captchaResponse))
{
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, "Captcha required.");
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, "Captcha required.",
new Dictionary<string, object> {
{ "HCaptcha_SiteKey", _captchaValidationService.SiteKey },
});
return;
}
var captchaValid = await _captchaValidationService.ValidateCaptchaResponseAsync(captchaResponse,
_currentContext.IpAddress);
var captchaValid = _captchaValidationService.ValidateCaptchaBypassToken(captchaResponse, user) ||
await _captchaValidationService.ValidateCaptchaResponseAsync(captchaResponse, _currentContext.IpAddress);
if (!captchaValid)
{
await BuildErrorResultAsync("Captcha is invalid.", false, context, null);
await BuildErrorResultAsync("Captcha is invalid. Please refresh and try again", false, context, null);
return;
}
bypassToken = _captchaValidationService.GenerateCaptchaBypassToken(user);
}
await ValidateAsync(context, context.Request);
if (context.Result.CustomResponse != null && bypassToken != null)
{
context.Result.CustomResponse["CaptchaBypassToken"] = bypassToken;
}
}
protected async override Task<(User, bool)> ValidateContextAsync(ResourceOwnerPasswordValidationContext context)