Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Track if backup codes have been verified #257

Merged
merged 2 commits into from
Sep 25, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion settings/src/components/backup-codes.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import ScreenLink from './screen-link';
export default function BackupCodes() {
const {
user: { backupCodesEnabled, hasPrimaryProvider, backupCodesRemaining },
backupCodesVerified,
} = useContext( GlobalContext );
const [ regenerating, setRegenerating ] = useState( false );

Expand All @@ -37,7 +38,12 @@ export default function BackupCodes() {
);
}

if ( ! backupCodesEnabled || backupCodesRemaining === 0 || regenerating ) {
if (
! backupCodesEnabled ||
backupCodesRemaining === 0 ||
regenerating ||
! backupCodesVerified
) {
return <Setup setRegenerating={ setRegenerating } />;
}

Expand All @@ -56,6 +62,7 @@ function Setup( { setRegenerating } ) {
user: { userRecord },
setError,
error,
setBackupCodesVerified,
} = useContext( GlobalContext );
const [ backupCodes, setBackupCodes ] = useState( [] );
const [ hasPrinted, setHasPrinted ] = useState( false );
Expand All @@ -79,6 +86,12 @@ function Setup( { setRegenerating } ) {
} );

setBackupCodes( response.codes );

// Update the Account Status screen in case they click `Back` without verifying the codes, but
// don't redirect to the Manage screen yet. This is mainly due to the side-effects of
// `two-factor/#507`, so it will need to be modified or maybe removed when that is fixed upstream.
setBackupCodesVerified( false );
await refreshRecord( userRecord );
} catch ( apiFetchError ) {
setError( apiFetchError );
}
Expand All @@ -91,6 +104,7 @@ function Setup( { setRegenerating } ) {
const handleFinished = useCallback( async () => {
// TODO: Add try catch here after https://github.com/WordPress/wporg-two-factor/pull/187/files is merged.
// The codes have already been saved to usermeta, see `generateCodes()` above.
setBackupCodesVerified( true );
await refreshRecord( userRecord ); // This has the intended side-effect of redirecting to the Manage screen.
setGlobalNotice( 'Backup codes have been enabled.' );
setRegenerating( false );
Expand Down
8 changes: 7 additions & 1 deletion settings/src/components/screen-link.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { useCallback, useContext } from '@wordpress/element';
import { GlobalContext } from '../script';

export default function ScreenLink( { screen, anchorText, buttonStyle = false, ariaLabel } ) {
const { navigateToScreen } = useContext( GlobalContext );
const { navigateToScreen, setBackupCodesVerified } = useContext( GlobalContext );
const classes = [];
const screenUrl = new URL( document.location.href );

Expand All @@ -26,6 +26,12 @@ export default function ScreenLink( { screen, anchorText, buttonStyle = false, a
const onClick = useCallback(
( event ) => {
event.preventDefault();

// When generating Backup Codes, they're automatically saved to the database, so clicking `Back` is
// implicitly verifying them, or at least needs to be treated that way. This should be removed once
// `two-factor/#507` is fixed, though.
setBackupCodesVerified( true );

navigateToScreen( screen );
},
[ navigateToScreen ]
Expand Down
11 changes: 10 additions & 1 deletion settings/src/script.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ function Main( { userId } ) {
} = user;
const [ globalNotice, setGlobalNotice ] = useState( '' );
const [ error, setError ] = useState( '' );
const [ backupCodesVerified, setBackupCodesVerified ] = useState( true );
Copy link
Contributor

@renintw renintw Sep 24, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just add some notes about my testing process for the logic here for future reference.

Initially, I thought of setting this value to false during testing.
And setting this part to true only when the Back button on the backup-codes screen is pressed.

However, there are two issues with this approach:

  1. Currently, within ScreenLink, we can't determine which screen the Back button is pressed from.
  2. Even if we could determine that, since the value is set to true only when the Back button on the backup-codes screen is pressed, this condition with ! backupCodesVerified would be true again when the user exits the account screen (when Main is unmounted) and returns to the account screen, leading to another unexpected re-generation of backup-codes.

So, the current logic is that every time the ScreenLink component is pressed, it will execute setBackupCodesVerified( true );. I believe this is an acceptable compromise, especially since this workaround was put in place primarily due to the side-effects of two-factor/#507.


let currentUrl = new URL( document.location.href );
const initialScreen = currentUrl.searchParams.get( 'screen' );
Expand Down Expand Up @@ -161,7 +162,15 @@ function Main( { userId } ) {

return (
<GlobalContext.Provider
value={ { navigateToScreen, user, setGlobalNotice, setError, error } }
value={ {
navigateToScreen,
user,
setGlobalNotice,
setError,
error,
backupCodesVerified,
setBackupCodesVerified,
} }
>
<GlobalNotice notice={ globalNotice } setNotice={ setGlobalNotice } />
{ currentScreenComponent }
Expand Down