Jump to content

Login inside Processwire with a JWT (SSO)


Orkun
 Share

Recommended Posts

Hi Guys

We are trying to build a Login Mask (with Node.js) which should act as a single sign on for two websites. The first website is a angular project on firebase the other one is processwire.

We are trying to achieve the project with JWTs (Json Web Tokens).

My Question is how can I make a user be loggedin in processwire with a JWT?

Has someone experiences with this?

Link to comment
Share on other sites

Hi Orkun!

I use the Firebase PHP JWT library when I need to use JWTs, and I would do it like this with PW:

 

use \Firebase\JWT\JWT;

// http://example.com/sso/?token=eyJhbGciOiJIUzI...

$token = wire('input')->get('token');

// If not token, show error?

$key = 'ABC123';  // Store/load your key somewhere (e.g. PW's $config var)
try {
	$decoded = JWT::decode($token, $key, array('HS256'));
} catch (\Exception $e) {
	// Error parsing/decoding token - do not accept. Show error/redirect.
}

// print_r($decoded);

// Find user based on supplied email.
// Use other PW user fields or properties from JWT as necessary.
$u = wire('users')->get('email=' . wire('sanitizer')->selectorValue($decoded->email));

if ( ! $u->id) {
	// Could not find user - don't exist.
	// Could create them at this point, if you have enough detail in the JWT to do so.
}

// Force user login
wire('session')->forceLogin($u);

// Go to another page (don't stay on this one)
wire('session')->redirect('/');

 

Edited by Craig A Rodway
Added post-login redirect
  • Like 3
Link to comment
Share on other sites

28 minutes ago, Craig A Rodway said:

Hi Orkun!

I use the Firebase PHP JWT library when I need to use JWTs, and I would do it like this with PW:

 


use \Firebase\JWT\JWT;

// http://example.com/sso/?token=eyJhbGciOiJIUzI...

$token = wire('input')->get('token');

// If not token, show error?

$key = 'ABC123';  // Store/load your key somewhere (e.g. PW's $config var)
try {
	$decoded = JWT::decode($token, $key, array('HS256'));
} catch (\Exception $e) {
	// Error parsing/decoding token - do not accept. Show error/redirect.
}

// print_r($decoded);

// Find user based on supplied email.
// Use other PW user fields or properties from JWT as necessary.
$u = wire('users')->get('email=' . wire('sanitizer')->selectorValue($decoded->email));

if ( ! $u->id) {
	// Could not find user - don't exist.
	// Could create them at this point, if you have enough detail in the JWT to do so.
}

// Force user login
wire('session')->forceLogin($u);

 

Hi @Craig A Rodway

Thank you very much! I tested it and it works great.

One question though, where would you place the code above? I placed it for the moment inside the _init.php because it is prepend for every template.

KR
Orkun

Link to comment
Share on other sites

Great!

The best location will depend on your circumstances and how you're directing unauthenticated users to the site.

I typically create a single SSO endpoint - like /sso/. In PW land this would be an 'sso' template with the template file contents having the above code, then a page named 'sso' using that template. Then you can point people to example.com/sso/?token=eyJhbGciOiJIUzI... and have the generated token in the query string. It will log them in, and then you can redirect them to the homepage, or other desired content.

If you're not pointing people to a specific SSO page - and instead just directing them to the site with the token in the query string - you definitely want to catch this as soon as possible. Using the _init.php file prepended to every template is one of several good places to put it, but you could also create a hook (like in site/init.php or site/ready.php) as well, but not sure (without testing) which methods I'd want to hook.

Using either of those approaches, I'd also wrap the code in a function you can call from them to keep it tidy and/or reusable - check for the token - if present, validate and log them in; if not - just do nothing.

Link to comment
Share on other sites

1 hour ago, Craig A Rodway said:

Great!

The best location will depend on your circumstances and how you're directing unauthenticated users to the site.

I typically create a single SSO endpoint - like /sso/. In PW land this would be an 'sso' template with the template file contents having the above code, then a page named 'sso' using that template. Then you can point people to example.com/sso/?token=eyJhbGciOiJIUzI... and have the generated token in the query string. It will log them in, and then you can redirect them to the homepage, or other desired content.

If you're not pointing people to a specific SSO page - and instead just directing them to the site with the token in the query string - you definitely want to catch this as soon as possible. Using the _init.php file prepended to every template is one of several good places to put it, but you could also create a hook (like in site/init.php or site/ready.php) as well, but not sure (without testing) which methods I'd want to hook.

Using either of those approaches, I'd also wrap the code in a function you can call from them to keep it tidy and/or reusable - check for the token - if present, validate and log them in; if not - just do nothing.

Hi @Craig A Rodway

Ok thank you very much!

I have made now a function called ssoLogin() and place the hook inside /site/ready.php:

function ssoLogin() {

    if(!wire('user')->isLoggedin()) {

        $token = wire('input')->get('token');

        // If not token, show error?
        try {
            $decoded = JWT::decode($token, wire('modules')->RestApi->jwtSecret, array('HS256'));
        } catch (\Exception $e) {
            // Error parsing/decoding token - do not accept. Show error/redirect.
            
        }
    
        // print_r($decoded);

        // Find user based on supplied email.
        // Use other PW user fields or properties from JWT as necessary.
        $u = wire('users')->get('email=' . wire('sanitizer')->selectorValue($decoded->claims->email));

        if ( ! $u->id) {
            // Could not find user - don't exist.
            // Could create them at this point, if you have enough detail in the JWT to do so.
        }
    
        // Force user login
        return wire('session')->forceLogin($u);
    }

    return false;
}
// Inside /site/ready.php
$this->addHookAfter('PageRender::renderPage', function(HookEvent $event) {
    if (strpos($_SERVER['REQUEST_URI'], $this->wire('config')->urls->admin) === 0 || $this->wire('page')->template->name == 'admin') return;
    
    ssoLogin();

});

KR
Orkun

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...