Welcome to EasyCodingWithAI!

Before you dive into coding with AI, take a moment to consider some valuable insights. Our articles cover the pros and cons of using AI in development, the importance of having a development environment, and how AI empowers hobbyists and small businesses:

Post Icon

Tutorial : WordPress – Add Support for Secondary Authentication (Pin)

Posted by Richard Robins on October 29, 2024 - Last modified on November 1, 2024.

Overview

The EasyCodingWithAI—Pin Security plugin enhances the security of your WordPress site by adding an additional layer of authentication during the admin login process.

This guide will walk you through the step-by-step process of creating, activating, and configuring the EasyCodingWithAI—Pin Security plugin.

Example Prompt.

Help me create a WordPress plugin called “EasyCodingWithAI – Pin Security” that adds an extra layer of authentication to the WordPress admin login. I want the admin to be able to configure settings like the maximum login attempts before lockout, the timeout duration for that lockout, and the minimum length for the authentication PIN. It would be great if there’s an option to enable IP lockout to prevent multiple failed attempts from the same IP address. Also, users should be able to set their own authentication PINs from their profile pages. Lastly, the plugin should have a mechanism to lock users out after too many failed attempts and display appropriate error messages. Custom styling options for the login field would be a nice touch too.

Step 1: Create Plugin Folder and File

  1. Access your WordPress installation: Use FTP or your hosting file manager to navigate to the wp-content/plugins directory.
  2. Create a new folder: Name it EasyCodingWithAI – Pin Security. The folder name is important as it is used for the plugin’s directory.
  3. Create the main plugin file: Inside the new folder, create a file named easycodingwithai-pin-security.php. This file will contain the main code for your plugin.

Step 2: Add Plugin Code

Open the easycodingwithai-pin-security.php file in a text editor and add the following code:

<?php
/*
Plugin Name: EasyCodingWithAI - Pin Security
Description: Adds an additional authentication layer to the WordPress admin login with configurable security settings.
Version: 1.0
Author: EasyCodingWithAI
*/

// Define default constants.
define('AUTH_CODE_MAX_ATTEMPTS', 3);
define('AUTH_CODE_TIMEOUT', 300); // Default timeout in seconds (5 minutes).
define('AUTH_CODE_MIN_LENGTH', 4); // Default minimum length for the authentication code.

add_action('admin_menu', 'easycodingwithai_pin_security_menu');
function easycodingwithai_pin_security_menu() {
add_submenu_page(
'options-general.php',
'Pin Security Settings',
'Pin Security',
'manage_options',
'pin_security_settings',
'pin_security_settings_page'
);
}

add_action('admin_init', 'pin_security_settings_init');
function pin_security_settings_init() {
register_setting('pin_security_settings_group', 'auth_code_max_attempts');
register_setting('pin_security_settings_group', 'auth_code_timeout');
register_setting('pin_security_settings_group', 'auth_code_min_length');
register_setting('pin_security_settings_group', 'auth_code_ip_lockout');

add_settings_section('pin_security_main_section', 'Security Settings', null, 'pin_security_settings');

add_settings_field('auth_code_max_attempts', 'Max Login Attempts', 'pin_security_max_attempts_field', 'pin_security_settings', 'pin_security_main_section');
add_settings_field('auth_code_timeout', 'Timeout Duration (seconds)', 'pin_security_timeout_field', 'pin_security_settings', 'pin_security_main_section');
add_settings_field('auth_code_min_length', 'Minimum PIN Length', 'pin_security_min_length_field', 'pin_security_settings', 'pin_security_main_section');
add_settings_field('auth_code_ip_lockout', 'Enable IP Lockout', 'pin_security_ip_lockout_field', 'pin_security_settings', 'pin_security_main_section');
}

// Max login attempts field.
function pin_security_max_attempts_field() {
$value = get_option('auth_code_max_attempts', AUTH_CODE_MAX_ATTEMPTS);
echo '<input type="number" name="auth_code_max_attempts" value="' . esc_attr($value) . '" />';
}

// Timeout duration field.
function pin_security_timeout_field() {
$value = get_option('auth_code_timeout', AUTH_CODE_TIMEOUT);
echo '<input type="number" name="auth_code_timeout" value="' . esc_attr($value) . '" />';
}

