oki-technologies / people-laravel
Sign people in with People, verify their tokens and keep their identities current — in Laravel, with a guard, a sync command and a test fake.
Package info
github.com/oki-technologies/people-laravel
pkg:composer/oki-technologies/people-laravel
Requires
- php: ^8.3
- guzzlehttp/psr7: ^2.6
- illuminate/auth: ^11.0 || ^12.0 || ^13.0
- illuminate/console: ^11.0 || ^12.0 || ^13.0
- illuminate/http: ^11.0 || ^12.0 || ^13.0
- illuminate/support: ^11.0 || ^12.0 || ^13.0
- oki-technologies/people-php: ^1.0
Requires (Dev)
- orchestra/testbench: ^9.0 || ^10.0 || ^11.0
- pestphp/pest: ^3.0 || ^4.0
- phpstan/phpstan: ^2.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
A guard that verifies People's tokens, the SDK wired into the container, a command that keeps identities current, and a fake for your tests.
Built on oki-technologies/people-php — read its README for what People
is and what each call checks.
composer require oki-technologies/people-laravel php artisan vendor:publish --tag=people-config
PEOPLE_ISSUER=https://people.example.com PEOPLE_CLIENT_ID=your-client-id PEOPLE_CLIENT_SECRET=your-secret # server-side applications only PEOPLE_AUDIENCE=api.example.com # defaults to APP_URL's hostname
Protecting an API
// config/auth.php 'guards' => [ 'people' => ['driver' => 'people'], ],
Route::middleware('auth:people')->get('/orders', function (Request $request) { $request->user()->getAuthIdentifier(); // People's user id Auth::guard('people')->token()->can('orders.read'); });
Every request is verified locally — no session, no call to People. By default the user
is a PeopleUser carrying the verified token. To use your own model:
PeopleGuard::resolveUsersUsing(fn (AccessToken $token) => User::firstWhere('people_id', $token->subject));
Returning null turns the request away. A token proves who is calling, never what they may do: decide that from your own records.
Signing somebody in
$request = app(People::class)->authorization()->begin(route('people.callback')); session(['people' => $request]); return redirect($request->url);
$tokens = app(People::class)->authorization()->exchange($request->query(), session()->pull('people'));
Keeping identities current
Implement HandlesIdentityEvents, keeping the cursor somewhere durable:
final class ApplyIdentityEvents implements HandlesIdentityEvents { public function cursor(): int { return (int) Setting::get('people.cursor', 0); } public function handle(IdentityEvent $event): void { match ($event->type) { IdentityEvent::UPDATED => User::where('people_id', $event->subject)->update(['name' => $event->payload['name'] ?? null]), IdentityEvent::DELETED => User::where('people_id', $event->subject)->delete(), default => null, }; } public function advance(int $sequence): void { Setting::set('people.cursor', $sequence); } }
// config/people.php 'events' => ['handler' => ApplyIdentityEvents::class],
Schedule::command('people:sync')->everyMinute()->withoutOverlapping();
You hear only about people who signed in to your application. Applying an event must be safe to repeat.
Testing
$people = FakePeople::install(); $this->withToken($people->token('user-1', ['scopes' => ['orders.read']])) ->getJson('/orders') ->assertOk();
Nothing goes over the network, and Http::fake() never sees People's calls.
Licence
MIT.