AuthenticationPlugin::handleRequest()   B
last analyzed

Complexity

Conditions 7
Paths 2

Size

Total Lines 38
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 7
eloc 20
nc 2
nop 3
dl 0
loc 38
rs 8.6666
c 1
b 0
f 0
1
<?php
2
declare(strict_types=1);
3
4
namespace Shoman4eg\Nalog\Http;
5
6
use Http\Client\Common\Plugin;
7
use Http\Promise\Promise;
8
use Psr\Http\Client\ClientExceptionInterface;
9
use Psr\Http\Message\RequestInterface;
10
use Psr\Http\Message\ResponseInterface;
11
use Shoman4eg\Nalog\Util\JSON;
12
13
/**
14
 * This will automatically refresh expired access token.
15
 *
16
 * @author Tobias Nyholm <[email protected]>
17
 */
18
final class AuthenticationPlugin implements Plugin
19
{
20
    public const RETRY_LIMIT = 2;
21
22
    private array $accessToken;
23
    private array $retryStorage = [];
24
    private Authenticator $authenticator;
25
26
    public function __construct(Authenticator $authenticator, string $accessToken)
27
    {
28
        $this->authenticator = $authenticator;
29
        $this->accessToken = JSON::decode($accessToken);
30
    }
31
32
    /**
33
     * @throws \Exception
34
     * @throws \JsonException
35
     * @throws ClientExceptionInterface
36
     */
37
    public function handleRequest(RequestInterface $request, callable $next, callable $first): Promise
38
    {
39
        if ($this->accessToken === [] || $request->hasHeader('Authorization')) {
40
            return $next($request);
41
        }
42
43
        $chainIdentifier = \spl_object_hash((object)$first);
44
        $header = \sprintf('Bearer %s', $this->accessToken['token'] ?? '');
45
        $request = $request->withHeader('Authorization', $header);
46
47
        return $next($request)->then(
48
            function (ResponseInterface $response) use ($request, $next, $first, $chainIdentifier) {
49
                if (!\array_key_exists($chainIdentifier, $this->retryStorage)) {
50
                    $this->retryStorage[$chainIdentifier] = 0;
51
                }
52
53
                if ($response->getStatusCode() !== 401 || $this->retryStorage[$chainIdentifier] >= self::RETRY_LIMIT) {
54
                    unset($this->retryStorage[$chainIdentifier]);
55
56
                    return $response;
57
                }
58
59
                $accessToken = $this->authenticator->refreshAccessToken($this->accessToken['refreshToken']);
60
                if ($accessToken === null) {
61
                    return $response;
62
                }
63
64
                // Save new token
65
                $this->accessToken = JSON::decode($accessToken);
66
67
                // Add new token to request
68
                $header = \sprintf('Bearer %s', $this->accessToken['token']);
69
                $request = $request->withHeader('Authorization', $header);
70
71
                // Retry
72
                ++$this->retryStorage[$chainIdentifier];
73
74
                return $this->handleRequest($request, $next, $first)->wait();
75
            }
76
        );
77
    }
78
}
79