Passed
Pull Request — master (#1)
by Niels
01:29
created

appendAuthBearerToRequestOptions()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 16
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 3

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 3
eloc 9
c 2
b 0
f 0
nc 3
nop 1
dl 0
loc 16
ccs 10
cts 10
cp 1
crap 3
rs 9.9666
1
<?php
2
3
namespace Superbrave\AuthZeroHttpClient;
4
5
use Symfony\Component\Cache\Adapter\ArrayAdapter;
6
use Symfony\Contracts\Cache\CacheInterface;
7
use Symfony\Contracts\Cache\ItemInterface;
8
use Symfony\Contracts\HttpClient\HttpClientInterface;
9
use Symfony\Contracts\HttpClient\ResponseInterface;
10
use Symfony\Contracts\HttpClient\ResponseStreamInterface;
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
    /**
20
     * @var HttpClientInterface
21
     */
22
    private $client;
23
24
    /**
25
     * @var AuthZeroConfiguration
26
     */
27
    private $authZeroConfiguration;
28
29
    /**
30
     * @var CacheInterface
31
     */
32
    private $accessTokenCache;
33
34
    /**
35
     * Constructs a new AuthZeroAuthenticatingHttpClient instance.
36
     *
37
     * @param HttpClientInterface   $client
38
     * @param AuthZeroConfiguration $authZeroConfiguration
39
     * @param CacheInterface|null   $accessTokenCache
40
     */
41 6
    public function __construct(
42
        HttpClientInterface $client,
43
        AuthZeroConfiguration $authZeroConfiguration,
44
        CacheInterface $accessTokenCache = null
45
    ) {
46 6
        $this->client = $client;
47 6
        $this->authZeroConfiguration = $authZeroConfiguration;
48
49 6
        if ($accessTokenCache === null) {
50 6
            $accessTokenCache = new ArrayAdapter();
51
        }
52
53 6
        $this->accessTokenCache = $accessTokenCache;
54 6
    }
55
56
    /**
57
     * Requests a JSON Web Token at Auth0 before executing the requested request.
58
     *
59
     * {@inheritdoc}
60
     */
61 5
    public function request(string $method, string $url, array $options = array()): ResponseInterface
62
    {
63 5
        $this->appendAuthBearerToRequestOptions($options);
64
65 5
        return $this->client->request($method, $url, $options);
66
    }
67
68
    /**
69
     * {@inheritdoc}
70
     */
71 1
    public function stream($responses, float $timeout = null): ResponseStreamInterface
72
    {
73 1
        return $this->client->stream($responses, $timeout);
74
    }
75
76
    /**
77
     * Appends the 'auth_bearer' option with the retrieved access token from Auth0.
78
     *
79
     * @param array $options
80
     */
81 5
    private function appendAuthBearerToRequestOptions(array &$options): void
82
    {
83 5
        if (isset($options['auth_bearer'])) {
84 1
            return;
85
        }
86
87 4
        $cacheKey = preg_replace('#[\{\}\(\)\/\\\@:]+#', '_', $this->authZeroConfiguration->getAudience());
88 4
        $accessToken = $this->accessTokenCache->get(
89 4
            $cacheKey,
90
            function (ItemInterface $item) {
91 4
                return $this->getAccessTokenCache($item);
92 4
            }
93
        );
94
95 4
        if ($accessToken instanceof AccessToken) {
96 2
            $options['auth_bearer'] = $accessToken->getToken();
97
        }
98 4
    }
99
100
    /**
101
     * @param ItemInterface $item
102
     *
103
     * @return AccessToken|null
104
     */
105 4
    private function getAccessTokenCache(ItemInterface $item): ?AccessToken
106
    {
107 4
        $accessToken = $this->requestAccessToken();
108
109 4
        if ($accessToken !== null) {
110 2
            $item->expiresAfter($accessToken->getTtl());
111
        }
112
113 4
        return $accessToken;
114
    }
115
116
    /**
117
     * Requests an access token at Auth0.
118
     *
119
     * @return AccessToken|null
120
     */
121 4
    private function requestAccessToken(): ?AccessToken
122
    {
123 4
        $response = $this->client->request(
124 4
            'POST',
125 4
            $this->authZeroConfiguration->getTenantTokenUrl(),
126
            array(
127 4
                'json' => $this->authZeroConfiguration->getAuthenticationPayload(),
128
            )
129
        );
130
131 4
        if ($response->getStatusCode() !== 200) {
132 1
            return null;
133
        }
134
135 3
        $responseJson = $response->toArray();
136 3
        if (isset($responseJson['access_token'], $responseJson['expires_in']) === false) {
137 1
            return null;
138
        }
139
140 2
        return new AccessToken($responseJson['access_token'], $responseJson['expires_in']);
141
    }
142
}
143