-
-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Better error message + throttling
- Loading branch information
1 parent
d6e478c
commit 86ecd9e
Showing
7 changed files
with
268 additions
and
49 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace App\Exceptions; | ||
|
||
use Illuminate\Http\JsonResponse; | ||
use Illuminate\Http\Request; | ||
use Laravel\Sanctum\Exceptions\MissingAbilityException; | ||
|
||
class CustomMissingAbilityException extends MissingAbilityException | ||
{ | ||
/** | ||
* The abilities that the user does not have. | ||
* | ||
* @var array<int, string> | ||
*/ | ||
protected $abilities; | ||
|
||
/** | ||
* Create a new exception instance. | ||
* | ||
* @param array<int, string>|string $abilities | ||
*/ | ||
public function __construct(array|string $abilities = []) | ||
{ | ||
parent::__construct($abilities); | ||
$this->abilities = is_string($abilities) ? [$abilities] : $abilities; | ||
} | ||
|
||
/** | ||
* Render the exception into an HTTP response. | ||
*/ | ||
public function render(Request $request): JsonResponse | ||
{ | ||
$message = 'Access denied due to insufficient permissions. '; | ||
$message .= 'Required token ability scopes: ' . implode(', ', $this->abilities); | ||
|
||
return response()->json(['message' => $message], 403); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,111 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace App\Http\Middleware; | ||
|
||
use App\Exceptions\CustomMissingAbilityException; | ||
use Closure; | ||
use Illuminate\Auth\AuthenticationException; | ||
use Illuminate\Http\Request; | ||
use InvalidArgumentException; | ||
use Laravel\Sanctum\Http\Middleware\CheckForAnyAbility as SanctumCheckForAnyAbility; | ||
|
||
class CustomCheckForAnyAbility extends SanctumCheckForAnyAbility | ||
{ | ||
/** | ||
* Handle the incoming request. | ||
* | ||
* @param mixed $request | ||
* @param mixed $next | ||
* @param mixed ...$abilities | ||
* | ||
* @throws AuthenticationException | ||
* @throws CustomMissingAbilityException | ||
* @throws InvalidArgumentException | ||
*/ | ||
public function handle($request, $next, ...$abilities): mixed | ||
{ | ||
$this->validateArguments($request, $next); | ||
$this->ensureAuthenticated($request); | ||
|
||
$stringAbilities = $this->validateAndFilterAbilities($abilities); | ||
|
||
if ($this->userHasAnyAbility($request, $stringAbilities)) { | ||
return $next($request); | ||
} | ||
|
||
throw new CustomMissingAbilityException($stringAbilities); | ||
} | ||
|
||
/** | ||
* Validate the incoming arguments. | ||
* | ||
* | ||
* @throws InvalidArgumentException | ||
*/ | ||
private function validateArguments(mixed $request, mixed $next): void | ||
{ | ||
if (! $request instanceof Request) { | ||
throw new InvalidArgumentException('$request must be an instance of Illuminate\Http\Request'); | ||
} | ||
|
||
if (! $next instanceof Closure) { | ||
throw new InvalidArgumentException('$next must be an instance of Closure'); | ||
} | ||
} | ||
|
||
/** | ||
* Ensure the user is authenticated. | ||
* | ||
* | ||
* @throws AuthenticationException | ||
*/ | ||
private function ensureAuthenticated(Request $request): void | ||
{ | ||
if (! $request->user() || ! $request->user()->currentAccessToken()) { | ||
throw new AuthenticationException('Authentication failed. Please log in.'); | ||
} | ||
} | ||
|
||
/** | ||
* Validate and filter the abilities. | ||
* | ||
* @param array<int|string, mixed> $abilities | ||
* @return array<int, string> | ||
* | ||
* @throws InvalidArgumentException | ||
*/ | ||
private function validateAndFilterAbilities(array $abilities): array | ||
{ | ||
$stringAbilities = array_values(array_filter($abilities, 'is_string')); | ||
|
||
if (count($stringAbilities) !== count($abilities)) { | ||
throw new InvalidArgumentException('All abilities must be strings'); | ||
} | ||
|
||
return $stringAbilities; | ||
} | ||
|
||
/** | ||
* Check if the user has any of the required abilities. | ||
* | ||
* @param array<int, string> $abilities | ||
*/ | ||
private function userHasAnyAbility(Request $request, array $abilities): bool | ||
{ | ||
$user = $request->user(); | ||
|
||
if (! $user) { | ||
return false; | ||
} | ||
|
||
foreach ($abilities as $ability) { | ||
if ($user->tokenCan($ability)) { | ||
return true; | ||
} | ||
} | ||
|
||
return false; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters