Step-by-Step Guide to building a custom two-factor authentication block for Gutenberg using SolidJS high-performance reactive components
Gutenberg Block Development Environment Setup
To begin building a custom Gutenberg block for two-factor authentication (2FA), we need a robust development environment. This involves setting up Node.js, npm (or yarn), and the WordPress Command Line Interface (WP-CLI) for efficient block scaffolding and management. We’ll leverage SolidJS for its high-performance reactive components, which will translate into a snappier user experience within the WordPress admin area.
First, ensure you have Node.js (version 14 or higher recommended) and npm installed. You can download them from nodejs.org. Verify your installation with:
node -v npm -v
Next, install WP-CLI globally. This tool is indispensable for managing WordPress installations, plugins, and themes from the command line.
curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar chmod +x wp-cli.phar sudo mv wp-cli.phar /usr/local/bin/wp
Now, let’s scaffold a new Gutenberg block plugin. Navigate to your WordPress plugins directory and execute the following command. Replace my-2fa-block with your desired plugin slug.
cd /path/to/your/wordpress/wp-content/plugins/ wp scaffold block my-2fa-block
This command generates a basic block structure, including necessary configuration files and example JavaScript. We will modify these to integrate SolidJS.
Integrating SolidJS into the Gutenberg Block
The default Gutenberg block setup uses React. To integrate SolidJS, we need to adjust the build process. The primary configuration file for the block’s JavaScript build is webpack.config.js (or similar, depending on the scaffolding tool). We’ll modify this to include SolidJS’s JSX transform.
First, install SolidJS and its JSX transform as development dependencies:
cd my-2fa-block npm install solid-js @babel/plugin-transform-react-jsx --save-dev
Next, update your webpack.config.js. The key change is to configure Babel to use the SolidJS JSX transform. If your project uses @wordpress/scripts, the Babel configuration is often managed internally. In such cases, you might need to eject or customize the Babel configuration. A common approach is to add a .babelrc file or modify the babel.config.js if one exists.
Create or modify a .babelrc file in the root of your block plugin directory:
{
"presets": [
"@babel/preset-env",
"@babel/preset-react"
],
"plugins": [
"@babel/plugin-transform-react-jsx",
{
"runtime": "automatic",
"importSource": "solid-js"
}
]
}
With the JSX transform configured, we can now start writing our block’s frontend and editor components using SolidJS syntax. The main entry point for your block’s JavaScript is typically src/index.js. Replace its content with the following structure:
// src/index.js
import { registerBlockType } from '@wordpress/blocks';
import { render } from 'solid-js/web';
import { TwoFactorAuthBlock } from './components/TwoFactorAuthBlock'; // We'll create this component
registerBlockType('my-2fa-block/two-factor-auth', {
title: 'Two-Factor Authentication',
icon: 'shield-alt', // WordPress dashicon
category: 'security',
edit: () => {
const element = document.createElement('div');
// In a real scenario, you'd likely use a wrapper and manage state.
// For simplicity here, we're directly rendering.
render(() => , element);
return element;
},
save: () => {
const element = document.createElement('div');
render(() => , element);
return element;
},
});
Building the SolidJS 2FA Component
Now, let’s create the core SolidJS component that will handle the 2FA logic. This component will manage the input for the 2FA code and interact with the WordPress backend for verification. We’ll create a new file, src/components/TwoFactorAuthBlock.jsx.
// src/components/TwoFactorAuthBlock.jsx
import { createSignal, Show } from 'solid-js';
import apiFetch from '@wordpress/api-fetch'; // WordPress API fetch utility
export function TwoFactorAuthBlock(props) {
const [code, setCode] = createSignal('');
const [message, setMessage] = createSignal('');
const [isLoading, setIsLoading] = createSignal(false);
const [isVerified, setIsVerified] = createSignal(false);
const handleCodeChange = (event) => {
setCode(event.target.value);
};
const handleSubmit = async (event) => {
event.preventDefault();
setIsLoading(true);
setMessage('');
try {
// Assuming a WordPress REST API endpoint for 2FA verification
const response = await apiFetch({
path: '/my-2fa-block/v1/verify-code',
method: 'POST',
data: { code: code() },
});
if (response.success) {
setIsVerified(true);
setMessage('Authentication successful!');
} else {
setMessage(response.message || 'Invalid code. Please try again.');
}
} catch (error) {
console.error('2FA Verification Error:', error);
setMessage('An error occurred during verification.');
} finally {
setIsLoading(false);
}
};
return (
<div class="two-factor-auth-block">
{props.isEditMode ? (
<p>Two-Factor Authentication (Edit Mode - Frontend view will be rendered here)</p>
) : (
<>
<h3>Enter your Two-Factor Code</h3>
<form onSubmit={handleSubmit}>
<input
type="text"
value={code()}
onInput={handleCodeChange}
placeholder="e.g., 123456"
required
disabled={isLoading() || isVerified()}
/>
<button type="submit" disabled={isLoading() || isVerified()}>
{isLoading() ? 'Verifying...' : 'Verify'}
</button>
</form>
<Show when={message()}>
<p class={isVerified() ? 'success-message' : 'error-message'}>{message()}</p>
</Show>
<Show when={isVerified()}>
<p>You are now authenticated.</p>
</Show>
</>
)}
</div>
);
}
In this component:
- We use
createSignalfrom SolidJS to manage the state of the input code, feedback messages, loading status, and verification status. apiFetch, a WordPress utility, is used to communicate with our custom backend endpoint.- The
Showcomponent conditionally renders messages and the success state. - The
isEditModeprop differentiates between the editor view and the frontend view.
Backend API Endpoint for Verification
For the 2FA verification to work, we need a corresponding backend endpoint in WordPress. This typically involves creating a custom plugin or adding functionality to your theme’s functions.php file to register a REST API route.
Here’s an example of how you might register this endpoint using PHP:
<?php
/**
* Plugin Name: My 2FA Block
* Description: Adds a custom 2FA block.
* Version: 1.0
* Author: Your Name
*/
// Prevent direct access
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
// Register the REST API endpoint
add_action( 'rest_api_init', function () {
register_rest_route( 'my-2fa-block/v1', '/verify-code', array(
'methods' => 'POST',
'callback' => 'my_2fa_block_verify_code_callback',
'permission_callback' => '__return_true', // Adjust permissions as needed
) );
} );
// Callback function for the REST API endpoint
function my_2fa_block_verify_code_callback( WP_REST_Request $request ) {
$received_code = sanitize_text_field( $request->get_param( 'code' ) );
// --- IMPORTANT SECURITY NOTE ---
// In a real-world scenario, you would:
// 1. Retrieve the user's expected 2FA code (e.g., from session, user meta, or a dedicated service).
// 2. Implement secure comparison (e.g., using hash_equals for time-attack resistance if codes are hashed).
// 3. Handle code expiration and rate limiting.
// 4. Associate the verified status with the current user session.
// For this example, we'll use a placeholder check.
// --- END SECURITY NOTE ---
// Placeholder verification logic: Replace with your actual 2FA implementation.
// Example: Check against a temporary code stored in user meta or session.
$user_id = get_current_user_id(); // Get the logged-in user ID
if ( ! $user_id ) {
return new WP_Error( 'not_logged_in', 'User not logged in.', array( 'status' => 401 ) );
}
$expected_code = get_user_meta( $user_id, '_my_2fa_temp_code', true ); // Example: retrieve temporary code
if ( ! empty( $expected_code ) && hash_equals( $expected_code, $received_code ) ) {
// Code is correct, clear the temporary code and mark as verified (e.g., in session)
delete_user_meta( $user_id, '_my_2fa_temp_code' );
// Set a session flag or cookie to indicate successful 2FA for this session
// Example: update_user_meta( $user_id, '_my_2fa_verified_session', time() );
return rest_ensure_response( array( 'success' => true, 'message' => '2FA code verified successfully.' ) );
} else {
// Code is incorrect
return rest_ensure_response( array( 'success' => false, 'message' => 'Invalid 2FA code.' ), 400 );
}
}
// --- Example: How to generate and store a temporary 2FA code (for demonstration) ---
// This would typically be triggered when 2FA is required, e.g., during login or sensitive action.
function my_2fa_block_generate_and_store_code( $user_id ) {
// Generate a 6-digit code (example)
$code = str_pad( wp_rand( 0, 999999 ), 6, '0', STR_PAD_LEFT );
update_user_meta( $user_id, '_my_2fa_temp_code', $code );
// In a real app, you'd send this code via SMS, email, or authenticator app.
// For this example, we'll just store it.
error_log( "Generated 2FA code for user {$user_id}: {$code}" ); // Log for debugging
return $code;
}
// Example hook to trigger code generation (e.g., after user logs in if 2FA is enabled)
// add_action( 'wp_login', 'my_2fa_block_trigger_code_generation', 10, 2 );
// function my_2fa_block_trigger_code_generation( $user_login, $user ) {
// if ( get_user_meta( $user->ID, '_my_2fa_enabled', true ) ) {
// my_2fa_block_generate_and_store_code( $user->ID );
// }
// }
?>
Security Considerations for the Backend:
- Never hardcode secrets or verification logic directly. Use secure methods for generating, storing, and comparing 2FA codes.
- Implement rate limiting on the verification endpoint to prevent brute-force attacks.
- Ensure the
permission_callbackis correctly configured. For sensitive operations, it should verify user capabilities or authentication status. - Consider using WordPress’s built-in hashing functions (like
wp_hash()andwp_check_password()orhash_equals()for comparing secrets) for security. - The example uses
get_current_user_id(), implying the user is already logged in. A full 2FA flow would integrate with the login process itself.
Styling the Block
To make the block presentable, you’ll need to add CSS. Create a src/style.scss file (or similar) and import it into your src/index.js. Ensure your build process (Webpack) is configured to handle SCSS compilation.
// src/style.scss
.two-factor-auth-block {
padding: 15px;
border: 1px solid #ccc;
border-radius: 4px;
background-color: #f9f9f9;
h3 {
margin-top: 0;
color: #333;
}
form {
display: flex;
gap: 10px;
margin-bottom: 15px;
input[type="text"] {
flex-grow: 1;
padding: 8px;
border: 1px solid #ddd;
border-radius: 3px;
}
button {
padding: 8px 15px;
background-color: #0073aa;
color: white;
border: none;
border-radius: 3px;
cursor: pointer;
transition: background-color 0.2s ease;
&:disabled {
background-color: #ccc;
cursor: not-allowed;
}
&:hover:not(:disabled) {
background-color: #005177;
}
}
}
.success-message {
color: green;
font-weight: bold;
}
.error-message {
color: red;
font-weight: bold;
}
}
Then, import this SCSS file in your src/index.js:
// src/index.js (add this line) import './style.scss';
Building and Activating the Block
After setting up the components and backend, you need to build the JavaScript assets and activate the plugin.
Run the build command in your block’s directory:
npm run build
This command compiles your SolidJS and SCSS code into optimized JavaScript and CSS files, typically placed in a build directory. These files are then enqueued by WordPress.
Finally, navigate to your WordPress admin area, go to “Plugins,” and activate “My 2FA Block.” You should now be able to add the “Two-Factor Authentication” block to your posts or pages.
Advanced Considerations and Next Steps
This guide provides a foundational implementation. For a production-ready solution, consider the following:
- User Experience: Implement more sophisticated error handling, loading states, and potentially a countdown timer for code expiration.
- Security: Integrate with a robust 2FA library or service. Ensure secure storage and transmission of sensitive data. Implement session management for verified states.
- Code Generation and Delivery: Develop a secure mechanism for generating 2FA codes (e.g., TOTP using libraries like Google Authenticator) and delivering them to users (SMS, email, push notifications).
- Block Editor Integration: For sensitive actions within the block editor itself (not just on the frontend), you might need to leverage WordPress’s authentication and authorization APIs more deeply, potentially requiring a full page reload or a more complex modal flow.
- Accessibility: Ensure the block is fully accessible, adhering to WCAG guidelines.
- Internationalization: Make strings translatable using WordPress’s i18n functions.