I want to give some feedback and a possible solution:
To provide a frontend only login (members area, personalized content like “Hello Name”) with credentials from a third party application (be it an api or external database) one has a few options:
-
DIY
authenticate against the third party and save the logged in status in a session and check that. Downside is, can’t use the Kirby->user() object
-
Add Users Programmatically
authenticate against the third party and create a new User to Kirby and
loginPasswordless()
(this helped me) -
Hybrid Temporary/Kirby Users
my final approach is inspired by @texnixe load users from database receipt that’s based on this forum post and with some hints from GitHub - thathoff/kirby-oauth: OAuth 2 Login for Kirby 3.
Hybrid Temporary/Kirby Users
The goal was to authenticate against a third party members collection[1] and not pollute Kirby’s user implementation (adding a ton of frontend-only users and potentially degrade performance).
- Authenticate the user on 3rd party app
- Create a new User object (
User::factory
) - Add the userdata to the
kirby()->users()
collection - Save the data of this new User to
session()->set('member', [...])
-
loginPasswordless()
(setskirby.userId
to find the user in collection) and redirect to the member page -
new CustomKirby
on boot checks ifsession()->get('member')
has a user and merges it into the Kirby Users collection. - Use
kirby()->user()
At runtime there are all real Kirby users available plus one temporary user in the session.
Authentication
// do some api calls, to get the user data
$userdata = $this->request($url, $params)->json();
$user = \User::factory([
'name' => A::get($userdata, 'email'),
'email' => A::get($userdata, 'email'),
//'id' => A::get($userdata,'id'), // might set my own unique id from API
'password' => bin2hex(random_bytes(32)),
'language' => A::get($userdata, 'language', 'de'),
'role' => 'visitor'
]);
$users = kirby()->users();
$users->add($user);
// add user to session and load it in `new CustomKirby`
kirby()->session()->set('member', $user->toArray());
kirby()->user(A::get($userdata, 'email'))->loginPasswordless();
go('/hidden-members-page');
Overload Users
As described here, it’s possible to overload the way, how Kirby loads the Users
collection.
<?php
use Kirby\Cms\Users;
/**
* Overload users method
* and merge users temporarily from a third party login
*
*/
class CustomKirby extends Kirby
{
/**
* Returns all users
*
* @see \kirby\src\cms\AppUsers.php:134
* @return \Kirby\Cms\Users
*/
public function users()
{
if (is_a($this->users, 'Kirby\Cms\Users') === true) {
return $this->users;
}
$this->users = Users::load($this->root('accounts'), ['kirby' => $this]);
if ($this->session()->get('member')) {
if ($this->users) {
$users = $this->users->toArray();
}
$users[] = $this->session()->get('member');
return $this->users = Users::Factory($users, ['kirby' => $this]);
}
return $this->users;
}
}
I’m kindly asking for feedback and potentially pitfalls of this solution (conflicts with ids, existing users, sessions, data leaks)
Thank you!
PS: I love kirby!
[1]: In my case I’m working with a members table administered by the Directus headless cms.