// Minimum PIN length field.
function pin_security_min_length_field() {
$min_length = get_option('auth_code_min_length', AUTH_CODE_MIN_LENGTH);
echo '<select name="auth_code_min_length">';
echo '<option value="4"' . selected($min_length, 4, false) . '>4</option>';
echo '<option value="6"' . selected($min_length, 6, false) . '>6</option>';
echo '<option value="8"' . selected($min_length, 8, false) . '>8</option>';
echo '</select>';
}

// IP lockout field.
function pin_security_ip_lockout_field() {
$value = get_option('auth_code_ip_lockout', false);
echo '<input type="checkbox" name="auth_code_ip_lockout" ' . checked(1, $value, false) . ' value="1" />';
}

// Render settings page.
function pin_security_settings_page() {
?>
<div class="wrap">
<h1>Pin Security Settings</h1>
<form method="post" action="options.php">
<?php
settings_fields('pin_security_settings_group');
do_settings_sections('pin_security_settings');
submit_button();
?>
</form>
</div>
<?php
}

// Hook into the login form to display the additional authentication field.
add_action('login_form', 'display_auth_code_field');

// Hook into the authenticate process.
add_filter('authenticate', 'verify_auth_code', 30, 3);

// Add custom scripts to the login page for styling (optional).
add_action('login_enqueue_scripts', 'custom_login_styles');

// Hook into the user profile page to add the additional authentication field.
add_action('show_user_profile', 'add_auth_code_field');
add_action('edit_user_profile', 'add_auth_code_field');

// Hook into saving the user profile.
add_action('personal_options_update', 'save_auth_code');
add_action('edit_user_profile_update', 'save_auth_code');

// Add the auth code field to the login form.
function display_auth_code_field() {
echo '<p>
<label for="auth_code">Authentication Code<br />
<input type="password" name="auth_code" id="auth_code" class="input" value="" size="20" /></label>
</p>';
}

// Verify the auth code during login.
function verify_auth_code($user, $username, $password) {
if (isset($_POST['auth_code'])) {
$auth_code = sanitize_text_field($_POST['auth_code']);

if ($user && !is_wp_error($user)) {
$stored_code = get_user_meta($user->ID, 'auth_code', true);
$failed_attempts = get_user_meta($user->ID, 'failed_auth_attempts', true);
$lockout_time = get_user_meta($user->ID, 'auth_lockout_time', true);
$ip_address = $_SERVER['REMOTE_ADDR'];
$min_length = get_option('auth_code_min_length', AUTH_CODE_MIN_LENGTH);

// Check if the user is locked out.
if ($lockout_time && time() < $lockout_time) {
return new WP_Error('auth_timeout', __('Too many failed attempts. Please try again later.'));
}

// IP lockout check.
if (get_option('auth_code_ip_lockout', false)) {
$failed_ip_attempts = get_option('failed_ip_attempts_' . $ip_address, 0);
if ($failed_ip_attempts >= get_option('auth_code_max_attempts', AUTH_CODE_MAX_ATTEMPTS)) {
return new WP_Error('auth_ip_lockout', __('Too many failed attempts from this IP address. Please try again later.'));
}
}

// Verify the auth code length.
if (strlen($auth_code) < $min_length) {
return new WP_Error('auth_invalid_length', __('The authentication code must be at least ' . $min_length . ' characters long.'));
}

// Verify the auth code.
if (empty($stored_code) || !password_verify($auth_code, $stored_code)) {
// Increment failed attempts.
$failed_attempts = $failed_attempts ? $failed_attempts + 1 : 1;
update_user_meta($user->ID, 'failed_auth_attempts', $failed_attempts);

if ($failed_attempts >= get_option('auth_code_max_attempts', AUTH_CODE_MAX_ATTEMPTS)) {
// Set lockout time.
$lockout_time = time() + get_option('auth_code_timeout', AUTH_CODE_TIMEOUT);
update_user_meta($user->ID, 'auth_lockout_time', $lockout_time);
return new WP_Error('auth_timeout', __('Too many failed attempts. Please try again later.'));
} else {
// Use a generic error message to avoid revealing specifics.
return new WP_Error('auth_invalid', __('Invalid username, password, or authentication code.'));
}
} else {
// Reset failed attempts on successful code entry.
delete_user_meta($user->ID, 'failed_auth_attempts');
delete_user_meta($user->ID, 'auth_lockout_time');
}
}
}
return $user;
}

