• Skip to secondary menu
  • Skip to main content
  • Skip to primary sidebar
  • Home
  • Projects
  • Products
  • Themes
  • Tools
  • Request for Quote

Vengala Vinay

Having 12+ Years of Experience in Software Development

  • Home
  • WordPress
  • PHP
    • Codeigniter
  • Django
  • Magento
  • Selenium
  • Server
Home » Step-by-Step Guide to building a custom two-factor authentication block for Gutenberg using Svelte standalone templates

Step-by-Step Guide to building a custom two-factor authentication block for Gutenberg using Svelte standalone templates

Gutenberg Block Development Environment Setup

Before diving into the custom two-factor authentication (2FA) block for Gutenberg, a robust development environment is paramount. This setup leverages Node.js, npm, and the WordPress `@wordpress/scripts` package for efficient block compilation and management. Ensure you have Node.js (LTS recommended) and npm installed globally.

Navigate to your WordPress theme’s directory or a custom plugin directory where you intend to house your Gutenberg blocks. Initialize a new project using `npm init -y` to create a `package.json` file. Then, install the necessary WordPress scripts package:

npm init -y
npm install @wordpress/scripts --save-dev

Next, configure your `package.json` to include build scripts. This allows for easy compilation of your Svelte components into JavaScript that WordPress can understand. Add the following scripts to your `package.json`:

{
  "name": "custom-2fa-block",
  "version": "1.0.0",
  "description": "Custom 2FA Gutenberg Block",
  "main": "build/index.js",
  "scripts": {
    "build": "wp-scripts build",
    "start": "wp-scripts start",
    "packages-update": "wp-scripts packages-update"
  },
  "keywords": ["wordpress", "gutenberg", "block", "svelte", "2fa"],
  "author": "Antigravity",
  "license": "GPL-2.0-or-later",
  "devDependencies": {
    "@wordpress/scripts": "^26.0.0"
  }
}

Create a `src` directory at the root of your project. Inside `src`, create an `index.js` file. This will be the entry point for your Gutenberg block registration. For Svelte integration, we’ll need a build process that transpiles Svelte components. The `@wordpress/scripts` package can be configured to do this. You’ll typically need a `svelte.config.js` and a `jsconfig.json` (or `tsconfig.json` if using TypeScript).

Svelte Component Structure for 2FA Input

We will build a standalone Svelte component that handles the user input for the 2FA code. This component will be self-contained and easily integrated into the Gutenberg block editor. Create a `components` directory within your `src` folder and add a file named `TwoFactorInput.svelte`.

<!-- src/components/TwoFactorInput.svelte -->
<script>
  import { createEventDispatcher } from 'svelte';

  export let label = 'Two-Factor Authentication Code';
  export let placeholder = 'Enter your code';
  export let value = '';

  const dispatch = createEventDispatcher();

  function handleChange(event) {
    value = event.target.value;
    dispatch('input', value);
  }

  function handleBlur() {
    dispatch('blur', value);
  }
</script>

<div class="two-factor-input-wrapper">
  <label>{label}</label>
  <input
    type="text"
    bind:value="{value}"
    placeholder="{placeholder}"
    on:input="{handleChange}"
    on:blur="{handleBlur}"
    maxlength="6"
    pattern="[0-9]*"
    inputmode="numeric"
  />
</div>

<style>
  .two-factor-input-wrapper {
    margin-bottom: 1rem;
  }
  .two-factor-input-wrapper label {
    display: block;
    margin-bottom: 0.5rem;
    font-weight: bold;
  }
  .two-factor-input-wrapper input {
    width: 100%;
    padding: 0.75rem;
    border: 1px solid #ccc;
    border-radius: 4px;
    box-sizing: border-box;
    font-size: 1rem;
  }
</style>

This Svelte component defines a reactive input field. It accepts `label`, `placeholder`, and `value` as props. It dispatches `input` and `blur` events, allowing the parent Gutenberg block to react to changes and validation. The `maxlength`, `pattern`, and `inputmode` attributes are optimized for numeric 2FA codes.

Gutenberg Block Registration and Svelte Integration

