Completed
Push — master ( 5598a9...d649df )
by Patrick
03:47 queued 02:46
created

Apple::getAccessToken34()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 23

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 23
ccs 0
cts 16
cp 0
rs 9.552
c 0
b 0
f 0
cc 1
nc 1
nop 2
crap 2
1
<?php
2
3
namespace League\OAuth2\Client\Provider;
4
5
use Exception;
6
use InvalidArgumentException;
7
use Lcobucci\JWT\Builder;
8
use Lcobucci\JWT\Signer\Ecdsa\Sha256;
9
use Lcobucci\JWT\Signer\Key;
10
use DateTimeImmutable;
11
12
use League\OAuth2\Client\Grant\AbstractGrant;
13
use League\OAuth2\Client\Provider\Exception\AppleAccessDeniedException;
14
use League\OAuth2\Client\Token\AccessToken;
15
use League\OAuth2\Client\Token\AccessTokenInterface;
16
use League\OAuth2\Client\Token\AppleAccessToken;
17
use League\OAuth2\Client\Tool\BearerAuthorizationTrait;
18
use Psr\Http\Message\ResponseInterface;
19
20
class Apple extends AbstractProvider
21
{
22
    use BearerAuthorizationTrait;
23
24
    /**
25
     * Default scopes
26
     *
27
     * @var array
28
     */
29
    public $defaultScopes = ['name', 'email'];
30
31
    /**
32
     * @var string the team id
33
     */
34
    protected $teamId;
35
36
    /**
37
     * @var string the key file id
38
     */
39
    protected $keyFileId;
40
41
    /**
42
     * @var string the key file path
43
     */
44
    protected $keyFilePath;
45
46
    /**
47
     * Constructs Apple's OAuth 2.0 service provider.
48
     *
49
     * @param array $options
50
     * @param array $collaborators
51
     */
52 13
    public function __construct(array $options = [], array $collaborators = [])
53
    {
54 13
        if (empty($options['teamId'])) {
55 1
            throw new InvalidArgumentException('Required option not passed: "teamId"');
56
        }
57
58 13
        if (empty($options['keyFileId'])) {
59 1
            throw new InvalidArgumentException('Required option not passed: "keyFileId"');
60
        }
61
62 13
        if (empty($options['keyFilePath'])) {
63 1
            throw new InvalidArgumentException('Required option not passed: "keyFilePath"');
64
        }
65
66 13
        parent::__construct($options, $collaborators);
67 13
    }
68
69
    /**
70
     * Creates an access token from a response.
71
     *
72
     * The grant that was used to fetch the response can be used to provide
73
     * additional context.
74
     *
75
     * @param  array         $response
76
     * @param  AbstractGrant $grant
77
     * @return AccessTokenInterface
78
     */
79
    protected function createAccessToken(array $response, AbstractGrant $grant)
80
    {
81
        return new AppleAccessToken($this->getHttpClient(), $response);
82
    }
83
84
    /**
85
     * Get the string used to separate scopes.
86
     *
87
     * @return string
88
     */
89 3
    protected function getScopeSeparator()
90
    {
91 3
        return ' ';
92
    }
93
94
    /**
95
     * Change response mode when scope requires it
96
     *
97
     * @param array $options
98
     *
99
     * @return array
100
     */
101 3
    protected function getAuthorizationParameters(array $options)
102
    {
103 3
        $options = parent::getAuthorizationParameters($options);
104 3
        if (strpos($options['scope'], 'name') !== false || strpos($options['scope'], 'email') !== false) {
105 2
            $options['response_mode'] = 'form_post';
106
        }
107 3
        return $options;
108
    }
109
110
    /**
111
     * @param AccessToken $token
112
     *
113
     * @return mixed
114
     */
115 2
    protected function fetchResourceOwnerDetails(AccessToken $token)
116
    {
117 2
        return json_decode(array_key_exists('user', $_GET) ? $_GET['user']
118 2
            : (array_key_exists('user', $_POST) ? $_POST['user'] : '[]'), true) ?: [];
119
    }
120
121
    /**
122
     * Get authorization url to begin OAuth flow
123
     *
124
     * @return string
125
     */
126 3
    public function getBaseAuthorizationUrl()
127
    {
128 3
        return 'https://appleid.apple.com/auth/authorize';
129
    }
130
131
    /**
132
     * Get access token url to retrieve token
133
     *
134
     * @return string
135
     */
136 1
    public function getBaseAccessTokenUrl(array $params)
137
    {
138 1
        return 'https://appleid.apple.com/auth/token';
139
    }
140
141
    /**
142
     * Get provider url to fetch user details
143
     *
144
     * @param AccessToken $token
145
     *
146
     * @return string
147
     * @throws Exception
148
     */
149 1
    public function getResourceOwnerDetailsUrl(AccessToken $token)
150
    {
151 1
        throw new Exception('No Apple ID REST API available yet!');
152
    }
153
154
    /**
155
     * Get the default scopes used by this provider.
156
     *
157
     * This should not be a complete list of all scopes, but the minimum
158
     * required for the provider user interface!
159
     *
160
     * @return array
161
     */
162 2
    protected function getDefaultScopes()
163
    {
164 2
        return $this->defaultScopes;
165
    }
166
167
    /**
168
     * Check a provider response for errors.
169
     *
170
     * @param  ResponseInterface $response
171
     * @param  array             $data     Parsed response data
172
     * @return void
173
     * @throws AppleAccessDeniedException
174
     */
175 1
    protected function checkResponse(ResponseInterface $response, $data)
176
    {
177 1
        if ($response->getStatusCode() >= 400) {
178 1
            throw new AppleAccessDeniedException(
179 1
                array_key_exists('error', $data) ? $data['error'] : $response->getReasonPhrase(),
180 1
                array_key_exists('code', $data) ? $data['code'] : $response->getStatusCode(),
181 1
                $response
0 ignored issues
show
Documentation introduced by
$response is of type object<Psr\Http\Message\ResponseInterface>, but the function expects a array|string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
182
            );
183
        }
184
    }
185
186
    /**
187
     * Generate a user object from a successful user details request.
188
     *
189
     * @param array $response
190
     * @param AccessToken $token
191
     * @return AppleResourceOwner
192
     */
193 1
    protected function createResourceOwner(array $response, AccessToken $token)
194
    {
195 1
        return new AppleResourceOwner(
196 1
            array_merge(
197 1
                $response,
198
                [
199 1
                    'email' => isset($token->getValues()['email'])
200 1
                        ? $token->getValues()['email'] : (isset($response['email']) ? $response['email'] : null),
201 1
                    'isPrivateEmail' => $token instanceof AppleAccessToken ? $token->isPrivateEmail() : null
202
                ]
203
            ),
204 1
            $token->getResourceOwnerId()
205
        );
206
    }
207
208
    /**
209
     * {@inheritDoc}
210
     */
211
    public function getAccessToken($grant, array $options = [])
212
    {
213
        if(class_exists('\Lcobucci\JWT\Configuration')){
214
            return $this->getAccessToken34($grant,$options);
215
        }
216
217
        $signer = new Sha256();
218
        $time = new \DateTimeImmutable();
219
        $expiresAt = $time->modify('+1 Hour');
220
221
        $token = (new Builder())
222
            ->issuedBy($this->teamId)
223
            ->permittedFor('https://appleid.apple.com')
224
            ->issuedAt($time->getTimestamp())
225
            ->expiresAt($expiresAt->getTimestamp())
226
            ->relatedTo($this->clientId)
227
            ->withHeader('alg', 'ES256')
228
            ->withHeader('kid', $this->keyFileId)
229
            ->getToken($signer, $this->getLocalKey());
230
231
        $options += [
232
            'client_secret' => (string) $token
233
        ];
234
235
        return parent::getAccessToken($grant, $options);
236
    }
237
238
    private function getAccessToken34($grant, array $options = [])
239
    {
240
        $signer = new Sha256();
241
        $now = new DateTimeImmutable();
242
        $key = Key\LocalFileReference::file($this->keyFilePath);
243
        $config = \Lcobucci\JWT\Configuration::forSymmetricSigner($signer, $key);
244
245
        $token = $config->builder()
246
            ->issuedBy($this->teamId)
247
            ->permittedFor('https://appleid.apple.com')
248
            ->issuedAt($now)
249
            ->expiresAt($now->modify('+10 minute'))
250
            ->relatedTo($this->clientId)
251
            ->withHeader('alg', 'ES256')
252
            ->withHeader('kid', $this->keyFileId)
253
            ->getToken($config->signer(), $this->getLocalKey());
254
255
        $options += [
256
            'client_secret' => (string) $token
257
        ];
258
259
        return parent::getAccessToken($grant, $options);
0 ignored issues
show
Comprehensibility Bug introduced by
It seems like you call parent on a different method (getAccessToken() instead of getAccessToken34()). Are you sure this is correct? If so, you might want to change this to $this->getAccessToken().

This check looks for a call to a parent method whose name is different than the method from which it is called.

Consider the following code:

class Daddy
{
    protected function getFirstName()
    {
        return "Eidur";
    }

    protected function getSurName()
    {
        return "Gudjohnsen";
    }
}

class Son
{
    public function getFirstName()
    {
        return parent::getSurname();
    }
}

The getFirstName() method in the Son calls the wrong method in the parent class.

Loading history...
260
    }
261
262
263
    /**
264
     * @return Key
265
     */
266 1
    public function getLocalKey()
267
    {
268 1
        return new Key('file://' . $this->keyFilePath);
269
    }
270
}
271