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>
This commit is contained in:
2026-09-03 17:35:10 +01:00
co-authored by Claude Sonnet 5
commit 7faef6fbff
23 changed files with 4054 additions and 0 deletions
+56
View File
@@ -0,0 +1,56 @@
<?php
declare(strict_types=1);
namespace App\Auth;
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
/**
* Issues and verifies stateless HS256 JSON Web Tokens for authenticated users.
*/
final class JwtService
{
private const ALGORITHM = 'HS256';
public function __construct(
private readonly string $secret,
private readonly int $ttl,
) {
}
/**
* @param array{id: int, email: string, ...} $user
* @return array{token: string, expires_at: string}
*/
public function issue(array $user): array
{
$issuedAt = time();
$expiresAt = $issuedAt + $this->ttl;
$token = JWT::encode([
'sub' => (int) $user['id'],
'email' => $user['email'],
'iat' => $issuedAt,
'exp' => $expiresAt,
], $this->secret, self::ALGORITHM);
return [
'token' => $token,
'expires_at' => gmdate('c', $expiresAt),
];
}
/**
* @return array<string, mixed> The decoded claims.
*
* @throws \Firebase\JWT\ExpiredException
* @throws \Firebase\JWT\SignatureInvalidException
* @throws \UnexpectedValueException
*/
public function verify(string $token): array
{
return (array) JWT::decode($token, new Key($this->secret, self::ALGORITHM));
}
}