Test Failed
Pull Request — master (#14)
by Beau
13:54
created

AuthZeroAuthenticatingHttpClient::stream()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 2
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
1
<?php
2
3
namespace Superbrave\AuthZeroHttpClient;
4
5
use Symfony\Component\Cache\Adapter\ArrayAdapter;
6
use Symfony\Component\HttpClient\DecoratorTrait;
7
use Symfony\Contracts\Cache\CacheInterface;
8
use Symfony\Contracts\Cache\ItemInterface;
9
use Symfony\Contracts\HttpClient\HttpClientInterface;
10
use Symfony\Contracts\HttpClient\ResponseInterface;
11
12
/**
13
 * Handles authentication with Auth0 before actual requests are made.
14
 *
15
 * @author Niels Nijens <[email protected]>
16
 */
17
class AuthZeroAuthenticatingHttpClient implements HttpClientInterface
18
{
19
    use DecoratorTrait;
20
21
    public function __construct(
22
        private HttpClientInterface $client,
23
        private readonly AuthZeroConfiguration $authZeroConfiguration,
24
        private ArrayAdapter|CacheInterface|null $accessTokenCache = null,
25
    ) {
26
        if ($this->accessTokenCache === null) {
27
            $this->accessTokenCache = new ArrayAdapter();
28
        }
29
    }
30
31
    /**
32
     * Requests a JSON Web Token at Auth0 before executing the requested request.
33
     *
34
     * {@inheritdoc}
35
     */
36
    public function request(string $method, string $url, array $options = []): ResponseInterface
37 6
    {
38
        $this->appendAuthBearerToRequestOptions($options);
39
40
        return $this->client->request($method, $url, $options);
41
    }
42 6
43 6
    /**
44
     * Appends the 'auth_bearer' option with the retrieved access token from Auth0.
45 6
     */
46 6
    private function appendAuthBearerToRequestOptions(array &$options): void
47
    {
48
        if (isset($options['auth_bearer'])) {
49 6
            return;
50 6
        }
51
52
        $accessToken = $this->getAccessTokenFromCache();
53
        if ($accessToken instanceof AccessToken) {
54
            $options['auth_bearer'] = $accessToken->getToken();
55
        }
56
    }
57 5
58
    /**
59 5
     * Returns an access token from the cache by the configured audience in the AuthZeroConfiguration.
60
     */
61 5
    private function getAccessTokenFromCache(): ?AccessToken
62
    {
63
        // Replace invalid cache key characters with an underscore.
64
        $cacheKey = preg_replace('#[\{\}\(\)\/\\\@:]+#', '_', $this->authZeroConfiguration->getAudience());
65
66
        return $this->accessTokenCache->get(
0 ignored issues
show
Bug introduced by
The method get() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

66
        return $this->accessTokenCache->/** @scrutinizer ignore-call */ get(

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
67 1
            $cacheKey,
68
            function (ItemInterface $item) {
69 1
                return $this->getNewAccessTokenForCache($item);
70
            }
71
        );
72
    }
73
74
    /**
75 5
     * Requests and returns a new AccessToken.
76
     *
77 5
     * This method is called by the access token cache on a cache miss.
78 1
     */
79
    private function getNewAccessTokenForCache(ItemInterface $item): ?AccessToken
80
    {
81 4
        $accessToken = $this->requestAccessToken();
82 4
83 2
        if ($accessToken !== null) {
84
            $item->expiresAfter($accessToken->getTtl());
85 4
        }
86
87
        return $accessToken;
88
    }
89
90 4
    /**
91
     * Requests an access token at Auth0.
92
     */
93 4
    private function requestAccessToken(): ?AccessToken
94
    {
95 4
        $response = $this->client->request(
96 4
            'POST',
97
            $this->authZeroConfiguration->getTenantTokenUrl(),
98 4
            [
99 4
                'json' => $this->authZeroConfiguration->getAuthenticationPayload(),
100
            ]
101
        );
102
103
        if ($response->getStatusCode() !== 200) {
104
            return null;
105
        }
106
107
        $responseJson = $response->toArray();
108 4
        if (isset($responseJson['access_token'], $responseJson['expires_in']) === false) {
109
            return null;
110 4
        }
111
112 4
        return new AccessToken($responseJson['access_token'], $responseJson['expires_in']);
113 2
    }
114
}
115