Now, let’s integrate this Svelte component into a Gutenberg block. We’ll use the `@wordpress/blocks` and `@wordpress/element` packages. The `index.js` file in your `src` directory will serve as the main entry point for registering the block.

// src/index.js
import { registerBlockType } from '@wordpress/blocks';
import { createElement } from '@wordpress/element';
import App from './App'; // We'll create this next

registerBlockType('custom-2fa/block', {
  title: 'Custom 2FA Input',
  icon: 'shield-alt', // Example icon
  category: 'security',
  attributes: {
    twoFactorCode: {
      type: 'string',
      default: '',
    },
  },
  edit: (props) => {
    const { attributes, setAttributes } = props;

    const handleCodeChange = (event) => {
      setAttributes({ twoFactorCode: event.detail });
    };

    // Render the Svelte component within the Gutenberg editor
    // This requires a Svelte renderer for the editor side.
    // For simplicity here, we'll use a placeholder and explain the Svelte integration.
    // In a real scenario, you'd compile Svelte to a JS module and import it.
    return createElement('div', { className: 'custom-2fa-editor' },
      createElement('p', {}, '2FA Input Block (Editor View)'),
      // Placeholder for Svelte component rendering in editor
      // This would involve mounting a Svelte component instance.
      createElement('div', { id: 'svelte-2fa-editor-mount' })
    );
  },
  save: (props) => {
    const { attributes } = props;
    // The 'save' function defines how the block's content is saved to the database.
    // For dynamic blocks or blocks that interact with server-side logic,
    // this might return null and rely on a render_callback.
    // For a simple input, we might save the value or a placeholder.
    return createElement('div', { className: 'custom-2fa-frontend' },
      createElement('p', {}, '2FA Input Block (Frontend View)'),
      // In a real scenario, this would render the Svelte component or its output.
      // For a security-sensitive input, you likely wouldn't save the code itself.
      // You might save a nonce or a flag indicating the block is present.
      createElement('div', { className: 'custom-2fa-frontend-mount' })
    );
  },
});

The `edit` function is responsible for rendering the block in the Gutenberg editor. The `save` function determines what gets saved to the post content. For a 2FA input, we typically don’t save the actual code. Instead, the block might serve as a UI element that triggers a server-side validation process.

To actually render the Svelte component within Gutenberg, we need a mechanism to compile and load the Svelte code. This is where a build tool like Vite or Webpack, configured for Svelte, becomes essential. The `@wordpress/scripts` package can be extended to support Svelte compilation. A common approach is to use a Svelte preprocessor within the build configuration.

Advanced Svelte Compilation for Gutenberg

To enable Svelte compilation with `@wordpress/scripts`, you’ll typically need a `svelte.config.js` file and potentially modify the `webpack.config.js` that `@wordpress/scripts` uses internally (though extending it directly is often discouraged; a better approach is to use a tool that integrates well with it, or a separate build step). A simpler path is to use a tool like Vite, which has excellent Svelte support, and then configure its output to be compatible with WordPress block registration.

Let’s assume a Vite-based build process. You would create a `vite.config.js`:

// vite.config.js
import { defineConfig } from 'vite';
import { svelte } from '@sveltejs/vite-plugin-svelte';
import path from 'path';

export default defineConfig({
  plugins: [svelte()],
  build: {
    outDir: 'build', // Matches @wordpress/scripts output directory
    emptyOutDir: true,
    lib: {
      entry: path.resolve(__dirname, 'src/index.js'), // Your main block entry point
      name: 'Custom2faBlock',
      fileName: (format) => `index.${format}.js`,
      formats: ['iife'], // Immediately Invoked Function Expression for WordPress
    },
    rollupOptions: {
      // Make sure to externalize deps that shouldn't be bundled
      // into your library (e.g., WordPress dependencies)
      external: ['react', 'react-dom'], // Example, adjust as needed
      output: {
        // Provide global variables to use in the UMD build
        // for externalized deps
        globals: {
          react: 'React',
          'react-dom': 'ReactDOM',
        },
      },
    },
  },
});

And a `svelte.config.js`:

// svelte.config.js
import adapter from '@sveltejs/adapter-auto'; // Or a specific adapter if needed

