Implement ALTCHA captcha service with challenge generation and solution verification

- Added AltchaService class for managing ALTCHA captcha challenges and solutions.
- Created CaptchaController to handle API requests for generating challenges.
- Introduced CaptchaGuard for validating captcha solutions on public endpoints.
- Developed unit tests for AltchaService to ensure challenge creation and solution verification functionality.
- Implemented integration tests for the Captcha API endpoint and captcha bypass behavior when disabled.
- Added documentation for the Captcha API in the corresponding markdown file.
This commit is contained in:
hamed
2026-07-10 10:31:59 +03:30
parent 11efed4100
commit 10b0743d9a
43 changed files with 5586 additions and 1554 deletions
+41
View File
@@ -0,0 +1,41 @@
<?php
namespace App\Tests\Shared\Captcha;
use App\Tests\ApiTestCase;
/**
* ALTCHA HTTP surface: public challenge endpoint, and captcha bypass on public
* endpoints while ALTCHA is disabled (the default in the test environment).
*/
class CaptchaFlowTest extends ApiTestCase
{
public function testChallengeEndpointIsPublicAndWellFormed(): void
{
$this->client->request('GET', '/api/v1/altcha/challenge');
self::assertSame(200, $this->responseCode());
$body = json_decode($this->client->getResponse()->getContent(), true);
self::assertSame('SHA-256', $body['algorithm']);
foreach (['challenge', 'salt', 'signature', 'maxnumber'] as $key) {
self::assertArrayHasKey($key, $body);
}
}
public function testPublicEndpointBypassesCaptchaWhenDisabled(): void
{
// ALTCHA_ENABLED is false in test → guard is a no-op, so send-code proceeds
// past the captcha check without an `altcha` field (fails later on validation only).
$this->client->request(
'POST',
'/api/v1/user/send-code',
server: ['CONTENT_TYPE' => 'application/json'],
content: json_encode(['mobile' => '09123456789']),
);
// Not a 422 captcha rejection: either success or a non-captcha error.
$body = json_decode($this->client->getResponse()->getContent(), true) ?? [];
$code = $body['errors'][0]['code'] ?? null;
self::assertNotSame('ERR_CAPTCHA_001', $code);
}
}