Test Setup Failed
Push — master ( 9d8f9a...f6d0c4 )
by
unknown
25:07
created

TokenManager::__construct()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 11
rs 9.9
c 0
b 0
f 0
cc 2
nc 2
nop 4
1
<?php
2
3
declare(strict_types=1);
4
5
namespace MovingImage\Client\VMPro\Manager;
6
7
use GuzzleHttp\ClientInterface;
8
use MovingImage\Client\VMPro\Entity\ApiCredentials;
9
use MovingImage\Client\VMPro\Entity\Token;
10
use MovingImage\Client\VMPro\Extractor\TokenExtractor;
11
use MovingImage\Client\VMPro\Util\Logging\Traits\LoggerAwareTrait;
12
use Psr\Log\LoggerAwareInterface;
13
14
class TokenManager implements LoggerAwareInterface
15
{
16
    use LoggerAwareTrait;
17
18
    private ClientInterface $httpClient;
0 ignored issues
show
Bug introduced by
This code did not parse for me. Apparently, there is an error somewhere around this line:

Syntax error, unexpected T_STRING, expecting T_FUNCTION or T_CONST
Loading history...
19
20
    protected ApiCredentials $credentials;
21
22
    private TokenExtractor $tokenExtractor;
23
24
    /**
25
     * @var Token
26
     */
27
    private $accessToken;
28
29
    /**
30
     * @var Token
31
     */
32
    private $refreshToken;
33
34
    public function __construct(
35
        ClientInterface $httpClient,
36
        ApiCredentials $credentials,
37
        TokenExtractor $tokenExtractor
38
    ) {
39
        $this->httpClient = $httpClient;
40
        $this->credentials = $credentials;
41
        $this->tokenExtractor = $tokenExtractor;
42
    }
43
44
    /**
45
     * Create completely fresh Access + Refresh tokens.
46
     *
47
     * @TODO Implement proper error handling
48
     */
49
    protected function createNewTokens(): array
50
    {
51
        $logger = $this->getLogger();
52
        $logger->debug('Starting request to create fresh access & refresh tokens');
53
54
        $body = [
55
            'client_id' => 'anonymous',
56
            'grant_type' => 'password',
57
            'response_type' => 'token',
58
            'scope' => 'openid',
59
            'username' => $this->credentials->getUsername(),
60
            'password' => $this->credentials->getPassword(),
61
        ];
62
63
        $response = $this->sendPostRequest($body);
64
65
        $logger->debug('Successfully retrieved new access & refresh tokens', $response);
66
67
        return [
68
            'accessToken' => new Token(
69
                $response['access_token'],
70
                $this->tokenExtractor->extract($response['access_token'])
71
            ),
72
            'refreshToken' => new Token(
73
                $response['refresh_token'],
74
                $this->tokenExtractor->extract($response['refresh_token'])
75
            ),
76
        ];
77
    }
78
79
    /**
80
     * Create a new access token for a video manager using a refresh token.
81
     */
82
    protected function createAccessTokenFromRefreshToken(Token $refreshToken): Token
83
    {
84
        $logger = $this->getLogger();
85
        $logger->debug('Starting request to create fresh access token from refresh token');
86
87
        $body = [
88
            'client_id' => 'anonymous',
89
            'grant_type' => 'refresh_token',
90
            'refresh_token' => $refreshToken->getTokenString(),
91
        ];
92
93
        $response = $this->sendPostRequest($body);
94
95
        $logger->debug('Successfully retrieved new access token', $response);
96
97
        return new Token(
98
            $response['access_token'],
99
            $this->tokenExtractor->extract($response['access_token'])
100
        );
101
    }
102
103
    /**
104
     * Log information about which tokens we have.
105
     */
106
    protected function logTokenData(): void
107
    {
108
        $this->getLogger()->debug('Token information', [
109
            'accessTokenExists' => isset($this->accessToken),
110
            'accessTokenExpiration' => isset($this->accessToken) ? $this->accessToken->getTokenData()['exp'] : null,
111
            'accessTokenHasExpired' => isset($this->accessToken) ? $this->accessToken->expired() : null,
112
            'refreshTokenExists' => isset($this->refreshToken),
113
            'refreshTokenExpiration' => isset($this->refreshToken) ? $this->refreshToken->getTokenData()['exp'] : null,
114
            'refreshTokenHasExpired' => isset($this->refreshToken) ? $this->refreshToken->expired() : null,
115
            'localTime' => time(),
116
        ]);
117
    }
118
119
    /**
120
     * Retrieve a valid token.
121
     */
122
    public function getToken(): string
123
    {
124
        $logger = $this->getLogger();
125
        $this->logTokenData();
126
127
        // Access token has expired, but expiration token has not expired.
128
        // Issue ourselves a new access token for the same video manager.
129
        if (!is_null($this->accessToken)
130
            && $this->accessToken->expired()
131
            && !is_null($this->refreshToken)
132
            && !$this->refreshToken->expired()) {
133
            $logger->info('Access token has expired - getting new one for same video manager with refresh token');
134
            $this->accessToken = $this->createAccessTokenFromRefreshToken($this->refreshToken);
135
        } elseif (is_null($this->accessToken)
136
            || (!is_null($this->refreshToken) && $this->refreshToken->expired())) {
137
            // Either we have no token, or the refresh token has expired
138
            // so we will need to generate completely new tokens
139
            $logger->info('No access token, or refresh token has expired - generate completely new ones');
140
            $tokenData = $this->createNewTokens();
141
142
            $this->accessToken = $tokenData['accessToken'];
143
            $this->refreshToken = $tokenData['refreshToken'];
144
        }
145
146
        return $this->accessToken->getTokenString();
147
    }
148
149
    /**
150
     * Sends a post request to the OAuth endpoint.
151
     *
152
     * @return mixed
153
     */
154
    private function sendPostRequest(array $body): array
155
    {
156
        $response = $this->httpClient->request('POST', '', [
157
            'form_params' => $body,
158
        ]);
159
160
        return \json_decode($response->getBody()->getContents(), true);
161
    }
162
}
163