You've already forked Php-Template
Some checks failed
🧪✨ Tests Workflow / 🧪 ✨ Database Migrations (push) Has started running
🧪✨ Tests Workflow / 🛡️ 🔒 License Check (push) Has started running
🧪✨ Tests Workflow / 🐙 🔍 Code Sniffer (push) Has started running
🧪✨ Tests Workflow / 🧪 ✅ Unit Tests (push) Has been cancelled
🧪✨ Tests Workflow / 🛡️ 🔒 Library Audit (push) Has started running
🧪✨ Tests Workflow / 📝 ✨ Code Lint (push) Has started running
256 lines
7.7 KiB
PHP
256 lines
7.7 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Siteworxpro\Tests\Http\Middleware;
|
|
|
|
use DateTimeImmutable;
|
|
use Lcobucci\JWT\JwtFacade;
|
|
use Lcobucci\JWT\Signer\Hmac\Sha256;
|
|
use Lcobucci\JWT\Signer\Key\InMemory;
|
|
use Lcobucci\JWT\Token\Builder;
|
|
use League\Route\Dispatcher;
|
|
use Nyholm\Psr7\Response;
|
|
use Nyholm\Psr7\ServerRequest;
|
|
use Siteworxpro\App\Attributes\Guards\Jwt;
|
|
use Siteworxpro\App\Http\Middleware\JwtMiddleware;
|
|
use Siteworxpro\App\Services\Facades\Config;
|
|
use Siteworxpro\HttpStatus\CodesEnum;
|
|
|
|
class JwtMiddlewareTest extends Middleware
|
|
{
|
|
private const string TEST_SIGNING_KEY = 'test_signing_key_123456444478901234';
|
|
|
|
public function getClass(): object
|
|
{
|
|
return new class {
|
|
public function getCallable(): array
|
|
{
|
|
return [$this, 'index'];
|
|
}
|
|
|
|
#[Jwt]
|
|
public function index()
|
|
{
|
|
// Dummy method for testing
|
|
}
|
|
};
|
|
}
|
|
|
|
protected function setUp(): void
|
|
{
|
|
parent::setUp();
|
|
Config::set('jwt.signing_key', self::TEST_SIGNING_KEY);
|
|
}
|
|
|
|
/**
|
|
* @throws \JsonException
|
|
*/
|
|
public function testIgnoresNoJwtAttribute()
|
|
{
|
|
$class = new class {
|
|
public function getCallable(): array
|
|
{
|
|
return [ $this, 'index' ];
|
|
}
|
|
|
|
public function index()
|
|
{
|
|
// Dummy method for testing
|
|
}
|
|
};
|
|
|
|
$handler = \Mockery::mock(Dispatcher::class);
|
|
$handler->shouldReceive('getMiddlewareStack')
|
|
->andReturn([$class]);
|
|
|
|
$handler
|
|
->shouldReceive('handle')
|
|
->once()
|
|
->andReturn(new Response(200));
|
|
|
|
$request = new ServerRequest('GET', '/');
|
|
$middleware = new JwtMiddleware();
|
|
$response = $middleware->process($request, $handler);
|
|
$this->assertEquals(CodesEnum::OK->value, $response->getStatusCode());
|
|
}
|
|
|
|
/**
|
|
* @throws \JsonException
|
|
*/
|
|
public function testIgnoresJwtAttributeButNoToken()
|
|
{
|
|
$class = $this->getClass();
|
|
|
|
$handler = \Mockery::mock(Dispatcher::class);
|
|
$handler->shouldReceive('getMiddlewareStack')
|
|
->andReturn([$class]);
|
|
|
|
$handler
|
|
->shouldReceive('handle')
|
|
->once()
|
|
->andReturn(new Response(200));
|
|
|
|
$request = new ServerRequest('GET', '/');
|
|
$middleware = new JwtMiddleware();
|
|
$response = $middleware->process($request, $handler);
|
|
$this->assertEquals(CodesEnum::UNAUTHORIZED->value, $response->getStatusCode());
|
|
}
|
|
|
|
/**
|
|
* @throws \JsonException
|
|
*/
|
|
public function testInvalidToken()
|
|
{
|
|
$class = $this->getClass();
|
|
|
|
$handler = \Mockery::mock(Dispatcher::class);
|
|
$handler->shouldReceive('getMiddlewareStack')
|
|
->andReturn([$class]);
|
|
|
|
$handler
|
|
->shouldReceive('handle')
|
|
->once()
|
|
->andReturn(new Response(200));
|
|
|
|
$request = new ServerRequest('GET', '/');
|
|
$request = $request->withHeader('Authorization', 'Bearer ' . 'invalid_token_string');
|
|
$middleware = new JwtMiddleware();
|
|
$response = $middleware->process($request, $handler);
|
|
$this->assertEquals(CodesEnum::UNAUTHORIZED->value, $response->getStatusCode());
|
|
$this->assertStringContainsString(
|
|
'Unauthorized: Invalid token',
|
|
$response->getBody()->getContents()
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @throws \JsonException
|
|
*/
|
|
public function testJwtAttributeWithTokenButWrongAud()
|
|
{
|
|
$class = $this->getClass();
|
|
|
|
$handler = \Mockery::mock(Dispatcher::class);
|
|
$handler->shouldReceive('getMiddlewareStack')
|
|
->andReturn([$class]);
|
|
|
|
$handler
|
|
->shouldReceive('handle')
|
|
->once()
|
|
->andReturn(new Response(200));
|
|
|
|
$request = new ServerRequest('GET', '/');
|
|
$request = $request->withHeader('Authorization', 'Bearer ' . $this->getJwt());
|
|
$middleware = new JwtMiddleware();
|
|
$response = $middleware->process($request, $handler);
|
|
$this->assertEquals(CodesEnum::UNAUTHORIZED->value, $response->getStatusCode());
|
|
$this->assertStringContainsString(
|
|
'The token is not allowed to be used by this audience',
|
|
$response->getBody()->getContents()
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @throws \JsonException
|
|
*/
|
|
public function testJwtAttributeWithTokenButWrongIss()
|
|
{
|
|
Config::set('jwt.audience', 'https://client-app.io');
|
|
|
|
$class = $this->getClass();
|
|
|
|
$handler = \Mockery::mock(Dispatcher::class);
|
|
$handler->shouldReceive('getMiddlewareStack')
|
|
->andReturn([$class]);
|
|
|
|
$handler
|
|
->shouldReceive('handle')
|
|
->once()
|
|
->andReturn(new Response(200));
|
|
|
|
$request = new ServerRequest('GET', '/');
|
|
$request = $request->withHeader('Authorization', 'Bearer ' . $this->getJwt());
|
|
$middleware = new JwtMiddleware();
|
|
$response = $middleware->process($request, $handler);
|
|
$this->assertEquals(CodesEnum::UNAUTHORIZED->value, $response->getStatusCode());
|
|
$this->assertStringContainsString(
|
|
'The token was not issued by the given issuers',
|
|
$response->getBody()->getContents()
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @throws \JsonException
|
|
*/
|
|
public function testJwtAttributeWithTokenWithDiffIssuer()
|
|
{
|
|
Config::set('jwt.audience', 'https://client-app.io');
|
|
Config::set('jwt.issuer', 'https://different-issuer.io');
|
|
|
|
$class = $this->getClass();
|
|
|
|
$handler = \Mockery::mock(Dispatcher::class);
|
|
$handler->shouldReceive('getMiddlewareStack')
|
|
->andReturn([$class]);
|
|
|
|
$handler
|
|
->shouldReceive('handle')
|
|
->once()
|
|
->andReturn(new Response(200));
|
|
|
|
$request = new ServerRequest('GET', '/');
|
|
$request = $request->withHeader('Authorization', 'Bearer ' . $this->getJwt());
|
|
$middleware = new JwtMiddleware();
|
|
$response = $middleware->process($request, $handler);
|
|
$this->assertEquals(CodesEnum::UNAUTHORIZED->value, $response->getStatusCode());
|
|
$this->assertStringContainsString(
|
|
'The token was not issued by the given issuers',
|
|
$response->getBody()->getContents()
|
|
);
|
|
}
|
|
|
|
public function testJwtAttributeWithToken()
|
|
{
|
|
Config::set('jwt.audience', 'https://client-app.io');
|
|
Config::set('jwt.issuer', 'https://api.my-awesome-app.io');
|
|
|
|
$class = $this->getClass();
|
|
|
|
$handler = \Mockery::mock(Dispatcher::class);
|
|
$handler->shouldReceive('getMiddlewareStack')
|
|
->andReturn([$class]);
|
|
|
|
$handler
|
|
->shouldReceive('handle')
|
|
->once()
|
|
->andReturn(new Response(200));
|
|
|
|
$request = new ServerRequest('GET', '/');
|
|
$request = $request->withHeader('Authorization', 'Bearer ' . $this->getJwt());
|
|
$middleware = new JwtMiddleware();
|
|
$response = $middleware->process($request, $handler);
|
|
$this->assertEquals(CodesEnum::OK->value, $response->getStatusCode());
|
|
}
|
|
|
|
private function getJwt(): string
|
|
{
|
|
$key = InMemory::plainText(self::TEST_SIGNING_KEY);
|
|
$signer = new Sha256();
|
|
|
|
$token = new JwtFacade()->issue(
|
|
$signer,
|
|
$key,
|
|
static fn (
|
|
Builder $builder,
|
|
DateTimeImmutable $issuedAt
|
|
): Builder => $builder
|
|
->issuedBy('https://api.my-awesome-app.io')
|
|
->permittedFor('https://client-app.io')
|
|
->expiresAt($issuedAt->modify('+10 minutes'))
|
|
);
|
|
|
|
return $token->toString();
|
|
}
|
|
}
|