<?php
namespace App\OAuth2; use GuzzleHttp\Psr7\ServerRequest; use League\OAuth2\Server\Exception\OAuthServerException; class ResourceServerValidator { private static ?ResourceServerValidator $instance = null; private ?int $userId = null; private ?\Entity\User $user = null; private array $scopes = []; public static function getInstance(): self { if (self::$instance === null) { self::$instance = new self(); } return self::$instance; } public function authenticateRequest(): bool { $request = ServerRequest::fromGlobals(); try { $validated = Server::getInstance()->getResourceServer()->validateAuthenticatedRequest($request); } catch (OAuthServerException $e) { $this->emitUnauthorized($e->getMessage()); } catch (\Throwable $e) { \Log::getInstance()->error('OAuth2 resource-server validation error', [ 'exception' => new \Engine\Logger\Context\StringRaw($e->getMessage()), ]); $this->emitUnauthorized('Invalid access token'); } $userId = $validated->getAttribute('oauth_user_id'); $scopes = $validated->getAttribute('oauth_scopes'); if (!$userId) { $this->emitUnauthorized('Access token is not bound to a user'); } if (!$this->establishIdentity((int)$userId)) { $this->emitUnauthorized('Token user no longer exists'); } $this->scopes = is_array($scopes) ? array_values($scopes) : []; return true; } public function getUserId(): ?int { return $this->userId; } public function getUser(): ?\Entity\User { return $this->user; } public function getScopes(): array { return $this->scopes; } public function tokenHasScope(string $scope): bool { return in_array($scope, $this->scopes, true); } public function establishIdentity(int $idUser): bool { $user = \Entity\User::select($idUser); if (!$user->recordExists() || $user->deleted) { return false; } global $S; $S['auth'] = [ 'username' => $user->username, 'idUser' => $user->getId(), 'idOffice' => $user->idOffice, 'idDept' => $user->idDepartment, 'loginCount' => $user->loginCount, 'thisLogin' => TIMESTAMP, 'lastClickTime' => TIMESTAMP, 'currentClickTime' => TIMESTAMP, 'lastClickDbUpdateTime' => TIMESTAMP, 'usersAccess' => $user->accessSectionsList, 'userOptionsAccess' => $user->accessOptionsList, ]; $S['global']['lang'] = $user->language; $this->userId = $idUser; $this->user = $user; return true; } private function emitUnauthorized(string $description): never { if ('cli' !== \PHP_SAPI) { header_remove(); http_response_code(401); header('WWW-Authenticate: Bearer realm="TINA", error="invalid_token"'); header('Content-Type: application/json'); } echo (string) json_encode([ 'error' => 'invalid_token', 'error_description' => $description, ], JSON_UNESCAPED_SLASHES); exit(0); } } 