// Optional: Add custom CSS for the auth code field.
function custom_login_styles() {
echo '<style>
#auth_code { margin-top: 10px; }
</style>';
}

// Add the auth code field to the user profile page.
function add_auth_code_field($user) {
?>
<h3><?php _e('Authentication Code Settings'); ?></h3>
<table class="form-table">
<tr>
<th><label for="auth_code"><?php _e('Authentication Code'); ?></label></th>
<td>
<input type="password" name="auth_code" id="auth_code" value="" class="regular-text" /><br />
<span class="description"><?php _e('Enter your new authentication code. It will be required in addition to your password.'); ?></span>
</td>
</tr>
<tr>
<th><label for="auth_code_confirm"><?php _e('Confirm Authentication Code'); ?></label></th>
<td>
<input type="password" name="auth_code_confirm" id="auth_code_confirm" value="" class="regular-text" /><br />
<span class="description"><?php _e('Please confirm your new authentication code.'); ?></span>
</td>
</tr>
</table>
<?php
}

// Save the auth code field when the user profile is updated.
function save_auth_code($user_id) {
// Make sure the user can edit their own profile or is an admin editing the profile.
if (!current_user_can('edit_user', $user_id)) {
return false;
}

// Sanitize and save the auth code as a hashed value if provided.
if (isset($_POST['auth_code']) && !empty($_POST['auth_code'])) {
$auth_code = sanitize_text_field($_POST['auth_code']);
$auth_code_confirm = sanitize_text_field($_POST['auth_code_confirm']);

// Check if the new PIN and confirmation match.
if ($auth_code === $auth_code_confirm) {
$hashed_code = password_hash($auth_code, PASSWORD_DEFAULT); // Hash the new PIN
update_user_meta($user_id, 'auth_code', $hashed_code);
} else {
// Optionally, add an error notice for mismatched PINs.
add_filter('user_profile_update_errors', function($errors) {
$errors->add('auth_code_mismatch', __('Authentication codes do not match. Please try again.'));
});
}
}
}
?>

Step 3: Activate the Plugin

  1. Log in to your WordPress admin dashboard.
  2. Navigate to Plugins > Installed Plugins.
  3. Find EasyCodingWithAI – Pin Security in the list and click Activate.

Plugin Features and Functionality

1. Additional Authentication Layer

This plugin adds an extra layer of security to the WordPress admin login process by requiring users to enter an additional authentication code (PIN) along with their username and password.

2. Configurable Security Settings

  • Max Login Attempts: Set the maximum number of failed login attempts before the user is temporarily locked out.
  • Timeout Duration: Configure how long a user will be locked out after exceeding the maximum login attempts.
  • Minimum PIN Length: Specify the minimum number of characters required for the authentication code.
  • IP Lockout: Enable or disable IP address-based lockout, restricting login attempts from the same IP address after exceeding the allowed attempts.

3. User Profile Management

Users can set their own authentication codes from their profile page. Admins can also manage these settings via the WordPress admin panel.

4. Lockout Mechanism

The plugin implements a lockout mechanism that prevents further login attempts for a specified period after too many failed attempts, adding to the security.

5. Custom Styling (Optional)

You can customize the login field’s appearance using custom CSS.


Richard Robins

Richard Robins

Richard is passionate about sharing how AI resources such as ChatGPT and Microsoft Copilot can be used to create addons and write code, saving small website owners time and money, freeing them to focus on making their site a success.


Disclaimer

The coding tips and guides provided on this website are intended for informational and educational purposes only. While we strive to offer accurate and helpful content, these tips are meant as a starting point for your own coding projects and should not be considered professional advice.

We do not guarantee the effectiveness, security, or safety of any code or techniques discussed on this site. Implementing these tips is done at your own risk, and we encourage you to thoroughly test and evaluate any code before deploying it on your own website or application.

By using this site, you acknowledge that we are not responsible for any issues, damages, or losses that may arise from your use of the information provided herein.