2

I have a subdirectory Wordpress network with >50 sites, it's on a primary domain of wordpress.example.com.

A client, johndoe, within my network will (intentionally) have a back-end that includes the main site (for branding and ownership reasons):

wordpress.example.com/johndoe/wp-admin/

then via domain mapping with WordPress MU Domain Mapping By Donncha O Caoimh, a front-end of:

johndoe.com/


My problem is that the login cookie associates only to wordpress.example.com and is completely unrelated and unaware of johndoe.com, so the front-end does not recognize a user is actually logged in. This yields:

  • no user tool-bar on front-end
  • a posts "PREVIEW changes" button doesn't work
  • is_user_logged_in() doesn't work in front-end
  • front-end page builder plugins won't work

By disabling 'remote login' I know I can make the back-end of the site johndoe.com/wp-admin/ which would solve all aforementioned problems. However, keeping the primary domain for the back-end is crucial. In all my readings I haven't found a solution, and I've let this question sit for years.

I know Wordpress.com (itself powered by a Wordpress network) seems to have solved this issue. When logged into wordpress.com venturing to a random wordpress.com blog like https://longitudes.ups.com I'm able to see my .com login, and the toolbar does not appear to be an iframe or anything silly done.

So, my question is, if the front-end domain of a Wordpress site is different than the back-end, is there anyway to tie the login cookie to both? If the answer is "you can't" (as all my research has returned), my follow up is, well how do the folks at Automattic do it?

2
  • 1
    You should read about SSO concept. Maybe to use already existing like Google account?
    – kierzniak
    Dec 22, 2017 at 7:38
  • 1
    You should also validate is the Domain Mapping plugin necessary. WP supports this without this plugin, is only necessary if you need alias in your domains. The cookie settings on default is also enough, see wptherightway.org/en/multisite with a little bid more information. Also see this answer for a solid MU install - wordpress.stackexchange.com/questions/181914/…
    – bueltge
    Dec 22, 2017 at 8:21

2 Answers 2

1
+100

WordPress is deciding whether you are logged in or not by checking AUTH_COOKIE and LOGGED_IN_COOKIE. As you have noticed these cookies are set in the same, let,s say, A domain which your site is. Adding the same cookies to your second B domain would make your user logged in in two A and B domains. Of course setting cookie from domain A for second B domain would be an enormous security flaw so you must send cookie values from domain A to domain B and set these cookies in domain B.

So this is what we have to do:

  • read cookies AUTH_COOKIE and LOGGED_IN_COOKIE on domain A
  • send cookies AUTH_COOKIE and LOGGED_IN_COOKIE from domain A to domain B
  • set cookies AUTH_COOKIE and LOGGED_IN_COOKIE on domain B

To read cookies we have to use two filtes set_auth_cookie and set_logged_in_cookie. To set cookies on domain B users browser must be on site B so we need to redirect user from domain A to domain B with cookie values. Redirecting with GET params is not an option, cookies are security sensitive, we must use POST request. To redirect user and send cookies data with POST we can create simple html form with url pointed to domain B. After user is redirected we can set cookies on domain B and turn back user to domain A.

I created working code for my implementation.

/**
 * DOMAIN A PART PLUGIN
 */

class WPSE_287556_Send_Cookies {

    /**
     * Domain which user have to be redirected
     *
     * @var array
     */
    private $domainB = 'example.com';

    /**
     * Array of cookies to send
     *
     * @var array
     */
    private $cookies = array();

    /**
     * WPSE_287556_Send_Cookies constructor.
     */
    public function __construct()
    {
        /**
         * Define plugin related hooks
         */
        $this->define_hooks();
    }

    /**
     * Save auth and logged in cookies to array
     */
    public function save_cookie( $cookie, $expire, $expiration, $user_id, $scheme, $token ) {

        $this->cookies[] = $data = array(
            'cookie' => $cookie,
            'expire' => $expire,
            'scheme' => $scheme,
        );
    }

    /**
     * Display redirect post form
     *
     * We should not redirect user with cookies in get parameters because this is
     * no safe. We also can not redirect user with post parameters. We can create
     * html post form and submit it with js.
     */
    public function display_redirect_form( $redirect_to, $requested_redirect_to, $user ) {

        if( is_array( $this->cookies ) && !empty( $this->cookies ) ):

            $url = ( is_ssl() ) ? 'https://' : 'http://' . $this->domainB . '/';
            ?>

            <form action="<?php echo esc_url( $url ); ?>" method="post" style="display: none;" id="post_redirect_form">

                <input type="hidden" name="action" value="set_cookies" >

                <?php foreach($this->cookies as $index => $cookie): ?>
                    <input type="hidden" name="cookies[<?php esc_attr_e( $index ); ?>][cookie]" value="<?php esc_attr_e( $cookie['cookie'] ); ?>" >
                    <input type="hidden" name="cookies[<?php esc_attr_e( $index ); ?>][expire]" value="<?php esc_attr_e( $cookie['expire'] ); ?>" >
                    <input type="hidden" name="cookies[<?php esc_attr_e( $index ); ?>][scheme]" value="<?php esc_attr_e( $cookie['scheme'] ); ?>" >
                <?php endforeach; ?>

                <input type="hidden" name="redirect_to" value="<?php esc_attr_e( $redirect_to ); ?>" >
            </form>
            <script> document.getElementById('post_redirect_form').submit(); </script>

            <?php exit; ?>

        <?php endif;

        return $redirect_to;
    }

