PluginProbe
Authorizer / 3.6.0
Authorizer v3.6.0
3.16.0 3.15.3 3.15.2 3.15.1 3.15.0 3.14.3 3.14.4 3.14.2 3.14.1 2.8.1 2.8.2 2.8.3 2.8.4 2.8.5 2.8.6 2.8.7 2.8.8 2.9.0 2.9.1 2.9.10 2.9.11 2.9.12 2.9.13 2.9.2 2.9.3 All 127 releases
← All changes | src/authorizer/class-login-form.php +182 -70 2.9.6 → 3.6.0 View file →
@@ -14,31 +14,34 @@
14 14
15 15 /**
16 16 * Contains modifications to the WordPress login form.
17 17 */
18 -class Login_Form extends Static_Instance {
18 +class Login_Form extends Singleton {
19 19
20 20 /**
21 - * Load external resources for the public-facing site.
21 + * Load script to display message to anonymous users browing a site (only
22 + * enqueue if configured to only allow logged in users to view the site and
23 + * show a warning to anonymous users).
22 24 *
23 25 * Action: wp_enqueue_scripts
24 26 */
25 27 public function auth_public_scripts() {
26 28 // Load (and localize) public scripts.
27 - $options = Options::get_instance();
28 - $current_path = ! empty( $_SERVER['REQUEST_URI'] ) ? esc_url_raw( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : home_url();
29 - wp_enqueue_script( 'auth_public_scripts', plugins_url( '/js/authorizer-public.js', plugin_root() ), array( 'jquery' ), '2.8.0', false );
30 - $auth_localized = array(
31 - 'wpLoginUrl' => wp_login_url( $current_path ),
32 - 'publicWarning' => get_option( 'auth_settings_advanced_public_notice' ),
33 - 'anonymousNotice' => $options->get( 'access_redirect_to_message' ),
34 - 'logIn' => esc_html__( 'Log In', 'authorizer' ),
35 - );
36 - wp_localize_script( 'auth_public_scripts', 'auth', $auth_localized );
37 -
38 - // Load public css.
39 - wp_register_style( 'authorizer-public-css', plugins_url( 'css/authorizer-public.css', plugin_root() ), array(), '2.8.0' );
40 - wp_enqueue_style( 'authorizer-public-css' );
29 + $options = Options::get_instance();
30 + if (
31 + 'logged_in_users' === $options->get( 'access_who_can_view' ) &&
32 + 'warning' === $options->get( 'access_public_warning' ) &&
33 + get_option( 'auth_settings_advanced_public_notice' )
34 + ) {
35 + $current_path = ! empty( $_SERVER['REQUEST_URI'] ) ? esc_url_raw( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : home_url();
36 + wp_enqueue_script( 'auth_public_scripts', plugins_url( '/js/authorizer-public.js', plugin_root() ), array( 'jquery' ), '3.2.2', false );
37 + $auth_localized = array(
38 + 'wpLoginUrl' => wp_login_url( $current_path ),
39 + 'anonymousNotice' => $options->get( 'access_redirect_to_message' ),
40 + 'logIn' => esc_html__( 'Log In', 'authorizer' ),
41 + );
42 + wp_localize_script( 'auth_public_scripts', 'auth', $auth_localized );
43 + }
41 44 }
42 45
43 46
44 47 /**
@@ -56,9 +59,9 @@
56 59 // Enqueue scripts appearing on wp-login.php.
57 60 wp_enqueue_script( 'auth_login_scripts', plugins_url( '/js/authorizer-login.js', plugin_root() ), array( 'jquery' ), '2.8.0', false );
58 61
59 62 // Enqueue styles appearing on wp-login.php.
60 - wp_register_style( 'authorizer-login-css', plugins_url( '/css/authorizer-login.css', plugin_root() ), array(), '2.8.0' );
63 + wp_register_style( 'authorizer-login-css', plugins_url( '/css/authorizer-login.css', plugin_root() ), array(), '3.2.0' );
61 64 wp_enqueue_style( 'authorizer-login-css' );
62 65
63 66 /**
64 67 * Developers can use the `authorizer_add_branding_option` filter
@@ -88,17 +91,8 @@
88 91 wp_register_style( 'authorizer-login-custom-css-' . sanitize_title( $branding_option['value'] ), $branding_option['css_url'], array(), '2.8.0' );
89 92 wp_enqueue_style( 'authorizer-login-custom-css-' . sanitize_title( $branding_option['value'] ) );
90 93 }
91 94 }
92 -
93 - // If we're using Google logins, load those resources.
94 - if ( '1' === $auth_settings['google'] ) {
95 - wp_enqueue_script( 'authorizer-login-custom-google', plugins_url( '/js/authorizer-login-custom_google.js', plugin_root() ), array( 'jquery' ), '2.8.0', false ); ?>
96 - <meta name="google-signin-clientid" content="<?php echo esc_attr( $auth_settings['google_clientid'] ); ?>" />
97 - <meta name="google-signin-scope" content="email" />
98 - <meta name="google-signin-cookiepolicy" content="single_host_origin" />
99 - <?php
100 - }
101 95 }
102 96
103 97
104 98 /**
@@ -112,9 +106,9 @@
112 106 $auth_settings = $options->get_all( Helper::SINGLE_CONTEXT, 'allow override' );
113 107 $ajaxurl = admin_url( 'admin-ajax.php' );
114 108 if ( '1' === $auth_settings['google'] ) :
115 109 ?>
116 -<script type="text/javascript">
110 +<script>
117 111 /* global location, window */
118 112 // Reload login page if reauth querystring param exists,
119 113 // since reauth interrupts external logins (e.g., google).
120 114 if ( location.search.indexOf( 'reauth=1' ) >= 0 ) {
@@ -132,26 +126,28 @@
132 126 }
133 127 }
134 128
135 129 // eslint-disable-next-line
136 -function signInCallback( authResult ) { // jshint ignore:line
130 +function signInCallback( credentialResponse ) { // jshint ignore:line
137 131 var $ = jQuery;
138 - if ( authResult.status && authResult.status.signed_in ) {
139 - // Hide the sign-in button now that the user is authorized, for example:
140 - $( '#googleplus_button' ).attr( 'style', 'display: none' );
141 132
142 - // Send the code to the server
133 + if ( credentialResponse.hasOwnProperty( 'credential' ) ) {
134 + // Send the JWT to the server
143 135 var ajaxurl = '<?php echo esc_attr( $ajaxurl ); ?>';
144 136 $.post(ajaxurl, {
145 137 action: 'process_google_login',
146 - code: authResult.code,
147 - nonce: $('#nonce_google_auth-<?php echo esc_attr( Helper::get_cookie_value() ); ?>' ).val(),
138 + credential: credentialResponse.credential,
139 + nonce: $('#g_id_onload' ).data( 'nonce' ),
148 140 }, function() {
149 - // Handle or verify the server response if necessary.
150 - // console.log( response );
151 -
152 141 // Reload wp-login.php to continue the authentication process.
153 142 var newHref = authUpdateQuerystringParam( location.href, 'external', 'google' );
143 +
144 + // If we have a login form embedded via [authorizer_login_form], we are
145 + // not on wp-login.php, so change the location to wp-login.php.
146 + if ( 'undefined' !== typeof auth && auth.hasOwnProperty( 'wpLoginUrl' ) ) {
147 + newHref = authUpdateQuerystringParam( auth.wpLoginUrl, 'external', 'google' );
148 + }
149 +
154 150 if ( location.href === newHref ) {
155 151 location.reload();
156 152 } else {
157 153 location.href = newHref;
@@ -162,12 +158,12 @@
162 158 // Possible error values:
163 159 // "user_signed_out" - User is signed-out
164 160 // "access_denied" - User denied access to your app
165 161 // "immediate_failed" - Could not automatically log in the user
166 - // console.log('Sign-in state: ' + authResult['error']);
162 + // console.log('Sign-in state: ' + credentialResponse['error']);
167 163
168 164 // If user denies access, reload the login page.
169 - if ( authResult.error === 'access_denied' || authResult.error === 'user_signed_out' ) {
165 + if ( credentialResponse.error === 'access_denied' || credentialResponse.error === 'user_signed_out' ) {
170 166 window.location.reload();
171 167 }
172 168 }
173 169 }
@@ -188,14 +184,47 @@
188 184 $auth_settings = $options->get_all( Helper::SINGLE_CONTEXT, 'allow override' );
189 185 ?>
190 186 <div id="auth-external-service-login">
191 187 <?php if ( '1' === $auth_settings['google'] ) : ?>
192 - <p><a id="googleplus_button" class="button button-primary button-external button-google"><span class="dashicons dashicons-googleplus"></span><span class="label"><?php esc_html_e( 'Sign in with Google', 'authorizer' ); ?></span></a></p>
193 - <?php wp_nonce_field( 'google_csrf_nonce', 'nonce_google_auth-' . Helper::get_cookie_value() ); ?>
188 + <script src="https://accounts.google.com/gsi/client" async defer></script>
189 + <div id="g_id_onload"
190 + data-client_id="<?php echo esc_attr( $auth_settings['google_clientid'] ); ?>"
191 + data-context="signin"
192 + data-ux_mode="popup"
193 + data-nonce="<?php echo esc_attr( wp_create_nonce( 'google_csrf_nonce' ) ); ?>"
194 + data-callback="signInCallback">
195 + </div>
196 + <div class="g_id_signin"
197 + data-type="standard"
198 + data-shape="pill"
199 + data-theme="filled_blue"
200 + data-text="signin_with"
201 + data-size="large"
202 + data-logo_alignment="left"
203 + data-width="270">
204 + </div>
205 + <br>
194 206 <?php endif; ?>
195 207
208 + <?php if ( '1' === $auth_settings['oauth2'] ) : ?>
209 + <p><a class="button button-primary button-external button-<?php echo esc_attr( $auth_settings['oauth2_provider'] ); ?>" href="<?php echo esc_attr( Helper::modify_current_url_for_external_login( 'oauth2' ) ); ?>">
210 + <span class="dashicons dashicons-lock"></span>
211 + <span class="label">
212 + <?php
213 + echo esc_html(
214 + sprintf(
215 + /* TRANSLATORS: %s: Custom OAuth2 label from authorizer options */
216 + __( 'Sign in with %s', 'authorizer' ),
217 + $auth_settings['oauth2_custom_label']
218 + )
219 + );
220 + ?>
221 + </span>
222 + </a></p>
223 + <?php endif; ?>
224 +
196 225 <?php if ( '1' === $auth_settings['cas'] ) : ?>
197 - <p><a class="button button-primary button-external button-cas" href="<?php echo esc_attr( Helper::modify_current_url_for_cas_login() ); ?>">
226 + <p><a class="button button-primary button-external button-cas" href="<?php echo esc_attr( Helper::modify_current_url_for_external_login( 'cas' ) ); ?>">
198 227 <span class="dashicons dashicons-lock"></span>
199 228 <span class="label">
200 229 <?php
201 230 echo esc_html(
@@ -209,14 +238,16 @@
209 238 </span>
210 239 </a></p>
211 240 <?php endif; ?>
212 241
213 - <?php if ( isset( $auth_settings['advanced_hide_wp_login'] ) && '1' === $auth_settings['advanced_hide_wp_login'] && isset( $_SERVER['QUERY_STRING'] ) && false === strpos( $_SERVER['QUERY_STRING'], 'external=wordpress' ) ) : // phpcs:ignore WordPress.Security.ValidatedSanitizedInput ?>
242 + <?php if ( ( isset( $auth_settings['advanced_hide_wp_login'] ) && '1' === $auth_settings['advanced_hide_wp_login'] && isset( $_SERVER['QUERY_STRING'] ) && false === strpos( $_SERVER['QUERY_STRING'], 'external=wordpress' ) ) || ( isset( $auth_settings['advanced_disable_wp_login'] ) && '1' === $auth_settings['advanced_disable_wp_login'] && '1' !== $auth_settings['ldap'] && ( '1' === $auth_settings['cas'] || '1' === $auth_settings['google'] ) ) ) : // phpcs:ignore WordPress.Security.ValidatedSanitizedInput ?>
214 243 <style type="text/css">
215 244 body.login-action-login form {
216 245 padding-bottom: 8px;
217 246 }
218 247 body.login-action-login form p > label,
248 + body.login-action-login form #user_login,
249 + body.login-action-login form .user-pass-wrap,
219 250 body.login-action-login form .forgetmenot,
220 251 body.login-action-login form .submit,
221 252 body.login-action-login #nav { /* csslint allow: ids */
222 253 display: none;
@@ -221,9 +252,9 @@
221 252 body.login-action-login #nav { /* csslint allow: ids */
222 253 display: none;
223 254 }
224 255 </style>
225 - <?php elseif ( '1' === $auth_settings['cas'] || '1' === $auth_settings['google'] ) : ?>
256 + <?php elseif ( '1' === $auth_settings['cas'] || '1' === $auth_settings['google'] || '1' === $auth_settings['oauth2'] ) : ?>
226 257 <h3> &mdash; <?php esc_html_e( 'or', 'authorizer' ); ?> &mdash; </h3>
227 258 <?php endif; ?>
228 259 </div>
229 260 <?php
@@ -244,8 +275,22 @@
244 275 * @param string $redirect_to Where to redirect on error.
245 276 * @return WP_Error|void WP Error object or void on redirect.
246 277 */
247 278 public function wp_login_errors__maybe_redirect_to_cas( $errors, $redirect_to ) {
279 + // If the query string 'checkemail=confirm' is set, we do not want to automatically redirect to
280 + // the CAS login screen using 'external=cas', and instead want to directly access the check email
281 + // confirmation page. So we will instead set the URL parameter 'external=wordpress' and redirect.
282 + // This is to prevent issues when going through the normal WordPress password reset process.
283 + if (
284 + isset( $_REQUEST['checkemail'] ) && // phpcs:ignore WordPress.Security.NonceVerification.Recommended
285 + 'confirm' === $_REQUEST['checkemail'] && // phpcs:ignore WordPress.Security.NonceVerification.Recommended
286 + isset( $_SERVER['QUERY_STRING'] ) &&
287 + strpos( $_SERVER['QUERY_STRING'], 'external=wordpress' ) === false // phpcs:ignore WordPress.Security.ValidatedSanitizedInput
288 + ) {
289 + wp_redirect( Helper::modify_current_url_for_external_login( 'wordpress' ) ); // phpcs:ignore WordPress.Security.SafeRedirect.wp_redirect_wp_redirect
290 + exit;
291 + }
292 +
248 293 // Grab plugin settings.
249 294 $options = Options::get_instance();
250 295 $auth_settings = $options->get_all( Helper::SINGLE_CONTEXT, 'allow override' );
251 296
@@ -256,11 +301,12 @@
256 301 array_key_exists( 'cas_auto_login', $auth_settings ) && '1' === $auth_settings['cas_auto_login'] &&
257 302 array_key_exists( 'cas', $auth_settings ) && '1' === $auth_settings['cas'] &&
258 303 ( ! array_key_exists( 'ldap', $auth_settings ) || '1' !== $auth_settings['ldap'] ) &&
259 304 ( ! array_key_exists( 'google', $auth_settings ) || '1' !== $auth_settings['google'] ) &&
305 + ( ! array_key_exists( 'oauth2', $auth_settings ) || '1' !== $auth_settings['oauth2'] ) &&
260 306 array_key_exists( 'advanced_hide_wp_login', $auth_settings ) && '1' === $auth_settings['advanced_hide_wp_login']
261 307 ) {
262 - wp_redirect( Helper::modify_current_url_for_cas_login() ); // phpcs:ignore WordPress.Security.SafeRedirect.wp_redirect_wp_redirect
308 + wp_redirect( Helper::modify_current_url_for_external_login( 'cas' ) ); // phpcs:ignore WordPress.Security.SafeRedirect.wp_redirect_wp_redirect
263 309 exit;
264 310 }
265 311
266 312 return $errors;
@@ -267,33 +313,8 @@
267 313 }
268 314
269 315
270 316 /**
271 - * Set a unique cookie to add to Google auth nonce to avoid CSRF detection.
272 - * Note: hook into login_init so this fires at the start of the visit to
273 - * wp-login.php, but before any html output is started (so setting the
274 - * cookie header doesn't complain about data already being sent).
275 - *
276 - * Action: login_init
277 - *
278 - * @return void
279 - */
280 - public function login_init__maybe_set_google_nonce_cookie() {
281 - // Grab plugin settings.
282 - $options = Options::get_instance();
283 - $auth_settings = $options->get_all( Helper::SINGLE_CONTEXT, 'allow override' );
284 -
285 - // If Google logins are enabled, make sure the cookie is set.
286 - if ( array_key_exists( 'google', $auth_settings ) && '1' === $auth_settings['google'] ) {
287 - if ( ! isset( $_COOKIE['login_unique'] ) ) {
288 - setcookie( 'login_unique', Helper::get_cookie_value(), time() + 1800, '/', defined( 'COOKIE_DOMAIN' ) ? COOKIE_DOMAIN : '' );
289 - $_COOKIE['login_unique'] = Helper::get_cookie_value();
290 - }
291 - }
292 - }
293 -
294 -
295 - /**
296 317 * Implements hook: do_action( 'wp_login_failed', $username );
297 318 * Update the user meta for the user that just failed logging in.
298 319 * Keep track of time of last failed attempt and number of failed attempts.
299 320 *
@@ -343,8 +364,31 @@
343 364 } else {
344 365 update_option( 'auth_settings_advanced_lockouts_time_last_failed', time() );
345 366 update_option( 'auth_settings_advanced_lockouts_failed_attempts', $num_attempts + 1 );
346 367 }
368 +
369 + // Log a lockout if we hit the configured limit (via Simple History plugin).
370 + $lockouts = $auth_settings['advanced_lockouts'];
371 + $num_attempts_short_lockout = $lockouts['attempts_1'];
372 + $num_attempts_long_lockout = $lockouts['attempts_1'] + $lockouts['attempts_2'];
373 + if ( $num_attempts >= $num_attempts_short_lockout ) {
374 + $lockout_length_in_seconds = $num_attempts >= $num_attempts_long_lockout ? $lockouts['duration_2'] * 60 : $lockouts['duration_1'] * 60;
375 + apply_filters(
376 + 'simple_history_log_warning',
377 + sprintf(
378 + /* TRANSLATORS: 1: duration of lockout 2: username 3: ordinal number of invalid attempts */
379 + __( 'Authorizer lockout triggered for %1$s on user %2$s after the %3$s invalid attempt.', 'authorizer' ),
380 + Helper::seconds_as_sentence( $lockout_length_in_seconds ),
381 + $username,
382 + Helper::ordinal( $num_attempts )
383 + ),
384 + array(
385 + 'seconds' => $lockout_length_in_seconds,
386 + 'username' => $username,
387 + 'attempts' => $num_attempts,
388 + )
389 + );
390 + }
347 391 }
348 392
349 393
350 394 /**
@@ -372,8 +416,58 @@
372 416 }
373 417
374 418
375 419 /**
420 + * Ensure that whenever we are on a wp-login.php page for WordPress and there is a log in link, it properly
421 + * generates a wp-login.php URL with the additional "wordpress=external" URL parameter.
422 + * Only affects the URL if the Hide WordPress Logins option is enabled.
423 + *
424 + * Filter: wp_login_url https://developer.wordpress.org/reference/functions/wp_login_url/
425 + *
426 + * @param string $login_url URL for the log in page.
427 + * @return string URL for the log in page.
428 + */
429 + public function maybe_add_external_wordpress_to_log_in_links( $login_url ) {
430 + // Initial check to make sure that we are on a wp-login.php page.
431 + if ( isset( $GLOBALS['pagenow'] ) && site_url( $GLOBALS['pagenow'], 'login' ) === $login_url ) {
432 + // Do a check in here within the $_REQUEST params to narrow down the scope of where we'll modify the URL
433 + // We need to check against the following: action=lostpassword, checkemail=confirm, action=rp, and action=resetpass.
434 + if (
435 + (
436 + isset( $_REQUEST['action'] ) && // phpcs:ignore WordPress.Security.NonceVerification.Recommended
437 + (
438 + 'lostpassword' === $_REQUEST['action'] || // phpcs:ignore WordPress.Security.NonceVerification.Recommended
439 + 'rp' === $_REQUEST['action'] || // phpcs:ignore WordPress.Security.NonceVerification.Recommended
440 + 'resetpass' === $_REQUEST['action'] // phpcs:ignore WordPress.Security.NonceVerification.Recommended
441 + )
442 + ) || (
443 + isset( $_REQUEST['checkemail'] ) && // phpcs:ignore WordPress.Security.NonceVerification.Recommended
444 + 'confirm' === $_REQUEST['checkemail'] // phpcs:ignore WordPress.Security.NonceVerification.Recommended
445 + )
446 + ) {
447 + // Grab plugins settings.
448 + $options = Options::get_instance();
449 + $auth_settings = $options->get_all( HELPER::SINGLE_CONTEXT, 'allow override' );
450 +
451 + // Only change the Log in URL if the Hide WordPress Logins option is enabled in Authorizer.
452 + if (
453 + array_key_exists( 'advanced_hide_wp_login', $auth_settings ) &&
454 + '1' === $auth_settings['advanced_hide_wp_login']
455 + ) {
456 + // Need to determine if existing URL has params already or not, then add the param and value.
457 + if ( strpos( $login_url, '?' ) === false ) {
458 + $login_url = $login_url . '?external=wordpress';
459 + } else {
460 + $login_url = $login_url . '&external=wordpress';
461 + }
462 + }
463 + }
464 + }
465 + return $login_url;
466 + }
467 +
468 +
469 + /**
376 470 * Add custom error message to login screen.
377 471 *
378 472 * Filter: login_errors
379 473 *
@@ -384,7 +478,25 @@
384 478 $error = get_option( 'auth_settings_advanced_login_error' );
385 479 delete_option( 'auth_settings_advanced_login_error' );
386 480 $errors = ' ' . $error . "<br />\n";
387 481 return $errors;
482 + }
483 +
484 +
485 + /**
486 + * Render the [authorizer_login_form] shortcode.
487 + *
488 + * Shortcode: authorizer_login_form
489 + */
490 + public function shortcode_authorizer_login_form() {
491 + ob_start();
492 +
493 + $this->login_enqueue_scripts_and_styles();
494 + $this->login_form_add_external_service_links();
495 + $this->load_login_footer_js();
496 +
497 + wp_login_form();
498 +
499 + return ob_get_clean();
388 500 }
389 501
390 502 }