/** @type {import('@sveltejs/kit').Config} */
const config = {
  kit: {
    adapter: adapter(),
    // You might need to configure paths for WordPress integration
    // paths: {
    //   base: '/wp-content/themes/your-theme/build', // Example
    // }
  },
  // Add any Svelte preprocessors or other configurations here
};

export default config;

With Vite configured, you would modify your `package.json` scripts to use Vite for building:

{
  "name": "custom-2fa-block",
  "version": "1.0.0",
  // ... other fields
  "scripts": {
    "build": "vite build",
    "dev": "vite",
    "preview": "vite preview"
  },
  // ... devDependencies
  "devDependencies": {
    "vite": "^5.0.0",
    "svelte": "^4.0.0",
    "@sveltejs/vite-plugin-svelte": "^3.0.0",
    "@sveltejs/adapter-auto": "^3.0.0",
    "@wordpress/scripts": "^26.0.0" // Keep for potential wp-specific utils if needed
  }
}

Now, when you run `npm run build`, Vite will compile your Svelte components and bundle them into `build/index.js` (and potentially other formats depending on your `vite.config.js`). This output JS file can then be enqueued by WordPress.

Server-Side Integration and Security Considerations

The Gutenberg block itself is primarily a client-side UI component. For 2FA to be effective, it must be validated on the server. This involves:

  • AJAX Endpoint: Create a WordPress AJAX endpoint (using `wp_ajax_` hooks) that the frontend JavaScript (or the block’s editor script) can call to submit the 2FA code.
  • Validation Logic: On the server, retrieve the submitted 2FA code, verify it against the user’s expected code (e.g., from a TOTP secret stored securely), and return a success or failure response.
  • Session Management: If the 2FA code is valid, update the user’s session to indicate that 2FA has been successfully completed. This might involve setting a specific cookie or transient.
  • Security Best Practices:
    • Never store raw 2FA secrets in the database without strong encryption.
    • Use HTTPS for all communication.
    • Implement rate limiting on the AJAX endpoint to prevent brute-force attacks.
    • Sanitize and validate all input.
    • Ensure the AJAX endpoint checks user capabilities and nonces.

Here’s a PHP example for registering an AJAX action:

<?php
/**
 * Plugin Name: Custom 2FA Block
 * Description: Adds a custom 2FA input block.
 * Version: 1.0
 * Author: Antigravity
 */

function custom_2fa_block_enqueue_scripts() {
    // Enqueue the compiled JavaScript file.
    // Ensure the path is correct based on your build output.
    wp_enqueue_script(
        'custom-2fa-block-js',
        plugins_url( 'build/index.js', __FILE__ ), // Adjust path if in theme
        array( 'wp-blocks', 'wp-element', 'wp-i18n' ),
        filemtime( plugin_dir_path( __FILE__ ) . 'build/index.js' ) // Cache busting
    );

    // Localize script for AJAX URL and nonce
    wp_localize_script( 'custom-2fa-block-js', 'custom2fa', array(
        'ajax_url' => admin_url( 'admin-ajax.php' ),
        'nonce'    => wp_create_nonce( 'custom_2fa_verify_nonce' ),
    ) );
}
add_action( 'enqueue_block_editor_assets', 'custom_2fa_block_enqueue_scripts' );
// Also enqueue for frontend if the block is used there directly
// add_action( 'wp_enqueue_scripts', 'custom_2fa_block_enqueue_scripts' );


function custom_2fa_register_block() {
    register_block_type( 'custom-2fa/block', array(
        'editor_script' => 'custom-2fa-block-js',
        // If you have a server-side rendering callback for the frontend:
        // 'render_callback' => 'custom_2fa_render_frontend',
    ) );
}
add_action( 'init', 'custom_2fa_register_block' );