    /**
     * Define plugin related hooks
     */
    private function define_hooks() {

        /**
         * Save cookies hook
         */
        add_action( 'set_auth_cookie', array($this, 'save_cookie'), 10, 6 );
        add_action( 'set_logged_in_cookie', array($this, 'save_cookie'), 10, 6 );

        /**
         * Display redirect post form
         *
         * This filter is used to modify redirect url after login. There is no
         * better place to modify page content after user login. Additionally
         * we have access to $redirect_to url which we can use later.
         */
        add_filter('login_redirect', array( $this, 'display_redirect_form' ), 10, 3);
    }
}

new WPSE_287556_Send_Cookies();

/**
 * END OF DOMAIN A PART PLUGIN
 */

/**
 * DOMAIN B PART PLUGIN
 */

class WPSE_287556_Set_Cookies {

    /**
     * WPSE_287556_Set_Cookies constructor.
     */
    public function __construct()
    {
        /**
         * Define plugin related hooks
         */
        $this->define_hooks();
    }

    /**
     * Set auth and logged in cookies
     */
    public function set_cookies() {

        // Check if request is "set auth cookie" request
        if( $_SERVER['REQUEST_METHOD'] === 'POST' && isset( $_POST['action'] ) && $_POST['action'] === 'set_cookies' ) {

            $args = array(
                'redirect_to'   => FILTER_SANITIZE_URL,
                'cookies'    => array(
                    'filter' => FILTER_SANITIZE_STRING,
                    'flags'  => FILTER_REQUIRE_ARRAY,
                ),
            );

            // Read and filter all post params
            $post = filter_input_array(INPUT_POST, $args);

            $redirect_to = $post['redirect_to'];
            $cookies     = $post['cookies'];

            foreach( $cookies as $cookie_params ){

                $scheme = $cookie_params['scheme'];
                $cookie = $cookie_params['cookie'];
                $expire = (int) $cookie_params['expire'];

                // Decide which cookie to set
                switch( $scheme ) {

                    case 'logged_in':

                        // Set logged in cookie, most of the code is from wp_set_auth_cookie function
                        setcookie( LOGGED_IN_COOKIE, $cookie, $expire, COOKIEPATH, COOKIE_DOMAIN, is_ssl(), true);

                        if ( COOKIEPATH != SITECOOKIEPATH )
                            setcookie(LOGGED_IN_COOKIE, $cookie, $expire, SITECOOKIEPATH, COOKIE_DOMAIN, is_ssl(), true);

                        break;

                    case 'secure_auth':
                    case 'auth':

                        // Set auth cookie, most of the code is from wp_set_auth_cookie function
                        if ( $scheme === 'secure_auth' ) {
                            $auth_cookie_name = SECURE_AUTH_COOKIE;
                        } else {
                            $auth_cookie_name = AUTH_COOKIE;
                        }

                        setcookie($auth_cookie_name, $cookie, $expire, PLUGINS_COOKIE_PATH, COOKIE_DOMAIN, is_ssl(), true);
                        setcookie($auth_cookie_name, $cookie, $expire, ADMIN_COOKIE_PATH, COOKIE_DOMAIN, is_ssl(), true);

                        break;
                }
            }

            // Redirect user to previous site
            header( 'Location: ' . esc_url( $redirect_to ) );
            exit;
        }
    }

    /**
     * Define plugin related hooks
     */
    private function define_hooks() {

        /**
         * Set cookies from request
         */
        add_action( 'init', array($this, 'set_cookies'));
    }
}

new WPSE_287556_Set_Cookies();

/**
 * END OF DOMAIN B PART PLUGIN
 */
1
  • Just curious, how secure it is? we're basically submiting auth cookies, is there a way, that some one can cath our auth cookies while sending using this method between domains?
    – Isu
    Mar 30, 2021 at 15:32
0

Could you check this definitions on your wp-config.php file?

define('ADMIN_COOKIE_PATH', '/');
define('COOKIE_DOMAIN', '');
define('COOKIEPATH', '');
define('SITECOOKIEPATH', ''); 

And also please check your multisite definitions.

define('WP_ALLOW_MULTISITE', true);
define('MULTISITE', true);
define('SUBDOMAIN_INSTALL', false);
define('DOMAIN_CURRENT_SITE', 'your-domain.com');
define('PATH_CURRENT_SITE', '/');
define('SITE_ID_CURRENT_SITE', 1);
define('BLOG_ID_CURRENT_SITE', 1);
define('SUNRISE', 'on');
4
  • No, I don't have any define's for ADMIN_COOKIE_PATH, COOKIE_DOMAIN, COOKIEPATH, or SITECOOKIEPATH. Yes, I have the all the rest for normal network defines, except my setup is not subdomain install, its sub folder. Dec 21, 2017 at 23:25
  • 1
    Could you add first definition set to your wp-config file? Also i find another question and similiar answer on wordpress.stackexchange.com/a/261087/23042 Dec 21, 2017 at 23:52
  • Thanks for that. the COOKIE_DOMAIN crashes the site, happens via wp-content/sunrise.php:6.. looking at that code it's defining COOKIE_DOMAIN for each site here with related mapped domain. Which seems like that might be the problem. I'll try dropping the mapping plugin and try domain mapping the native-wordpress way.. hopefully WP doesn't change COOKIE_DOMAIN on me anyways. (If that works the bounties yours). Dec 22, 2017 at 0:31
  • Disabling the plugin, using wordpress default domain mapping, and using the four cookie-related define()'s did not solve my issue. I'm still unable to keep a login status through domain mapped sites on the same wp network (cache's were cleared, used multiple browsers in private-mode to test). Dec 22, 2017 at 5:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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