Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to allow Laravel and WordPress to share logins?

I'm a Laravel developer. I develop one ecommerce plugin with Laravel and I just want to combine WordPress with Laravel. So I need to share or make common login session between Laravel and WordPress.

How could I implement this? And are there special plugins available for this? Or could I use laravel-Auth?

like image 964
Shankar Thiyagaraajan Avatar asked Sep 26 '22 06:09

Shankar Thiyagaraajan


2 Answers

The right way of doing it is to Have a Laravel (or Wordpress) as an Auth server And create like an SSO plugin. I was doing the same with NodeBB Forum login from Laravel.

Steps that I suggest:

  1. Look at this package Laravel OAuth Server
  2. Create or find any SSO plugin for wordpress

So you have all users in laravel (Registration and etc) and if they want to login to Wordpress they login to Laravel App and give permission to login to wordpress. Think of it Like you add Facebook Login to your site Reading more for wordpress SSO

But to play with session and cookies it can be security issues. Hope Helped.

like image 167
Froxz Avatar answered Sep 28 '22 04:09

Froxz


Enabling Single-Sign-On in WordPress took me 18+ hours of struggle but might take you only a few minutes:

I experimented with all sorts of things: Laravel Passport (OAuth2), OpenID Connect, etc.

But the only solution I could get to work was to have the WordPress login page redirect to an auth-protected Laravel route that generates a JWT (JSON Web Token) and redirects back to a special callback URL on WordPress that either creates a new user or logs in an existing user.

It works well.

class JwtController extends Controller {

    /**
     * Inspired by https://github.com/WebDevStudios/aad-first-party-sso-wordpress/tree/master/lib/php-jwt
     * 
     * @param Request $request
     * @return ResponseInterface
     */
    public function redirectWithToken(Request $request) {
        $key = config('jwt.key');
        $wpJwtUrl = $request->input('callback');
        $redirectUrlAfterLogin = $request->input('redirect_to'); //Get the original intended destination and append as URL param to /jwt. 
        $tokenArray = $this->getToken(auth()->user(), $redirectUrlAfterLogin);
        $jwt = \Firebase\JWT\JWT::encode($tokenArray, $key);
        $wpJwtUrlWithTokenAsParam = $wpJwtUrl . '?token=' . $jwt;
        return redirect()->away($wpJwtUrlWithTokenAsParam);
    }

    /**
     * 
     * @param \App\User $user
     * @param string $redirectUrlAfterLogin
     * @return array
     */
    public function getToken($user, $redirectUrlAfterLogin) {
        $now = \Carbon\Carbon::now();
        $aud = config('jwt.audience'); //root URL of the WordPress site
        $firstName = StrT::getFirstNameFromFullName($user->name);
        $expirationMins = config('jwt.expirationMins');
        $token = [
            "iss" => url("/"),
            "aud" => $aud, //"audience" https://tools.ietf.org/html/rfc7519#section-4.1.3
            "iat" => $now->timestamp, //"issued at" https://tools.ietf.org/html/rfc7519#section-4.1.6
            "exp" => $now->addMinutes($expirationMins)->timestamp, //"expiration" https://tools.ietf.org/html/rfc7519#section-4.1.4
            "attributes" => [
                'emailAddress' => $user->email,
                'firstName' => $firstName,
                'lastName' => StrT::getLastNameFromFullName($user->name),
                'nickname' => $firstName,
                'displayName' => $user->name,
                'redirectUrlAfterLogin' => $redirectUrlAfterLogin//In plugin: use redirectUrlAfterLogin from attributes after login.
            ]
        ];
        return $token;
    }
}
  • Install this WordPress plugin, but don't activate it until you're finished with everything else: https://wordpress.org/plugins/wp-force-login/
  • Install this WordPress plugin: https://as.wordpress.org/plugins/jwt-authenticator/

And then edit its auth.php to be this:

// register the callback
add_action('rest_api_init', function () {
    register_rest_route('jwt-auth/v1', 'callback', [
        'methods' => 'GET',
        'callback' => 'ja_login'
            ], true);
});

require_once('JWT.php');

function ja_login() {
    //get all attributes
    $options = get_option('ja_settings');
    $token_name = $options['token_name'];
    $secret_key = $options['secret_key'];
    $iss = $options['iss'];
    $aud = $options['aud'];

    // decode the token
    $token = $_GET[$token_name];
    $key = $secret_key;
    $JWT = new JWT;
    $json = $JWT->decode($token, $key);
    $jwt = json_decode($json, true);

    // use unix time for comparision
    $exp = is_int($jwt['exp']) ? $jwt['exp'] : strtotime($jwt['exp']);
    $nbf = $jwt['nbf'] ?? null;
    $now = strtotime("now");

    // if authentication successful
    if (($jwt['iss'] == $iss) && ($jwt['aud'] == $aud) && ($exp > $now) && ($now > $nbf)) {
        return getUserFromValidToken($options, $jwt);
    } else {
        return 'Login failed. Please let us know exactly what happened, and we will help you out right away.';
    }
}

