first commit

This commit is contained in:
2025-04-27 04:02:46 +02:00
commit 05e8cca347
47 changed files with 2723 additions and 0 deletions

View File

@@ -0,0 +1,27 @@
<?php
namespace Anikeen\Id\Http\Middleware;
use Anikeen\Id\Exceptions\MissingScopeException;
use stdClass;
class CheckClientCredentials extends CheckCredentials
{
/**
* Validate token credentials.
*
* @throws MissingScopeException
*/
protected function validateScopes(stdClass $token, array $scopes): void
{
if (in_array('*', $token->scopes)) {
return;
}
foreach ($scopes as $scope) {
if (!in_array($scope, $token->scopes)) {
throw new MissingScopeException($scopes);
}
}
}
}

View File

@@ -0,0 +1,29 @@
<?php
namespace Anikeen\Id\Http\Middleware;
use Anikeen\Id\Exceptions\MissingScopeException;
use stdClass;
class CheckClientCredentialsForAnyScope extends CheckCredentials
{
/**
* Validate token credentials.
*
* @throws MissingScopeException
*/
protected function validateScopes(stdClass $token, array $scopes): void
{
if (in_array('*', $token->scopes)) {
return;
}
foreach ($scopes as $scope) {
if (in_array($scope, $token->scopes)) {
return;
}
}
throw new MissingScopeException($scopes);
}
}

View File

@@ -0,0 +1,40 @@
<?php
namespace Anikeen\Id\Http\Middleware;
use Anikeen\Id\Exceptions\MissingScopeException;
use Anikeen\Id\Helpers\JwtParser;
use Closure;
use Illuminate\Auth\AuthenticationException;
use Illuminate\Http\Request;
use stdClass;
abstract class CheckCredentials
{
/**
* Handle an incoming request.
*
* @throws AuthenticationException|MissingScopeException
*/
public function handle(Request $request, Closure $next, ...$scopes): mixed
{
$decoded = $this->getJwtParser()->decode($request);
$request->attributes->set('oauth_access_token_id', $decoded->jti);
$request->attributes->set('oauth_client_id', $decoded->aud);
//$request->attributes->set('oauth_client_trusted', $decoded->client->trusted);
$request->attributes->set('oauth_user_id', $decoded->sub);
$request->attributes->set('oauth_scopes', $decoded->scopes);
$this->validateScopes($decoded, $scopes);
return $next($request);
}
private function getJwtParser(): JwtParser
{
return app(JwtParser::class);
}
abstract protected function validateScopes(stdClass $token, array $scopes);
}

View File

@@ -0,0 +1,32 @@
<?php
namespace Anikeen\Id\Http\Middleware;
use Anikeen\Id\Exceptions\MissingScopeException;
use Closure;
use Illuminate\Auth\AuthenticationException;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
class CheckForAnyScope
{
/**
* Handle the incoming request.
*
* @throws AuthenticationException|MissingScopeException
*/
public function handle(Request $request, Closure $next, ...$scopes): Response
{
if (!$request->user() || !$request->user()->anikeenToken()) {
throw new AuthenticationException;
}
foreach ($scopes as $scope) {
if ($request->user()->anikeenTokenCan($scope)) {
return $next($request);
}
}
throw new MissingScopeException($scopes);
}
}

View File

@@ -0,0 +1,32 @@
<?php
namespace Anikeen\Id\Http\Middleware;
use Anikeen\Id\Exceptions\MissingScopeException;
use Closure;
use Illuminate\Auth\AuthenticationException;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
class CheckScopes
{
/**
* Handle the incoming request.
*
* @throws AuthenticationException|MissingScopeException
*/
public function handle(Request $request, Closure $next, ...$scopes): Response
{
if (!$request->user() || !$request->user()->anikeenToken()) {
throw new AuthenticationException;
}
foreach ($scopes as $scope) {
if (!$request->user()->anikeenTokenCan($scope)) {
throw new MissingScopeException($scope);
}
}
return $next($request);
}
}

View File

@@ -0,0 +1,87 @@
<?php
namespace Anikeen\Id\Http\Middleware;
use Anikeen\Id\ApiTokenCookieFactory;
use Anikeen\Id\Facades\AnikeenId;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
class CreateFreshApiToken
{
/**
* The authentication guard.
*
* @var string
*/
protected string $guard;
/**
* Create a new middleware instance.
*
* @return void
*/
public function __construct(protected ApiTokenCookieFactory $cookieFactory)
{
//
}
/**
* Handle an incoming request.
*/
public function handle(Request $request, Closure $next, string $guard = null): mixed
{
$this->guard = $guard;
$response = $next($request);
if ($this->shouldReceiveFreshToken($request, $response)) {
$response->withCookie($this->cookieFactory->make(
$request->user($this->guard)->getAuthIdentifier(), $request->session()->token()
));
}
return $response;
}
/**
* Determine if the given request should receive a fresh token.
*/
protected function shouldReceiveFreshToken(Request $request, Response $response): bool
{
return $this->requestShouldReceiveFreshToken($request) &&
$this->responseShouldReceiveFreshToken($response);
}
/**
* Determine if the request should receive a fresh token.
*/
protected function requestShouldReceiveFreshToken(Request $request): bool
{
return $request->isMethod('GET') && $request->user($this->guard);
}
/**
* Determine if the response should receive a fresh token.
*/
protected function responseShouldReceiveFreshToken(Response $response): bool
{
return !$this->alreadyContainsToken($response);
}
/**
* Determine if the given response already contains an API token.
* This avoids us overwriting a just "refreshed" token.
*/
protected function alreadyContainsToken(Response $response): bool
{
foreach ($response->headers->getCookies() as $cookie) {
if ($cookie->getName() === AnikeenId::cookie()) {
return true;
}
}
return false;
}
}