Skip to content

Instantly share code, notes, and snippets.

@ollieread
Created March 3, 2026 19:42
Show Gist options
  • Select an option

  • Save ollieread/370172775d185e1fe037b3d78d49c0d5 to your computer and use it in GitHub Desktop.

Select an option

Save ollieread/370172775d185e1fe037b3d78d49c0d5 to your computer and use it in GitHub Desktop.
<?php
declare(strict_types=1);
namespace Modules\Auth\Actions;
use Engine\Auth\AuthTokenStore;
use Engine\Core\Context;
use Exception;
use Modules\Auth\Actions\Results\AuthTokenResult;
final readonly class ListAuthTokens
{
public function __construct(
private AuthTokenStore $store,
private Context $context,
)
{
}
/**
* @return array<\Modules\Auth\Actions\Results\AuthTokenResult>
*
* @throws \Exception
*/
public function __invoke(): array
{
if ($this->context->user === null) {
throw new Exception('User not authenticated');
}
$tokens = $this->store->getForUser($this->context->user->id);
return array_map(fn ($token) => new AuthTokenResult(
$token->id,
$token->device,
$token->timestamps->get('expires_at'),
$token->timestamps->get('last_used_at'),
$token->timestamps->get('revoked_at'),
$token->revokedReason,
), $tokens);
}
}
<?php
declare(strict_types=1);
namespace Modules\Auth\Actions\Query;
final readonly class ListAuthTokensQuery
{
public function __construct(
#[Query('limit', min: 1, max: 100)]
public int $limit = 10,
#[Query('offset', min: 0)]
public int $offset = 0,
)
{
}
}
<?php
declare(strict_types=1);
namespace Modules\Auth\Actions\Results;
use Carbon\CarbonImmutable;
use Engine\Auth\AuthTokenId;
use Engine\Auth\Values\Device;
final readonly class AuthTokenResult
{
public function __construct(
public AuthTokenId $id,
public Device $device,
public ?CarbonImmutable $expiresAt,
public ?CarbonImmutable $lastUsedAt,
public ?CarbonImmutable $revokedAt,
public ?string $revokedReason,
)
{
}
}
<?php
declare(strict_types=1);
namespace Modules\Auth;
use Engine\Actions\Collectors\ActionCollector;
use Engine\Actions\Collectors\ActionResourceBuilder;
use Engine\Auth\AuthToken;
use Engine\Auth\AuthTokenStore;
use Engine\Collectors\Attributes\Collect;
use Engine\Core\OperatingContext;
use Modules\Auth\Actions\ListAuthTokens;
use Modules\Auth\Actions\Query\ListAuthTokensQuery;
use Modules\Auth\Actions\RevokeAuthToken;
use Modules\Auth\Actions\Schemas\RevokeTokenSchema;
final readonly class AuthModule
{
#[Collect(OperatingContext::Account)]
public function collectAccountActions(ActionCollector $actions): void
{
// Login action (POST /api/v1/auth/login)
$actions->action('login', UserLogin::class)
->schema(UserLoginSchema::class)
->unauthenticated()
->write();
// Refresh token action (POST /api/v1/auth/refresh)
$actions->action('refresh', RefreshAuthToken::class)
->schema(RefreshTokenSchema::class)
->unauthenticated()
->write();
// Set up the auth token resource (/api/v1/auth/tokens)
$actions->resource(
'tokens',
AuthToken::class,
AuthTokenStore::class,
function (ActionResourceBuilder $builder) {
// The resource is only available to authenticated users
$builder->authenticated();
// Add the list action (GET /api/v1/auth/tokens)
$builder->list(ListAuthTokens::class)
->query(ListAuthTokensQuery::class);
// Add the delete action (DELETE /api/v1/auth/tokens/:id)
$builder->delete(RevokeAuthToken::class)
->schema(RevokeTokenSchema::class);
}
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment