Completed
Push — master ( d75a47...7295a1 )
by Chad
10s
created

Authentication::createOwnerCredentials()   B

Complexity

Conditions 2
Paths 1

Size

Total Lines 46
Code Lines 37

Duplication

Lines 0
Ratio 0 %

Importance

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