Changeset 3440551
- Timestamp:
- 01/15/2026 05:35:10 PM (5 weeks ago)
- Location:
- loginease/trunk
- Files:
-
- 3 edited
-
includes/class-login.php (modified) (12 diffs)
-
loginease.php (modified) (1 diff)
-
readme.txt (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
loginease/trunk/includes/class-login.php
r3362368 r3440551 1 1 <?php 2 if ( ! defined( 'ABSPATH' )) {2 if (!defined('ABSPATH')) { 3 3 exit; 4 4 } … … 11 11 * enqueuing external CSS/JS files. 12 12 */ 13 class LoginEase_Login { 13 class LoginEase_Login 14 { 14 15 private static $opts = null; 15 16 16 public static function init_hooks() { 17 self::$opts = get_option( 'loginease_settings', [] ); 18 19 add_filter( 'wp_redirect', [ __CLASS__, 'prevent_login_redirect' ], 0, 2 ); 20 add_action( 'init', [ __CLASS__, 'maybe_block_default_login_and_admin' ], 1 ); 21 add_action( 'admin_init', [ __CLASS__, 'maybe_block_wp_admin' ], 1 ); 22 add_action( 'login_init', [ __CLASS__, 'maybe_block_wp_login' ], 1 ); 23 add_action( 'login_init', [ __CLASS__, 'block_lost_password_if_passwordless' ], 0 ); 24 add_action( 'init', [ __CLASS__, 'add_custom_login_rewrite' ], 5 ); 25 add_filter( 'query_vars', [ __CLASS__, 'add_query_vars' ] ); 26 add_action( 'template_redirect', [ __CLASS__, 'maybe_handle_custom_slug' ], 1 ); 27 add_filter( 'authenticate', [ __CLASS__, 'loginease_authenticate' ], 30, 3 ); 28 add_filter( 'login_message', [ __CLASS__, 'add_sent_message' ] ); 29 add_filter( 'body_class', [ __CLASS__, 'body_class_notice' ] ); 30 add_action( 'login_form_login', [ __CLASS__, 'process_login_request' ] ); 31 add_action( 'login_form', [ __CLASS__, 'add_loginease_button' ], 20 ); 32 add_action( 'login_enqueue_scripts', [ __CLASS__, 'enqueue_styles_scripts' ] ); 33 add_action( 'login_enqueue_scripts', [ __CLASS__, 'disable_lost_password_link' ] ); 34 add_action( 'login_enqueue_scripts', [ __CLASS__, 'hide_password_login_fields' ], 20 ); 35 add_filter( 'login_url', [ __CLASS__, 'filter_login_url' ], 10, 3 ); 36 add_filter( 'allow_password_reset', [ __CLASS__, 'disable_password_reset_if_passwordless' ], 10, 2 ); 37 } 38 39 private static function get_options() { 40 if ( null === self::$opts ) { 41 self::$opts = get_option( 'loginease_settings', [] ); 17 public static function init_hooks() 18 { 19 self::$opts = get_option('loginease_settings', []); 20 21 add_filter('wp_redirect', [__CLASS__, 'prevent_login_redirect'], 0, 2); 22 add_action('init', [__CLASS__, 'maybe_block_default_login_and_admin'], 1); 23 add_action('admin_init', [__CLASS__, 'maybe_block_wp_admin'], 1); 24 add_action('login_init', [__CLASS__, 'maybe_block_wp_login'], 1); 25 add_action('login_init', [__CLASS__, 'block_lost_password_if_passwordless'], 0); 26 add_action('init', [__CLASS__, 'add_custom_login_rewrite'], 5); 27 add_filter('query_vars', [__CLASS__, 'add_query_vars']); 28 add_action('template_redirect', [__CLASS__, 'maybe_handle_custom_slug'], 1); 29 add_filter('authenticate', [__CLASS__, 'loginease_authenticate'], 30, 3); 30 add_filter('login_message', [__CLASS__, 'add_sent_message']); 31 add_filter('body_class', [__CLASS__, 'body_class_notice']); 32 add_action('login_form_login', [__CLASS__, 'process_login_request']); 33 add_action('login_form', [__CLASS__, 'add_loginease_button'], 20); 34 add_action('login_enqueue_scripts', [__CLASS__, 'enqueue_styles_scripts']); 35 add_action('login_enqueue_scripts', [__CLASS__, 'disable_lost_password_link']); 36 add_action('login_enqueue_scripts', [__CLASS__, 'hide_password_login_fields'], 20); 37 add_filter('login_url', [__CLASS__, 'filter_login_url'], 10, 3); 38 add_filter('allow_password_reset', [__CLASS__, 'disable_password_reset_if_passwordless'], 10, 2); 39 } 40 41 private static function get_options() 42 { 43 if (null === self::$opts) { 44 self::$opts = get_option('loginease_settings', []); 42 45 } 43 46 return self::$opts; 44 47 } 45 48 46 public static function is_custom_login_enabled() { 49 public static function is_custom_login_enabled() 50 { 47 51 $options = self::get_options(); 48 return ! empty( $options['enable_custom_login'] ); 49 } 50 51 public static function get_custom_login_slug() { 52 return !empty($options['enable_custom_login']); 53 } 54 55 public static function get_custom_login_slug() 56 { 52 57 $options = self::get_options(); 53 $slug = isset( $options['custom_login_slug'] ) && $options['custom_login_slug'] !== '' ? $options['custom_login_slug'] : 'connexion'; 54 return sanitize_title( $slug ); 55 } 56 57 public static function block_lost_password_if_passwordless() { 58 $opts = self::get_options(); 59 if ( empty( $opts['disable_password_login'] ) ) { 60 return; 61 } 62 $action = isset( $_GET['action'] ) ? sanitize_text_field( wp_unslash( $_GET['action'] ) ) : ''; 63 if ( in_array( $action, [ 'lostpassword', 'resetpass' ], true ) ) { 64 wp_safe_redirect( self::get_login_url() ); 65 exit; 66 } 67 } 68 69 public static function disable_password_reset_if_passwordless( $allow, $user_id ) { 70 $opts = self::get_options(); 71 if ( ! empty( $opts['disable_password_login'] ) ) { 58 $slug = isset($options['custom_login_slug']) && $options['custom_login_slug'] !== '' ? $options['custom_login_slug'] : 'connexion'; 59 return sanitize_title($slug); 60 } 61 62 public static function block_lost_password_if_passwordless() 63 { 64 $opts = self::get_options(); 65 if (empty($opts['disable_password_login'])) { 66 return; 67 } 68 $action = isset($_GET['action']) ? sanitize_text_field(wp_unslash($_GET['action'])) : ''; 69 if (in_array($action, ['lostpassword', 'resetpass'], true)) { 70 wp_safe_redirect(self::get_login_url()); 71 exit; 72 } 73 } 74 75 public static function disable_password_reset_if_passwordless($allow, $user_id) 76 { 77 $opts = self::get_options(); 78 if (!empty($opts['disable_password_login'])) { 72 79 return false; 73 80 } … … 75 82 } 76 83 77 public static function disable_lost_password_link() { 78 $opts = self::get_options(); 79 if ( empty( $opts['disable_password_login'] ) ) { 80 return; 81 } 82 add_filter( 'gettext', function ( $translated_text, $text, $domain ) { 83 if ( 'Lost your password?' === $text || 'Forgot your password?' === $text ) { 84 public static function disable_lost_password_link() 85 { 86 $opts = self::get_options(); 87 if (empty($opts['disable_password_login'])) { 88 return; 89 } 90 add_filter('gettext', function ($translated_text, $text, $domain) { 91 if ('Lost your password?' === $text || 'Forgot your password?' === $text) { 84 92 return ''; 85 93 } 86 94 return $translated_text; 87 }, 10, 3 ); 88 } 89 90 public static function hide_password_login_fields() { 91 $opts = self::get_options(); 92 if ( empty( $opts['disable_password_login'] ) ) { 93 return; 94 } 95 $current_action = isset( $_GET['action'] ) ? sanitize_text_field( wp_unslash( $_GET['action'] ) ) : 'login'; 96 if ( 'login' !== $current_action ) { 95 }, 10, 3); 96 } 97 98 public static function hide_password_login_fields() 99 { 100 $opts = self::get_options(); 101 if (empty($opts['disable_password_login'])) { 102 return; 103 } 104 $current_action = isset($_GET['action']) ? sanitize_text_field(wp_unslash($_GET['action'])) : 'login'; 105 if ('login' !== $current_action) { 97 106 return; 98 107 } … … 100 109 } 101 110 102 public static function enqueue_styles_scripts() { 103 $opts = self::get_options(); 104 105 $disable_password_login = ! empty( $opts['disable_password_login'] ); 106 $current_action = isset( $_GET['action'] ) ? sanitize_text_field( wp_unslash( $_GET['action'] ) ) : 'login'; 107 108 if ( $disable_password_login && in_array( $current_action, [ 'lostpassword', 'resetpass' ], true ) ) { 109 wp_safe_redirect( self::get_login_url() ); 110 exit; 111 } 112 if ( in_array( $current_action, [ 'lostpassword', 'resetpass' ], true ) ) { 113 return; 114 } 115 116 // Construction correcte de la base URL du plugin 117 $plugin_url_base = plugin_dir_url( dirname( __FILE__ ) ); 118 119 // Enqueue sans numéro de version (false) 120 wp_enqueue_style( 121 'loginease-style', 122 $plugin_url_base . 'assets/css/loginease-style.css', 123 [] 124 ); 125 126 wp_enqueue_script( 127 'loginease-script', 128 $plugin_url_base . 'assets/js/loginease-script.js', 129 [], 130 ); 131 } 132 133 public static function add_custom_login_rewrite() { 134 $opts = self::get_options(); 135 if ( empty( $opts['enable_custom_login'] ) ) { 111 public static function enqueue_styles_scripts() 112 { 113 $opts = self::get_options(); 114 115 $disable_password_login = !empty($opts['disable_password_login']); 116 $current_action = isset($_GET['action']) ? sanitize_text_field(wp_unslash($_GET['action'])) : 'login'; 117 118 if ($disable_password_login && in_array($current_action, ['lostpassword', 'resetpass'], true)) { 119 wp_safe_redirect(self::get_login_url()); 120 exit; 121 } 122 if (in_array($current_action, ['lostpassword', 'resetpass'], true)) { 123 return; 124 } 125 126 // Construction correcte de la base URL du plugin 127 $plugin_url_base = plugin_dir_url(dirname(__FILE__)); 128 129 // Enqueue sans numéro de version (false) 130 wp_enqueue_style( 131 'loginease-style', 132 $plugin_url_base . 'assets/css/loginease-style.css', 133 [] 134 ); 135 136 wp_enqueue_script( 137 'loginease-script', 138 $plugin_url_base . 'assets/js/loginease-script.js', 139 [], 140 ); 141 } 142 143 public static function add_custom_login_rewrite() 144 { 145 $opts = self::get_options(); 146 if (empty($opts['enable_custom_login'])) { 136 147 return; 137 148 } 138 149 $slug = self::get_custom_login_slug(); 139 150 140 add_rewrite_tag( '%loginease_custom%', '([^&]+)');141 add_rewrite_rule( '^' . preg_quote( $slug, '/' ) . '/?$', 'index.php?loginease_custom=1', 'top');151 add_rewrite_tag('%loginease_custom%', '([^&]+)'); 152 add_rewrite_rule('^' . preg_quote($slug, '/') . '/?$', 'index.php?loginease_custom=1', 'top'); 142 153 143 154 global $wp_rewrite; 144 if ( isset( $wp_rewrite ) && is_object( $wp_rewrite ) && property_exists( $wp_rewrite, 'rules' )) {155 if (isset($wp_rewrite) && is_object($wp_rewrite) && property_exists($wp_rewrite, 'rules')) { 145 156 $rule_key = '^' . $slug . '/?$'; 146 157 $rule_value = 'index.php?loginease_custom=1'; 147 158 $rules = (array) $wp_rewrite->rules; 148 if ( ! isset( $rules[ $rule_key ] ) ) { 149 $wp_rewrite->rules = [ $rule_key => $rule_value ] + $rules; 150 } 151 } 152 } 153 154 public static function add_query_vars( $vars ) { 159 if (!isset($rules[$rule_key])) { 160 $wp_rewrite->rules = [$rule_key => $rule_value] + $rules; 161 } 162 } 163 } 164 165 public static function add_query_vars($vars) 166 { 155 167 $vars[] = 'loginease_custom'; 156 168 return $vars; 157 169 } 158 170 159 public static function maybe_handle_custom_slug() { 160 $opts = self::get_options(); 161 if ( empty( $opts['enable_custom_login'] ) ) { 171 public static function maybe_handle_custom_slug() 172 { 173 $opts = self::get_options(); 174 if (empty($opts['enable_custom_login'])) { 162 175 return; 163 176 } 164 177 $slug = self::get_custom_login_slug(); 165 $qv = get_query_var( 'loginease_custom');166 $matched = ( (string) $qv === '1');167 if ( ! $matched) {168 $request_uri = isset( $_SERVER['REQUEST_URI'] ) ? wp_unslash( $_SERVER['REQUEST_URI']) : '';169 $request_path = wp_parse_url( $request_uri, PHP_URL_PATH);170 $site_path = wp_parse_url( site_url(), PHP_URL_PATH);171 $site_path = $site_path ? ( '/' . trim( $site_path, '/' )) : '';172 $expected = rtrim( $site_path, '/') . '/' . $slug . '/';173 if ( strpos( $expected, '/' ) !== 0) {174 $expected = '/' . ltrim( $expected, '/');175 } 176 if ( untrailingslashit( $request_path ) === untrailingslashit( $expected )) {178 $qv = get_query_var('loginease_custom'); 179 $matched = ((string) $qv === '1'); 180 if (!$matched) { 181 $request_uri = isset($_SERVER['REQUEST_URI']) ? wp_unslash($_SERVER['REQUEST_URI']) : ''; 182 $request_path = wp_parse_url($request_uri, PHP_URL_PATH); 183 $site_path = wp_parse_url(site_url(), PHP_URL_PATH); 184 $site_path = $site_path ? ('/' . trim($site_path, '/')) : ''; 185 $expected = rtrim($site_path, '/') . '/' . $slug . '/'; 186 if (strpos($expected, '/') !== 0) { 187 $expected = '/' . ltrim($expected, '/'); 188 } 189 if (untrailingslashit($request_path) === untrailingslashit($expected)) { 177 190 $matched = true; 178 191 } 179 192 } 180 if ( $matched ) { 181 $target = site_url( 'wp-login.php' ); 182 $args = [ 'loginease' => $slug ]; 183 if ( isset( $_GET['redirect_to'] ) && $_GET['redirect_to'] !== '' ) { 184 $args['redirect_to'] = wp_unslash( $_GET['redirect_to'] ); 185 } 186 if ( isset( $_GET['loginease_token'] ) && $_GET['loginease_token'] !== '' ) { 187 $args['loginease_token'] = wp_unslash( $_GET['loginease_token'] ); 188 } 189 $target = add_query_arg( $args, $target ); 190 wp_safe_redirect( $target, 302 ); 191 exit; 192 } 193 } 194 195 public static function maybe_block_default_login_and_admin() { 196 $opts = self::get_options(); 197 if ( empty( $opts['enable_custom_login'] ) ) { 198 return; 199 } 200 if ( defined( 'WP_CLI' ) && WP_CLI ) { 201 return; 202 } 203 if ( function_exists( 'is_user_logged_in' ) && is_user_logged_in() ) { 204 return; 205 } 206 $request_uri = isset( $_SERVER['REQUEST_URI'] ) ? wp_unslash( $_SERVER['REQUEST_URI'] ) : ''; 207 $path = wp_parse_url( $request_uri, PHP_URL_PATH ); 208 $basename = strtolower( basename( $path ) ); 209 $disable_password_login = ! empty( $opts['disable_password_login'] ); 210 $action = isset( $_GET['action'] ) ? sanitize_text_field( wp_unslash( $_GET['action'] ) ) : ''; 211 if ( $disable_password_login && in_array( $action, [ 'lostpassword', 'resetpass' ], true ) ) { 193 if ($matched) { 194 $target = site_url('wp-login.php'); 195 $args = ['loginease' => $slug]; 196 if (isset($_GET['redirect_to']) && $_GET['redirect_to'] !== '') { 197 $args['redirect_to'] = wp_unslash($_GET['redirect_to']); 198 } 199 if (isset($_GET['loginease_token']) && $_GET['loginease_token'] !== '') { 200 $args['loginease_token'] = wp_unslash($_GET['loginease_token']); 201 } 202 $target = add_query_arg($args, $target); 203 wp_safe_redirect($target, 302); 204 exit; 205 } 206 } 207 208 public static function maybe_block_default_login_and_admin() 209 { 210 $opts = self::get_options(); 211 if (empty($opts['enable_custom_login'])) { 212 return; 213 } 214 if (defined('WP_CLI') && WP_CLI) { 215 return; 216 } 217 if (function_exists('is_user_logged_in') && is_user_logged_in()) { 218 return; 219 } 220 $request_uri = isset($_SERVER['REQUEST_URI']) ? wp_unslash($_SERVER['REQUEST_URI']) : ''; 221 $path = wp_parse_url($request_uri, PHP_URL_PATH); 222 $basename = strtolower(basename($path)); 223 $disable_password_login = !empty($opts['disable_password_login']); 224 $action = isset($_GET['action']) ? sanitize_text_field(wp_unslash($_GET['action'])) : ''; 225 if ($disable_password_login && in_array($action, ['lostpassword', 'resetpass'], true)) { 212 226 self::send_404_and_exit(); 213 227 } 214 if ( in_array( $basename, [ 'admin-ajax.php', 'admin-post.php' ], true )) {215 return; 216 } 217 if ( isset( $_GET['loginease_token'] ) && $_GET['loginease_token'] !== '') {218 return; 219 } 220 if ( isset( $_GET['loginease'] ) && $_GET['loginease'] === ( isset( $opts['custom_login_slug'] ) ? sanitize_title( $opts['custom_login_slug'] ) : 'connexion' )) {221 return; 222 } 223 if ( $basename === 'wp-login.php' || strpos( '/' . ltrim( (string) $path, '/' ), '/wp-admin' ) === 0) {228 if (in_array($basename, ['admin-ajax.php', 'admin-post.php'], true)) { 229 return; 230 } 231 if (isset($_GET['loginease_token']) && $_GET['loginease_token'] !== '') { 232 return; 233 } 234 if (isset($_GET['loginease']) && $_GET['loginease'] === (isset($opts['custom_login_slug']) ? sanitize_title($opts['custom_login_slug']) : 'connexion')) { 235 return; 236 } 237 if ($basename === 'wp-login.php' || strpos('/' . ltrim((string) $path, '/'), '/wp-admin') === 0) { 224 238 self::send_404_and_exit(); 225 239 } 226 240 } 227 241 228 public static function maybe_block_wp_admin() { 229 $opts = self::get_options(); 230 if ( empty( $opts['enable_custom_login'] ) ) { 231 return; 232 } 233 if ( defined( 'WP_CLI' ) && WP_CLI ) { 234 return; 235 } 236 if ( function_exists( 'is_user_logged_in' ) && is_user_logged_in() ) { 242 public static function maybe_block_wp_admin() 243 { 244 $opts = self::get_options(); 245 if (empty($opts['enable_custom_login'])) { 246 return; 247 } 248 if (defined('WP_CLI') && WP_CLI) { 249 return; 250 } 251 if (function_exists('is_user_logged_in') && is_user_logged_in()) { 237 252 return; 238 253 } 239 254 $basename = ''; 240 if ( isset( $_SERVER['PHP_SELF'] )) {241 $basename = strtolower( basename( wp_unslash( $_SERVER['PHP_SELF'] ) ));242 if ( in_array( $basename, [ 'admin-ajax.php', 'admin-post.php' ], true )) {255 if (isset($_SERVER['PHP_SELF'])) { 256 $basename = strtolower(basename(wp_unslash($_SERVER['PHP_SELF']))); 257 if (in_array($basename, ['admin-ajax.php', 'admin-post.php'], true)) { 243 258 return; 244 259 } 245 260 } 246 261 if ( 247 ( isset( $_GET['loginease'] ) && $_GET['loginease'] === self::get_custom_login_slug())248 || ( isset( $_GET['loginease_token'] ) && $_GET['loginease_token'] !== '')262 (isset($_GET['loginease']) && $_GET['loginease'] === self::get_custom_login_slug()) 263 || (isset($_GET['loginease_token']) && $_GET['loginease_token'] !== '') 249 264 ) { 250 265 return; … … 253 268 } 254 269 255 public static function maybe_block_wp_login() { 256 $opts = self::get_options(); 257 if ( empty( $opts['enable_custom_login'] ) ) { 258 return; 259 } 260 if ( defined( 'WP_CLI' ) && WP_CLI ) { 261 return; 262 } 263 if ( function_exists( 'is_user_logged_in' ) && is_user_logged_in() ) { 264 return; 265 } 266 if ( isset( $_GET['loginease_token'] ) && $_GET['loginease_token'] !== '' ) { 267 return; 268 } 269 if ( isset( $_GET['loginease'] ) && $_GET['loginease'] === self::get_custom_login_slug() ) { 270 return; 271 } 272 $request_uri = isset( $_SERVER['REQUEST_URI'] ) ? wp_unslash( $_SERVER['REQUEST_URI'] ) : ''; 270 public static function maybe_block_wp_login() 271 { 272 $opts = self::get_options(); 273 if (empty($opts['enable_custom_login'])) { 274 return; 275 } 276 if (defined('WP_CLI') && WP_CLI) { 277 return; 278 } 279 if (function_exists('is_user_logged_in') && is_user_logged_in()) { 280 return; 281 } 282 if (isset($_GET['loginease_token']) && $_GET['loginease_token'] !== '') { 283 return; 284 } 285 if (isset($_GET['loginease']) && $_GET['loginease'] === self::get_custom_login_slug()) { 286 return; 287 } 288 $request_uri = isset($_SERVER['REQUEST_URI']) ? wp_unslash($_SERVER['REQUEST_URI']) : ''; 273 289 $path_info = ''; 274 if ( ! empty( $_SERVER['PATH_INFO'] )) {275 $path_info = trim( wp_unslash( $_SERVER['PATH_INFO'] ), '/');290 if (!empty($_SERVER['PATH_INFO'])) { 291 $path_info = trim(wp_unslash($_SERVER['PATH_INFO']), '/'); 276 292 } else { 277 $pos = strpos( $request_uri, 'wp-login.php');278 if ( $pos !== false) {279 $after = substr( $request_uri, $pos + strlen( 'wp-login.php' ));280 $path_info = trim( wp_parse_url( $after, PHP_URL_PATH ), '/');281 } 282 } 283 if ( $path_info !== '' && $path_info === self::get_custom_login_slug()) {293 $pos = strpos($request_uri, 'wp-login.php'); 294 if ($pos !== false) { 295 $after = substr($request_uri, $pos + strlen('wp-login.php')); 296 $path_info = trim(wp_parse_url($after, PHP_URL_PATH), '/'); 297 } 298 } 299 if ($path_info !== '' && $path_info === self::get_custom_login_slug()) { 284 300 return; 285 301 } … … 287 303 } 288 304 289 public static function prevent_login_redirect( $location, $status ) { 290 $opts = self::get_options(); 291 if ( empty( $opts['enable_custom_login'] ) ) { 305 public static function prevent_login_redirect($location, $status) 306 { 307 $opts = self::get_options(); 308 if (empty($opts['enable_custom_login'])) { 292 309 return $location; 293 310 } 294 if ( function_exists( 'is_user_logged_in' ) && is_user_logged_in()) {311 if (function_exists('is_user_logged_in') && is_user_logged_in()) { 295 312 return $location; 296 313 } 297 if ( defined( 'WP_CLI' ) && WP_CLI) {314 if (defined('WP_CLI') && WP_CLI) { 298 315 return $location; 299 316 } 300 $request_uri = isset( $_SERVER['REQUEST_URI'] ) ? wp_unslash( $_SERVER['REQUEST_URI']) : '';301 $path = wp_parse_url( $request_uri, PHP_URL_PATH);302 $normalized = '/' . ltrim( (string) $path, '/');303 if ( strpos( $normalized, '/wp-admin' ) === 0) {304 if ( false !== stripos( $location, 'wp-login.php' )) {317 $request_uri = isset($_SERVER['REQUEST_URI']) ? wp_unslash($_SERVER['REQUEST_URI']) : ''; 318 $path = wp_parse_url($request_uri, PHP_URL_PATH); 319 $normalized = '/' . ltrim((string) $path, '/'); 320 if (strpos($normalized, '/wp-admin') === 0) { 321 if (false !== stripos($location, 'wp-login.php')) { 305 322 self::send_404_and_exit(); 306 323 } 307 if ( function_exists( 'wp_login_url' )) {324 if (function_exists('wp_login_url')) { 308 325 $login_url = wp_login_url(); 309 if ( $login_url && 0 === strpos( $location, $login_url )) {326 if ($login_url && 0 === strpos($location, $login_url)) { 310 327 self::send_404_and_exit(); 311 328 } … … 315 332 } 316 333 317 private static function send_404_and_exit() { 318 if ( ! headers_sent() ) { 319 status_header( 404 ); 334 private static function send_404_and_exit() 335 { 336 if (!headers_sent()) { 337 status_header(404); 320 338 nocache_headers(); 321 339 } 322 if ( function_exists( 'get_404_template' )) {340 if (function_exists('get_404_template')) { 323 341 $template = get_404_template(); 324 if ( $template && file_exists( $template )) {342 if ($template && file_exists($template)) { 325 343 include $template; 326 344 exit; 327 345 } 328 346 } 329 if ( function_exists( 'wp_die' )) {330 wp_die( '<h1>404 Not Found</h1>', '404 Not Found', [ 'response' => 404 ]);331 } 332 if ( isset( $_SERVER['SERVER_PROTOCOL'] )) {333 header( sanitize_text_field( wp_unslash( $_SERVER['SERVER_PROTOCOL'] ) ) . ' 404 Not Found', true, 404);347 if (function_exists('wp_die')) { 348 wp_die('<h1>404 Not Found</h1>', '404 Not Found', ['response' => 404]); 349 } 350 if (isset($_SERVER['SERVER_PROTOCOL'])) { 351 header(sanitize_text_field(wp_unslash($_SERVER['SERVER_PROTOCOL'])) . ' 404 Not Found', true, 404); 334 352 } else { 335 header( 'HTTP/1.0 404 Not Found', true, 404);353 header('HTTP/1.0 404 Not Found', true, 404); 336 354 } 337 355 echo '<h1>404 Not Found</h1>'; … … 339 357 } 340 358 341 public static function add_sent_message( $message ) { 342 if ( isset( $_GET['loginease_status'] ) ) { 343 $status = sanitize_text_field( wp_unslash( $_GET['loginease_status'] ) ); 344 switch ( $status ) { 359 public static function add_sent_message($message) 360 { 361 if (isset($_GET['loginease_status'])) { 362 $status = sanitize_text_field(wp_unslash($_GET['loginease_status'])); 363 switch ($status) { 345 364 case 'sent': 346 $message .= '<p class="message loginease-sent-message">' . esc_html__( 'A magic login link has been sent to your email.', 'loginease') . '</p>';365 $message .= '<p class="message loginease-sent-message">' . esc_html__('A magic login link has been sent to your email.', 'loginease') . '</p>'; 347 366 break; 348 367 case 'invalid': 349 $message .= '<p class="message loginease-error-message">' . esc_html__( 'Invalid or expired login link.', 'loginease') . '</p>';368 $message .= '<p class="message loginease-error-message">' . esc_html__('Invalid or expired login link.', 'loginease') . '</p>'; 350 369 break; 351 370 } … … 354 373 } 355 374 356 public static function body_class_notice( $classes ) { 357 if ( isset( $_GET['loginease_status'] ) && sanitize_text_field( wp_unslash( $_GET['loginease_status'] ) ) === 'sent' ) { 375 public static function body_class_notice($classes) 376 { 377 if (isset($_GET['loginease_status']) && sanitize_text_field(wp_unslash($_GET['loginease_status'])) === 'sent') { 358 378 $classes[] = 'loginease-email-sent'; 359 379 } … … 361 381 } 362 382 363 public static function filter_login_url( $login_url, $redirect = '', $force_reauth = false ) { 364 if ( ! self::is_custom_login_enabled() ) { 383 public static function filter_login_url($login_url, $redirect = '', $force_reauth = false) 384 { 385 if (!self::is_custom_login_enabled()) { 365 386 return $login_url; 366 387 } 367 388 $slug = self::get_custom_login_slug(); 368 $url = site_url( '/' . $slug . '/');369 if ( ! empty( $redirect )) {370 $url = add_query_arg( 'redirect_to', rawurlencode( $redirect ), $url);389 $url = site_url('/' . $slug . '/'); 390 if (!empty($redirect)) { 391 $url = add_query_arg('redirect_to', rawurlencode($redirect), $url); 371 392 } 372 393 return $url; 373 394 } 374 395 375 public static function get_login_url( $redirect = '' ) { 376 return wp_login_url( $redirect ); 377 } 378 379 public static function process_login_request() { 380 $opts = self::get_options(); 381 $disable_password_login = ! empty( $opts['disable_password_login'] ); 382 if ( function_exists( 'is_user_logged_in' ) && is_user_logged_in() ) { 383 return; 384 } 385 if ( $disable_password_login && isset( $_POST['wp-submit'] ) ) { 386 wp_safe_redirect( wp_login_url() ); 387 exit; 388 } 389 if ( isset( $_POST['loginease_submit'] ) && check_admin_referer( 'login', '_wpnonce' ) ) { 390 $login_or_email_raw = isset( $_POST['log'] ) ? wp_unslash( $_POST['log'] ) : ''; 391 $login_or_email = sanitize_text_field( $login_or_email_raw ); 392 $redirect_url = add_query_arg( 'loginease_status', 'sent', self::get_login_url() ); 393 if ( empty( $login_or_email ) ) { 394 wp_safe_redirect( add_query_arg( 'login', 'empty', self::get_login_url() ) ); 396 public static function get_login_url($redirect = '') 397 { 398 return wp_login_url($redirect); 399 } 400 401 public static function process_login_request() 402 { 403 $opts = self::get_options(); 404 $disable_password_login = !empty($opts['disable_password_login']); 405 if (function_exists('is_user_logged_in') && is_user_logged_in()) { 406 return; 407 } 408 if ($disable_password_login && isset($_POST['wp-submit'])) { 409 wp_safe_redirect(wp_login_url()); 410 exit; 411 } 412 if (isset($_POST['loginease_submit']) && check_admin_referer('login', '_wpnonce')) { 413 $login_or_email_raw = isset($_POST['log']) ? wp_unslash($_POST['log']) : ''; 414 $login_or_email = sanitize_text_field($login_or_email_raw); 415 $redirect_url = add_query_arg('loginease_status', 'sent', self::get_login_url()); 416 if (empty($login_or_email)) { 417 wp_safe_redirect(add_query_arg('login', 'empty', self::get_login_url())); 395 418 exit; 396 419 } 397 $user = is_email( $login_or_email ) ? get_user_by( 'email', $login_or_email ) : get_user_by( 'login', $login_or_email);398 if ( ! $user) {399 wp_safe_redirect( $redirect_url);420 $user = is_email($login_or_email) ? get_user_by('email', $login_or_email) : get_user_by('login', $login_or_email); 421 if (!$user) { 422 wp_safe_redirect($redirect_url); 400 423 exit; 401 424 } 402 $token = self::generate_token_for_user( $user->ID ); 403 self::send_login_email( $user, $token ); 404 wp_safe_redirect( $redirect_url ); 405 exit; 406 } 407 if ( isset( $_GET['loginease_token'] ) ) { 408 $token = sanitize_text_field( wp_unslash( $_GET['loginease_token'] ) ); 409 self::login_with_token( $token ); 410 } 411 } 412 413 public static function loginease_authenticate( $user, $username, $password ) { 414 if ( isset( $_POST['loginease_submit'] ) ) { 415 if ( empty( $username ) ) { 416 return new WP_Error( 'empty_username', __( 'Missing username or email.', 'loginease' ) ); 425 $token = self::generate_token_for_user($user->ID); 426 self::send_login_email($user, $token); 427 wp_safe_redirect($redirect_url); 428 exit; 429 } 430 431 // Afficher formulaire de confirmation si token fourni dans URL mais pas encore confirmé 432 if (isset($_GET['loginease_token']) && !isset($_POST['confirm_login'])) { 433 $token = sanitize_text_field(wp_unslash($_GET['loginease_token'])); 434 435 global $wpdb; 436 $table = $wpdb->prefix . 'loginease_tokens'; 437 $now = current_time('mysql'); 438 439 $row = $wpdb->get_row($wpdb->prepare( 440 "SELECT * FROM $table WHERE token = %s AND expire > %s AND used = 0", 441 $token, 442 $now 443 )); 444 445 if (!$row) { 446 wp_safe_redirect(add_query_arg('loginease_status', 'invalid', self::get_login_url())); 447 exit; 448 } 449 450 // Affiche formulaire simple de confirmation (vous pouvez customiser HTML/CSS) 451 wp_die( 452 '<form method="POST" class="loginease-confirm-form" style="max-width:400px;margin:2em auto;padding:1em;border:1px solid #ccc;border-radius:4px;text-align:center;">' . 453 wp_nonce_field('confirm_login', '_wpnonce', true, false) . 454 '<input type="hidden" name="loginease_token" value="' . esc_attr($token) . '" />' . 455 '<p style="margin-bottom:1em;">' . esc_html__('Vous avez cliqué sur un lien de connexion sécurisé. Confirmez-vous que vous souhaitez vous connecter ?', 'loginease') . '</p>' . 456 '<button type="submit" name="confirm_login" style="background:#21759b;color:#fff;border:none;padding:0.5em 1em;cursor:pointer;border-radius:2px;">' . esc_html__('Confirmer la connexion', 'loginease') . '</button>' . 457 '</form>', 458 esc_html__('Confirmation de connexion', 'loginease'), 459 ['response' => 200] 460 ); 461 exit; 462 } 463 464 // Traitement de la confirmation : validation du token, login et marquage du token utilisé 465 if (isset($_POST['confirm_login']) && check_admin_referer('confirm_login', '_wpnonce')) { 466 $token = isset($_POST['loginease_token']) ? sanitize_text_field(wp_unslash($_POST['loginease_token'])) : ''; 467 468 global $wpdb; 469 $table = $wpdb->prefix . 'loginease_tokens'; 470 $now = current_time('mysql'); 471 472 $row = $wpdb->get_row( 473 $wpdb->prepare( 474 "SELECT * FROM $table WHERE token = %s AND expire > %s AND used = 0", 475 $token, 476 $now 477 ) 478 ); 479 480 if (!$row) { 481 wp_safe_redirect(add_query_arg('loginease_status', 'invalid', self::get_login_url())); 482 exit; 483 } 484 485 // Marquer le token comme utilisé 486 $wpdb->update($table, ['used' => 1], ['id' => $row->id], ['%d'], ['%d']); 487 488 // Connexion effective de l'utilisateur 489 wp_set_current_user($row->user_id); 490 wp_set_auth_cookie($row->user_id); 491 492 if (class_exists('LoginEase_Logger')) { 493 LoginEase_Logger::log($row->user_id, 'Magic link confirmed'); 494 LoginEase_Logger::cleanup(); 495 } 496 497 // Redirection finale (vers redirect_to ou tableau de bord) 498 $redirect_to = isset($_REQUEST['redirect_to']) ? esc_url_raw(wp_unslash($_REQUEST['redirect_to'])) : admin_url(); 499 wp_safe_redirect($redirect_to); 500 exit; 501 } 502 } 503 504 public static function loginease_authenticate($user, $username, $password) 505 { 506 if (isset($_POST['loginease_submit'])) { 507 if (empty($username)) { 508 return new WP_Error('empty_username', __('Missing username or email.', 'loginease')); 417 509 } 418 510 // Return null to prevent default password login attempt and allow magic login process. … … 422 514 } 423 515 424 private static function generate_token_for_user( $user_id ) { 516 private static function generate_token_for_user($user_id) 517 { 425 518 global $wpdb; 426 519 $table = $wpdb->prefix . 'loginease_tokens'; 427 $now = current_time( 'mysql');428 $wpdb->query( $wpdb->prepare( "DELETE FROM $table WHERE expire < %s OR used = 1", $now ));429 $token = wp_generate_password( 20, false, false);430 $opts = self::get_options(); 431 $minutes = isset( $opts['token_expiration']) ? (int) $opts['token_expiration'] : 15;432 $expire_time = date_i18n( 'Y-m-d H:i:s', current_time( 'timestamp' ) + ( $minutes * 60 ));520 $now = current_time('mysql'); 521 $wpdb->query($wpdb->prepare("DELETE FROM $table WHERE expire < %s OR used = 1", $now)); 522 $token = wp_generate_password(20, false, false); 523 $opts = self::get_options(); 524 $minutes = isset($opts['token_expiration']) ? (int) $opts['token_expiration'] : 15; 525 $expire_time = date_i18n('Y-m-d H:i:s', current_time('timestamp') + ($minutes * 60)); 433 526 $wpdb->insert( 434 527 $table, 435 528 [ 436 529 'user_id' => $user_id, 437 'token' => $token,438 'expire' => $expire_time,439 'used' => 0,530 'token' => $token, 531 'expire' => $expire_time, 532 'used' => 0, 440 533 ], 441 [ '%d', '%s', '%s', '%d']534 ['%d', '%s', '%s', '%d'] 442 535 ); 443 536 return $token; 444 537 } 445 538 446 private static function send_login_email( $user, $token ) { 447 $opts = self::get_options(); 448 $minutes = isset( $opts['token_expiration'] ) ? (int) $opts['token_expiration'] : 15; 449 $expire_date = date_i18n( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), current_time( 'timestamp' ) + $minutes * 60 ); 450 $login_url = add_query_arg( 'loginease_token', rawurlencode( $token ), self::get_login_url() ); 451 $subject = sprintf( __( 'Your login link for %s', 'loginease' ), get_bloginfo( 'name' ) ); 539 private static function send_login_email($user, $token) 540 { 541 $opts = self::get_options(); 542 $minutes = isset($opts['token_expiration']) ? (int) $opts['token_expiration'] : 15; 543 $expire_date = date_i18n(get_option('date_format') . ' ' . get_option('time_format'), current_time('timestamp') + $minutes * 60); 544 $login_url = add_query_arg('loginease_token', rawurlencode($token), self::get_login_url()); 545 $subject = sprintf(__('Your login link for %s', 'loginease'), get_bloginfo('name')); 452 546 $message = sprintf( 453 __( "Hello,\n\nHere is your secure login link:\n\n%1\$s\n\nValid until %2\$s\n\nRegards,\n%3\$s", 'loginease'),547 __("Hello,\n\nHere is your secure login link:\n\n%1\$s\n\nValid until %2\$s\n\nRegards,\n%3\$s", 'loginease'), 454 548 $login_url, 455 549 $expire_date, 456 get_bloginfo( 'name')550 get_bloginfo('name') 457 551 ); 458 wp_mail( $user->user_email, $subject, $message ); 459 } 460 461 public static function login_with_token( $token ) { 552 wp_mail($user->user_email, $subject, $message); 553 } 554 555 public static function login_with_token($token) 556 { 462 557 global $wpdb; 463 558 $table = $wpdb->prefix . 'loginease_tokens'; 464 $now = current_time( 'mysql');559 $now = current_time('mysql'); 465 560 $row = $wpdb->get_row( 466 561 $wpdb->prepare( … … 470 565 ) 471 566 ); 472 if ( ! $row) {473 wp_safe_redirect( add_query_arg( 'loginease_status', 'invalid', self::get_login_url() ));474 exit; 475 } 476 $wpdb->update( $table, [ 'used' => 1 ], [ 'id' => $row->id ], [ '%d' ], [ '%d' ]);477 wp_set_current_user( $row->user_id);478 wp_set_auth_cookie( $row->user_id);479 if ( class_exists( 'LoginEase_Logger' )) {480 LoginEase_Logger::log( $row->user_id, 'Magic link');567 if (!$row) { 568 wp_safe_redirect(add_query_arg('loginease_status', 'invalid', self::get_login_url())); 569 exit; 570 } 571 $wpdb->update($table, ['used' => 1], ['id' => $row->id], ['%d'], ['%d']); 572 wp_set_current_user($row->user_id); 573 wp_set_auth_cookie($row->user_id); 574 if (class_exists('LoginEase_Logger')) { 575 LoginEase_Logger::log($row->user_id, 'Magic link'); 481 576 LoginEase_Logger::cleanup(); 482 577 } 483 $redirect_to = isset( $_REQUEST['redirect_to'] ) ? esc_url_raw( wp_unslash( $_REQUEST['redirect_to'] )) : admin_url();484 wp_safe_redirect( $redirect_to);578 $redirect_to = isset($_REQUEST['redirect_to']) ? esc_url_raw(wp_unslash($_REQUEST['redirect_to'])) : admin_url(); 579 wp_safe_redirect($redirect_to); 485 580 exit; 486 581 } 487 582 488 public static function add_loginease_button() { 489 $opts = self::get_options(); 490 $disable_password_login = ! empty( $opts['disable_password_login'] ); 491 if ( isset( $_GET['action'] ) && sanitize_text_field( wp_unslash( $_GET['action'] ) ) !== 'login' ) { 492 return; 493 } 494 wp_nonce_field( 'login', '_wpnonce' ); 583 public static function add_loginease_button() 584 { 585 $opts = self::get_options(); 586 $disable_password_login = !empty($opts['disable_password_login']); 587 if (isset($_GET['action']) && sanitize_text_field(wp_unslash($_GET['action'])) !== 'login') { 588 return; 589 } 590 wp_nonce_field('login', '_wpnonce'); 495 591 $btn_class = $disable_password_login ? 'button button-primary' : 'button button-secondary'; 496 echo '<input type="submit" name="loginease_submit" id="loginease_submit" class="' . esc_attr( $btn_class ) . '" value="' . esc_attr__( 'Send me a login link', 'loginease') . '" />';592 echo '<input type="submit" name="loginease_submit" id="loginease_submit" class="' . esc_attr($btn_class) . '" value="' . esc_attr__('Send me a login link', 'loginease') . '" />'; 497 593 } 498 594 } -
loginease/trunk/loginease.php
r3362368 r3440551 3 3 * Plugin Name: LoginEase 4 4 * Description: Passwordless login via magic link directly on the native WordPress login form, without shortcode. 5 * Version: 1. 0.05 * Version: 1.1.0 6 6 * Requires at least: 6.0 7 * Requires PHP: 7.47 * Requires PHP: 8.0 8 8 * Author: Killian Santos 9 9 * Author URI: https://killian-santos.com/ -
loginease/trunk/readme.txt
r3362368 r3440551 4 4 Tags: login, passwordless, magic link, authentication, security 5 5 Requires at least: 6.0 6 Tested up to: 6. 87 Requires PHP: 7.48 Stable tag: 1. 0.06 Tested up to: 6.9 7 Requires PHP: 8.0 8 Stable tag: 1.1.0 9 9 License: GPLv2 or later 10 10 License URI: https://www.gnu.org/licenses/gpl-2.0.html … … 41 41 == Changelog == 42 42 43 = 1.1.0 = 44 * NEW: When logging in via a magic link, a confirmation message is now displayed for the user to explicitly approve the login. This step prevents the link from expiring prematurely, especially for users whose email security systems automatically scan links, which could cause the link to expire before the user can actually log in. 45 43 46 = 1.0.0 = 44 47 * Launch of the first public version of LoginEase
Note: See TracChangeset
for help on using the changeset viewer.