- Delete old Vite+Svelte frontend - Initialize new SvelteKit project with TypeScript - Configure Tailwind CSS v4 + DaisyUI - Implement JWT authentication with auto-refresh - Create login page with form validation (Zod) - Add protected route guards - Update Docker configuration for single-stage build - Add E2E tests with Playwright (6/11 passing) - Fix Svelte 5 reactivity with $state() runes Known issues: - 5 E2E tests failing (timing/async issues) - Token refresh implementation needs debugging - Validation error display timing
83 lines
2.0 KiB
PHP
83 lines
2.0 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Middleware;
|
|
|
|
use Closure;
|
|
use Illuminate\Http\Request;
|
|
use Symfony\Component\HttpFoundation\Response;
|
|
|
|
class JwtAuth
|
|
{
|
|
public function handle(Request $request, Closure $next): Response
|
|
{
|
|
$token = $this->extractToken($request);
|
|
|
|
if (! $token) {
|
|
return response()->json([
|
|
'message' => 'Authentication required',
|
|
], 401);
|
|
}
|
|
|
|
$payload = $this->decodeJWT($token);
|
|
|
|
if (! $payload) {
|
|
return response()->json([
|
|
'message' => 'Invalid token',
|
|
], 401);
|
|
}
|
|
|
|
if ($payload->exp < time()) {
|
|
return response()->json([
|
|
'message' => 'Token expired',
|
|
], 401);
|
|
}
|
|
|
|
$user = \App\Models\User::find($payload->sub);
|
|
|
|
if (! $user) {
|
|
return response()->json([
|
|
'message' => 'User not found',
|
|
], 401);
|
|
}
|
|
|
|
auth()->setUser($user);
|
|
$request->setUserResolver(fn () => $user);
|
|
|
|
return $next($request);
|
|
}
|
|
|
|
protected function extractToken(Request $request): ?string
|
|
{
|
|
$header = $request->header('Authorization');
|
|
|
|
if (! $header || ! str_starts_with($header, 'Bearer ')) {
|
|
return null;
|
|
}
|
|
|
|
return substr($header, 7);
|
|
}
|
|
|
|
protected function decodeJWT(string $token): ?object
|
|
{
|
|
$parts = explode('.', $token);
|
|
|
|
if (count($parts) !== 3) {
|
|
return null;
|
|
}
|
|
|
|
list($header, $payload, $signature) = $parts;
|
|
|
|
$expectedSignature = hash_hmac('sha256', $header . '.' . $payload, config('app.key'), true);
|
|
$expectedSignature = base64_encode($expectedSignature);
|
|
$expectedSignature = str_replace(['+', '/', '='], ['-', '_', ''], $expectedSignature);
|
|
|
|
if (! hash_equals($expectedSignature, $signature)) {
|
|
return null;
|
|
}
|
|
|
|
$payload = base64_decode(str_replace(['-', '_'], ['+', '/'], $payload));
|
|
|
|
return json_decode($payload);
|
|
}
|
|
}
|