Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Phenix framework

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

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.

Expand Down
21 changes: 5 additions & 16 deletions src/Auth/Middlewares/Authenticated.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use Phenix\Auth\AuthenticationManager;
use Phenix\Auth\Events\FailedTokenValidation;
use Phenix\Auth\Events\TokenValidated;
use Phenix\Auth\Middlewares\Concerns\InteractsWithBearerTokens;
use Phenix\Auth\User;
use Phenix\Facades\Config;
use Phenix\Facades\Event;
Expand All @@ -21,15 +22,17 @@

class Authenticated implements Middleware
{
use InteractsWithBearerTokens;

public function handleRequest(Request $request, RequestHandler $next): Response
{
$authorizationHeader = $request->getHeader('Authorization');

if (! $this->hasToken($authorizationHeader)) {
if (! $this->hasBearerScheme($authorizationHeader)) {
return $this->unauthorized();
}

$token = $this->extractToken($authorizationHeader);
$token = $this->extractBearerToken($authorizationHeader);

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

protected function hasToken(string|null $token): bool
{
return $token !== null
&& trim($token) !== ''
&& str_starts_with($token, 'Bearer ');
}

protected function extractToken(string $authorizationHeader): string|null
{
$parts = explode(' ', $authorizationHeader, 2);

return isset($parts[1]) ? trim($parts[1]) : null;
}

protected function unauthorized(): Response
{
return response()->json([
Expand Down
38 changes: 38 additions & 0 deletions src/Auth/Middlewares/Concerns/InteractsWithBearerTokens.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

declare(strict_types=1);

namespace Phenix\Auth\Middlewares\Concerns;

trait InteractsWithBearerTokens
{
protected function hasBearerScheme(string|null $authorizationHeader): bool
{
if ($authorizationHeader === null) {
return false;
}

$authorizationHeader = trim($authorizationHeader);

if ($authorizationHeader === '') {
return false;
}

return preg_match('/^Bearer(?:\s+.*)?$/i', $authorizationHeader) === 1;
}

protected function extractBearerToken(string|null $authorizationHeader): string|null
{
if (! $this->hasBearerScheme($authorizationHeader)) {
return null;
}

$authorizationHeader = trim((string) $authorizationHeader);

if (preg_match('/^Bearer\s+([A-Za-z0-9._~+\\/-]+=*)$/i', $authorizationHeader, $matches) !== 1) {

Check warning on line 32 in src/Auth/Middlewares/Concerns/InteractsWithBearerTokens.php

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove duplicates in this character class.

See more on https://sonarcloud.io/project/issues?id=phenixphp_framework&issues=AZ0mCEXphkAMMveQyN9K&open=AZ0mCEXphkAMMveQyN9K&pullRequest=115
return null;
}

return trim($matches[1]) !== '' ? $matches[1] : null;
}
}
45 changes: 45 additions & 0 deletions src/Auth/Middlewares/Guest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php

declare(strict_types=1);

namespace Phenix\Auth\Middlewares;

use Amp\Http\Server\Middleware;
use Amp\Http\Server\Request;
use Amp\Http\Server\RequestHandler;
use Amp\Http\Server\Response;
use Phenix\App;
use Phenix\Auth\AuthenticationManager;
use Phenix\Auth\Middlewares\Concerns\InteractsWithBearerTokens;
use Phenix\Http\Constants\HttpStatus;

class Guest implements Middleware
{
use InteractsWithBearerTokens;

public function handleRequest(Request $request, RequestHandler $next): Response
{
$header = $request->getHeader('Authorization');
$token = $this->hasBearerScheme($header) ? $this->extractBearerToken($header) : null;

if ($token === null) {
return $next->handleRequest($request);
}

/** @var AuthenticationManager $auth */
$auth = App::make(AuthenticationManager::class);

if ($auth->validate($token)) {
return $this->unauthorized();
}

return $next->handleRequest($request);
}

protected function unauthorized(): Response
{
return response()->json([
'message' => 'Unauthorized',
], HttpStatus::UNAUTHORIZED)->send();
}
}
7 changes: 4 additions & 3 deletions src/Auth/Middlewares/TokenRateLimit.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,20 @@
use Amp\Http\Server\Response;
use Phenix\App;
use Phenix\Auth\AuthenticationManager;
use Phenix\Auth\Middlewares\Concerns\InteractsWithBearerTokens;
use Phenix\Facades\Config;
use Phenix\Http\Constants\HttpStatus;
use Phenix\Http\Ip;

use function str_starts_with;

class TokenRateLimit implements Middleware
{
use InteractsWithBearerTokens;

public function handleRequest(Request $request, RequestHandler $next): Response
{
$authorizationHeader = $request->getHeader('Authorization');

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

Expand Down
Loading
Loading