mirror of
https://github.com/bitwarden/browser
synced 2026-01-07 02:53:28 +00:00
Feature/use hcaptcha if bot (#430)
* Handle hcaptch required identity response * Refactor iframe component for captcha and webauthn * Send captcha token to server * Add captcha callback * Clear captcha state * Remove captcha storage * linter fixes * Rename iframe components to include IFrame * Remove callback in favor of extenting submit * Limit publickey credentials access * Use captcha bypass token to bypass captcha for twofactor auth flows * Linter fixes * Set iframe version in components
This commit is contained in:
@@ -160,6 +160,7 @@ import { ChallengeResponse } from '../models/response/twoFactorWebAuthnResponse'
|
||||
import { TwoFactorYubiKeyResponse } from '../models/response/twoFactorYubiKeyResponse';
|
||||
import { UserKeyResponse } from '../models/response/userKeyResponse';
|
||||
|
||||
import { IdentityCaptchaResponse } from '../models/response/identityCaptchaResponse';
|
||||
import { SendAccessView } from '../models/view/sendAccessView';
|
||||
|
||||
export class ApiService implements ApiServiceAbstraction {
|
||||
@@ -215,7 +216,7 @@ export class ApiService implements ApiServiceAbstraction {
|
||||
|
||||
// Auth APIs
|
||||
|
||||
async postIdentityToken(request: TokenRequest): Promise<IdentityTokenResponse | IdentityTwoFactorResponse> {
|
||||
async postIdentityToken(request: TokenRequest): Promise<IdentityTokenResponse | IdentityTwoFactorResponse | IdentityCaptchaResponse> {
|
||||
const headers = new Headers({
|
||||
'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8',
|
||||
'Accept': 'application/json',
|
||||
@@ -245,6 +246,9 @@ export class ApiService implements ApiServiceAbstraction {
|
||||
Object.keys(responseJson.TwoFactorProviders2).length) {
|
||||
await this.tokenService.clearTwoFactorToken(request.email);
|
||||
return new IdentityTwoFactorResponse(responseJson);
|
||||
} else if (response.status === 400 && responseJson.HCaptcha_SiteKey &&
|
||||
Object.keys(responseJson.HCaptcha_SiteKey).length) {
|
||||
return new IdentityCaptchaResponse(responseJson);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -87,6 +87,7 @@ export class AuthService implements AuthServiceAbstraction {
|
||||
clientSecret: string;
|
||||
twoFactorProvidersData: Map<TwoFactorProviderType, { [key: string]: string; }>;
|
||||
selectedTwoFactorProviderType: TwoFactorProviderType = null;
|
||||
captchaToken: string;
|
||||
|
||||
private key: SymmetricCryptoKey;
|
||||
|
||||
@@ -120,14 +121,14 @@ export class AuthService implements AuthServiceAbstraction {
|
||||
TwoFactorProviders[TwoFactorProviderType.Yubikey].description = this.i18nService.t('yubiKeyDesc');
|
||||
}
|
||||
|
||||
async logIn(email: string, masterPassword: string): Promise<AuthResult> {
|
||||
async logIn(email: string, masterPassword: string, captchaToken?: string): Promise<AuthResult> {
|
||||
this.selectedTwoFactorProviderType = null;
|
||||
const key = await this.makePreloginKey(masterPassword, email);
|
||||
const hashedPassword = await this.cryptoService.hashPassword(masterPassword, key);
|
||||
const localHashedPassword = await this.cryptoService.hashPassword(masterPassword, key,
|
||||
HashPurpose.LocalAuthorization);
|
||||
return await this.logInHelper(email, hashedPassword, localHashedPassword, null, null, null, null, null,
|
||||
key, null, null, null);
|
||||
key, null, null, null, captchaToken);
|
||||
}
|
||||
|
||||
async logInSso(code: string, codeVerifier: string, redirectUrl: string): Promise<AuthResult> {
|
||||
@@ -146,7 +147,7 @@ export class AuthService implements AuthServiceAbstraction {
|
||||
remember?: boolean): Promise<AuthResult> {
|
||||
return await this.logInHelper(this.email, this.masterPasswordHash, this.localMasterPasswordHash, this.code,
|
||||
this.codeVerifier, this.ssoRedirectUrl, this.clientId, this.clientSecret, this.key, twoFactorProvider,
|
||||
twoFactorToken, remember);
|
||||
twoFactorToken, remember, this.captchaToken);
|
||||
}
|
||||
|
||||
async logInComplete(email: string, masterPassword: string, twoFactorProvider: TwoFactorProviderType,
|
||||
@@ -272,7 +273,7 @@ export class AuthService implements AuthServiceAbstraction {
|
||||
|
||||
private async logInHelper(email: string, hashedPassword: string, localHashedPassword: string, code: string,
|
||||
codeVerifier: string, redirectUrl: string, clientId: string, clientSecret: string, key: SymmetricCryptoKey,
|
||||
twoFactorProvider?: TwoFactorProviderType, twoFactorToken?: string, remember?: boolean): Promise<AuthResult> {
|
||||
twoFactorProvider?: TwoFactorProviderType, twoFactorToken?: string, remember?: boolean, captchaToken?: string): Promise<AuthResult> {
|
||||
const storedTwoFactorToken = await this.tokenService.getTwoFactorToken(email);
|
||||
const appId = await this.appIdService.getAppId();
|
||||
const deviceRequest = new DeviceRequest(appId, this.platformUtilsService);
|
||||
@@ -300,24 +301,27 @@ export class AuthService implements AuthServiceAbstraction {
|
||||
let request: TokenRequest;
|
||||
if (twoFactorToken != null && twoFactorProvider != null) {
|
||||
request = new TokenRequest(emailPassword, codeCodeVerifier, clientIdClientSecret, twoFactorProvider,
|
||||
twoFactorToken, remember, deviceRequest);
|
||||
twoFactorToken, remember, captchaToken, deviceRequest);
|
||||
} else if (storedTwoFactorToken != null) {
|
||||
request = new TokenRequest(emailPassword, codeCodeVerifier, clientIdClientSecret, TwoFactorProviderType.Remember,
|
||||
storedTwoFactorToken, false, deviceRequest);
|
||||
request = new TokenRequest(emailPassword, codeCodeVerifier, clientIdClientSecret,
|
||||
TwoFactorProviderType.Remember, storedTwoFactorToken, false, captchaToken, deviceRequest);
|
||||
} else {
|
||||
request = new TokenRequest(emailPassword, codeCodeVerifier, clientIdClientSecret, null,
|
||||
null, false, deviceRequest);
|
||||
null, false, captchaToken, deviceRequest);
|
||||
}
|
||||
|
||||
const response = await this.apiService.postIdentityToken(request);
|
||||
|
||||
this.clearState();
|
||||
const result = new AuthResult();
|
||||
result.twoFactor = !(response as any).accessToken;
|
||||
result.captchaSiteKey = (response as any).siteKey;
|
||||
if (!!result.captchaSiteKey) {
|
||||
return result;
|
||||
}
|
||||
result.twoFactor = !!(response as any).twoFactorProviders2;
|
||||
|
||||
if (result.twoFactor) {
|
||||
// two factor required
|
||||
const twoFactorResponse = response as IdentityTwoFactorResponse;
|
||||
this.email = email;
|
||||
this.masterPasswordHash = hashedPassword;
|
||||
this.localMasterPasswordHash = localHashedPassword;
|
||||
@@ -327,8 +331,10 @@ export class AuthService implements AuthServiceAbstraction {
|
||||
this.clientId = clientId;
|
||||
this.clientSecret = clientSecret;
|
||||
this.key = this.setCryptoKeys ? key : null;
|
||||
const twoFactorResponse = response as IdentityTwoFactorResponse;
|
||||
this.twoFactorProvidersData = twoFactorResponse.twoFactorProviders2;
|
||||
result.twoFactorProviders = twoFactorResponse.twoFactorProviders2;
|
||||
this.captchaToken = twoFactorResponse.captchaToken;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user