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>
82 lines
2.5 KiB
PHP
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);
|
|
}
|
|
}
|
|
}
|