Skip to content

Commit a697243

Browse files
authored
Merge pull request #114 from phenixphp/feature/improve-guest-middleware
Feature/improve guest middleware
2 parents 4aa5081 + 90133a2 commit a697243

6 files changed

Lines changed: 315 additions & 21 deletions

File tree

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
# Phenix framework
22

33
[![run-tests](https://github.com/phenixphp/framework/actions/workflows/run-tests.yml/badge.svg)](https://github.com/phenixphp/framework/actions/workflows/run-tests.yml)
4-
<a href="https://packagist.org/packages/phenixphp/framework"><img src="https://img.shields.io/packagist/v/phenixphp/phenix" alt="Latest Stable Version"></a>
5-
<a href="https://packagist.org/packages/phenixphp/framework"><img src="https://img.shields.io/packagist/l/phenixphp/phenix" alt="License"></a>
4+
<a href="https://packagist.org/packages/phenixphp/framework"><img src="https://img.shields.io/packagist/v/phenixphp/framework" alt="Latest Stable Version"></a>
5+
<a href="https://packagist.org/packages/phenixphp/framework"><img src="https://img.shields.io/packagist/l/phenixphp/framework" alt="License"></a>
66

77
Phenix is a web framework built on pure PHP, without external extensions, based on the [Amphp](https://amphp.org/) ecosystem, which provides non-blocking operations, asynchronism and parallel code execution natively. It runs in the PHP SAPI CLI and on its own server, it is simply powerful.
88

src/Auth/Middlewares/Authenticated.php

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use Phenix\Auth\AuthenticationManager;
1313
use Phenix\Auth\Events\FailedTokenValidation;
1414
use Phenix\Auth\Events\TokenValidated;
15+
use Phenix\Auth\Middlewares\Concerns\InteractsWithBearerTokens;
1516
use Phenix\Auth\User;
1617
use Phenix\Facades\Config;
1718
use Phenix\Facades\Event;
@@ -21,15 +22,17 @@
2122

2223
class Authenticated implements Middleware
2324
{
25+
use InteractsWithBearerTokens;
26+
2427
public function handleRequest(Request $request, RequestHandler $next): Response
2528
{
2629
$authorizationHeader = $request->getHeader('Authorization');
2730

28-
if (! $this->hasToken($authorizationHeader)) {
31+
if (! $this->hasBearerScheme($authorizationHeader)) {
2932
return $this->unauthorized();
3033
}
3134

32-
$token = $this->extractToken($authorizationHeader);
35+
$token = $this->extractBearerToken($authorizationHeader);
3336

3437
/** @var AuthenticationManager $auth */
3538
$auth = App::make(AuthenticationManager::class);
@@ -63,20 +66,6 @@ public function handleRequest(Request $request, RequestHandler $next): Response
6366
return $next->handleRequest($request);
6467
}
6568

66-
protected function hasToken(string|null $token): bool
67-
{
68-
return $token !== null
69-
&& trim($token) !== ''
70-
&& str_starts_with($token, 'Bearer ');
71-
}
72-
73-
protected function extractToken(string $authorizationHeader): string|null
74-
{
75-
$parts = explode(' ', $authorizationHeader, 2);
76-
77-
return isset($parts[1]) ? trim($parts[1]) : null;
78-
}
79-
8069
protected function unauthorized(): Response
8170
{
8271
return response()->json([
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Phenix\Auth\Middlewares\Concerns;
6+
7+
trait InteractsWithBearerTokens
8+
{
9+
protected function hasBearerScheme(string|null $authorizationHeader): bool
10+
{
11+
if ($authorizationHeader === null) {
12+
return false;
13+
}
14+
15+
$authorizationHeader = trim($authorizationHeader);
16+
17+
if ($authorizationHeader === '') {
18+
return false;
19+
}
20+
21+
return preg_match('/^Bearer(?:\s+.*)?$/i', $authorizationHeader) === 1;
22+
}
23+
24+
protected function extractBearerToken(string|null $authorizationHeader): string|null
25+
{
26+
if (! $this->hasBearerScheme($authorizationHeader)) {
27+
return null;
28+
}
29+
30+
$authorizationHeader = trim((string) $authorizationHeader);
31+
32+
if (preg_match('/^Bearer\s+([A-Za-z0-9._~+\\/-]+=*)$/i', $authorizationHeader, $matches) !== 1) {
33+
return null;
34+
}
35+
36+
return trim($matches[1]) !== '' ? $matches[1] : null;
37+
}
38+
}

src/Auth/Middlewares/Guest.php

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Phenix\Auth\Middlewares;
6+
7+
use Amp\Http\Server\Middleware;
8+
use Amp\Http\Server\Request;
9+
use Amp\Http\Server\RequestHandler;
10+
use Amp\Http\Server\Response;
11+
use Phenix\App;
12+
use Phenix\Auth\AuthenticationManager;
13+
use Phenix\Auth\Middlewares\Concerns\InteractsWithBearerTokens;
14+
use Phenix\Http\Constants\HttpStatus;
15+
16+
class Guest implements Middleware
17+
{
18+
use InteractsWithBearerTokens;
19+
20+
public function handleRequest(Request $request, RequestHandler $next): Response
21+
{
22+
$header = $request->getHeader('Authorization');
23+
$token = $this->hasBearerScheme($header) ? $this->extractBearerToken($header) : null;
24+
25+
if ($token === null) {
26+
return $next->handleRequest($request);
27+
}
28+
29+
/** @var AuthenticationManager $auth */
30+
$auth = App::make(AuthenticationManager::class);
31+
32+
if ($auth->validate($token)) {
33+
return $this->unauthorized();
34+
}
35+
36+
return $next->handleRequest($request);
37+
}
38+
39+
protected function unauthorized(): Response
40+
{
41+
return response()->json([
42+
'message' => 'Unauthorized',
43+
], HttpStatus::UNAUTHORIZED)->send();
44+
}
45+
}

src/Auth/Middlewares/TokenRateLimit.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,20 @@
1010
use Amp\Http\Server\Response;
1111
use Phenix\App;
1212
use Phenix\Auth\AuthenticationManager;
13+
use Phenix\Auth\Middlewares\Concerns\InteractsWithBearerTokens;
1314
use Phenix\Facades\Config;
1415
use Phenix\Http\Constants\HttpStatus;
1516
use Phenix\Http\Ip;
1617

17-
use function str_starts_with;
18-
1918
class TokenRateLimit implements Middleware
2019
{
20+
use InteractsWithBearerTokens;
21+
2122
public function handleRequest(Request $request, RequestHandler $next): Response
2223
{
2324
$authorizationHeader = $request->getHeader('Authorization');
2425

25-
if ($authorizationHeader === null || ! str_starts_with($authorizationHeader, 'Bearer ')) {
26+
if (! $this->hasBearerScheme($authorizationHeader)) {
2627
return $next->handleRequest($request);
2728
}
2829

0 commit comments

Comments
 (0)