Skip to content

Commit fd1f5a1

Browse files
committed
feat: add Filters\JWTAuth
1 parent eaa561e commit fd1f5a1

File tree

3 files changed

+151
-0
lines changed

3 files changed

+151
-0
lines changed

src/Config/Registrar.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use CodeIgniter\Shield\Collectors\Auth;
77
use CodeIgniter\Shield\Filters\AuthRates;
88
use CodeIgniter\Shield\Filters\ChainAuth;
9+
use CodeIgniter\Shield\Filters\JWTAuth;
910
use CodeIgniter\Shield\Filters\SessionAuth;
1011
use CodeIgniter\Shield\Filters\TokenAuth;
1112

@@ -20,6 +21,7 @@ public static function Filters(): array
2021
'aliases' => [
2122
'session' => SessionAuth::class,
2223
'tokens' => TokenAuth::class,
24+
'jwt' => JWTAuth::class,
2325
'chain' => ChainAuth::class,
2426
'auth-rates' => AuthRates::class,
2527
],

src/Filters/JWTAuth.php

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
<?php
2+
3+
namespace CodeIgniter\Shield\Filters;
4+
5+
use CodeIgniter\Filters\FilterInterface;
6+
use CodeIgniter\HTTP\RequestInterface;
7+
use CodeIgniter\HTTP\Response;
8+
use CodeIgniter\HTTP\ResponseInterface;
9+
use CodeIgniter\Shield\Authentication\Authenticators\JWT;
10+
use Config\Services;
11+
12+
/**
13+
* JWT Authentication Filter.
14+
*
15+
* JSON Web Token authentication for web applications.
16+
*/
17+
class JWTAuth implements FilterInterface
18+
{
19+
/**
20+
* Do whatever processing this filter needs to do.
21+
* By default it should not return anything during
22+
* normal execution. However, when an abnormal state
23+
* is found, it should return an instance of
24+
* CodeIgniter\HTTP\Response. If it does, script
25+
* execution will end and that Response will be
26+
* sent back to the client, allowing for error pages,
27+
* redirects, etc.
28+
*
29+
* @param array|null $arguments
30+
*
31+
* @return Response|void
32+
*/
33+
public function before(RequestInterface $request, $arguments = null)
34+
{
35+
helper(['auth', 'setting']);
36+
37+
/** @var JWT $authenticator */
38+
$authenticator = auth('jwt')->getAuthenticator();
39+
40+
$result = $authenticator->attempt([
41+
'token' => $request->getHeaderLine(
42+
setting('Auth.authenticatorHeader')['jwt'] ?? 'Authorization'
43+
),
44+
]);
45+
46+
if (! $result->isOK()) {
47+
return Services::response()
48+
->setJSON([
49+
'error' => $result->reason(),
50+
])
51+
->setStatusCode(ResponseInterface::HTTP_UNAUTHORIZED);
52+
}
53+
54+
if (setting('Auth.recordActiveDate')) {
55+
$authenticator->recordActiveDate();
56+
}
57+
}
58+
59+
/**
60+
* We don't have anything to do here.
61+
*
62+
* @param Response|ResponseInterface $response
63+
* @param array|null $arguments
64+
*
65+
* @return void
66+
*/
67+
public function after(RequestInterface $request, ResponseInterface $response, $arguments = null)
68+
{
69+
}
70+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
<?php
2+
3+
namespace Tests\Authentication\Filters;
4+
5+
use CodeIgniter\Config\Factories;
6+
use CodeIgniter\Shield\Authentication\TokenGenerator\JWTGenerator;
7+
use CodeIgniter\Shield\Entities\User;
8+
use CodeIgniter\Shield\Filters\JWTAuth;
9+
use CodeIgniter\Shield\Models\UserModel;
10+
use CodeIgniter\Test\FeatureTestTrait;
11+
use Config\Services;
12+
use Tests\Support\DatabaseTestCase;
13+
14+
/**
15+
* @internal
16+
*/
17+
final class JWTFilterTest extends DatabaseTestCase
18+
{
19+
use FeatureTestTrait;
20+
21+
protected $namespace;
22+
23+
protected function setUp(): void
24+
{
25+
Services::reset(true);
26+
27+
parent::setUp();
28+
29+
$_SESSION = [];
30+
31+
// Register our filter
32+
$filterConfig = \config('Filters');
33+
$filterConfig->aliases['jwtAuth'] = JWTAuth::class;
34+
Factories::injectMock('filters', 'filters', $filterConfig);
35+
36+
// Add a test route that we can visit to trigger.
37+
$routes = \service('routes');
38+
$routes->group('/', ['filter' => 'jwtAuth'], static function ($routes) {
39+
$routes->get('protected-route', static function () {
40+
echo 'Protected';
41+
});
42+
});
43+
$routes->get('open-route', static function () {
44+
echo 'Open';
45+
});
46+
$routes->get('login', 'AuthController::login', ['as' => 'login']);
47+
Services::injectMock('routes', $routes);
48+
}
49+
50+
public function testFilterNotAuthorized()
51+
{
52+
$result = $this->call('get', 'protected-route');
53+
54+
$result->assertStatus(401);
55+
56+
$result = $this->get('open-route');
57+
58+
$result->assertStatus(200);
59+
$result->assertSee('Open');
60+
}
61+
62+
public function testFilterSuccess()
63+
{
64+
/** @var User $user */
65+
$user = \fake(UserModel::class);
66+
67+
$generator = new JWTGenerator();
68+
$token = $generator->generateAccessToken($user);
69+
70+
$result = $this->withHeaders(['Authorization' => 'Bearer ' . $token])
71+
->get('protected-route');
72+
73+
$result->assertStatus(200);
74+
$result->assertSee('Protected');
75+
76+
$this->assertSame($user->id, \auth('jwt')->id());
77+
$this->assertSame($user->id, \auth('jwt')->user()->id);
78+
}
79+
}

0 commit comments

Comments
 (0)