Skip to content

Commit

Permalink
Two Factor: Allow users to log in with backup codes and improve the s…
Browse files Browse the repository at this point in the history
…ecurity keys flow (#87128)

* Two Factor Security Keys: Automatically prompt for a security key.

The current 2fa authentication flow with security keys require users to
click on the "Continue with security key" button before we attempting to
initiate this flow.

This change automatically asks for a security key if the given account
supports it. If it fails for some reason, the old button will still work
as always if the user decides to retry the same action.

* Allow users to log in with backup codes when the enhanced security mode
is on.

* Remove useless code.

This removes the `setTimeout()` call as well as the `event.preventDefault()`
statements which don't seem to be needed.

* Remove the data-e2e-link attribute

* Remove useless setTimeout/event.preventDefault statements.

* Reinstante event.preventDefault()

This partially reverts 1fb7185 and
31663da

`event.preventDefault()` is needed one one manually clicks on the
"Continue with security" key button after a failed authentication
attempt. If it's missing, the form is submitted and there's a full page
reload.
  • Loading branch information
xknown authored Mar 18, 2024
1 parent 0ce1cea commit 93f882a
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,11 @@ class SecurityKeyForm extends Component {
isAuthenticating: false,
};

initiateSecurityKeyAuthentication = ( event ) => {
event.preventDefault();
componentDidMount() {
this.initiateSecurityKeyAuthentication();
}

initiateSecurityKeyAuthentication = () => {
const { onSuccess } = this.props;
this.setState( { isAuthenticating: true } );
this.props
Expand All @@ -50,7 +52,10 @@ class SecurityKeyForm extends Component {
className={ classNames( 'two-factor-authentication__verification-code-form-wrapper', {
isWoo: isWoo,
} ) }
onSubmit={ this.initiateSecurityKeyAuthentication }
onSubmit={ ( event ) => {
event.preventDefault();
this.initiateSecurityKeyAuthentication();
} }
>
<Card compact className="two-factor-authentication__verification-code-form">
{ ! this.state.isAuthenticating && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,15 @@ class TwoFactorActions extends Component {

this.props.switchTwoFactorAuthType( 'authenticator' );
};

recordBackupLinkClick = ( event ) => {
event.preventDefault();

this.props.recordTracksEvent( 'calypso_login_two_factor_switch_to_backup_link_click' );

this.props.switchTwoFactorAuthType( 'backup' );
};

recordSecurityKey = ( event ) => {
event.preventDefault();
this.props.switchTwoFactorAuthType( 'webauthn' );
Expand All @@ -52,19 +61,26 @@ class TwoFactorActions extends Component {
render() {
const {
isAuthenticatorSupported,
isBackupCodeSupported,
isSecurityKeySupported,
isSmsSupported,
translate,
twoFactorAuthType,
} = this.props;

const isSmsAvailable = isSmsSupported && twoFactorAuthType !== 'sms';
const isBackupCodeAvailable = isBackupCodeSupported && twoFactorAuthType !== 'backup';
const isAuthenticatorAvailable =
isAuthenticatorSupported && twoFactorAuthType !== 'authenticator';
const isSecurityKeyAvailable =
isWebAuthnSupported() && isSecurityKeySupported && twoFactorAuthType !== 'webauthn';

if ( ! isSmsAvailable && ! isAuthenticatorAvailable && ! isSecurityKeyAvailable ) {
if (
! isSmsAvailable &&
! isAuthenticatorAvailable &&
! isSecurityKeyAvailable &&
! isBackupCodeAvailable
) {
return null;
}

Expand All @@ -89,6 +105,12 @@ class TwoFactorActions extends Component {
{ translate( 'Continue with your authenticator\u00A0app' ) }
</Button>
) }

{ isBackupCodeAvailable && (
<Button onClick={ this.recordBackupLinkClick }>
{ translate( 'Continue with a backup code' ) }
</Button>
) }
</Card>
</Fragment>
);
Expand All @@ -98,6 +120,7 @@ class TwoFactorActions extends Component {
export default connect(
( state ) => ( {
isAuthenticatorSupported: isTwoFactorAuthTypeSupported( state, 'authenticator' ),
isBackupCodeSupported: isTwoFactorAuthTypeSupported( state, 'backup' ),
isSmsSupported: isTwoFactorAuthTypeSupported( state, 'sms' ),
isSecurityKeySupported: isTwoFactorAuthTypeSupported( state, 'webauthn' ),
isWoo:
Expand Down
16 changes: 12 additions & 4 deletions client/me/reauth-required/security-key-form.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,11 @@ class SecurityKeyForm extends Component {
showError: false,
};

initiateSecurityKeyAuthentication = ( event, retryRequest = true ) => {
event.preventDefault();
componentDidMount() {
this.initiateSecurityKeyAuthentication();
}

initiateSecurityKeyAuthentication = ( retryRequest = true ) => {
this.setState( { isAuthenticating: true, showError: false } );

this.props.twoStepAuthorization
Expand All @@ -33,7 +36,7 @@ class SecurityKeyForm extends Component {
if ( errors.some( ( e ) => e.code === 'invalid_two_step_nonce' ) ) {
this.props.twoStepAuthorization.fetch( () => {
if ( retryRequest ) {
this.initiateSecurityKeyAuthentication( event, false );
this.initiateSecurityKeyAuthentication( false );
} else {
// We only retry once, so let's show the original error.
this.setState( { isAuthenticating: false, showError: true } );
Expand All @@ -58,7 +61,12 @@ class SecurityKeyForm extends Component {
const { isAuthenticating } = this.state;

return (
<form onSubmit={ this.initiateSecurityKeyAuthentication }>
<form
onSubmit={ ( event ) => {
event.preventDefault();
this.initiateSecurityKeyAuthentication();
} }
>
<Card compact className="security-key-form__verification-code-form">
{ ! isAuthenticating ? (
<div>
Expand Down

0 comments on commit 93f882a

Please sign in to comment.