Completed
Pull Request — master (#42)
by Chad
07:43 queued 06:21
created

Authentication::createClientCredentials()   B

Complexity

Conditions 2
Paths 1

Size

Total Lines 38
Code Lines 28

Duplication

Lines 0
Ratio 0 %

Importance

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