7

Assuming a user has an existing WordPress session, is it possible to take the 'wordpress_logged_in_[hash]' cookie, encode it in a query string, and resume the session in a different browser?

The specific use case I'm exploring is if a user has logged in to my mobile application, I would like to give them the option to open the WordPress page they are currently viewing in a webview in an external browser without losing their session.

  • 3
    This would open up a huge security risk so it would be a really really bad idea. – Wyck Jan 4 '15 at 4:24
  • 1
    I have to agree with @Wyck what you want to do is not very different to accept a password as query string, that is a bad idea. You can mitigate the security risk putting in place a system to accept one-time password (the same query string is valid only once). – gmazzap Jan 4 '15 at 11:37
  • Thank you for your thoughtful responses. The one-time password or authentication code seems like a good solution. I may ask another, more specific question looking for ideas on how to accomplish that. I would have thought there would be a plugin or something for it. – Jacob Tabak Jan 5 '15 at 20:45
5

Here (with some trepidation) is a sketch of a use-once link solution that might be safeish enough if one can depend on the ip being fairly constant (at least in the short-term) to the phone, using a query var and a transient based on $_SERVER['REMOTE_ADDR'], though as @Wyck and @G. M. say, any such backdoor is a security risk...

// Make query var & transient name unique to site url and user's IP.
$siteurl = get_site_option( 'siteurl' );
// Could use eg $siteurl = COOKIEHASH; if handier for mobile app.
// Other stuff such as $device_id would be good if available.
define( 'WPSE173878', 'wpse173878' . md5( $siteurl . $_SERVER['REMOTE_ADDR'] ) );

add_action( 'init', function () {
    if ( is_user_logged_in() ) {
        if ( wpse173878_is_set_transient() ) {
            $time = time();
            wpse173878_set_transient( $time );
            // For testing output link in footer.
            add_action( 'wp_footer', function () use ( $time ) {
                // Could just use time() instead as check fudged below in check_transient().
                $link = add_query_arg( WPSE173878, $time, home_url( '/' ) );
                echo '<a href="' . esc_attr( $link ) . '">Copy into a browser</a>';
            } );
        }
        add_action( 'clear_auth_cookie', function() { delete_transient( WPSE173878 ); } );
    } else {
        if ( isset( $_GET[WPSE173878] ) ) {
            wpse173878_check_transient();
        }
    }
} );

// Set transient.
function wpse173878_set_transient( $time ) {
    $user_id = get_current_user_id();
    $remember = wpse173878_remember( $user_id );
    // Will be compromised if ip changes...
    set_transient( WPSE173878, array( $user_id, $time, $remember ), 1 * MINUTE_IN_SECONDS );
}

// Check transient and login.
function wpse173878_check_transient() {
    if ( list( $user_id, $time, $remember ) = get_transient( WPSE173878 ) ) {
        // Fudge time test so that it's estimatable by mobile app.
        if ( $_GET[WPSE173878] + 5 >= $time && $_GET[WPSE173878] - 5 <= $time ) {
            delete_transient( WPSE173878 );
            if ( $user = get_user_by( 'id', $user_id ) ) {
                // Login.
                wp_set_auth_cookie( $user->ID, $remember );
                // Might want to do_action( 'wp_login', $user->user_login, $user );
            }
        }
    }
    // Redirect regardless.
    wp_redirect( remove_query_arg( WPSE173878, wp_unslash( $_SERVER['REQUEST_URI'] ) ) );
    exit;
}

// Whether to set transient.
function wpse173878_is_set_transient() {
    if ( wpse173878_is_in_webview() && ! is_admin() ) {
        // Other conditions such as user pressed button are necessary.
        return true;
    }
    return false;
}

// Are we in a webview?
function wpse173878_is_in_webview() {
    // Pretend Firefox is webview for testing.
    return strpos( $_SERVER['HTTP_USER_AGENT'], 'Firefox' ) !== false;
}

// From "wp-includes/user.php" wp_update_user().
function wpse173878_remember( $user_id ) {
    // Here we calculate the expiration length of the current auth cookie and compare it to the default expiration.
    // If it's greater than this, then we know the user checked 'Remember Me' when they logged in.
    $logged_in_cookie    = wp_parse_auth_cookie( '', 'logged_in' );
    /** This filter is documented in wp-includes/pluggable.php */
    $default_cookie_life = apply_filters( 'auth_cookie_expiration', ( 2 * DAY_IN_SECONDS ), $user_id, false );
    return ( ( $logged_in_cookie['expiration'] - time() ) > $default_cookie_life );
}
  • Thank you so much for the incredible answer. I'll try it in practice in the next few days! Really impressive work. – Jacob Tabak Jan 6 '15 at 19:03

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.