Changeset 3467049
- Timestamp:
- 02/22/2026 06:46:47 PM (5 weeks ago)
- Location:
- headless-rest-api-security/trunk
- Files:
-
- 4 edited
-
headless-rest-api-security.php (modified) (1 diff)
-
includes/admin.css (modified) (1 diff)
-
includes/settings.php (modified) (8 diffs)
-
readme.txt (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
headless-rest-api-security/trunk/headless-rest-api-security.php
r3443475 r3467049 1 1 <?php 2 /* 3 Plugin Name: Headless REST API Security4 Plugin URI: https://wordpress.org/plugins/headless-rest-api-security/5 Description: Security Solution for Headless WordPress. Lock down your REST API, block scrapers, and secure your Next.js/React backend with one click.6 Version: 2.0 7 Author: Md. Rakib Ullah8 Author URI: https://www.linkedin.com/in/rakib417/9 Author Email: rakib417@gmail.com10 Text Domain: headless-rest-api-security11 Domain Path: /languages12 License: GPLv2 or later13 License URI: https://www.gnu.org/licenses/gpl-2.0.html14 */2 /** 3 * Plugin Name: Headless REST API Security 4 * Plugin URI: https://wordpress.org/plugins/headless-rest-api-security/ 5 * Description: Secure your Headless WordPress REST API by disabling public access, enabling strict route whitelisting, and managing API authentication keys. 6 * Version: 2.2 7 * Author: Md. Rakib Ullah 8 * Author URI: https://www.linkedin.com/in/rakib417/ 9 * Author Email: rakib417@gmail.com 10 * Text Domain: headless-rest-api-security 11 * Domain Path: /languages 12 * License: GPLv2 or later 13 * License URI: https://www.gnu.org/licenses/gpl-2.0.html 14 */ 15 15 16 if (!defined('ABSPATH')) 16 if (!defined('ABSPATH')) { 17 17 exit; 18 } 18 19 19 20 // 1. Load Admin Interface -
headless-rest-api-security/trunk/includes/admin.css
r3443475 r3467049 1 .wrap h1 { 2 color: #0073aa; 3 } 4 hr { 5 margin: 10px 0; 6 } 1 .hras-table-wrapper{max-height:600px;overflow-y:auto;border:1px solid #E2E8F0;background:#FFFFFF;margin-top:15px;box-shadow:0 4px 6px -1px rgba(15,23,42,0.05);border-radius:6px}.hras-table{border-collapse:collapse;width:100%;background:#FFFFFF}.hras-table thead th{position:sticky;top:0;z-index:10;background:#F1F5F9;color:#0F172A;padding:15px;font-weight:700;font-size:13px;text-transform:uppercase;letter-spacing:0.5px;text-align:center;border-bottom:2px solid #CBD5E1}.hras-table thead th.allow-header{background:#16A34A;color:#FFFFFF;border-bottom:2px solid #14532d;width:90px}.hras-table td.route-name{text-align:left;padding:12px 15px;width:320px;border-right:1px solid #E2E8F0}.hras-table td.route-name code{font-size:13px;font-family:'SFMono-Regular',Consolas,'Liberation Mono',Menlo,monospace;color:#94A3B8;background:transparent;transition:color 0.2s ease}.hras-table td{text-align:center;vertical-align:middle;border-bottom:1px solid #E2E8F0;padding:10px}.hras-table td.enable-col{background-color:#DCFCE7;border-right:1px solid #E2E8F0}tr.active-row td{background-color:#FFFFFF}tr.active-row td.route-name code{color:#0F172A;font-weight:600}table.hras-table.widefat{border-left:4px solid #2563eb63}tr[data-type="core"] td.route-name{border-left:4px solid #2563eb63}tr[data-type="plugin"] td.route-name{border-left:4px solid #757272a8}tr.active-row[data-type="core"] td.route-name{border-left-color:#2563EB;background:#EFF6FF}tr.active-row[data-type="plugin"] td.route-name{border-left-color:#0F172A}input[disabled]{opacity:0.3;cursor:not-allowed;filter:grayscale(100%)}input[type="checkbox"]{transform:scale(1.3);border:1px solid #94A3B8;border-radius:4px;background:#FFFFFF;cursor:pointer;transition:all 0.2s ease;appearance:none;-webkit-appearance:none;width:16px;height:16px;position:relative}input[type="checkbox"]::after{content:'';position:absolute;display:none;left:5px;top:1px;width:4px;height:9px;border:solid white;border-width:0 2px 2px 0;transform:rotate(45deg)}input[type="checkbox"]:checked{background-color:#16A34A;border-color:#16A34A}input[type="checkbox"]:checked::after{display:block}input[type="checkbox"]:focus{box-shadow:0 0 0 2px #DCFCE7;border-color:#16A34A;outline:none}.hras-table tbody tr:hover td{background-color:#F8FAFC}.hras-table tbody tr:hover td.enable-col{background-color:#bbf7d0} -
headless-rest-api-security/trunk/includes/settings.php
r3443475 r3467049 4 4 } 5 5 6 /** 7 * 1. ENQUEUE SCRIPTS & STYLES 8 */ 9 function hras_enqueue_assets($hook) 10 { 11 // Only load on our specific settings page 12 // phpcs:ignore WordPress.Security.NonceVerification.Recommended 13 if (isset($_GET['page']) && $_GET['page'] !== 'hras-settings' && $_GET['page'] !== 'headless-rest-api-security') { 14 return; 15 } 16 17 // A. Enqueue the CSS file 18 wp_enqueue_style('hras-admin-style', plugin_dir_url(__FILE__) . 'admin.css', array(), '2.2'); 19 20 // B. Add the Javascript Logic (Inline) 21 $custom_js = " 22 document.addEventListener('DOMContentLoaded', function () { 23 const rows = document.querySelectorAll('.hras-row'); 24 rows.forEach(row => { 25 const masterToggle = row.querySelector('.row-master-toggle'); 26 const methodChecks = row.querySelectorAll('.method-check'); 27 28 if(masterToggle) { 29 masterToggle.addEventListener('change', function () { 30 const isEnabled = this.checked; 31 if (isEnabled) { 32 row.classList.add('active-row'); 33 methodChecks.forEach(box => { box.removeAttribute('disabled'); box.checked = true; }); 34 } else { 35 row.classList.remove('active-row'); 36 methodChecks.forEach(box => { box.setAttribute('disabled', 'disabled'); box.checked = false; }); 37 } 38 }); 39 } 40 }); 41 }); 42 "; 43 44 wp_add_inline_script('jquery-core', $custom_js); 45 } 46 add_action('admin_enqueue_scripts', 'hras_enqueue_assets'); 47 48 /** 49 * 2. REGISTER SETTINGS 50 */ 6 51 add_action('admin_init', 'hras_register_settings'); 7 52 8 53 function hras_register_settings() 9 54 { 10 // Simple integer sanitization11 55 register_setting('hras_settings_group', 'hras_enabled', 'intval'); 12 13 // URL sanitization14 56 register_setting('hras_settings_group', 'hras_headless_redirect', 'esc_url_raw'); 15 16 // Text sanitization17 57 register_setting('hras_settings_group', 'hras_api_key', 'sanitize_text_field'); 18 58 register_setting('hras_settings_group', 'hras_allowed_domain', 'sanitize_text_field'); 19 20 // Custom callback for array sanitization21 59 register_setting('hras_settings_group', 'hras_whitelisted_routes', 'hras_sanitize_routes_cb'); 22 60 … … 30 68 } 31 69 32 /**33 * Custom Sanitization Callback for Route Array34 */35 70 function hras_sanitize_routes_cb($input) 36 71 { … … 38 73 return array(); 39 74 } 40 41 75 $clean = array(); 42 76 foreach ($input as $route => $methods) { … … 83 117 } 84 118 85 /* 🌟 GRID UI WITH SMART SORTING🌟 */119 /* 🌟 GRID UI CALLBACK 🌟 */ 86 120 function hras_routes_grid_cb() 87 121 { 88 // FIX: Added phpcs ignore comment because we are intentionally triggering a Core hook 89 if (!did_action('rest_api_init')) { 90 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound 91 do_action('rest_api_init'); 92 } 93 122 // Safely load the REST server without crashing other plugins 94 123 $server = rest_get_server(); 95 96 // Safety check: if server is missing (rare), create a dummy to prevent fatal error 97 if (!$server) { 98 $server = new WP_REST_Server(); 99 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound 100 do_action('rest_api_init', $server); 101 } 102 103 $all_routes = array_keys($server->get_routes()); 124 $all_routes = $server ? array_keys($server->get_routes()) : array(); 125 104 126 $saved_rules = get_option('hras_whitelisted_routes', array()); 105 127 $http_methods = array('GET', 'POST', 'PUT', 'DELETE'); 106 128 107 // 1. Consolidate Routes108 129 $display_routes = array(); 109 130 foreach ($all_routes as $route) { … … 111 132 continue; 112 133 } 134 113 135 $parts = explode('/', trim($route, '/')); 114 115 136 $is_core = (isset($parts[0], $parts[1]) && $parts[0] === 'wp' && $parts[1] === 'v2'); 116 137 … … 127 148 } 128 149 129 // 2. Custom Sorting 130 uksort( 131 $display_routes, 132 function ($a, $b) { 133 $priority_map = array( 134 '/wp/v2/posts' => 10, 135 '/wp/v2/pages' => 20, 136 '/wp/v2/users' => 40, 137 '/wp/v2' => 80, 138 '/' => 999, 139 ); 140 141 $get_score = function ($route) use ($priority_map) { 142 foreach ($priority_map as $key => $score) { 143 if (strpos($route, $key) === 0) { 144 return $score; 145 } 150 uksort($display_routes, function ($a, $b) { 151 $priority_map = array('/wp/v2/posts' => 10, '/wp/v2/pages' => 20, '/wp/v2/users' => 40, '/wp/v2' => 80, '/' => 999); 152 $get_score = function ($route) use ($priority_map) { 153 foreach ($priority_map as $key => $score) { 154 if (strpos($route, $key) === 0) { 155 return $score; 146 156 } 147 return 999;148 };149 150 $score_a = $get_score($a);151 $score_b = $get_score($b);152 153 if ($score_a !== $score_b) {154 return $score_a - $score_b;155 157 } 156 return strcmp($a, $b); 157 } 158 ); 158 return 999; 159 }; 160 $score_a = $get_score($a); 161 $score_b = $get_score($b); 162 return ($score_a !== $score_b) ? $score_a - $score_b : strcmp($a, $b); 163 }); 159 164 160 165 ?> 161 <style>162 .hras-table-wrapper {163 max-height: 600px;164 overflow-y: auto;165 border: 1px solid #c3c4c7;166 background: #fff;167 }168 169 .hras-table {170 width: 100%;171 border-collapse: collapse;172 }173 174 .hras-table th {175 position: sticky;176 top: 0;177 background: #f0f0f1;178 z-index: 10;179 padding: 12px;180 text-align: center;181 border-bottom: 2px solid #c3c4c7;182 }183 184 .hras-table td {185 border-bottom: 1px solid #eee;186 padding: 8px;187 text-align: center;188 vertical-align: middle;189 }190 191 .hras-table td.route-name {192 text-align: left;193 background: #f9f9f9;194 font-family: monospace;195 font-weight: 600;196 color: #d63638;197 border-right: 1px solid #eee;198 padding-left: 15px;199 }200 201 .enable-col {202 background-color: #e5f5fa !important;203 border-right: 1px solid #eee;204 }205 206 tr.active-row td.route-name {207 color: #008a20;208 background-color: #f6f7f7;209 }210 211 input[disabled] {212 opacity: 0.3;213 cursor: not-allowed;214 }215 216 tr[data-type="plugin"] td.route-name {217 border-left: 4px solid #0073aa;218 }219 220 tr[data-type="core"] td.route-name {221 border-left: 4px solid #d63638;222 }223 224 tr.active-row[data-type="core"] td.route-name {225 border-left-color: #00a32a;226 }227 </style>228 229 166 <div class="hras-table-wrapper"> 230 <table class="hras-table ">167 <table class="hras-table widefat"> 231 168 <thead> 232 169 <tr> 233 <th style="text-align:left; min-width: 250px;">Main API Path</th>234 <th style="width: 80px; background:#dcdcde; color:#000;">ALLOW</th>170 <th class="route-header">Main API Path</th> 171 <th class="allow-header">ALLOW</th> 235 172 <?php foreach ($http_methods as $method): ?> 236 <th style="width: 80px;">173 <th class="method-header"> 237 174 <?php echo esc_html($method); ?> 238 175 </th> … … 246 183 $is_row_active = !empty($saved_rules[$route]); 247 184 $row_class = $is_row_active ? 'hras-row active-row' : 'hras-row'; 185 // Determine if it's Core or Plugin for CSS styling 248 186 $row_type = (strpos($route, '/wp/v2') === 0) ? 'core' : 'plugin'; 249 187 ?> 250 <tr class="<?php echo esc_attr($row_class); ?>" id="row-<?php echo esc_attr($row_id); ?>" 251 data-type="<?php echo esc_attr($row_type); ?>"> 188 <tr class="<?php echo esc_attr($row_class); ?>" data-type="<?php echo esc_attr($row_type); ?>"> 252 189 <td class="route-name"> 253 < ?php echo esc_html($route); ?>190 <code><?php echo esc_html($route); ?></code> 254 191 </td> 255 <td class="enable-col"><input type="checkbox" class="row-master-toggle" <?php checked($is_row_active, true); ?>></td> 256 <?php 257 foreach ($http_methods as $method): 192 <td class="enable-col"> 193 <input type="checkbox" class="row-master-toggle" <?php checked($is_row_active, true); ?>> 194 </td> 195 <?php foreach ($http_methods as $method): 258 196 $is_checked = isset($saved_rules[$route][$method]) ? 'checked' : ''; 259 197 $field_name = 'hras_whitelisted_routes[' . esc_attr($route) . '][' . esc_attr($method) . ']'; 260 198 $disabled_attr = $is_row_active ? '' : 'disabled'; 261 199 ?> 262 <td class="check-col"> <input type="checkbox" name="<?php echo esc_attr($field_name); ?>" value="1"263 class="method-check" <?php echo esc_attr($is_checked); ?>264 <?php echo esc_attr($disabled_attr); ?>>200 <td class="check-col"> 201 <input type="checkbox" name="<?php echo esc_attr($field_name); ?>" value="1" class="method-check" 202 <?php echo esc_attr($is_checked); ?> <?php echo esc_attr($disabled_attr); ?>> 265 203 </td> 266 204 <?php endforeach; ?> … … 270 208 </table> 271 209 </div> 272 <script>273 document.addEventListener('DOMContentLoaded', function () {274 const rows = document.querySelectorAll('.hras-row');275 rows.forEach(row => {276 const masterToggle = row.querySelector('.row-master-toggle');277 const methodChecks = row.querySelectorAll('.method-check');278 masterToggle.addEventListener('change', function () {279 const isEnabled = this.checked;280 if (isEnabled) {281 row.classList.add('active-row');282 methodChecks.forEach(box => { box.removeAttribute('disabled'); box.checked = true; });283 } else {284 row.classList.remove('active-row');285 methodChecks.forEach(box => { box.setAttribute('disabled', 'disabled'); box.checked = false; });286 }287 });288 });289 });290 </script>291 210 <?php 292 211 } -
headless-rest-api-security/trunk/readme.txt
r3443475 r3467049 1 1 === Headless REST API Security === 2 2 Contributors: rakib417 3 Tags: headless, rest api, security, authentication3 Tags: headless, rest api, access control, authentication, permissions 4 4 Requires at least: 5.8 5 5 Tested up to: 6.9 6 Requires PHP: 7. 17 Stable tag: 2. 06 Requires PHP: 7.4 7 Stable tag: 2.3 8 8 License: GPLv2 or later 9 9 License URI: https://www.gnu.org/licenses/gpl-2.0.html 10 10 11 The #1 Security Solution for Headless WordPress. Lock down your REST API, block scrapers, and secure your Next.js/React backend with one click.11 Manage access to the WordPress REST API by restricting public endpoints, enabling specific route allow-listing, and handling API key authentication. 12 12 13 13 == Description == 14 14 15 **Headless REST API Security is the "Swiss Army Knife" of API protection for WordPress.** 15 Running a Headless WordPress site often involves exposing the REST API. Headless REST API Security provides tools for administrators to control which endpoints are accessible to the public or external applications. 16 16 17 If you are running a Headless WordPress site (Next.js, Gatsby, Nuxt, or Mobile App), your REST API is **exposed to the public** by default. This leaves your data vulnerable to scrapers, bots, and unauthorized users.17 This plugin restricts public access to REST API endpoints by default and offers a settings interface to allow-list only the specific routes required by a frontend application (such as Next.js, Gatsby, or mobile apps). 18 18 19 **Headless REST API Security** solves this instantly. It is the **FIRST** and **ONLY** plugin designed specifically to lock down Headless architectures with a "Strict Whitelist" model. We give you the power to disable ALL API routes by default and only allow exactly what your app needs. 19 ### Features 20 20 21 ### 📺 Video Tutorial: How to Configure 22 Watch this step-by-step guide to see how to lock down your API in under 2 minutes: 21 * **Access Control:** Restrict default public access to REST API endpoints. 22 * **Route Allow-Listing:** Specific API routes (e.g., `/wp/v2/posts`) can be enabled while others remain restricted. 23 * **API Key Authentication:** Supports an `X-API-KEY` header for server-to-server or frontend requests. 24 * **Headless Redirect:** Option to redirect users accessing the backend API URL to a specified frontend domain. 25 * **Admin Access:** Logged-in Administrators and Editors retain access to the API to support the Block Editor (Gutenberg) functionality. 26 * **Plugin Support:** Detects routes registered by third-party plugins for configuration. 23 27 24 https://www.youtube.com/watch?v=h4l3c5GRxd4 28 ### Usage 25 29 26 --- 27 28 🛑 **STOP** unauthorized data scraping. 29 🔒 **SECURE** your content and user data. 30 🚀 **BOOST** performance by blocking bad requests. 31 32 ### 🚀 Why Headless REST API Security is the Best Choice? 33 34 We didn't just build a security plugin; we built a **Headless Firewall**. Unlike generic security plugins that only look for malware, we control the flow of data itself. 35 36 * **🛡️ Strict Security Mode (Whitelist):** The only plugin that blocks 100% of API requests by default. You choose what to unlock. 37 * **↩️ Smart Headless Redirects:** Automatically redirects visitors who find your backend URL (e.g., `api.yoursite.com`) directly to your frontend (e.g., `www.yoursite.com`). 38 * **🔑 API Key Authentication:** Secure your mobile apps and frontend fetch requests with a simple, secure `X-API-KEY` header. 39 * **⚡ Blazing Fast Performance:** Runs before WordPress loads most core files, ensuring blocked requests don't slow down your server. 40 * **🕵️ Admin Bypass:** Smart detection allows logged-in Administrators to use the WP Dashboard and Gutenberg Block Editor without interruption. 41 42 --- 43 44 ### 🔥 Features at a Glance 45 46 * **1-Click Lockdown:** Instantly secure your entire REST API. 47 * **Route-Level Control:** Enable specific endpoints like `/wp/v2/posts` while keeping `/wp/v2/users` hidden. 48 * **Smart Grouping:** Automatically groups routes (Core vs. Plugins) for easy management. 49 * **Domain Binding:** Restrict API access to *only* your frontend domain. 50 * **Plugin Compatibility:** Works perfectly with Rank Math, WooCommerce, Contact Form 7, and ACF. 51 * **Developer Friendly:** Clean code, native WordPress hooks, and zero bloat. 52 53 --- 54 55 ### 💡 Perfect For: 56 57 * **Headless Sites:** Next.js, Gatsby, Frontity, Faust.js, Nuxt.js. 58 * **Mobile Applications:** React Native, Flutter, iOS, Android. 59 * **Static Sites:** Jamstack architectures needing secure dynamic data. 60 * **Intranets:** Private internal dashboards. 61 62 --- 63 64 ### 🏗️ How It Works 65 66 1. **Activate** the plugin. 67 2. **Turn On** the "Master Switch" to block all public access. 68 3. **Whitelist** only the routes your frontend needs (e.g., `/wp/v2/posts`). 69 4. **Add** your API Key to your frontend environment variables. 70 5. **Relax!** Your API is now invisible to the rest of the world. 71 72 > "Security is not an option; it's a necessity. Headless REST API Security makes it simple." 73 74 --- 75 76 ### ❤️ Love Headless REST API Security? 77 78 If this plugin helped you secure your site, please **rate us 5 stars** on WordPress.org! It helps us keep updates coming. 30 1. Navigate to **Settings > Headless Security** in the WordPress dashboard. 31 2. Enable the **Master Switch** to activate the access restrictions. 32 3. Review the list of REST API routes and check the **Allow** box for endpoints the application requires. 33 4. Copy the generated **API Key** for use in application headers. 34 5. (Optional) Enter a **Headless Frontend URL** to configure redirects for visitors. 79 35 80 36 == Installation == 81 37 82 1. Upload the `headless-rest-api-security` folder to the `/wp-content/plugins/` directory. 83 2. Activate the plugin through the 'Plugins' menu in WordPress. 84 3. Go to the **Headless Security** menu in your dashboard sidebar. 85 4. Enable "Master Switch" to turn on Strict Mode. 86 5. Set your "Headless Frontend URL" to enable redirects. 87 88 == Configuration == 89 90 **1. Headless Redirect (New)** 91 Enter your frontend URL (e.g., `https://www.mysite.com`) in the "Headless Frontend URL" field. 92 * Visitors to your API site will now be redirected there. 93 * `/wp-admin` and `/wp-json` requests are excluded from redirection. 94 95 **2. Whitelisting Routes** 96 Check the "ALLOW" box next to any route you want to make public (to your frontend). 97 * **Note:** You must enable the "Master Switch" for the blocking to take effect. 98 99 **3. Setting up the API Key** 100 Copy the API Key generated in the settings page. Add it to your frontend requests header: 101 `X-API-KEY: your_secret_key_here` 38 1. Upload the plugin files to the `/wp-content/plugins/headless-rest-api-security` directory, or install the plugin through the WordPress plugins screen. 39 2. Activate the plugin through the 'Plugins' screen in WordPress. 40 3. Go to the **Headless Security** menu to configure allowed routes. 102 41 103 42 == Frequently Asked Questions == 104 43 105 = Does this plugin replace WordPress authentication? =106 No. It adds a security firewall layer *before* WordPress processes the request. It works alongside existing auth methods (like JWT or Cookies).44 = Does this modify WordPress Core files? = 45 No. The plugin uses standard WordPress hooks (`rest_authentication_errors` and `template_redirect`) to manage access. 107 46 108 = Will this breakthe Block Editor (Gutenberg)? =109 No. The plugin includes an "Admin Bypass" feature. If you are logged in as an Administrator or Editor, the API restrictions are skipped so you can work normally.47 = Will this affect the Block Editor (Gutenberg)? = 48 The plugin checks for logged-in users with the `edit_posts` capability, allowing the backend editor to function normally while restrictions are active. 110 49 111 = Can I use this with Rank Math, WooCommerce, or CF7? =112 Yes. The plugin automatically detects routes registered by other plugins. You can see them in the list and whitelist them (e.g., `/wc/v3` or `/contact-form-7/v1`).50 = Can I use this with custom endpoints? = 51 Yes. Registered REST API routes appear in the settings list and can be allow-listed. 113 52 114 = What happens if I lose my API Key? = 115 You can view or generate a new key anytime from the settings page. 53 = Where is the API Key placed? = 54 The key is sent in the request header. Example: 55 `X-API-KEY: your_generated_key_here` 116 56 117 57 == Screenshots == 118 58 119 1. ** Dashboard:** The main settings interface with the Master Switch and Redirectoptions.120 2. ** Whitelist Grid:** The smart list of API routes allowing you to toggle access.59 1. **General Settings:** The main configuration screen with the Master Switch and Redirect URL options. 60 2. **Route Manager:** The grid view for allowing or restricting specific API namespaces and endpoints. 121 61 122 62 == Changelog == 123 63 124 = 2.0 = 125 * New: Added Headless Redirect to main domain function. 126 * New: Introduced Strict Security (Whitelist) mode. 127 * New: Added Smart Grouping for cleaner route management. 128 * Improvement: Added Admin Bypass for logged-in users. 64 = 2.3 = 65 * Fix: Resolved a critical error on the settings page caused by third-party plugin conflicts with REST API initialization. 66 * Fix: Resolved stable tag and version mismatch issues for WordPress.org compliance. 129 67 130 = 1.1.0 = 131 * Added dynamic REST route detection. 132 * Added route-level access control. 133 * Editable API key. 134 * Domain binding support. 68 = 2.2 = 69 * Updated UI styles for better accessibility. 70 * Improved checkbox contrast. 135 71 136 = 1.0.0 = 137 * Initial Release. 138 139 == Upgrade Notice == 72 = 2.1 = 73 * Minor code improvements. 140 74 141 75 = 2.0 = 142 Major update introducing Strict Whitelist Mode and Headless Redirects. Please review your allowed routes after upgrading. 76 * Added route allow-listing functionality. 77 * Added headless frontend redirect feature. 78 * Added admin bypass for authenticated users. 143 79 144 == Contact == 145 Author: Md. Rakib Ullah 146 Email: rakib417@gmail.com 147 Linkedin: https://www.linkedin.com/in/rakib417/ 80 = 1.0 = 81 * Initial release.
Note: See TracChangeset
for help on using the changeset viewer.