postJson($path, [ 'email' => $this->admin->email, 'password' => 'password', ]); $this->assertNoServerError($response, 'valid login at ' . $path); if ($response->getStatusCode() >= 200 && $response->getStatusCode() < 300) { $this->assertTrue( $response->json('token') !== null || $response->json('access_token') !== null || $response->json('user') !== null, $path . ' must return a usable login contract, not a mystery envelope.' ); } } foreach ($loginRoutes as $path) { $response = $this->postJson($path, [ 'email' => $this->admin->email, 'password' => 'definitely-wrong-password', ]); $this->assertStatusIn($response, [400, 401, 403, 419, 422], 'invalid login at ' . $path); $this->assertNoServerError($response, 'invalid login at ' . $path); } foreach (['/api/v1/auth/me', '/api/v1/me', '/api/user'] as $path) { $response = $this->actingAs($this->admin, 'api')->getJson($path); $this->assertNoServerError($response, 'authenticated identity lookup at ' . $path); if ($response->getStatusCode() === 200) { $this->assertTrue( $response->json('user') !== null || $response->json('id') !== null || $response->json('data') !== null, $path . ' must expose a stable identity payload.' ); } } foreach (['/api/v1/auth/logout', '/api/logout', '/user/logout'] as $path) { $first = $this->actingAs($this->admin, 'api')->postJson($path); $second = $this->actingAs($this->admin, 'api')->postJson($path); $this->assertNoServerError($first, 'first logout at ' . $path); $this->assertNoServerError($second, 'repeat logout at ' . $path); $this->assertStatusIn($first, [200, 202, 204, 302, 401, 404, 419, 422], 'first logout at ' . $path); $this->assertStatusIn($second, [200, 202, 204, 302, 401, 404, 419, 422], 'repeat logout at ' . $path); } } public function test_password_reset_and_registration_like_routes_do_not_leak_account_existence(): void { $routes = $this->apiRoutesContainingAny(['forgot', 'password', 'reset', 'register', 'verify']); foreach ($routes as $route) { $method = $this->primaryMethod($route); if (! in_array($method, ['POST', 'PUT', 'PATCH'], true)) { continue; } $uri = $route->uri(); $response = $this->json($method, $this->materializePath($uri), [ 'email' => 'nobody.' . uniqid() . '@example.test', 'password' => 'password', 'password_confirmation' => 'not-the-same', 'token' => 'not-a-real-token', ]); $this->assertControlled($response, $method, $uri); $this->assertNoServerError($response, $method . ' ' . $uri); $this->assertStringNotContainsStringIgnoringCase('select * from', $response->getContent(), $uri . ' must not leak account lookup SQL.'); $this->assertStringNotContainsStringIgnoringCase('No query results for model', $response->getContent(), $uri . ' must not leak framework model lookups.'); } } }