// AJAX handler for 2FA verification
function custom_2fa_verify_code() {
    check_ajax_referer( 'custom_2fa_verify_nonce', 'nonce' );

    if ( ! isset( $_POST['code'] ) || empty( $_POST['code'] ) ) {
        wp_send_json_error( array( 'message' => '2FA code is required.' ) );
    }

    $submitted_code = sanitize_text_field( $_POST['code'] );
    $user_id = get_current_user_id();

    if ( ! $user_id ) {
        wp_send_json_error( array( 'message' => 'User not logged in.' ) );
    }

    // --- Replace with your actual 2FA verification logic ---
    // Example: Check against a stored TOTP secret or a temporary code.
    // This is a placeholder and requires a robust implementation.
    $is_valid = false; // Assume invalid by default
    // $user_secret = get_user_meta( $user_id, '_2fa_secret', true );
    // if ( $user_secret && verify_totp_code( $user_secret, $submitted_code ) ) {
    //     $is_valid = true;
    // }
    // --- End of placeholder logic ---

    // For demonstration, let's simulate a valid code if it's '123456'
    if ( $submitted_code === '123456' ) {
        $is_valid = true;
    }


    if ( $is_valid ) {
        // Mark user as 2FA authenticated for this session
        // Example: Set a transient or update user meta
        set_transient( '2fa_authenticated_' . $user_id, true, HOUR_IN_SECONDS );
        wp_send_json_success( array( 'message' => '2FA code verified successfully.' ) );
    } else {
        wp_send_json_error( array( 'message' => 'Invalid 2FA code.' ) );
    }
}
add_action( 'wp_ajax_custom_2fa_verify', 'custom_2fa_verify_code' );
// Add for non-logged-in users if applicable (e.g., login screen)
// add_action( 'wp_ajax_nopriv_custom_2fa_verify', 'custom_2fa_verify_code' );

// Function to verify TOTP (requires a library like PHPGangsta/PHPGangsta_GoogleAuthenticator)
// function verify_totp_code( $secret, $code ) {
//     // Implementation details...
//     return false;
// }

// Optional: Server-side rendering for the frontend if needed
// function custom_2fa_render_frontend( $attributes ) {
//     // This function would return the HTML to render on the frontend.
//     // For a 2FA input, you might render a form that submits to the AJAX endpoint.
//     // You would likely need to enqueue frontend scripts here as well.
//     return '<div class="custom-2fa-frontend-wrapper"><p>Please enter your 2FA code.</p></div>';
// }

?>

The frontend JavaScript (which would be part of your compiled Svelte application or a separate script enqueued for the frontend) would then use `fetch` or `XMLHttpRequest` to send the code to `custom_2fa_verify_code` via `wp_ajax_custom_2fa_verify`. The `wp_localize_script` function is crucial for passing the AJAX URL and nonce to the client-side JavaScript.

Frontend JavaScript for AJAX Communication

You’ll need JavaScript on the frontend (and potentially within the editor’s `edit` function if you want live preview of validation) to handle the submission of the 2FA code to the AJAX endpoint. This can be done within your main Svelte `App.svelte` component or a dedicated utility file.

// src/frontend.js (or integrated into App.svelte)

// Assuming 'custom2fa' object is available from wp_localize_script
// and your Svelte component dispatches an 'input' event.

function send2FACode(code) {
  const data = new URLSearchParams();
  data.append('action', 'custom_2fa_verify');
  data.append('nonce', custom2fa.nonce);
  data.append('code', code);

  fetch(custom2fa.ajax_url, {
    method: 'POST',
    body: data,
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
    },
  })
  .then(response => response.json())
  .then(result => {
    if (result.success) {
      console.log('2FA Success:', result.data.message);
      // Redirect user, update UI, etc.
      window.location.reload(); // Example: reload after successful 2FA
    } else {
      console.error('2FA Error:', result.data.message);
      // Display error message to the user
      alert('Invalid 2FA code. Please try again.');
    }
  })
  .catch(error => {
    console.error('AJAX Request Failed:', error);
    alert('An error occurred. Please try again later.');
  });
}

// Example usage: If your Svelte component has an ID 'svelte-2fa-frontend-mount'
// and you have a way to trigger 'send2FACode' from your Svelte component.
// You might pass a callback function from the Gutenberg block's edit/save props.

// In src/App.svelte (or similar main component)
// ...
// <script>
//   import TwoFactorInput from './components/TwoFactorInput.svelte';
//   import { send2FACode } from './api'; // Assuming send2FACode is exported from api.js

//   let currentCode = '';
//   let isSubmitting = false;

//   function handleCodeInput(event) {
//     currentCode = event.detail;
//   }

