PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 8.9.4
Jetpack – WP Security, Backup, Speed, & Growth v8.9.4
12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 13.8.3 13.9.2 14.0.1 14.1.1 14.2.2 14.3.1 14.4.2 All 500 releases
← All changes | modules/sso.php +268 -359 12.8.38.9.4 View file →
@@ -1,19 +1,13 @@
1 -<?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
2 -/**
3 - * Jetpack_SSO module main class file.
4 - *
5 - * @package automattic/jetpack
6 - */
1 +<?php
7 2
8 -use Automattic\Jetpack\Connection\Manager as Connection_Manager;
9 3 use Automattic\Jetpack\Roles;
10 4 use Automattic\Jetpack\Status;
11 -use Automattic\Jetpack\Status\Host;
12 5 use Automattic\Jetpack\Tracking;
6 +use Automattic\Jetpack\Redirect;
13 7
14 -require_once JETPACK__PLUGIN_DIR . 'modules/sso/class.jetpack-sso-helpers.php';
15 -require_once JETPACK__PLUGIN_DIR . 'modules/sso/class.jetpack-sso-notices.php';
8 +require_once( JETPACK__PLUGIN_DIR . 'modules/sso/class.jetpack-sso-helpers.php' );
9 +require_once( JETPACK__PLUGIN_DIR . 'modules/sso/class.jetpack-sso-notices.php' );
16 10
17 11 /**
18 12 * Module Name: Secure Sign On
19 13 * Module Description: Allow users to log in to this site using WordPress.com accounts
@@ -20,68 +14,35 @@
20 14 * Sort Order: 30
21 15 * Recommendation Order: 5
22 16 * First Introduced: 2.6
23 17 * Requires Connection: Yes
24 - * Requires User Connection: Yes
25 18 * Auto Activate: No
26 19 * Module Tags: Developers
27 20 * Feature: Security
28 21 * Additional Search Queries: sso, single sign on, login, log in, 2fa, two-factor
29 22 */
23 +
30 24 class Jetpack_SSO {
31 - /**
32 - * Jetpack_SSO instance.
33 - *
34 - * @var Jetpack_SSO
35 - */
36 - public static $instance = null;
25 + static $instance = null;
37 26
38 - /**
39 - * Jetpack_SSO constructor.
40 - */
41 27 private function __construct() {
42 28
43 29 self::$instance = $this;
44 30
45 - add_action( 'admin_init', array( $this, 'maybe_authorize_user_after_sso' ), 1 );
46 - add_action( 'admin_init', array( $this, 'register_settings' ) );
47 - add_action( 'login_init', array( $this, 'login_init' ) );
48 - add_action( 'delete_user', array( $this, 'delete_connection_for_user' ) );
49 - add_filter( 'jetpack_xmlrpc_methods', array( $this, 'xmlrpc_methods' ) );
50 - add_action( 'init', array( $this, 'maybe_logout_user' ), 5 );
51 - add_action( 'jetpack_modules_loaded', array( $this, 'module_configure_button' ) );
52 - add_action( 'login_form_logout', array( $this, 'store_wpcom_profile_cookies_on_logout' ) );
53 - add_action( 'jetpack_unlinked_user', array( $this, 'delete_connection_for_user' ) );
54 - add_action( 'jetpack_site_before_disconnected', array( static::class, 'disconnect' ) );
55 - add_action( 'wp_login', array( 'Jetpack_SSO', 'clear_cookies_after_login' ) );
31 + add_action( 'admin_init', array( $this, 'maybe_authorize_user_after_sso' ), 1 );
32 + add_action( 'admin_init', array( $this, 'register_settings' ) );
33 + add_action( 'login_init', array( $this, 'login_init' ) );
34 + add_action( 'delete_user', array( $this, 'delete_connection_for_user' ) );
35 + add_filter( 'jetpack_xmlrpc_methods', array( $this, 'xmlrpc_methods' ) );
36 + add_action( 'init', array( $this, 'maybe_logout_user' ), 5 );
37 + add_action( 'jetpack_modules_loaded', array( $this, 'module_configure_button' ) );
38 + add_action( 'login_form_logout', array( $this, 'store_wpcom_profile_cookies_on_logout' ) );
39 + add_action( 'jetpack_unlinked_user', array( $this, 'delete_connection_for_user') );
40 + add_action( 'wp_login', array( 'Jetpack_SSO', 'clear_cookies_after_login' ) );
41 + add_action( 'jetpack_jitm_received_envelopes', array( $this, 'inject_sso_jitm' ), 10, 2 );
56 42
57 43 // Adding this action so that on login_init, the action won't be sanitized out of the $action global.
58 44 add_action( 'login_form_jetpack-sso', '__return_true' );
59 -
60 - add_filter( 'wp_login_errors', array( $this, 'sso_reminder_logout_wpcom' ) );
61 -
62 - /**
63 - * Filter to include Force 2FA feature.
64 - *
65 - * By default, `manage_options` users are forced when enable. The capability can be modified
66 - * with the `jetpack_force_2fa_cap` filter.
67 - *
68 - * To enable the feature, add the following code:
69 - * add_filter( 'jetpack_force_2fa', '__return_true' );
70 - *
71 - * @param bool $force_2fa Whether to force 2FA or not.
72 - *
73 - * @todo Provide a UI to enable/disable the feature.
74 - *
75 - * @since 12.7
76 - * @module SSO
77 - * @return bool
78 - */
79 - if ( ! class_exists( 'Jetpack_Force_2FA' ) && apply_filters( 'jetpack_force_2fa', false ) ) {
80 - // Checking for the class to avoid collisions with existing standalone Jetpack Force 2FA plugin and break out if so.
81 - require_once JETPACK__PLUGIN_DIR . 'modules/sso/class-jetpack-force-2fa.php';
82 - new Jetpack_Force_2FA();
83 - }
84 45 }
85 46
86 47 /**
87 48 * Returns the single instance of the Jetpack_SSO object
@@ -89,14 +50,13 @@
89 50 * @since 2.8
90 51 * @return Jetpack_SSO
91 52 **/
92 53 public static function get_instance() {
93 - if ( self::$instance !== null ) {
54 + if ( ! is_null( self::$instance ) ) {
94 55 return self::$instance;
95 56 }
96 57
97 - self::$instance = new Jetpack_SSO();
98 - return self::$instance;
58 + return self::$instance = new Jetpack_SSO;
99 59 }
100 60
101 61 /**
102 62 * Add configure button and functionality to the module card on the Jetpack screen
@@ -105,37 +65,8 @@
105 65 Jetpack::enable_module_configurable( __FILE__ );
106 66 }
107 67
108 68 /**
109 - * Safety heads-up added to the logout messages when SSO is enabled.
110 - * Some folks on a shared computer don't know that they need to log out of WordPress.com as well.
111 - *
112 - * @param WP_Error $errors WP_Error object.
113 - */
114 - public function sso_reminder_logout_wpcom( $errors ) {
115 - if ( ( new Host() )->is_wpcom_platform() ) {
116 - return $errors;
117 - }
118 -
119 - if ( ! empty( $errors->errors['loggedout'] ) ) {
120 - $logout_message = wp_kses(
121 - sprintf(
122 - /* translators: %1$s is a link to the WordPress.com account settings page. */
123 - __( 'If you are on a shared computer, remember to also <a href="%1$s">log out of WordPress.com</a>.', 'jetpack' ),
124 - 'https://wordpress.com/me'
125 - ),
126 - array(
127 - 'a' => array(
128 - 'href' => array(),
129 - ),
130 - )
131 - );
132 - $errors->add( 'jetpack-sso-show-logout', $logout_message, 'message' );
133 - }
134 - return $errors;
135 - }
136 -
137 - /**
138 69 * If jetpack_force_logout == 1 in current user meta the user will be forced
139 70 * to logout and reauthenticate with the site.
140 71 **/
141 72 public function maybe_logout_user() {
@@ -140,9 +71,9 @@
140 71 **/
141 72 public function maybe_logout_user() {
142 73 global $current_user;
143 74
144 - if ( 1 === (int) $current_user->jetpack_force_logout ) {
75 + if ( 1 == $current_user->jetpack_force_logout ) {
145 76 delete_user_meta( $current_user->ID, 'jetpack_force_logout' );
146 77 self::delete_connection_for_user( $current_user->ID );
147 78 wp_logout();
148 79 wp_safe_redirect( wp_login_url() );
@@ -152,9 +83,9 @@
152 83
153 84 /**
154 85 * Adds additional methods the WordPress xmlrpc API for handling SSO specific features
155 86 *
156 - * @param array $methods API methods.
87 + * @param array $methods
157 88 * @return array
158 89 **/
159 90 public function xmlrpc_methods( $methods ) {
160 91 $methods['jetpack.userDisconnect'] = array( $this, 'xmlrpc_user_disconnect' );
@@ -163,20 +94,18 @@
163 94
164 95 /**
165 96 * Marks a user's profile for disconnect from WordPress.com and forces a logout
166 97 * the next time the user visits the site.
167 - *
168 - * @param int $user_id User to disconnect from the site.
169 98 **/
170 99 public function xmlrpc_user_disconnect( $user_id ) {
171 100 $user_query = new WP_User_Query(
172 101 array(
173 - 'meta_key' => 'wpcom_user_id',
102 + 'meta_key' => 'wpcom_user_id',
174 103 'meta_value' => $user_id,
175 104 )
176 105 );
177 - $user = $user_query->get_results();
178 - $user = $user[0];
106 + $user = $user_query->get_results();
107 + $user = $user[0];
179 108
180 109 if ( $user instanceof WP_User ) {
181 110 $user = wp_set_current_user( $user->ID );
182 111 update_user_meta( $user->ID, 'jetpack_force_logout', '1' );
@@ -201,16 +130,16 @@
201 130 } else {
202 131 wp_enqueue_style( 'jetpack-sso-login', plugins_url( 'modules/sso/jetpack-sso-login.css', JETPACK__PLUGIN_FILE ), array( 'login', 'genericons' ), JETPACK__VERSION );
203 132 }
204 133
205 - wp_enqueue_script( 'jetpack-sso-login', plugins_url( 'modules/sso/jetpack-sso-login.js', JETPACK__PLUGIN_FILE ), array( 'jquery' ), JETPACK__VERSION, false );
134 + wp_enqueue_script( 'jetpack-sso-login', plugins_url( 'modules/sso/jetpack-sso-login.js', JETPACK__PLUGIN_FILE ), array( 'jquery' ), JETPACK__VERSION );
206 135 }
207 136
208 137 /**
209 138 * Adds Jetpack SSO classes to login body
210 139 *
211 - * @param array $classes Array of classes to add to body tag.
212 - * @return array Array of classes to add to body tag.
140 + * @param array $classes Array of classes to add to body tag
141 + * @return array Array of classes to add to body tag
213 142 */
214 143 public function login_body_class( $classes ) {
215 144 global $action;
216 145
@@ -230,9 +159,9 @@
230 159 * The default_to_sso_login() method allows us to dynamically decide whether we show the SSO login form or not.
231 160 * The SSO module uses the method to display the default login form if we can not find a user to log in via SSO.
232 161 * But, the method could be filtered by a site admin to always show the default login form if that is preferred.
233 162 */
234 - if ( empty( $_GET['jetpack-sso-show-default-form'] ) && Jetpack_SSO_Helpers::show_sso_login() ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
163 + if ( empty( $_GET['jetpack-sso-show-default-form'] ) && Jetpack_SSO_Helpers::show_sso_login() ) {
235 164 $classes[] = 'jetpack-sso-form-display';
236 165 }
237 166 }
238 167
@@ -238,11 +167,8 @@
238 167
239 168 return $classes;
240 169 }
241 170
242 - /**
243 - * Inlined admin styles for SSO.
244 - */
245 171 public function print_inline_admin_css() {
246 172 ?>
247 173 <style>
248 174 .jetpack-sso .message {
@@ -266,9 +192,9 @@
266 192 public function register_settings() {
267 193
268 194 add_settings_section(
269 195 'jetpack_sso_settings',
270 - __( 'Secure Sign On', 'jetpack' ),
196 + __( 'Secure Sign On' , 'jetpack' ),
271 197 '__return_false',
272 198 'jetpack-sso'
273 199 );
274 200
@@ -283,9 +209,9 @@
283 209 );
284 210
285 211 add_settings_field(
286 212 'jetpack_sso_require_two_step',
287 - '', // Output done in render $callback: __( 'Require Two-Step Authentication' , 'jetpack' ).
213 + '', // __( 'Require Two-Step Authentication' , 'jetpack' ),
288 214 array( $this, 'render_require_two_step' ),
289 215 'jetpack-sso',
290 216 'jetpack_sso_settings'
291 217 );
@@ -300,9 +226,9 @@
300 226 );
301 227
302 228 add_settings_field(
303 229 'jetpack_sso_match_by_email',
304 - '', // Output done in render $callback: __( 'Match by Email' , 'jetpack' ).
230 + '', // __( 'Match by Email' , 'jetpack' ),
305 231 array( $this, 'render_match_by_email' ),
306 232 'jetpack-sso',
307 233 'jetpack_sso_settings'
308 234 );
@@ -322,18 +248,16 @@
322 248 name="jetpack_sso_require_two_step"
323 249 <?php checked( Jetpack_SSO_Helpers::is_two_step_required() ); ?>
324 250 <?php disabled( Jetpack_SSO_Helpers::is_require_two_step_checkbox_disabled() ); ?>
325 251 >
326 - <?php esc_html_e( 'Require Two-Step Authentication', 'jetpack' ); ?>
252 + <?php esc_html_e( 'Require Two-Step Authentication' , 'jetpack' ); ?>
327 253 </label>
328 254 <?php
329 255 }
330 256
331 257 /**
332 - * Validate the require two step checkbox in Settings > General.
258 + * Validate the require two step checkbox in Settings > General
333 259 *
334 - * @param bool $input The jetpack_sso_require_two_step option setting.
335 - *
336 260 * @since 2.7
337 261 * @return boolean
338 262 **/
339 263 public function validate_jetpack_sso_require_two_step( $input ) {
@@ -360,12 +284,10 @@
360 284 <?php
361 285 }
362 286
363 287 /**
364 - * Validate the match by email check in Settings > General.
288 + * Validate the match by email check in Settings > General
365 289 *
366 - * @param bool $input The jetpack_sso_match_by_email option setting.
367 - *
368 290 * @since 2.9
369 291 * @return boolean
370 292 **/
371 293 public function validate_jetpack_sso_match_by_email( $input ) {
@@ -385,13 +307,13 @@
385 307 **/
386 308 private function wants_to_login() {
387 309 $wants_to_login = false;
388 310
389 - // Cover default WordPress behavior.
390 - $action = isset( $_REQUEST['action'] ) ? filter_var( wp_unslash( $_REQUEST['action'] ) ) : 'login'; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
311 + // Cover default WordPress behavior
312 + $action = isset( $_REQUEST['action'] ) ? $_REQUEST['action'] : 'login';
391 313
392 - // And now the exceptions.
393 - $action = isset( $_GET['loggedout'] ) ? 'loggedout' : $action; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
314 + // And now the exceptions
315 + $action = isset( $_GET['loggedout'] ) ? 'loggedout' : $action;
394 316
395 317 if ( Jetpack_SSO_Helpers::display_sso_form_for_action( $action ) ) {
396 318 $wants_to_login = true;
397 319 }
@@ -398,19 +320,9 @@
398 320
399 321 return $wants_to_login;
400 322 }
401 323
402 - /**
403 - * Checks to determine if the user has indicated they want to use the wp-admin interface.
404 - */
405 - private function use_wp_admin_interface() {
406 - return 'wp-admin' === get_option( 'wpcom_admin_interface' );
407 - }
408 -
409 - /**
410 - * Initialization for a SSO request.
411 - */
412 - public function login_init() {
324 + function login_init() {
413 325 global $action;
414 326
415 327 $tracking = new Tracking();
416 328
@@ -436,28 +348,35 @@
436 348 add_filter( 'login_message', array( 'Jetpack_SSO_Notices', 'msg_login_by_jetpack' ) );
437 349 }
438 350 }
439 351
440 - if ( 'jetpack-sso' === $action ) {
441 - if ( isset( $_GET['result'] ) && isset( $_GET['user_id'] ) && isset( $_GET['sso_nonce'] ) && 'success' === $_GET['result'] ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
352 + if ( 'jetpack-sso' === $action ) {
353 + if ( isset( $_GET['result'], $_GET['user_id'], $_GET['sso_nonce'] ) && 'success' == $_GET['result'] ) {
442 354 $this->handle_login();
443 355 $this->display_sso_login_form();
444 - } elseif ( ( new Status() )->is_staging_site() ) {
445 - add_filter( 'login_message', array( 'Jetpack_SSO_Notices', 'sso_not_allowed_in_staging' ) );
446 356 } else {
447 - // Is it wiser to just use wp_redirect than do this runaround to wp_safe_redirect?
448 - add_filter( 'allowed_redirect_hosts', array( 'Jetpack_SSO_Helpers', 'allowed_redirect_hosts' ) );
449 - $reauth = ! empty( $_GET['force_reauth'] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
450 - $sso_url = $this->get_sso_url_or_die( $reauth );
357 + if ( ( new Status() )->is_staging_site() ) {
358 + add_filter( 'login_message', array( 'Jetpack_SSO_Notices', 'sso_not_allowed_in_staging' ) );
359 + } else {
360 + // Is it wiser to just use wp_redirect than do this runaround to wp_safe_redirect?
361 + add_filter( 'allowed_redirect_hosts', array( 'Jetpack_SSO_Helpers', 'allowed_redirect_hosts' ) );
362 + $reauth = ! empty( $_GET['force_reauth'] );
363 + $sso_url = $this->get_sso_url_or_die( $reauth );
451 364
452 - $tracking->record_user_event( 'sso_login_redirect_success' );
453 - wp_safe_redirect( $sso_url );
454 - exit;
365 + // Is this our first SSO Login. Set an option.
366 + if ( ! Jetpack_Options::get_option( 'sso_first_login' ) ) {
367 + Jetpack_options::update_option( 'sso_first_login', true );
368 + }
369 +
370 + $tracking->record_user_event( 'sso_login_redirect_success' );
371 + wp_safe_redirect( $sso_url );
372 + exit;
373 + }
455 374 }
456 - } elseif ( Jetpack_SSO_Helpers::display_sso_form_for_action( $action ) ) {
375 + } else if ( Jetpack_SSO_Helpers::display_sso_form_for_action( $action ) ) {
457 376
458 - // Save cookies so we can handle redirects after SSO.
459 - static::save_cookies();
377 + // Save cookies so we can handle redirects after SSO
378 + $this->save_cookies();
460 379
461 380 /**
462 381 * Check to see if the site admin wants to automagically forward the user
463 382 * to the WordPress.com login page AND that the request to wp-login.php
@@ -462,11 +381,11 @@
462 381 * Check to see if the site admin wants to automagically forward the user
463 382 * to the WordPress.com login page AND that the request to wp-login.php
464 383 * is not something other than login (Like logout!)
465 384 */
466 - if ( ! $this->use_wp_admin_interface() && Jetpack_SSO_Helpers::bypass_login_forward_wpcom() && $this->wants_to_login() ) {
385 + if ( Jetpack_SSO_Helpers::bypass_login_forward_wpcom() && $this->wants_to_login() ) {
467 386 add_filter( 'allowed_redirect_hosts', array( 'Jetpack_SSO_Helpers', 'allowed_redirect_hosts' ) );
468 - $reauth = ! empty( $_GET['force_reauth'] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
387 + $reauth = ! empty( $_GET['force_reauth'] );
469 388 $sso_url = $this->get_sso_url_or_die( $reauth );
470 389 $tracking->record_user_event( 'sso_login_redirect_bypass_success' );
471 390 wp_safe_redirect( $sso_url );
472 391 exit;
@@ -481,9 +400,9 @@
481 400 * up the hooks required to display the SSO form.
482 401 */
483 402 public function display_sso_login_form() {
484 403 add_filter( 'login_body_class', array( $this, 'login_body_class' ) );
485 - add_action( 'login_head', array( $this, 'print_inline_admin_css' ) );
404 + add_action( 'login_head', array( $this, 'print_inline_admin_css' ) );
486 405
487 406 if ( ( new Status() )->is_staging_site() ) {
488 407 add_filter( 'login_message', array( 'Jetpack_SSO_Notices', 'sso_not_allowed_in_staging' ) );
489 408 return;
@@ -493,9 +412,9 @@
493 412 if ( is_wp_error( $sso_nonce ) ) {
494 413 return;
495 414 }
496 415
497 - add_action( 'login_form', array( $this, 'login_form' ) );
416 + add_action( 'login_form', array( $this, 'login_form' ) );
498 417 add_action( 'login_enqueue_scripts', array( $this, 'login_enqueue_scripts' ) );
499 418 }
500 419
501 420 /**
@@ -509,10 +428,9 @@
509 428 }
510 429
511 430 setcookie(
512 431 'jetpack_sso_original_request',
513 - // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Sniff misses the wrapping esc_url_raw().
514 - esc_url_raw( set_url_scheme( ( isset( $_SERVER['HTTP_HOST'] ) ? wp_unslash( $_SERVER['HTTP_HOST'] ) : '' ) . ( isset( $_SERVER['REQUEST_URI'] ) ? wp_unslash( $_SERVER['REQUEST_URI'] ) : '' ) ) ),
432 + esc_url_raw( set_url_scheme( $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'] ) ),
515 433 time() + HOUR_IN_SECONDS,
516 434 COOKIEPATH,
517 435 COOKIE_DOMAIN,
518 436 is_ssl(),
@@ -518,15 +436,15 @@
518 436 is_ssl(),
519 437 true
520 438 );
521 439
522 - if ( ! empty( $_GET['redirect_to'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
523 - // If we have something to redirect to.
524 - $url = esc_url_raw( wp_unslash( $_GET['redirect_to'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
440 + if ( ! empty( $_GET['redirect_to'] ) ) {
441 + // If we have something to redirect to
442 + $url = esc_url_raw( $_GET['redirect_to'] );
525 443 setcookie( 'jetpack_sso_redirect_to', $url, time() + HOUR_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN, is_ssl(), true );
526 444 } elseif ( ! empty( $_COOKIE['jetpack_sso_redirect_to'] ) ) {
527 445 // Otherwise, if it's already set, purge it.
528 - setcookie( 'jetpack_sso_redirect_to', ' ', time() - YEAR_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN, is_ssl(), true );
446 + setcookie( 'jetpack_sso_redirect_to', ' ', time() - YEAR_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN );
529 447 }
530 448 }
531 449
532 450 /**
@@ -532,9 +450,9 @@
532 450 /**
533 451 * Outputs the Jetpack SSO button and description as well as the toggle link
534 452 * for switching between Jetpack SSO and default login.
535 453 */
536 - public function login_form() {
454 + function login_form() {
537 455 $site_name = get_bloginfo( 'name' );
538 456 if ( ! $site_name ) {
539 457 $site_name = get_bloginfo( 'url' );
540 458 }
@@ -539,12 +457,12 @@
539 457 $site_name = get_bloginfo( 'url' );
540 458 }
541 459
542 460 $display_name = ! empty( $_COOKIE[ 'jetpack_sso_wpcom_name_' . COOKIEHASH ] )
543 - ? sanitize_text_field( wp_unslash( $_COOKIE[ 'jetpack_sso_wpcom_name_' . COOKIEHASH ] ) )
461 + ? $_COOKIE[ 'jetpack_sso_wpcom_name_' . COOKIEHASH ]
544 462 : false;
545 - $gravatar = ! empty( $_COOKIE[ 'jetpack_sso_wpcom_gravatar_' . COOKIEHASH ] )
546 - ? esc_url_raw( wp_unslash( $_COOKIE[ 'jetpack_sso_wpcom_gravatar_' . COOKIEHASH ] ) )
463 + $gravatar = ! empty( $_COOKIE[ 'jetpack_sso_wpcom_gravatar_' . COOKIEHASH ] )
464 + ? $_COOKIE[ 'jetpack_sso_wpcom_gravatar_' . COOKIEHASH ]
547 465 : false;
548 466
549 467 ?>
550 468 <div id="jetpack-sso-wrap">
@@ -557,20 +475,18 @@
557 475 * @since 8.6.0
558 476 */
559 477 do_action( 'jetpack_sso_login_form_above_wpcom' );
560 478
561 - if ( $display_name && $gravatar ) :
562 - ?>
479 + if ( $display_name && $gravatar ) : ?>
563 480 <div id="jetpack-sso-wrap__user">
564 481 <img width="72" height="72" src="<?php echo esc_html( $gravatar ); ?>" />
565 482
566 483 <h2>
567 484 <?php
568 - echo wp_kses(
569 - /* translators: %s a user display name. */
570 - sprintf( __( 'Log in as <span>%s</span>', 'jetpack' ), esc_html( $display_name ) ),
571 - array( 'span' => true )
572 - );
485 + echo wp_kses(
486 + sprintf( __( 'Log in as <span>%s</span>', 'jetpack' ), esc_html( $display_name ) ),
487 + array( 'span' => true )
488 + );
573 489 ?>
574 490 </h2>
575 491 </div>
576 492
@@ -577,9 +493,9 @@
577 493 <?php endif; ?>
578 494
579 495
580 496 <div id="jetpack-sso-wrap__action">
581 - <?php echo $this->build_sso_button( array(), 'is_primary' ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Escaping done in build_sso_button() ?>
497 + <?php echo $this->build_sso_button( array(), 'is_primary' ); ?>
582 498
583 499 <?php if ( $display_name && $gravatar ) : ?>
584 500 <a rel="nofollow" class="jetpack-sso-wrap__reauth" href="<?php echo esc_url( $this->build_sso_button_url( array( 'force_reauth' => '1' ) ) ); ?>">
585 501 <?php esc_html_e( 'Log in as a different WordPress.com user', 'jetpack' ); ?>
@@ -586,26 +502,14 @@
586 502 </a>
587 503 <?php else : ?>
588 504 <p>
589 505 <?php
590 - /**
591 - * Filter the messeage displayed below the SSO button.
592 - *
593 - * @module sso
594 - *
595 - * @since 10.3.0
596 - *
597 - * @param string $sso_explanation Message displayed below the SSO button.
598 - */
599 - $sso_explanation = apply_filters(
600 - 'jetpack_sso_login_form_explanation_text',
506 + echo esc_html(
601 507 sprintf(
602 - /* Translators: %s is the name of the site. */
603 508 __( 'You can now save time spent logging in by connecting your WordPress.com account to %s.', 'jetpack' ),
604 509 esc_html( $site_name )
605 510 )
606 511 );
607 - echo esc_html( $sso_explanation );
608 512 ?>
609 513 </p>
610 514 <?php endif; ?>
611 515 </div>
@@ -619,10 +523,9 @@
619 523 * @since 8.6.0
620 524 */
621 525 do_action( 'jetpack_sso_login_form_below_wpcom' );
622 526
623 - if ( ! Jetpack_SSO_Helpers::should_hide_login_form() ) :
624 - ?>
527 + if ( ! Jetpack_SSO_Helpers::should_hide_login_form() ) : ?>
625 528 <div class="jetpack-sso-or">
626 529 <span><?php esc_html_e( 'Or', 'jetpack' ); ?></span>
627 530 </div>
628 531
@@ -627,15 +530,15 @@
627 530 </div>
628 531
629 532 <a href="<?php echo esc_url( add_query_arg( 'jetpack-sso-show-default-form', '1' ) ); ?>" class="jetpack-sso-toggle wpcom">
630 533 <?php
631 - esc_html_e( 'Log in with username and password', 'jetpack' )
534 + esc_html_e( 'Log in with username and password', 'jetpack' )
632 535 ?>
633 536 </a>
634 537
635 538 <a href="<?php echo esc_url( add_query_arg( 'jetpack-sso-show-default-form', '0' ) ); ?>" class="jetpack-sso-toggle default">
636 539 <?php
637 - esc_html_e( 'Log in with WordPress.com', 'jetpack' )
540 + esc_html_e( 'Log in with WordPress.com', 'jetpack' )
638 541 ?>
639 542 </a>
640 543 <?php endif; ?>
641 544 </div>
@@ -645,9 +548,9 @@
645 548 /**
646 549 * Clear the cookies that store the profile information for the last
647 550 * WPCOM user to connect.
648 551 */
649 - public static function clear_wpcom_profile_cookies() {
552 + static function clear_wpcom_profile_cookies() {
650 553 if ( isset( $_COOKIE[ 'jetpack_sso_wpcom_name_' . COOKIEHASH ] ) ) {
651 554 setcookie(
652 555 'jetpack_sso_wpcom_name_' . COOKIEHASH,
653 556 ' ',
@@ -653,10 +556,9 @@
653 556 ' ',
654 557 time() - YEAR_IN_SECONDS,
655 558 COOKIEPATH,
656 559 COOKIE_DOMAIN,
657 - is_ssl(),
658 - true
560 + is_ssl()
659 561 );
660 562 }
661 563
662 564 if ( isset( $_COOKIE[ 'jetpack_sso_wpcom_gravatar_' . COOKIEHASH ] ) ) {
@@ -665,10 +567,9 @@
665 567 ' ',
666 568 time() - YEAR_IN_SECONDS,
667 569 COOKIEPATH,
668 570 COOKIE_DOMAIN,
669 - is_ssl(),
670 - true
571 + is_ssl()
671 572 );
672 573 }
673 574 }
674 575
@@ -676,11 +577,11 @@
676 577 * Clear cookies that are no longer needed once the user has logged in.
677 578 *
678 579 * @since 4.8.0
679 580 */
680 - public static function clear_cookies_after_login() {
581 + static function clear_cookies_after_login() {
681 582 self::clear_wpcom_profile_cookies();
682 - if ( isset( $_COOKIE['jetpack_sso_nonce'] ) ) {
583 + if ( isset( $_COOKIE[ 'jetpack_sso_nonce' ] ) ) {
683 584 setcookie(
684 585 'jetpack_sso_nonce',
685 586 ' ',
686 587 time() - YEAR_IN_SECONDS,
@@ -685,14 +586,13 @@
685 586 ' ',
686 587 time() - YEAR_IN_SECONDS,
687 588 COOKIEPATH,
688 589 COOKIE_DOMAIN,
689 - is_ssl(),
690 - true
590 + is_ssl()
691 591 );
692 592 }
693 593
694 - if ( isset( $_COOKIE['jetpack_sso_original_request'] ) ) {
594 + if ( isset( $_COOKIE[ 'jetpack_sso_original_request' ] ) ) {
695 595 setcookie(
696 596 'jetpack_sso_original_request',
697 597 ' ',
698 598 time() - YEAR_IN_SECONDS,
@@ -697,14 +597,13 @@
697 597 ' ',
698 598 time() - YEAR_IN_SECONDS,
699 599 COOKIEPATH,
700 600 COOKIE_DOMAIN,
701 - is_ssl(),
702 - true
601 + is_ssl()
703 602 );
704 603 }
705 604
706 - if ( isset( $_COOKIE['jetpack_sso_redirect_to'] ) ) {
605 + if ( isset( $_COOKIE[ 'jetpack_sso_redirect_to' ] ) ) {
707 606 setcookie(
708 607 'jetpack_sso_redirect_to',
709 608 ' ',
710 609 time() - YEAR_IN_SECONDS,
@@ -709,41 +608,21 @@
709 608 ' ',
710 609 time() - YEAR_IN_SECONDS,
711 610 COOKIEPATH,
712 611 COOKIE_DOMAIN,
713 - is_ssl(),
714 - true
612 + is_ssl()
715 613 );
716 614 }
717 615 }
718 616
719 - /**
720 - * Clean up after Jetpack gets disconnected.
721 - *
722 - * @since 10.7
723 - */
724 - public static function disconnect() {
725 - if ( Jetpack::connection()->is_user_connected() ) {
726 - static::delete_connection_for_user( get_current_user_id() );
727 - }
728 - }
729 -
730 - /**
731 - * Remove an SSO connection for a user.
732 - *
733 - * @param int $user_id The local user id.
734 - */
735 - public static function delete_connection_for_user( $user_id ) {
736 - $wpcom_user_id = get_user_meta( $user_id, 'wpcom_user_id', true );
737 - if ( ! $wpcom_user_id ) {
617 + static function delete_connection_for_user( $user_id ) {
618 + if ( ! $wpcom_user_id = get_user_meta( $user_id, 'wpcom_user_id', true ) ) {
738 619 return;
739 620 }
740 621
741 - $xml = new Jetpack_IXR_Client(
742 - array(
743 - 'wpcom_user_id' => $user_id,
744 - )
745 - );
622 + $xml = new Jetpack_IXR_Client( array(
623 + 'wpcom_user_id' => $user_id,
624 + ) );
746 625 $xml->query( 'jetpack.sso.removeUser', $wpcom_user_id );
747 626
748 627 if ( $xml->isError() ) {
749 628 return false;
@@ -748,22 +627,19 @@
748 627 if ( $xml->isError() ) {
749 628 return false;
750 629 }
751 630
752 - // Clean up local data stored for SSO.
631 + // Clean up local data stored for SSO
753 632 delete_user_meta( $user_id, 'wpcom_user_id' );
754 - delete_user_meta( $user_id, 'wpcom_user_data' );
633 + delete_user_meta( $user_id, 'wpcom_user_data' );
755 634 self::clear_wpcom_profile_cookies();
756 635
757 636 return $xml->getResponse();
758 637 }
759 638
760 - /**
761 - * Retrieves nonce used for SSO form.
762 - */
763 - public static function request_initial_nonce() {
764 - $nonce = ! empty( $_COOKIE['jetpack_sso_nonce'] )
765 - ? sanitize_key( wp_unslash( $_COOKIE['jetpack_sso_nonce'] ) )
639 + static function request_initial_nonce() {
640 + $nonce = ! empty( $_COOKIE[ 'jetpack_sso_nonce' ] )
641 + ? $_COOKIE[ 'jetpack_sso_nonce' ]
766 642 : false;
767 643
768 644 if ( ! $nonce ) {
769 645 $xml = new Jetpack_IXR_Client();
@@ -772,9 +648,9 @@
772 648 if ( $xml->isError() ) {
773 649 return new WP_Error( $xml->getErrorCode(), $xml->getErrorMessage() );
774 650 }
775 651
776 - $nonce = sanitize_key( $xml->getResponse() );
652 + $nonce = $xml->getResponse();
777 653
778 654 setcookie(
779 655 'jetpack_sso_nonce',
780 656 $nonce,
@@ -780,22 +656,21 @@
780 656 $nonce,
781 657 time() + ( 10 * MINUTE_IN_SECONDS ),
782 658 COOKIEPATH,
783 659 COOKIE_DOMAIN,
784 - is_ssl(),
785 - true
660 + is_ssl()
786 661 );
787 662 }
788 663
789 - return $nonce;
664 + return sanitize_key( $nonce );
790 665 }
791 666
792 667 /**
793 668 * The function that actually handles the login!
794 669 */
795 - public function handle_login() {
796 - $wpcom_nonce = isset( $_GET['sso_nonce'] ) ? sanitize_key( $_GET['sso_nonce'] ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
797 - $wpcom_user_id = isset( $_GET['user_id'] ) ? (int) $_GET['user_id'] : 0; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
670 + function handle_login() {
671 + $wpcom_nonce = sanitize_key( $_GET['sso_nonce'] );
672 + $wpcom_user_id = (int) $_GET['user_id'];
798 673
799 674 $xml = new Jetpack_IXR_Client();
800 675 $xml->query( 'jetpack.sso.validateResult', $wpcom_nonce, $wpcom_user_id );
801 676
@@ -806,9 +681,9 @@
806 681 return;
807 682 }
808 683
809 684 $user_data = (object) $user_data;
810 - $user = null;
685 + $user = null;
811 686
812 687 /**
813 688 * Fires before Jetpack's SSO modifies the log in form.
814 689 *
@@ -824,14 +699,11 @@
824 699
825 700 if ( Jetpack_SSO_Helpers::is_two_step_required() && 0 === (int) $user_data->two_step_enabled ) {
826 701 $this->user_data = $user_data;
827 702
828 - $tracking->record_user_event(
829 - 'sso_login_failed',
830 - array(
831 - 'error_message' => 'error_msg_enable_two_step',
832 - )
833 - );
703 + $tracking->record_user_event( 'sso_login_failed', array(
704 + 'error_message' => 'error_msg_enable_two_step'
705 + ) );
834 706
835 707 $error = new WP_Error( 'two_step_required', __( 'You must have Two-Step Authentication enabled on your WordPress.com account.', 'jetpack' ) );
836 708
837 709 /** This filter is documented in core/src/wp-includes/pluggable.php */
@@ -842,21 +714,14 @@
842 714
843 715 $user_found_with = '';
844 716 if ( empty( $user ) && isset( $user_data->external_user_id ) ) {
845 717 $user_found_with = 'external_user_id';
846 - $user = get_user_by( 'id', (int) $user_data->external_user_id );
718 + $user = get_user_by( 'id', intval( $user_data->external_user_id ) );
847 719 if ( $user ) {
848 720 $expected_id = get_user_meta( $user->ID, 'wpcom_user_id', true );
849 - if ( $expected_id && $expected_id != $user_data->ID ) { // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison, Universal.Operators.StrictComparisons.LooseNotEqual
721 + if ( $expected_id && $expected_id != $user_data->ID ) { // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
850 722 $error = new WP_Error( 'expected_wpcom_user', __( 'Something got a little mixed up and an unexpected WordPress.com user logged in.', 'jetpack' ) );
851 723
852 - $tracking->record_user_event(
853 - 'sso_login_failed',
854 - array(
855 - 'error_message' => 'error_unexpected_wpcom_user',
856 - )
857 - );
858 -
859 724 /** This filter is documented in core/src/wp-includes/pluggable.php */
860 725 do_action( 'wp_login_failed', $user_data->login, $error );
861 726 add_filter( 'login_message', array( 'Jetpack_SSO_Notices', 'error_invalid_response_data' ) ); // @todo Need to have a better notice. This is only for the sake of testing the validation.
862 727 return;
@@ -867,9 +732,9 @@
867 732
868 733 // If we don't have one by wpcom_user_id, try by the email?
869 734 if ( empty( $user ) && Jetpack_SSO_Helpers::match_by_email() ) {
870 735 $user_found_with = 'match_by_email';
871 - $user = get_user_by( 'email', $user_data->email );
736 + $user = get_user_by( 'email', $user_data->email );
872 737 if ( $user ) {
873 738 update_user_meta( $user->ID, 'wpcom_user_id', $user_data->ID );
874 739 }
875 740 }
@@ -874,10 +739,10 @@
874 739 }
875 740 }
876 741
877 742 // If we've still got nothing, create the user.
878 - $new_user_override_role = Jetpack_SSO_Helpers::new_user_override( $user_data );
879 - if ( empty( $user ) && ( get_option( 'users_can_register' ) || $new_user_override_role ) ) {
743 + $new_user_override_role = false;
744 + if ( empty( $user ) && ( get_option( 'users_can_register' ) || ( $new_user_override_role = Jetpack_SSO_Helpers::new_user_override( $user_data ) ) ) ) {
880 745 /**
881 746 * If not matching by email we still need to verify the email does not exist
882 747 * or this blows up
883 748 *
@@ -892,14 +757,11 @@
892 757 }
893 758
894 759 $user = Jetpack_SSO_Helpers::generate_user( $user_data );
895 760 if ( ! $user ) {
896 - $tracking->record_user_event(
897 - 'sso_login_failed',
898 - array(
899 - 'error_message' => 'could_not_create_username',
900 - )
901 - );
761 + $tracking->record_user_event( 'sso_login_failed', array(
762 + 'error_message' => 'could_not_create_username'
763 + ) );
902 764 add_filter( 'login_message', array( 'Jetpack_SSO_Notices', 'error_unable_to_create_user' ) );
903 765 return;
904 766 }
905 767
@@ -906,14 +768,11 @@
906 768 $user_found_with = $new_user_override_role
907 769 ? 'user_created_new_user_override'
908 770 : 'user_created_users_can_register';
909 771 } else {
910 - $tracking->record_user_event(
911 - 'sso_login_failed',
912 - array(
913 - 'error_message' => 'error_msg_email_already_exists',
914 - )
915 - );
772 + $tracking->record_user_event( 'sso_login_failed', array(
773 + 'error_message' => 'error_msg_email_already_exists'
774 + ) );
916 775
917 776 $this->user_data = $user_data;
918 777 add_action( 'login_message', array( 'Jetpack_SSO_Notices', 'error_msg_email_already_exists' ) );
919 778 return;
@@ -932,12 +791,12 @@
932 791 */
933 792 do_action( 'jetpack_sso_handle_login', $user, $user_data );
934 793
935 794 if ( $user ) {
936 - // Cache the user's details, so we can present it back to them on their user screen.
795 + // Cache the user's details, so we can present it back to them on their user screen
937 796 update_user_meta( $user->ID, 'wpcom_user_data', $user_data );
938 797
939 - add_filter( 'auth_cookie_expiration', array( 'Jetpack_SSO_Helpers', 'extend_auth_cookie_expiration_for_sso' ) );
798 + add_filter( 'auth_cookie_expiration', array( 'Jetpack_SSO_Helpers', 'extend_auth_cookie_expiration_for_sso' ) );
940 799 wp_set_auth_cookie( $user->ID, true );
941 800 remove_filter( 'auth_cookie_expiration', array( 'Jetpack_SSO_Helpers', 'extend_auth_cookie_expiration_for_sso' ) );
942 801
943 802 /** This filter is documented in core/src/wp-includes/user.php */
@@ -944,44 +803,40 @@
944 803 do_action( 'wp_login', $user->user_login, $user );
945 804
946 805 wp_set_current_user( $user->ID );
947 806
948 - $_request_redirect_to = isset( $_REQUEST['redirect_to'] ) ? esc_url_raw( wp_unslash( $_REQUEST['redirect_to'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
949 - $redirect_to = user_can( $user, 'edit_posts' ) ? admin_url() : self::profile_page_url();
807 + $_request_redirect_to = isset( $_REQUEST['redirect_to'] ) ? esc_url_raw( $_REQUEST['redirect_to'] ) : '';
808 + $redirect_to = user_can( $user, 'edit_posts' ) ? admin_url() : self::profile_page_url();
950 809
951 - // If we have a saved redirect to request in a cookie.
810 + // If we have a saved redirect to request in a cookie
952 811 if ( ! empty( $_COOKIE['jetpack_sso_redirect_to'] ) ) {
953 - // Set that as the requested redirect to.
954 - $redirect_to = esc_url_raw( wp_unslash( $_COOKIE['jetpack_sso_redirect_to'] ) );
955 - $_request_redirect_to = $redirect_to;
812 + // Set that as the requested redirect to
813 + $redirect_to = $_request_redirect_to = esc_url_raw( $_COOKIE['jetpack_sso_redirect_to'] );
956 814 }
957 815
958 816 $json_api_auth_environment = Jetpack_SSO_Helpers::get_json_api_auth_environment();
959 817
960 818 $is_json_api_auth = ! empty( $json_api_auth_environment );
961 - $is_user_connected = ( new Connection_Manager( 'jetpack' ) )->is_user_connected( $user->ID );
819 + $is_user_connected = Jetpack::is_user_connected( $user->ID );
962 820 $roles = new Roles();
963 - $tracking->record_user_event(
964 - 'sso_user_logged_in',
965 - array(
966 - 'user_found_with' => $user_found_with,
967 - 'user_connected' => (bool) $is_user_connected,
968 - 'user_role' => $roles->translate_current_user_to_role(),
969 - 'is_json_api_auth' => (bool) $is_json_api_auth,
970 - )
971 - );
821 + $tracking->record_user_event( 'sso_user_logged_in', array(
822 + 'user_found_with' => $user_found_with,
823 + 'user_connected' => (bool) $is_user_connected,
824 + 'user_role' => $roles->translate_current_user_to_role(),
825 + 'is_json_api_auth' => (bool) $is_json_api_auth,
826 + ) );
972 827
973 828 if ( $is_json_api_auth ) {
974 829 Jetpack::init()->verify_json_api_authorization_request( $json_api_auth_environment );
975 830 Jetpack::init()->store_json_api_authorization_token( $user->user_login, $user );
976 831
977 - } elseif ( ! $is_user_connected ) {
832 + } else if ( ! $is_user_connected ) {
978 833 wp_safe_redirect(
979 834 add_query_arg(
980 835 array(
981 836 'redirect_to' => $redirect_to,
982 837 'request_redirect_to' => $_request_redirect_to,
983 - 'calypso_env' => ( new Host() )->get_calypso_env(),
838 + 'calypso_env' => Jetpack::get_calypso_env(),
984 839 'jetpack-sso-auth-redirect' => '1',
985 840 ),
986 841 admin_url()
987 842 )
@@ -998,14 +853,11 @@
998 853 }
999 854
1000 855 add_filter( 'jetpack_sso_default_to_sso_login', '__return_false' );
1001 856
1002 - $tracking->record_user_event(
1003 - 'sso_login_failed',
1004 - array(
1005 - 'error_message' => 'cant_find_user',
1006 - )
1007 - );
857 + $tracking->record_user_event( 'sso_login_failed', array(
858 + 'error_message' => 'cant_find_user'
859 + ) );
1008 860
1009 861 $this->user_data = $user_data;
1010 862
1011 863 $error = new WP_Error( 'account_not_found', __( 'Account not found. If you already have an account, make sure you have connected to WordPress.com.', 'jetpack' ) );
@@ -1014,12 +866,9 @@
1014 866 do_action( 'wp_login_failed', $user_data->login, $error );
1015 867 add_filter( 'login_message', array( 'Jetpack_SSO_Notices', 'cant_find_user' ) );
1016 868 }
1017 869
1018 - /**
1019 - * Retreive the admin profile page URL.
1020 - */
1021 - public static function profile_page_url() {
870 + static function profile_page_url() {
1022 871 return admin_url( 'profile.php' );
1023 872 }
1024 873
1025 874 /**
@@ -1025,19 +874,19 @@
1025 874 /**
1026 875 * Builds the "Login to WordPress.com" button that is displayed on the login page as well as user profile page.
1027 876 *
1028 877 * @param array $args An array of arguments to add to the SSO URL.
1029 - * @param boolean $is_primary If the button have the `button-primary` class.
878 + * @param boolean $is_primary Should the button have the `button-primary` class?
1030 879 * @return string Returns the HTML markup for the button.
1031 880 */
1032 - public function build_sso_button( $args = array(), $is_primary = false ) {
1033 - $url = $this->build_sso_button_url( $args );
881 + function build_sso_button( $args = array(), $is_primary = false ) {
882 + $url = $this->build_sso_button_url( $args );
1034 883 $classes = $is_primary
1035 884 ? 'jetpack-sso button button-primary'
1036 885 : 'jetpack-sso button';
1037 886
1038 887 return sprintf(
1039 - '<a rel="nofollow" href="%1$s" class="%2$s">%3$s %4$s</a>',
888 + '<a rel="nofollow" href="%1$s" class="%2$s"><span>%3$s %4$s</span></a>',
1040 889 esc_url( $url ),
1041 890 $classes,
1042 891 '<span class="genericon genericon-wordpress"></span>',
1043 892 esc_html__( 'Log in with WordPress.com', 'jetpack' )
@@ -1046,20 +895,20 @@
1046 895
1047 896 /**
1048 897 * Builds a URL with `jetpack-sso` action and option args which is used to setup SSO.
1049 898 *
1050 - * @param array $args An array of arguments to add to the SSO URL.
899 + * @param array $args An array of arguments to add to the SSO URL.
1051 900 * @return string The URL used for SSO.
1052 901 */
1053 - public function build_sso_button_url( $args = array() ) {
902 + function build_sso_button_url( $args = array() ) {
1054 903 $defaults = array(
1055 - 'action' => 'jetpack-sso',
904 + 'action' => 'jetpack-sso',
1056 905 );
1057 906
1058 907 $args = wp_parse_args( $args, $defaults );
1059 908
1060 - if ( ! empty( $_GET['redirect_to'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
1061 - $args['redirect_to'] = rawurlencode( esc_url_raw( wp_unslash( $_GET['redirect_to'] ) ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
909 + if ( ! empty( $_GET['redirect_to'] ) ) {
910 + $args['redirect_to'] = urlencode( esc_url_raw( $_GET['redirect_to'] ) );
1062 911 }
1063 912
1064 913 return add_query_arg( $args, wp_login_url() );
1065 914 }
@@ -1066,18 +915,13 @@
1066 915
1067 916 /**
1068 917 * Retrieves a WordPress.com SSO URL with appropriate query parameters or dies.
1069 918 *
1070 - * @param boolean $reauth If the user be forced to reauthenticate on WordPress.com.
1071 - * @param array $args Optional query parameters.
919 + * @param boolean $reauth Should the user be forced to reauthenticate on WordPress.com?
920 + * @param array $args Optional query parameters.
1072 921 * @return string The WordPress.com SSO URL.
1073 922 */
1074 - public function get_sso_url_or_die( $reauth = false, $args = array() ) {
1075 - $custom_login_url = Jetpack_SSO_Helpers::get_custom_login_url();
1076 - if ( $custom_login_url ) {
1077 - $args['login_url'] = rawurlencode( $custom_login_url );
1078 - }
1079 -
923 + function get_sso_url_or_die( $reauth = false, $args = array() ) {
1080 924 if ( empty( $reauth ) ) {
1081 925 $sso_redirect = $this->build_sso_url( $args );
1082 926 } else {
1083 927 self::clear_wpcom_profile_cookies();
@@ -1088,16 +932,13 @@
1088 932 if ( is_wp_error( $sso_redirect ) ) {
1089 933 $error_message = sanitize_text_field(
1090 934 sprintf( '%s: %s', $sso_redirect->get_error_code(), $sso_redirect->get_error_message() )
1091 935 );
1092 - $tracking = new Tracking();
1093 - $tracking->record_user_event(
1094 - 'sso_login_redirect_failed',
1095 - array(
1096 - 'error_message' => $error_message,
1097 - )
1098 - );
1099 - wp_die( esc_html( $error_message ) );
936 + $tracking = new Tracking();
937 + $tracking->record_user_event( 'sso_login_redirect_failed', array(
938 + 'error_message' => $error_message
939 + ) );
940 + wp_die( $error_message );
1100 941 }
1101 942
1102 943 return $sso_redirect;
1103 944 }
@@ -1109,9 +950,9 @@
1109 950 * @return string WordPress.com SSO URL
1110 951 */
1111 952 public function build_sso_url( $args = array() ) {
1112 953 $sso_nonce = ! empty( $args['sso_nonce'] ) ? $args['sso_nonce'] : self::request_initial_nonce();
1113 - $defaults = array(
954 + $defaults = array(
1114 955 'action' => 'jetpack-sso',
1115 956 'site_id' => Jetpack_Options::get_option( 'id' ),
1116 957 'sso_nonce' => $sso_nonce,
1117 958 'calypso_auth' => '1',
@@ -1122,9 +963,19 @@
1122 963 if ( is_wp_error( $args['sso_nonce'] ) ) {
1123 964 return $args['sso_nonce'];
1124 965 }
1125 966
1126 - return add_query_arg( $args, 'https://wordpress.com/wp-login.php' );
967 + $query = add_query_arg( $args, '' );
968 + $query = trim( $query, '?' );
969 +
970 + $url = Redirect::get_url(
971 + 'wpcom-login',
972 + array(
973 + 'query' => $query,
974 + )
975 + );
976 +
977 + return $url;
1127 978 }
1128 979
1129 980 /**
1130 981 * Build WordPress.com SSO URL with appropriate query parameters,
@@ -1135,14 +986,9 @@
1135 986 * @return string WordPress.com SSO URL
1136 987 */
1137 988 public function build_reauth_and_sso_url( $args = array() ) {
1138 989 $sso_nonce = ! empty( $args['sso_nonce'] ) ? $args['sso_nonce'] : self::request_initial_nonce();
1139 - $redirect = $this->build_sso_url(
1140 - array(
1141 - 'force_auth' => '1',
1142 - 'sso_nonce' => $sso_nonce,
1143 - )
1144 - );
990 + $redirect = $this->build_sso_url( array( 'force_auth' => '1', 'sso_nonce' => $sso_nonce ) );
1145 991
1146 992 if ( is_wp_error( $redirect ) ) {
1147 993 return $redirect;
1148 994 }
@@ -1151,9 +997,9 @@
1151 997 'action' => 'jetpack-sso',
1152 998 'site_id' => Jetpack_Options::get_option( 'id' ),
1153 999 'sso_nonce' => $sso_nonce,
1154 1000 'reauth' => '1',
1155 - 'redirect_to' => rawurlencode( $redirect ),
1001 + 'redirect_to' => urlencode( $redirect ),
1156 1002 'calypso_auth' => '1',
1157 1003 );
1158 1004
1159 1005 $args = wp_parse_args( $args, $defaults );
@@ -1161,9 +1007,19 @@
1161 1007 if ( is_wp_error( $args['sso_nonce'] ) ) {
1162 1008 return $args['sso_nonce'];
1163 1009 }
1164 1010
1165 - return add_query_arg( $args, 'https://wordpress.com/wp-login.php' );
1011 + $query = add_query_arg( $args, '' );
1012 + $query = trim( $query, '?' );
1013 +
1014 + $url = Redirect::get_url(
1015 + 'wpcom-login',
1016 + array(
1017 + 'query' => $query,
1018 + )
1019 + );
1020 +
1021 + return $url;
1166 1022 }
1167 1023
1168 1024 /**
1169 1025 * Determines local user associated with a given WordPress.com user ID.
@@ -1169,19 +1025,17 @@
1169 1025 * Determines local user associated with a given WordPress.com user ID.
1170 1026 *
1171 1027 * @since 2.6.0
1172 1028 *
1173 - * @param int $wpcom_user_id User ID from WordPress.com.
1029 + * @param int $wpcom_user_id User ID from WordPress.com
1174 1030 * @return object Local user object if found, null if not.
1175 1031 */
1176 - public static function get_user_by_wpcom_id( $wpcom_user_id ) {
1177 - $user_query = new WP_User_Query(
1178 - array(
1179 - 'meta_key' => 'wpcom_user_id',
1180 - 'meta_value' => (int) $wpcom_user_id,
1181 - 'number' => 1,
1182 - )
1183 - );
1032 + static function get_user_by_wpcom_id( $wpcom_user_id ) {
1033 + $user_query = new WP_User_Query( array(
1034 + 'meta_key' => 'wpcom_user_id',
1035 + 'meta_value' => intval( $wpcom_user_id ),
1036 + 'number' => 1,
1037 + ) );
1184 1038
1185 1039 $users = $user_query->get_results();
1186 1040 return $users ? array_shift( $users ) : null;
1187 1041 }
@@ -1192,15 +1046,15 @@
1192 1046 *
1193 1047 * We redirect here instead of in handle_login() because Jetpack::init()->build_connect_url
1194 1048 * calls menu_page_url() which doesn't work properly until admin menus are registered.
1195 1049 */
1196 - public function maybe_authorize_user_after_sso() {
1197 - if ( empty( $_GET['jetpack-sso-auth-redirect'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
1050 + function maybe_authorize_user_after_sso() {
1051 + if ( empty( $_GET['jetpack-sso-auth-redirect'] ) ) {
1198 1052 return;
1199 1053 }
1200 1054
1201 - $redirect_to = ! empty( $_GET['redirect_to'] ) ? esc_url_raw( wp_unslash( $_GET['redirect_to'] ) ) : admin_url(); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
1202 - $request_redirect_to = ! empty( $_GET['request_redirect_to'] ) ? esc_url_raw( wp_unslash( $_GET['request_redirect_to'] ) ) : $redirect_to; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
1055 + $redirect_to = ! empty( $_GET['redirect_to'] ) ? esc_url_raw( $_GET['redirect_to'] ) : admin_url();
1056 + $request_redirect_to = ! empty( $_GET['request_redirect_to'] ) ? esc_url_raw( $_GET['request_redirect_to'] ) : $redirect_to;
1203 1057
1204 1058 /** This filter is documented in core/src/wp-login.php */
1205 1059 $redirect_after_auth = apply_filters( 'login_redirect', $redirect_to, $request_redirect_to, wp_get_current_user() );
1206 1060
@@ -1212,13 +1066,9 @@
1212 1066 $redirect_after_auth = wp_validate_redirect( $redirect_after_auth, admin_url() );
1213 1067
1214 1068 /**
1215 1069 * Return the raw connect URL with our redirect and attribute connection to SSO.
1216 - * We remove any other filters that may be turning on the in-place connection
1217 - * since we will be redirecting the user as opposed to iFraming.
1218 1070 */
1219 - remove_all_filters( 'jetpack_use_iframe_authorization_flow' );
1220 - add_filter( 'jetpack_use_iframe_authorization_flow', '__return_false' );
1221 1071 $connect_url = Jetpack::init()->build_connect_url( true, $redirect_after_auth, 'sso' );
1222 1072
1223 1073 add_filter( 'allowed_redirect_hosts', array( 'Jetpack_SSO_Helpers', 'allowed_redirect_hosts' ) );
1224 1074 wp_safe_redirect( $connect_url );
@@ -1228,10 +1078,10 @@
1228 1078 /**
1229 1079 * Cache user's display name and Gravatar so it can be displayed on the login screen. These cookies are
1230 1080 * stored when the user logs out, and then deleted when the user logs in.
1231 1081 */
1232 - public function store_wpcom_profile_cookies_on_logout() {
1233 - if ( ! ( new Connection_Manager( 'jetpack' ) )->is_user_connected( get_current_user_id() ) ) {
1082 + function store_wpcom_profile_cookies_on_logout() {
1083 + if ( ! Jetpack::is_user_connected( get_current_user_id() ) ) {
1234 1084 return;
1235 1085 }
1236 1086
1237 1087 $user_data = $this->get_user_data( get_current_user_id() );
@@ -1244,10 +1094,9 @@
1244 1094 $user_data->display_name,
1245 1095 time() + WEEK_IN_SECONDS,
1246 1096 COOKIEPATH,
1247 1097 COOKIE_DOMAIN,
1248 - is_ssl(),
1249 - true
1098 + is_ssl()
1250 1099 );
1251 1100
1252 1101 setcookie(
1253 1102 'jetpack_sso_wpcom_gravatar_' . COOKIEHASH,
@@ -1252,18 +1101,14 @@
1252 1101 setcookie(
1253 1102 'jetpack_sso_wpcom_gravatar_' . COOKIEHASH,
1254 1103 get_avatar_url(
1255 1104 $user_data->email,
1256 - array(
1257 - 'size' => 144,
1258 - 'default' => 'mystery',
1259 - )
1105 + array( 'size' => 144, 'default' => 'mystery' )
1260 1106 ),
1261 1107 time() + WEEK_IN_SECONDS,
1262 1108 COOKIEPATH,
1263 1109 COOKIE_DOMAIN,
1264 - is_ssl(),
1265 - true
1110 + is_ssl()
1266 1111 );
1267 1112 }
1268 1113
1269 1114 /**
@@ -1269,9 +1114,9 @@
1269 1114 /**
1270 1115 * Determines if a local user is connected to WordPress.com
1271 1116 *
1272 1117 * @since 2.8
1273 - * @param integer $user_id - Local user id.
1118 + * @param integer $user_id - Local user id
1274 1119 * @return boolean
1275 1120 **/
1276 1121 public function is_user_connected( $user_id ) {
1277 1122 return $this->get_user_data( $user_id );
@@ -1280,13 +1125,77 @@
1280 1125 /**
1281 1126 * Retrieves a user's WordPress.com data
1282 1127 *
1283 1128 * @since 2.8
1284 - * @param integer $user_id - Local user id.
1129 + * @param integer $user_id - Local user id
1285 1130 * @return mixed null or stdClass
1286 1131 **/
1287 1132 public function get_user_data( $user_id ) {
1288 1133 return get_user_meta( $user_id, 'wpcom_user_data', true );
1134 + }
1135 +
1136 + /**
1137 + * Mark SSO as discovered when an SSO JITM is viewed.
1138 + *
1139 + * @since 6.9.0
1140 + *
1141 + * @param array $envelopes Array of JITM messages received after API call.
1142 + * @param string $message_path The message path to ask for.
1143 + *
1144 + * @return array $envelopes New array of JITM messages. May now contain only one message, about SSO.
1145 + */
1146 + public function inject_sso_jitm( $envelopes, $message_path = null) {
1147 + /*
1148 + * Bail early if:
1149 + * - the request does not originate from wp-admin main dashboard.
1150 + * - that's not the first time the user uses SSO.
1151 + */
1152 + if (
1153 + 'wp:dashboard:admin_notices' !== $message_path
1154 + || true !== Jetpack_Options::get_option( 'sso_first_login' )
1155 + ) {
1156 + return $envelopes;
1157 + }
1158 +
1159 + // Update our option to mark that SSO was discovered.
1160 + Jetpack_Options::update_option( 'sso_first_login', false );
1161 +
1162 + return $this->prepare_sso_first_login_jitm();
1163 + }
1164 +
1165 + /**
1166 + * Prepare JITM array for new SSO users
1167 + *
1168 + * @since 6.9.0
1169 + *
1170 + * @return array $sso_first_login_jitm array containting one object of information about our message.
1171 + */
1172 + private function prepare_sso_first_login_jitm() {
1173 + // Build our custom SSO JITM.
1174 + $discover_sso_message = array(
1175 + 'content' => array(
1176 + 'message' => esc_html__( "You've successfully signed in with WordPress.com Secure Sign On!", 'jetpack' ),
1177 + 'icon' => 'jetpack',
1178 + 'list' => array(),
1179 + 'description' => esc_html__( 'Interested in learning more about how Secure Sign On keeps your site safer?', 'jetpack' ),
1180 + 'classes' => '',
1181 + ),
1182 + 'CTA' => array(
1183 + 'message' => esc_html__( 'Learn More', 'jetpack' ),
1184 + 'hook' => '',
1185 + 'newWindow' => true,
1186 + 'primary' => true,
1187 + ),
1188 + 'template' => 'default',
1189 + 'ttl' => 300,
1190 + 'id' => 'sso_discover',
1191 + 'feature_class' => 'sso',
1192 + 'expires' => 3628800,
1193 + 'max_dismissal' => 1,
1194 + 'activate_module' => null,
1195 + );
1196 +
1197 + return array( json_decode( json_encode( $discover_sso_message ) ) );
1289 1198 }
1290 1199 }
1291 1200
1292 1201 Jetpack_SSO::get_instance();