Lines 76.74% 33 / 43
Methods 83.33% 5 / 6
Classes 0.00% 0 / 1
Covered by tests of size
Name Lines Methods CRAP
 __construct 100.00% 1 / 1 100.00% 1 / 1 1
 setTokenValidator 100.00% 1 / 1 100.00% 1 / 1 1
 authenticate 58.33% 14 / 24 0.00% 0 / 1 14.86
 isEnabled 100.00% 2 / 2 100.00% 1 / 1 4
 getConfigString 100.00% 2 / 2 100.00% 1 / 1 2
 toPsr7Request 100.00% 13 / 13 100.00% 1 / 1 3
29final class ResourceServer
30{
31    /** @var callable(Request): ?int|null */
32    private $tokenValidator = null;
33
34    public function __construct(
35        private readonly Configuration $configuration,
36    ) {
37    }
38
39    /**
40     * Registers OAuth2 token validation logic.
41     *
42     * @param callable(Request): ?int $validator
43     */
44    public function setTokenValidator(callable $validator): void
45    {
46        $this->tokenValidator = $validator;
47    }
48
49    /**
50     * Authenticates bearer tokens and returns the authenticated user ID if available.
51     */
52    public function authenticate(Request $request): ?int
53    {
54        $authorizationHeader = trim((string) $request->headers->get('Authorization', ''));
55        if (!str_starts_with($authorizationHeader, 'Bearer ')) {
56            return null;
57        }
58
59        if ($this->tokenValidator !== null) {
60            return ($this->tokenValidator)($request);
61        }
62
63        if (!class_exists(\League\OAuth2\Server\ResourceServer::class)) {
64            return null;
65        }
66
67        if (!$this->isEnabled()) {
68            return null;
69        }
70
71        $publicKeyPath = $this->getConfigString('oauth2.publicKeyPath');
72        if ($publicKeyPath === '') {
73            return null;
74        }
75
76        try {
77            $psrRequest = $this->toPsr7Request($request);
78            $server = new LeagueResourceServer(
79                new AccessTokenRepository($this->configuration),
80                new CryptKey($publicKeyPath),
81            );
82            $validatedRequest = $server->validateAuthenticatedRequest($psrRequest);
83            $userId = $validatedRequest->getAttribute('oauth_user_id');
84
85            if ($userId === null || $userId === '') {
86                return null;
87            }
88
89            return (int) $userId;
90        } catch (\Throwable) {
91            return null;
92        }
93    }
94
95    private function isEnabled(): bool
96    {
97        $value = $this->configuration->get('oauth2.enable');
98        return $value === true || $value === 'true' || $value === 1 || $value === '1';
99    }
100
101    private function getConfigString(string $key): string
102    {
103        $value = $this->configuration->get($key);
104        return is_string($value) ? trim($value) : '';
105    }
106
107    private function toPsr7Request(Request $request): Psr7ServerRequest
108    {
109        $headers = [];
110        foreach ($request->headers->all() as $name => $values) {
111            $headers[$name] = implode(', ', $values);
112        }
113
114        $psrRequest = new Psr7ServerRequest(
115            $request->getMethod(),
116            $request->getUri(),
117            $headers,
118            $request->getContent(),
119        );
120
121        $parsedBody = $request->request->all();
122        if ($parsedBody !== []) {
123            $psrRequest = $psrRequest->withParsedBody($parsedBody);
124        }
125
126        return $psrRequest->withQueryParams($request->query->all());
127    }
128}