Issue with Google ReCaptcha Password Leak API

Hi, I am trying to follow instructions to embed the Google Recaptcha Password Leak check API into my application. https://cloud.google.com/recaptcha-enterprise/docs/check-passwords

The code works fine, but I always get true response for the leak check.. no matter what username and password I try, it always says it is compromised. Has anyone tried this API? am I missing something?

3 7 814
7 REPLIES 7

@nabbasi_1  I am also facing the same issue, any workaround?

Same here. Here is a code sample written in NestJs as a validator that replicates the problem.

 

@ValidatorConstraint({ name: "IsGoodPassword" })
export class IsGoodPasswordConstraint implements ValidatorConstraintInterface {
	private readonly logger = new Logger("IsGoodPasswordConstraint");
	protected client: RecaptchaEnterpriseServiceClient =
		new RecaptchaEnterpriseServiceClient();

	async validate(password: string, args: ValidationArguments) {
		const username = args.object["username"];

		const verification: PasswordCheckVerification =
			await PasswordCheckVerification.create(username, password);

		const lookupHashPrefix: string = Buffer.from(
			verification.getLookupHashPrefix()
		).toString("base64");

		const encryptedUserCredentialsHash: string = Buffer.from(
			verification.getEncryptedUserCredentialsHash()
		).toString("base64");

		const credentials: google.cloud.recaptchaenterprise.v1.IPrivatePasswordLeakVerification =
			await this.createPasswordLeakAssessment(
				lookupHashPrefix,
				encryptedUserCredentialsHash
			);

		const reencryptedUserCredentialsHash: Uint8Array = Buffer.from(
			credentials.reencryptedUserCredentialsHash!.toString(),
			"base64"
		);

		const encryptedLeakMatchPrefixes: Uint8Array[] =
			credentials.encryptedLeakMatchPrefixes!.map(prefix => {
				return Buffer.from(prefix.toString(), "base64");
			});

		const status = verification.verify(reencryptedUserCredentialsHash, encryptedLeakMatchPrefixes);
		console.log("STATUS:", status);

		console.log(`Credential leaked: ${status.areCredentialsLeaked()}`);
		return !status.areCredentialsLeaked();
	}

	async createPasswordLeakAssessment(
		lookupHashPrefix: string,
		encryptedUserCredentialsHash: string
	): Promise<google.cloud.recaptchaenterprise.v1.IPrivatePasswordLeakVerification> {
		const createAssessmentRequest: google.cloud.recaptchaenterprise.v1.ICreateAssessmentRequest =
			{
				parent: `projects/**censored**`,
				assessment: {
					privatePasswordLeakVerification: {
						lookupHashPrefix,
						encryptedUserCredentialsHash
					}
				}
			};

		const [response] = await this.client.createAssessment(
			createAssessmentRequest
		);

		if (!response?.privatePasswordLeakVerification) {
			this.logger.error(response);

			throw new InternalServerErrorException(
				"Error obtaining response from reCaptcha"
			);
		}

		return response.privatePasswordLeakVerification;
	}
}

export function IsGoodPassword(
	username?: string,
	validationOptions?: ValidationOptions
) {
	// eslint-disable-next-line @typescript-eslint/no-explicit-any
	return function (object: any, propertyName: string) {
		registerDecorator({
			target: object.constructor,
			propertyName: propertyName,
			constraints: [username],
			options: {
				message: `${propertyName} was compromised`,
				...validationOptions
			},
			validator: IsGoodPasswordConstraint
		});
	};
}

 

Also facing the same issue. @nabbasi_1 @PloggCTO were either of you able to identify the root cause/the bug?

No news on that unfortunately.

No, no solution yet.. 

By the way, using the sample code from this repo seemed to do the trick.

Thanks, will look at it ASAP.