Files
alrahma_sunday_school_api/tests/Feature/Api/V1/FullSurface/ApiRateLimitTokenAndSessionHardeningContractTest.php
T
2026-06-11 11:46:12 -04:00

87 lines
3.3 KiB
PHP

<?php
namespace Tests\Feature\Api\V1\FullSurface;
use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase;
class ApiRateLimitTokenAndSessionHardeningContractTest extends FullSurfaceE2EContractCase
{
public function test_repeated_bad_login_attempts_stay_controlled_and_never_return_a_token(): void
{
$paths = array_values(array_unique(array_filter([
'/api/v1/auth/login',
'/api/login',
'/api/v1/login',
], fn (string $path): bool => $this->routeExistsFor('POST', $path))));
$this->assertNotEmpty($paths, 'At least one API login route should exist for brute-force hardening coverage.');
foreach ($paths as $path) {
for ($attempt = 1; $attempt <= 8; $attempt++) {
$response = $this->postJson($path, [
'email' => $this->admin->email,
'password' => 'definitely-not-the-password-'.$attempt,
]);
$this->assertStatusIn($response, [400, 401, 403, 419, 422, 429], "Bad login attempt {$attempt} for {$path}");
$response->assertJsonMissingPath('token');
$response->assertJsonMissingPath('access_token');
$response->assertJsonMissingPath('user.remember_token');
}
}
}
public function test_expired_or_garbage_bearer_tokens_are_rejected_without_fallback_identity(): void
{
foreach ($this->apiRoutesContainingAny(['auth/me', 'dashboard', 'profile', 'settings', 'finance', 'inventory']) as $route) {
$method = $this->primaryMethod($route);
$uri = $route->uri();
if ($method !== 'GET') {
continue;
}
$response = $this->withHeader('Authorization', 'Bearer garbage-token-that-must-not-authenticate')
->json('GET', $this->materializePath($uri));
$this->assertStatusIn($response, [401, 403, 404, 419], "Garbage bearer token should not authenticate {$uri}");
}
}
public function test_logout_is_safe_to_repeat_and_does_not_leak_token_state(): void
{
$logoutRoutes = $this->apiRoutesContainingAny(['logout']);
$this->assertNotEmpty($logoutRoutes, 'Logout routes should be covered by repeat/logout contract tests.');
foreach ($logoutRoutes as $route) {
if ($this->primaryMethod($route) !== 'POST') {
continue;
}
$path = $this->materializePath($route->uri());
$first = $this->requestAs($this->admin, 'POST', $route->uri());
$this->assertControlled($first, 'POST', $route->uri());
$first->assertJsonMissingPath('token');
$first->assertJsonMissingPath('access_token');
$second = $this->postJson($path);
$this->assertStatusIn($second, [200, 204, 401, 403, 419], 'Repeated logout should stay controlled.');
$second->assertJsonMissingPath('token');
$second->assertJsonMissingPath('access_token');
}
}
private function routeExistsFor(string $method, string $path): bool
{
foreach ($this->apiRoutes() as $route) {
if ($this->primaryMethod($route) === $method && '/'.ltrim($route->uri(), '/') === $path) {
return true;
}
}
return false;
}
}