//   function handleSubmit() {
//     if (currentCode && !isSubmitting) {
//       isSubmitting = true;
//       send2FACode(currentCode)
//         .then(response => {
//           // Handle success/error from response
//           alert(response.message);
//         })
//         .catch(error => {
//           alert('Error: ' + error.message);
//         })
//         .finally(() => {
//           isSubmitting = false;
//         });
//     }
//   }
// </script>

// <div class="custom-2fa-app">
//   <TwoFactorInput
//     label="Enter your verification code"
//     bind:value="{currentCode}"
//     on:input="{handleCodeInput}"
//   />
//   <button on:click="{handleSubmit}" disabled="{isSubmitting || !currentCode}">
//     {isSubmitting ? 'Verifying...' : 'Verify'}
//   </button>
// </div>
// ...

This setup provides a foundation for a secure and customizable two-factor authentication mechanism within WordPress, leveraging the power of Svelte for a modern UI and Gutenberg for seamless integration.

Primary Sidebar

A little about the Author

Having 12+ Years of Experience in Software Development, Vinay is a principal software architect, senior systems engineer, and elite technical consultant. He specializes in bespoke PHP/WordPress development, high-performance Magento 2 & Shopify architectures, custom plugin/theme development from scratch, and legacy code modernization (including VB6, VB.NET, PyQt, and Crystal Reports). Known for solving complex database bottlenecks, speed optimization (Core Web Vitals), and advanced security code auditing, Vinay engineers production-ready systems designed to scale under heavy concurrent load conditions.



Chat on WhatsApp

Recent Posts

  • Optimizing p99 database query response latency in multi-site Singleton Registry Pattern custom tables
  • Step-by-Step Guide to building a custom Elasticsearch search bar block for Gutenberg using React components
  • Troubleshooting guide: Resolving memory leak spikes caused by unclosed custom database loops in customer support tickets
  • Optimizing p99 database query response latency in multi-site Domain-driven architecture (DDD) blocks custom tables
  • How to design a modular Action-hook Event Mediator architecture for enterprise-level custom plugins

Categories

  • apache (1)
  • Business & Monetization (390)
  • Centos (4)
  • Comparisons & Decision Making (55)
  • Debian (2)
  • Debugging & Troubleshooting (658)
  • Desktop Applications (14)
  • DevOps (7)
  • DevOps & Cloud Scaling (962)
  • Django (1)
  • Laravel (4)
  • Migration & Architecture (192)
  • Mobile Applications (24)
  • MySQL (1)
  • Performance & Optimization (872)
  • PHP (5)
  • PHP Development (41)
  • Plugins & Themes (244)
  • Programming Languages (9)
  • Python (20)
  • Ruby on Rails (1)
  • Security & Compliance (639)
  • SEO & Growth (492)
  • Server (23)
  • Ubuntu (9)
  • VB6 & VB.NET (8)
  • Web Applications & Frontend (19)
  • Web Assembly (Wasm) (2)
  • WordPress (22)
  • WordPress Plugin Development (68)
  • WordPress Plugin Development (73)
  • WordPress Plugin Development (330)
  • WordPress Theme Development (357)

Recent Posts

  • Optimizing p99 database query response latency in multi-site Singleton Registry Pattern custom tables
  • Step-by-Step Guide to building a custom Elasticsearch search bar block for Gutenberg using React components
  • Troubleshooting guide: Resolving memory leak spikes caused by unclosed custom database loops in customer support tickets

Top Categories

  • DevOps & Cloud Scaling (962)
  • Performance & Optimization (872)
  • Debugging & Troubleshooting (658)
  • Security & Compliance (639)
  • SEO & Growth (492)
  • Business & Monetization (390)

Our Products

  • ERP & LMS Systems (4)
  • Directories & Marketplaces (4)
  • Healthcare Portals (3)
  • Point of Sale (POS) (2)
  • E-Commerce Engines (2)

Our Services

  • E-Commerce Development (10)
  • WordPress Development (8)
  • Python & Desktop GUI (7)
  • General Consulting (7)
  • Legacy Modernization (5)
  • Mobile App Development (4)

Copyright © 2026 · Vinay Vengala