Page MenuHomePhabricator

epic: Support hCaptcha for API editing interfaces
Open, Needs TriagePublic

Description

Summary

Similar to account creation, which now requires an hCaptcha token for form and API based account creations form users without skipcaptcha, we should aim to ensure that 100% of API edits from higher risk sessions without skipcaptcha are going through hCaptcha's bot detection/mitigation checks.

Event Timeline

Dreamy_Jazz subscribed.

For user script maintainers, there is the ext.confirmEdit.CaptchaWidget module that you can load and then use the mw.libs.confirmEdit.CaptchaWidget class to get a hCaptcha widget that provides you a CAPTCHA

If it's helpful for documentation to be created we could add something to docs.wikimedia.org

If it's helpful for documentation to be created we could add something to docs.wikimedia.org

It would be helpful.

From what I understand, the structure for an edit using mw.libs.confirmEdit.CaptchaWidget is something like this where only renderCaptcha and getCaptchaDataForSubmission are necessary. Am I right?

// First edit attempt
const apiResponse = await api.postWithEditToken( { ...params } );
// Failure caused by captcha
if ( apiResponse.edit.result === 'Failure' && editResponse.edit.captcha ) {
	const captchaData = editResponse.edit.captcha;

	await mw.loader.using( 'ext.confirmEdit.CaptchaWidget' );
	const captchaWidget = new mw.libs.confirmEdit.CaptchaWidget( {
		container: myContainer,
		...captchaData
	} );
	captchaWidget.renderCaptcha();
	const captchaResponse = await captchaWidget.getCaptchaDataForSubmission()

	// Second edit attempt using captcha response
	const apiResponse2 = api.postWithEditToken( { ...params, ...captchaResponse } );
}

updateForFailure is necessary. I would also recommend not calling getCaptchaDataForSubmission until the user has pressed save changes again (because there may be a text box to fill first and for hCaptcha we want users to see the privacy policy links before having hCaptcha executed on their device)

So, I'd recommend doing something more like:

let captchaWidget;

// First edit attempt
const apiResponse = api.postWithEditToken( { ...params } );
// Failure caused by captcha
if ( apiResponse.edit.result === 'Failure' && editResponse.edit.captcha ) {
	const captchaData = editResponse.edit.captcha;

	await mw.loader.using( 'ext.confirmEdit.CaptchaWidget' );
	const captchaWidget = mw.libs.confirmEdit.CaptchaWidget( {
		container: myContainer
	} );
	captchaWidget.updateForFailure( captchaData );
	captchaWidget.renderCaptcha();
}

...

function onSecondEditButtonPress() {
	const captchaResponse = await captchaWidget.getCaptchaDataForSubmission();

	// Second edit attempt using captcha response
	const apiResponse2 = api.postWithEditToken( { ...params, ...captchaResponse } );
}