Files
landing-api/app/Services/Pireon/AccessToken.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

87 lines
2.6 KiB
PHP

<?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,
];
}
}