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>
This commit is contained in:
86
app/Services/Pireon/AccessToken.php
Normal file
86
app/Services/Pireon/AccessToken.php
Normal file
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Services\Pireon;
|
||||
|
||||
use Carbon\CarbonImmutable;
|
||||
use Illuminate\Contracts\Support\Arrayable;
|
||||
|
||||
/**
|
||||
* A token set returned by the identity server's /oauth/token endpoint.
|
||||
*
|
||||
* @implements Arrayable<string, mixed>
|
||||
*/
|
||||
final readonly class AccessToken implements Arrayable
|
||||
{
|
||||
public function __construct(
|
||||
public string $accessToken,
|
||||
public ?string $refreshToken,
|
||||
public ?CarbonImmutable $expiresAt,
|
||||
public string $tokenType = 'Bearer',
|
||||
/** @var array<int, string> */
|
||||
public array $scopes = [],
|
||||
) {}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $payload
|
||||
*/
|
||||
public static function fromResponse(array $payload): self
|
||||
{
|
||||
$expiresIn = isset($payload['expires_in']) ? (int) $payload['expires_in'] : null;
|
||||
|
||||
return new self(
|
||||
accessToken: (string) $payload['access_token'],
|
||||
refreshToken: isset($payload['refresh_token']) ? (string) $payload['refresh_token'] : null,
|
||||
// Shave a minute off so we refresh slightly before the server
|
||||
// would start rejecting the token mid-request.
|
||||
expiresAt: $expiresIn !== null ? CarbonImmutable::now()->addSeconds($expiresIn)->subMinute() : null,
|
||||
tokenType: (string) ($payload['token_type'] ?? 'Bearer'),
|
||||
scopes: array_values(array_filter(explode(' ', (string) ($payload['scope'] ?? '')))),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<string, mixed> $state
|
||||
*/
|
||||
public static function fromArray(array $state): self
|
||||
{
|
||||
return new self(
|
||||
accessToken: (string) $state['access_token'],
|
||||
refreshToken: $state['refresh_token'] ?? null,
|
||||
expiresAt: isset($state['expires_at']) ? CarbonImmutable::parse($state['expires_at']) : null,
|
||||
tokenType: (string) ($state['token_type'] ?? 'Bearer'),
|
||||
scopes: $state['scopes'] ?? [],
|
||||
);
|
||||
}
|
||||
|
||||
public function hasExpired(): bool
|
||||
{
|
||||
return $this->expiresAt !== null && $this->expiresAt->isPast();
|
||||
}
|
||||
|
||||
public function isRefreshable(): bool
|
||||
{
|
||||
return $this->refreshToken !== null;
|
||||
}
|
||||
|
||||
public function hasScope(string $scope): bool
|
||||
{
|
||||
return in_array($scope, $this->scopes, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function toArray(): array
|
||||
{
|
||||
return [
|
||||
'access_token' => $this->accessToken,
|
||||
'refresh_token' => $this->refreshToken,
|
||||
'expires_at' => $this->expiresAt?->toIso8601String(),
|
||||
'token_type' => $this->tokenType,
|
||||
'scopes' => $this->scopes,
|
||||
];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user