twofa_settings = get_option( Two_Factor_Authentication::OPTION_NAME, [] ); add_action( 'show_user_profile', [ $this, 'add_user_2fa_section' ] ); add_action( 'edit_user_profile', [ $this, 'add_user_2fa_section' ] ); add_action( 'admin_enqueue_scripts', [ $this, 'enqueue_scripts' ] ); add_action( 'wp_ajax_adminoptim-setup-2fa', [ $this, 'setup_2fa' ] ); add_action( 'wp_ajax_adminoptim-reset-2fa', [ $this, 'reset_2fa' ] ); add_action( 'wp_ajax_adminoptim-generate-recovery-code', [ $this, 'setup_recovery_code' ] ); } /** * Function to add user 2FA setup section * * @param \WP_User $user Current user class. * * @return void */ public function add_user_2fa_section( $user ) { $current_user = wp_get_current_user(); $can_setup_2fa = false; if ( $current_user->ID === $user->ID ) { $can_setup_2fa = true; } $user_2fa = get_user_meta( $user->ID, Two_Factor_Authentication::OPTION_NAME, true ); ?>
|
ID . 'user_2fa_info', 'user_2fa_nonce' );
if ( Two_Factor_Authentication::is_secret_unreadable( (int) $user->ID ) ) :
?>
generate_secret();
// Must match the algorithm/digits/period used by verify_code() in setup_2fa() and at login.
$uri = $totp->generate_uri( $secret, $user->user_login, get_bloginfo( 'name' ), 'sha1', 6, 30 );
?>
twofa_settings['enable_recovery_code'] ) && ! empty( $user_2fa['secret'] ) ) : ?>
document.addEventListener("DOMContentLoaded", (event) => {
const matrix = QrCode.generate();
const uri = QrCode.render('svg-uri', matrix);
let img = document.createElement('img');
img.src = uri;
img.width = '200';
img.height = '200';
img.id = '2fa-qrcode';
document.getElementById('2fa-qrcode-wrap').appendChild(img);
});
|
' . __( 'Write these down! Once you navigate away from this page, you will not be able to view these codes again.', 'admin-optimizer' ) . '
'; $message .= '' . __( 'Download the codes here. Once you navigate away from this page, you will not be able to download the codes again.', 'admin-optimizer' ) . '
'; $message .= ''; $response['message'] = $message; wp_send_json_success( $response ); } } /** * Function to generate recovery codes * * @return array * @throws \Random\RandomException Throws error if generation fails. */ private function generate_recovery_code() { $code_length = 10; $codes = []; $hashed_codes = []; for ( $i = 0; $i < 10; $i++ ) { $codes[] = strtoupper( bin2hex( random_bytes( $code_length / 2 ) ) ); } for ( $i = 0; $i < 10; $i++ ) { $hashed_codes[] = wp_hash_password( $codes[ $i ] ); } $download_link = $this->download_rc_link( $codes ); return [ 'codes' => $codes, 'hashed_codes' => $hashed_codes, 'download_link' => $download_link, ]; } /** * Generate link for user to download recovery codes. * * @param string[] $codes A list of generated recovery codes. * * @return string */ public function download_rc_link( $codes ) { $download_link = ''; if ( ! empty( $codes ) && is_array( $codes ) ) { $blogname = get_bloginfo( 'name' ); $current_date = wp_date( 'Y-m-d H:i:s' ); $timezone = wp_timezone_string(); $message = rawurlencode( "Recovery codes for {$blogname}\n" ); $message .= rawurlencode( "Keep these backup codes somewhere safe but accessible.\n\n" ); foreach ( $codes as $code ) { $message .= rawurlencode( "{$code}\n" ); } $message .= rawurlencode( "\n" ); $message .= rawurlencode( "* You can only use each backup code once.\n" ); $message .= rawurlencode( "* These codes were generated on {$current_date} ({$timezone})" ); $download_link = 'data:application/text;charset=utf-8,' . $message; } return $download_link; } /** * Check whether the current user can modify 2FA settings for a target user. * * @param int $user_id Target user ID. * * @return void */ private function validate_user_2fa_permission( int $user_id ) { $current_user_id = get_current_user_id(); if ( $user_id < 1 || ( $current_user_id !== $user_id && ! current_user_can( 'edit_user', $user_id ) ) ) { wp_send_json_error( new \WP_Error( 'adminoptim-unauthorized-user', ao_get_admin_notice( __( 'Unauthorized action.', 'admin-optimizer' ), [ 'type' => 'error', ] ) ) ); } } }