/**
 * 
 * @param array $options
 * @param array $jwt
 * @return string
 */
function getUserFromValidToken($options, $jwt) {
    $attributesKey = $options['attributes'];
    $mail = $options['mail'];
    $givenname = $options['first_name'];
    $surname = $options['last_name'];
    $nickname = $options['nickname'];
    $displayname = $options['displayname'];
    $default_role = $options['default_role'];    
    $attributes = $jwt[$attributesKey];
    $redirectUrlAfterLogin = $attributes['redirectUrlAfterLogin'] ?? get_site_url();
    $_SESSION['attributes'] = $attributes;
    $_SESSION['jwt'] = $jwt;

    // find or create user
    $user = ja_find_or_create_user($attributes[$mail], $attributes[$mail], $attributes[$givenname], $attributes[$surname], $attributes[$nickname], $attributes[$displayname], $default_role);
    // login user
    if ($user) {
        wp_clear_auth_cookie();
        wp_set_current_user($user->ID, $user->user_login);
        wp_set_auth_cookie($user->ID);
        do_action('wp_login', $user->user_login);        
        wp_safe_redirect($redirectUrlAfterLogin);
        exit();
    } else {
        return 'getUserFromValidToken failed!';
    }
}

/**
 * 
 * @param string $username
 * @param string $emailAddress
 * @param string $firstName
 * @param string $lastName
 * @param string $nickname
 * @param string $displayName
 * @param string $defaultRole
 * @return mixed 
 */
function ja_find_or_create_user($username, $emailAddress, $firstName, $lastName, $nickname, $displayName, $defaultRole) {
    // if user exists, return user
    if (username_exists($username)) {
        return get_user_by('login', $username);
    } elseif (email_exists($emailAddress)) {
        return get_user_by('email', $emailAddress);
    } else {//  create user
        $length = 16;
        $include_standard_special_chars = false;
        $random_password = wp_generate_password($length, $include_standard_special_chars);
        // create user
        $user_id = wp_create_user($username, $random_password, $emailAddress);
        // update user metadata and return user id
        $userData = [
            'ID' => $user_id,
            'first_name' => $firstName,
            'last_name' => $lastName,
            'nickname' => $nickname,
            'display_name' => $displayName,
            'role' => $defaultRole
        ];
        return wp_update_user($userData);//(If successful, returns the user_id, otherwise returns a WP_Error object.)
    }
}

/**
 * Get login message link HTML for adding to the login form
 * @return string
 */
function getLoginMessage() {
    $options = get_option('ja_settings');
    $redirect_to = $_GET['redirect_to'] ?? null;
    $login_url = $options['login_url'] . '?callback=' . urlencode(site_url('/wp-json/jwt-auth/v1/callback'));
    if($redirect_to){
        $login_url .= '&redirect_to=' . urlencode($redirect_to);
    }
    $login_message = $options['login_message'];
    return "<a id='jwt_link' href='{$login_url}'>{$login_message}</a>";
}

add_filter('login_message', 'getLoginMessage');
add_action( 'load-profile.php', function() {//https://wordpress.stackexchange.com/a/195370/51462 Redirect from profile.php to the dashboard since there is no reason for WordPress users to see or manage their profile since their main account is on the other site.
    if( ! current_user_can( 'manage_options' ) ){
        $redirectUrl = get_site_url();//admin_url()
        exit( wp_safe_redirect( $redirectUrl ) );
    }
} );
function show_admin_bar_conditionally(){//remove the WordPress admin toolbar https://premium.wpmudev.org/blog/remove-the-wordpress-admin-toolbar/
    return current_user_can( 'manage_options' );
}
add_filter('show_admin_bar', 'show_admin_bar_conditionally');//can use 'show_admin_bar_conditionally' or '__return_false' for never.
//------------------------------------------------------------------
//for https://wordpress.org/support/topic/rest-api-26/#post-9915078 
//and https://github.com/kevinvess/wp-force-login/issues/35 
//and https://wordpress.org/support/topic/rest-api-26/page/2/#post-10000740
//and https://wordpress.org/support/topic/jwt-authentication/#post-10698307
add_filter( 'rest_authentication_errors', '__return_true' );

This belongs in functions.php of your theme in WordPress:

// https://codex.wordpress.org/Customizing_the_Login_Form
function my_custom_login_page() { ?>
    <style type="text/css">
        #loginform, #login #nav{display: none;}
        #jwt_link{font-weight: bold; font-size: 20px;}
    </style>
    <script>
        document.addEventListener("DOMContentLoaded", function(event) { 
            document.getElementById('jwt_link').click();//immediately upon load of login page, click the JWT link automatically
        });
    </script>
<?php }
add_action( 'login_enqueue_scripts', 'my_custom_login_page' );
like image 20
Ryan Avatar answered Sep 28 '22 03:09

Ryan