Make WordPress Core

Opened 4 months ago

Last modified 4 months ago

#61874 new defect (bug)

Unable to access WordPress login session during login

Reported by: dd32's profile dd32 Owned by:
Milestone: Awaiting Review Priority: normal
Severity: normal Version:
Component: Users Keywords:
Focuses: Cc:

Description (last modified by dd32)

WordPress Sessions are based on a Key which is stored in the Auth cookies, and during each login a new session is initiated.

This session token value is only stored in the authentication cookie. In order to retrieve it, the auth cookies must be set in $_COOKIE, which is only done by a client-request.

This leads to an awkward situation, where WordPress will create a login session, create cookies based on it, and then have no ability to let any further executed code know the session details.

To complicate matters, the logic for retrieving the current user is awkward as Core applies two different methodologies between login and logout:

  • wp_signon() doesn't set the $current_user upon login (#39385) despite
  • wp_logout() clears the $current_user global upon logout (#35488)

As a result of that, during the login_redirect filter the user will be logged out, and during the logout_redirect filter the user will also be logged out (despite the user being logged in according to the cookies superglobal).

For example, the following code does two things:

  1. Sets additional data in the user session on login (Similar to the Two-Factor plugin)
  2. Hooks to login_redirect filter with the intention of acting upon data in the current user session.
<?php
add_filter( 'attach_session_information', function( $session ) {
        $session['foo'] = 'bar';
        return $session;
} );

add_filter( 'login_redirect', function( $redirect, $orig_redirect, $user ) {
        var_dump( [
                'variant' => 'Current',
                '$user->ID' => $user->ID,
                'get_current_user_id()' => get_current_user_id(),
                'wp_get_session_token()' => wp_get_session_token(),
                'session_data' => WP_Session_Tokens::get_instance( $user->ID )->get( wp_get_session_token() )
        ] );
        die();
}, 10, 3 );

which results in this output:

wp-content/mu-plugins/example.php:
array (size=5)
  'variant' => string 'Current' (length=7)
  '$user->ID' => int 1
  'get_current_user_id()' => int 0
  'wp_get_session_token()' => string '' (length=0)
  'session_data' => null

tl;dr: It's not possible (without hoops, see comments) to retrieve the current user session data after login.

Change History (4)

#1 @dd32
4 months ago

There is a workaround, by watching for a cookie set action:

<?php
// The workaround.
add_action('set_auth_cookie', function( $auth_cookie, $expire, $expiration, $user_id, $scheme, $token ) {
        global $session_token;

        $session_token = $token;
}, 10, 6 );

add_filter( 'login_redirect', function( $redirect, $orig_redirect, $user ) {
        global $session_token;

        var_dump( [
                'variant' => 'The Workaround',
                '$user->ID' => $user->ID,
                'get_current_user_id()' => get_current_user_id(),
                'wp_get_session_token()' => wp_get_session_token(),
                'session_data' => WP_Session_Tokens::get_instance( $user->ID )->get( wp_get_session_token() ),
                'global $session_token' => $session_token,
                'session_data (via global token)' => WP_Session_Tokens::get_instance( $user->ID )->get( $session_token )
        ] );
        die();
}, 20, 3 );

which then does allow access to the session data:

wp-content/mu-plugins/example.php:
array (size=7)
  'variant' => string 'The Workaround' (length=14)
  '$user->ID' => int 1
  'get_current_user_id()' => int 0
  'wp_get_session_token()' => string '' (length=0)
  'session_data' => null
  'global $session_token' => string '8XpzO0LxNDfqeHHaHduytWPFVWKYjnfBounb39FfPhK' (length=43)
  'session_data (via global token)' => 
    array (size=6)
      'foo' => string 'bar' (length=3)
      ....
Last edited 4 months ago by dd32 (previous) (diff)

#2 follow-up: @dd32
4 months ago

One partial option is that we could have wp_set_auth_cookie() fill in the $_COOKIE super-global when setting cookies, this would have the effect of allowing wp_get_session_token() to operate, but would not set the current user global:

  • wp-includes/pluggable.php

     
    10901090                        return;
    10911091                }
    10921092
     1093                $_COOKIE[ $auth_cookie_name ] = $auth_cookie;
     1094                $_COOKIE[ LOGGED_IN_COOKIE ]  = $logged_in_cookie;
     1095
    10931096                setcookie( $auth_cookie_name, $auth_cookie, $expire, PLUGINS_COOKIE_PATH, COOKIE_DOMAIN, $secure, true );
    10941097                setcookie( $auth_cookie_name, $auth_cookie, $expire, ADMIN_COOKIE_PATH, COOKIE_DOMAIN, $secure, true );
    10951098                setcookie( LOGGED_IN_COOKIE, $logged_in_cookie, $expire, COOKIEPATH, COOKIE_DOMAIN, $secure_logged_in_cookie, true );

which would result in this:

wp-content/mu-plugins/example.php:
array (size=5)
  'variant' => string 'Diff' (length=4)
  '$user->ID' => int 1
  'get_current_user_id()' => int 0
  'wp_get_session_token()' => string '8XpzO0LxNDfqeHHaHduytWPFVWKYjnfBounb39FfPhK' (length=43)
  'session_data' => 
    array (size=7)
      'foo' => string 'bar' (length=3)
      ....

Still not perfect, as now wp_get_session_token() can determine the current user session, but it's apparently for no user, as no user is logged in, as a result of the outcome of #39385.

#3 in reply to: ↑ 2 @dd32
4 months ago

Replying to dd32:
However, that fits nicely with this next edge-case:

Going back to the logout_redirect filter, it's also possible to retrieve the session token at that point in time, although the data will have been lost (although the token is still known):

<?php
add_filter( 'logout_redirect', function( $redirect, $requested_redirect_to, $user ) {
        var_dump( [
                'variant' => 'logout_redirect',
                'is_user_logged_in()' => is_user_logged_in(),
                '$user->ID' => $user->ID,
                'get_current_user_id()' => get_current_user_id(),
                'wp_get_session_token()' => wp_get_session_token(),
                'session_data' => WP_Session_Tokens::get_instance( $user->ID )->get( wp_get_session_token() ),
        ] );
        die();
}, 10, 3 );

which generates:

wp-content/mu-plugins/example.php:
array (size=6)
  'variant' => string 'logout_redirect' (length=15)
  'is_user_logged_in()' => boolean false
  '$user->ID' => int 1
  'get_current_user_id()' => int 0
  'wp_get_session_token()' => string '8XpzO0LxNDfqeHHaHduytWPFVWKYjnfBounb39FfPhK' (length=43)
  'session_data' => null

Again.. the session token is known during logout, but the current user global is unset and what data was in the session has been lost.

Last edited 4 months ago by dd32 (previous) (diff)

#4 @dd32
4 months ago

  • Description modified (diff)
Note: See TracTickets for help on using tickets.