Files
project-manager/src/Auth/AuthMiddleware.php
T
aneurinandClaude Sonnet 5 7faef6fbff Add stage 1: authentication REST API
Slim 4 + SQLite todo-list API providing email/password registration,
login, and an authenticated GET /me endpoint. Stateless HS256 JWTs,
bcrypt password hashing, uniform JSON error envelope, and a SQL
migration runner. Includes PHPUnit feature tests and stage-1 docs.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-03 17:35:10 +01:00

50 lines
1.5 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Auth;
use App\Exception\ApiException;
use App\Repository\UserRepository;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Http\Server\MiddlewareInterface;
use Psr\Http\Server\RequestHandlerInterface as RequestHandler;
use Throwable;
/**
* Requires a valid `Authorization: Bearer <jwt>` header. On success the resolved
* user row is attached to the request as the `user` attribute.
*/
final class AuthMiddleware implements MiddlewareInterface
{
public function __construct(
private readonly JwtService $jwt,
private readonly UserRepository $users,
) {
}
public function process(Request $request, RequestHandler $handler): Response
{
$header = $request->getHeaderLine('Authorization');
if (preg_match('/^Bearer\s+(\S+)$/i', $header, $matches) !== 1) {
throw new ApiException('Missing or malformed Authorization header.', 401);
}
try {
$claims = $this->jwt->verify($matches[1]);
} catch (Throwable) {
throw new ApiException('The access token is invalid or has expired.', 401);
}
$user = $this->users->findById((int) ($claims['sub'] ?? 0));
if ($user === null) {
throw new ApiException('The account for this token no longer exists.', 401);
}
return $handler->handle($request->withAttribute('user', $user));
}
}