Making WordPress.org


Ignore:
Timestamp:
05/29/2020 03:14:48 AM (6 years ago)
Author:
dd32
Message:

Login: Add remote-login functionality to the SSO login code.

This will allow for instances of WordPress in the WordPress.org network (and using it's user tables) to login via login.wordpress.org.

See #5239.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • sites/trunk/common/includes/wporg-sso/wp-plugin.php

    r9833 r9941  
    6767                                        add_filter( 'register_url', [ $this, 'add_locale' ], 21 );
    6868                                        add_filter( 'lostpassword_url', [ $this, 'add_locale' ], 21 );
     69                                } else {
     70                                        add_filter( 'login_redirect', [ $this, 'maybe_add_remote_login_bounce_to_post_login_url' ], 10, 3 );
    6971                                }
    7072                        }
     
    186188                                if ( preg_match( '!/wp-login\.php$!', $this->script ) ) {
    187189                                        // Don't redirect the 'confirmaction' wp-login handlers to login.wordpress.org.
    188                                         if ( isset( $_GET['action']  ) && empty( $_POST ) && 'confirmaction' == $_GET['action'] ) {
     190                                        if ( isset( $_GET['action'] ) && empty( $_POST ) && 'confirmaction' == $_GET['action'] ) {
    189191                                                return;
     192                                        }
     193
     194                                        // Allow logout on non-dotorg hosts.
     195                                        if ( isset( $_GET['action'] ) && empty( $_POST ) && 'logout' == $_GET['action'] ) {
     196                                                if ( ! preg_match( '!wordpress\.org$!', $_SERVER['HTTP_HOST'] ) ) {
     197                                                        return;
     198                                                }
     199                                        }
     200
     201                                        // Remote SSO login?
     202                                        if ( isset( $_GET['action'] ) && 'remote-login' === $_GET['action'] && ! empty( $_GET['sso_token'] ) ) {
     203                                                $this->_maybe_perform_remote_login();
    190204                                        }
    191205
     
    200214                                        // Pay extra attention to the post-process redirect_to
    201215                                        $redirect_to_sso_login = add_query_arg( 'redirect_to', urlencode( $redirect_req ), $redirect_to_sso_login );
     216                                        if ( ! preg_match( '!wordpress\.org$!', $this->host ) ) {
     217                                                $redirect_to_sso_login = add_query_arg( 'from', $this->host, $redirect_to_sso_login );
     218                                        }
    202219
    203220                                        // And actually redirect to the SSO login
     
    244261                                                                // Else let the theme render, or redirect if logged in
    245262                                                                if ( is_user_logged_in() ) {
    246                                                                         $this->_redirect_to_profile();
     263                                                                        $this->_redirect_to_source_or_profile();
    247264                                                                } else {
    248265                                                                        if ( empty( $_GET['screen'] ) ) {
     
    259276                                                } else if ( is_user_logged_in() ) {
    260277                                                        // Otherwise, redirect to the their profile.
    261                                                         $this->_redirect_to_profile();
     278                                                        $this->_redirect_to_source_or_profile();
    262279                                                }
    263280                                        } elseif ( ( is_admin() && is_super_admin() ) || 0 === strpos( $_SERVER['REQUEST_URI'], '/wp-json' ) || 0 === strpos( $_SERVER['REQUEST_URI'], '/xmlrpc.php' ) ) {
     
    265282                                        } elseif ( is_user_logged_in() ) {
    266283                                                // Logged in catch all, before last fallback
    267                                                 $this->_redirect_to_profile();
     284                                                $this->_redirect_to_source_or_profile();
    268285                                        } else {
    269286                                                // Otherwise, redirect to the login screen.
     
    365382
    366383                /**
    367                  * Redirects the user to her/his (support) profile.
    368                  */
    369                 protected function _redirect_to_profile() {
    370                         if ( ! is_user_logged_in() ) {
    371                                 return;
    372                         }
    373 
    374                         if ( ! empty( $_GET['redirect_to'] ) ) {
    375                                 $this->_safe_redirect( $this->_get_safer_redirect_to() );
     384                 * Redirects the user back to where they came from (or w.org profile)
     385                 */
     386                protected function _redirect_to_source_or_profile() {
     387                        $redirect = $this->_get_safer_redirect_to();
     388
     389                        if ( $redirect ) {
     390                                $this->_safe_redirect( $this->_maybe_add_remote_login_bounce( $redirect ) );
     391                        } elseif ( is_user_logged_in() ) {
     392                                $this->_safe_redirect( 'https://profiles.wordpress.org/' . wp_get_current_user()->user_nicename . '/' );
    376393                        } else {
    377                                 $this->_safe_redirect( 'https://profiles.wordpress.org/' . wp_get_current_user()->user_nicename . '/' );
    378                         }
     394                                $this->_safe_redirect( 'https://wordpress.org/' );
     395                        }
     396                }
     397
     398                /**
     399                 * Logs in a user on the current domain on a remote-login action.
     400                 */
     401                protected function _maybe_perform_remote_login() {
     402                        $remote_token = wp_unslash( $_GET['sso_token'] );
     403                        if ( ! is_string( $remote_token ) || 3 !== substr_count( $remote_token, '|' ) ) {
     404                                wp_die( 'Invalid token.' );
     405                        }
     406
     407                        list( $user_id, $sso_hash, $valid_until, $remember_me ) = explode( '|', $remote_token, 4 );
     408
     409                        $remote_expiration_valid = (
     410                                // +/- 5s on a 5s timeout.
     411                                $valid_until >= ( time() - 5 ) &&
     412                                $valid_until <= ( time() + 10 )
     413                        );
     414
     415                        $valid_remote_hash = false;
     416                        $user = get_user_by( 'id', $user_id );
     417                        if ( $user ) {
     418                                $valid_remote_hash = hash_equals(
     419                                        $this->_generate_remote_login_hash( $user, $valid_until, $remember_me ),
     420                                        $sso_hash
     421                                );
     422                        }
     423
     424                        if ( $remote_expiration_valid && $valid_remote_hash ) {
     425                                wp_set_current_user( (int) $user_id );
     426                                wp_set_auth_cookie( (int) $user_id, (bool) $remember_me );
     427
     428                                if ( isset( $_GET['redirect_to'] ) ) {
     429                                        $this->_safe_redirect( wp_unslash( $_GET['redirect_to'] ) );
     430                                } else {
     431                                        $this->_safe_redirect( home_url( '/' ) );
     432                                }
     433                                exit;
     434                        }
     435
     436                        return false;
     437                }
     438
     439                public function maybe_add_remote_login_bounce_to_post_login_url( $redirect, $requested, $user ) {
     440                        return $this->_maybe_add_remote_login_bounce( $redirect, $user );
     441                }
     442
     443                protected function _maybe_add_remote_login_bounce( $redirect, $user = false ) {
     444                        if ( ! $user ) {
     445                                $user = wp_get_current_user();
     446                        }
     447
     448                        // If it's on a different _supported_ host, bounce through the remote-login.
     449                        $redirect_host = parse_url( $redirect, PHP_URL_HOST );
     450
     451                        if ( $user && $this->_is_valid_targeted_domain( $redirect_host ) && ! preg_match( '!wordpress.org$!i', $redirect_host ) ) {
     452
     453                                // Fetch auth cookie parts to find out if the user has selected 'remember me'.
     454                                $auth_cookie_parts = wp_parse_auth_cookie( '', 'secure_auth' );
     455
     456                                $valid_until = time() + 5; // Super short timeout.
     457                                $remember_me = ! empty( $_POST['rememberme'] ) || ( $auth_cookie_parts && $auth_cookie_parts['expiration'] >= ( time() + ( 2 * DAY_IN_SECONDS ) ) );
     458
     459                                $hash        = $this->_generate_remote_login_hash( $user, $valid_until, $remember_me );
     460                                $sso_token   = $user->ID . '|' . $hash . '|' . $valid_until . '|' . $remember_me;
     461
     462                                $redirect = add_query_arg(
     463                                        array(
     464                                                'action'      => 'remote-login',
     465                                                'sso_token'   => urlencode( $sso_token ),
     466                                                'redirect_to' => urlencode( $redirect ),
     467                                        ),
     468                                        'https://' . $redirect_host . '/wp-login.php' // Assume that wp-login exists and is accessible
     469                                );
     470                        }
     471
     472                        return $redirect;
     473                }
     474
     475                /**
     476                 * Generate a hash for remote-login for non-wordpress.org domains
     477                 */
     478                protected function _generate_remote_login_hash( $user, $valid_until, $remember_me = false ) {
     479                        // re-use the same frag that Auth cookies use to invalidate sessions.
     480                        $pass_frag = substr( $user->user_pass, 8, 4 );
     481                        $key       = wp_hash( $user->user_login . '|' . $pass_frag . '|' . $valid_until );
     482                        $hash      = hash_hmac( 'sha256', $user->user_login . '|' . $valid_until . '|' . (int) $remember_me, $key );
     483
     484                        return $hash;
    379485                }
    380486        }
Note: See TracChangeset for help on using the changeset viewer.