Completed
Pull Request — master (#42)
by Chad
04:56
created

Authentication::parseTokenResponse()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 15
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 15
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 11
nc 1
nop 1
1
<?php
2
namespace TraderInteractive\Api;
3
4
use TraderInteractive\Util;
5
use TraderInteractive\Util\Arrays;
6
use TraderInteractive\Util\Http;
7
use GuzzleHttp\Psr7\Request;
8
use Psr\Http\Message\RequestInterface;
9
use Psr\Http\Message\ResponseInterface;
10
11
/**
12
 * Layer for OAuth2 Authentication
13
 */
14
final class Authentication
15
{
16
    /**
17
     * Function to create a Request object for obtaining a new token from the API
18
     *
19
     * @var callable
20
     */
21
    private $getTokenRequestFunc;
22
23
    /**
24
     * Private constructor to safeguard undeclared functions
25
     *
26
     * @param callable $getTokenRequestFunc Function to create a Request object for obtaining a new token from the API
27
     */
28
    private function __construct(callable $getTokenRequestFunc)
29
    {
30
        $this->getTokenRequestFunc = $getTokenRequestFunc;
31
    }
32
33
    /**
34
     * Creates a new instance of Authentication for Client Credentials grant type
35
     *
36
     * @param string $clientId The oauth client id
37
     * @param string $clientSecret The oauth client secret
38
     * @param string $refreshResource The refresh token resource of the API
39
     *     Only needed since apigee doesnt use the token resource that is in the oauth2 spec
40
     *
41
     * @return Authentication
42
     */
43
    public static function createClientCredentials(
44
        string $clientId,
45
        string $clientSecret,
46
        string $refreshResource = 'token',
47
        string $tokenResource = 'token'
48
    ) : Authentication {
49
        $getTokenRequestFunc = function (
50
            string $baseUrl,
51
            string $refreshToken = null
52
        ) use (
53
            $clientId,
54
            $clientSecret,
55
            $refreshResource,
56
            $tokenResource
57
        ) {
58
            if ($refreshToken !== null) {
59
                return self::getRefreshTokenRequest(
60
                    $baseUrl,
61
                    $clientId,
62
                    $clientSecret,
63
                    $refreshResource,
64
                    $refreshToken
65
                );
66
            }
67
68
            $data = ['client_id' => $clientId, 'client_secret' => $clientSecret, 'grant_type' => 'client_credentials'];
69
            return new Request(
70
                'POST',
71
                "{$baseUrl}/{$tokenResource}",
72
                ['Content-Type' => 'application/x-www-form-urlencoded'],
73
                Http::buildQueryString($data)
74
            );
75
        };
76
77
        return new self($getTokenRequestFunc);
78
    }
79
80
    /**
81
     * Creates a new instance of Authentication for Owner Credentials grant type
82
     *
83
     * @param string $clientId The oauth client id
84
     * @param string $clientSecret The oauth client secret
85
     * @param string $username The oauth username
86
     * @param string $password The oauth password
87
     * @param string $refreshResource The refresh token resource of the API
88
     *     Only needed since apigee doesnt use the token resource that is in the oauth2 spec
89
     *
90
     * @return Authentication
91
     */
92
    public static function createOwnerCredentials(
93
        string $clientId,
94
        string $clientSecret,
95
        string $username,
96
        string $password,
97
        string $refreshResource = 'token',
98
        string $tokenResource = 'token'
99
    ) : Authentication {
100
        $getTokenRequestFunc = function (
101
            string $baseUrl,
102
            string $refreshToken = null
103
        ) use (
104
            $clientId,
105
            $clientSecret,
106
            $username,
107
            $password,
108
            $refreshResource,
109
            $tokenResource
110
        ) {
111
            if ($refreshToken !== null) {
112
                return self::getRefreshTokenRequest(
113
                    $baseUrl,
114
                    $clientId,
115
                    $clientSecret,
116
                    $refreshResource,
117
                    $refreshToken
118
                );
119
            }
120
121
            $data = [
122
                'client_id' => $clientId,
123
                'client_secret' => $clientSecret,
124
                'username' => $username,
125
                'password' => $password,
126
                'grant_type' => 'password',
127
            ];
128
            return new Request(
129
                'POST',
130
                "{$baseUrl}/{$tokenResource}",
131
                ['Content-Type' => 'application/x-www-form-urlencoded'],
132
                Http::buildQueryString($data)
133
            );
134
        };
135
136
        return new self($getTokenRequestFunc);
137
    }
138
139
    /**
140
     * Extracts an access token from the given API response
141
     *
142
     * @param ResponseInterface $response The API response containing the access token
143
     *
144
     * @return array Array containing the access token, refresh token and expires timestamp
145
     */
146
    public static function parseTokenResponse(ResponseInterface $response)
147
    {
148
        $parsedJson = json_decode((string)$response->getBody(), true);
149
        Util::ensureNot('invalid_client', Arrays::get($parsedJson, 'error'), 'Invalid Credentials');
150
        Util::ensure(
151
            200,
152
            $response->getStatusCode(),
153
            Arrays::get($parsedJson, 'error_description', 'Unknown API error')
154
        );
155
        return [
156
            $parsedJson['access_token'],
157
            Arrays::get($parsedJson, 'refresh_token'),
158
            time() + (int)$parsedJson['expires_in'],
159
        ];
160
    }
161
162
    /**
163
     * Creates a Request object for obtaining a new token from the API
164
     *
165
     * @param string      $baseUrl      The base url of the API
166
     * @param string|null $refreshToken The refresh token of the API
167
     *
168
     * @return RequestInterface
169
     */
170
    public function getTokenRequest(string $baseUrl, string $refreshToken = null) : RequestInterface
171
    {
172
        return call_user_func($this->getTokenRequestFunc, $baseUrl, $refreshToken);
173
    }
174
175
    /**
176
     * Build a refresh token request
177
     *
178
     * @param string $baseUrl API base url
179
     * @param string $clientId The client id
180
     * @param string $clientSecret The client secret
181
     * @param string $refreshResource The refresh token resource of the API
182
     *     Only needed since apigee doesnt use the token resource that is in the oauth2 spec
183
     * @param string $refreshToken The refresh token of the API
184
     *
185
     * @return RequestInterface The built token refresh request
186
     */
187
    private static function getRefreshTokenRequest(
188
        string $baseUrl,
189
        string $clientId,
190
        string $clientSecret,
191
        string $refreshResource,
192
        string $refreshToken
193
    ) : RequestInterface {
194
        //NOTE client_id and client_secret are needed for Apigee but are not in the oauth2 spec
195
        $data = [
196
            'client_id' => $clientId,
197
            'client_secret' => $clientSecret,
198
            'refresh_token' => $refreshToken,
199
            'grant_type' => 'refresh_token',
200
        ];
201
202
        //NOTE the oauth2 spec says the refresh resource should be the same as the token resource, which is impossible
203
        //in Apigee and why the $refreshResource variable exists
204
        return new Request(
205
            'POST',
206
            "{$baseUrl}/{$refreshResource}",
207
            ['Content-Type' => 'application/x-www-form-urlencoded'],
208
            Http::buildQueryString($data)
209
        );
210
    }
211
}
212