Files
landing-api/app/Actions/Auth/SyncPireonUser.php
Josep Piguillem 3897113313 Initial commit: Pireon Admin — headless admin panel and public API
Laravel 13 + Filament 5 admin panel and REST API for a non-profit developer
collective, backed by the Pireon OAuth identity server. The public site is a
separate, server-rendered application consuming /api/public/v1.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-19 17:55:27 +02:00

82 lines
2.5 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Actions\Auth;
use App\Models\User;
use App\Services\Pireon\PireonIdentity;
use Illuminate\Support\Facades\DB;
use Spatie\Permission\Models\Role;
/**
* Mirror an identity from the OAuth server into the local users table.
*
* Identity fields are overwritten on every login — the identity server is the
* source of truth for who someone is. Local roles are never touched here: this
* application owns authorisation, so a role granted in the panel survives every
* subsequent login, and `app_roles` reported upstream grant nothing.
*/
class SyncPireonUser
{
public function handle(PireonIdentity $identity): User
{
return DB::transaction(function () use ($identity): User {
$user = User::query()
->where('oauth_id', $identity->id)
->first();
// Adopt a pre-existing local account created by a seeder or an
// earlier import, so the first real login claims it instead of
// colliding with the unique email index.
$user ??= User::query()
->whereNull('oauth_id')
->where('email', $identity->email)
->first();
$isNew = $user === null;
$user ??= new User;
$user->fill([
'oauth_id' => $identity->id,
'name' => $identity->name,
'email' => $identity->email,
'is_active' => $identity->isActive,
'remote_roles' => $identity->roles,
'last_login_at' => now(),
]);
// The upstream account is already verified by the identity server;
// recording it here keeps Laravel's own checks satisfied.
$user->email_verified_at ??= now();
$user->save();
if ($isNew) {
$this->assignDefaultRole($user);
}
return $user->refresh();
});
}
/**
* A brand new identity gets the least-privileged role. Promoting them is a
* deliberate act performed by an administrator in the panel.
*/
protected function assignDefaultRole(User $user): void
{
$role = (string) config('pireon.default_role');
if (blank($role) || $user->roles()->exists()) {
return;
}
// Silently skip when the role has not been seeded yet, so a login can
// never fail because of a missing local role.
if (Role::where('name', $role)->exists()) {
$user->assignRole($role);
}
}
}