Completed
Push — master ( aef04b...0c132b )
by Patrick
01:11
created

Apple   A

Complexity

Total Complexity 23

Size/Duplication

Total Lines 213
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 10

Test Coverage

Coverage 29.82%

Importance

Changes 0
Metric Value
wmc 23
lcom 1
cbo 10
dl 0
loc 213
ccs 17
cts 57
cp 0.2982
rs 10
c 0
b 0
f 0

13 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 8 2
A createAccessToken() 0 4 1
A getScopeSeparator() 0 4 1
A getAuthorizationParameters() 0 8 3
A fetchResourceOwnerDetails() 0 5 3
A getBaseAuthorizationUrl() 0 4 1
A getBaseAccessTokenUrl() 0 4 1
A getResourceOwnerDetailsUrl() 0 4 1
A getDefaultScopes() 0 4 1
A checkResponse() 0 10 4
A createResourceOwner() 0 7 3
A getAccessToken() 0 28 1
A getLocalKey() 0 4 1
1
<?php
2
3
namespace League\OAuth2\Client\Provider;
4
5
use Exception;
6
use InvalidArgumentException;
7
use Jose\Component\Core\AlgorithmManager;
8
use Jose\Component\KeyManagement\JWKFactory;
9
use Jose\Component\Signature\Algorithm\ES256;
10
use Jose\Component\Signature\JWSBuilder;
11
use Jose\Component\Signature\Serializer\CompactSerializer;
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 4
    public function __construct(array $options = [], array $collaborators = [])
53
    {
54 4
        if (empty($options['keyFilePath'])) {
55
            throw new InvalidArgumentException('Required option not passed: "keyFilePath"');
56
        }
57
58 4
        parent::__construct($options, $collaborators);
59 4
    }
60
61
    /**
62
     * Creates an access token from a response.
63
     *
64
     * The grant that was used to fetch the response can be used to provide
65
     * additional context.
66
     *
67
     * @param  array $response
68
     * @param  AbstractGrant $grant
69
     * @return AccessTokenInterface
70
     */
71
    protected function createAccessToken(array $response, AbstractGrant $grant)
72
    {
73
        return new AppleAccessToken($response);
74
    }
75
76
    /**
77
     * Get the string used to separate scopes.
78
     *
79
     * @return string
80
     */
81 3
    protected function getScopeSeparator()
82
    {
83 3
        return ' ';
84
    }
85
86
    /**
87
     * Change response mode when scope requires it
88
     *
89
     * @param array $options
90
     *
91
     * @return array
92
     */
93 3
    protected function getAuthorizationParameters(array $options)
94
    {
95 3
        $options = parent::getAuthorizationParameters($options);
96 3
        if (strpos($options['scope'], 'name') !== false || strpos($options['scope'], 'email') !== false) {
97 2
            $options['response_mode'] = 'form_post';
98
        }
99 3
        return $options;
100
    }
101
102
    /**
103
     * @param AccessToken $token
104
     *
105
     * @return mixed
106
     */
107
    protected function fetchResourceOwnerDetails(AccessToken $token)
108
    {
109
        return json_decode(array_key_exists('user', $_GET)
110
            ? $_GET['user'] : (array_key_exists('user', $_POST) ? $_POST['user'] : '[]'), true);
111
    }
112
113
    /**
114
     * Get authorization url to begin OAuth flow
115
     *
116
     * @return string
117
     */
118 3
    public function getBaseAuthorizationUrl()
119
    {
120 3
        return 'https://appleid.apple.com/auth/authorize';
121
    }
122
123
    /**
124
     * Get access token url to retrieve token
125
     *
126
     * @return string
127
     */
128 1
    public function getBaseAccessTokenUrl(array $params)
129
    {
130 1
        return 'https://appleid.apple.com/auth/token';
131
    }
132
133
    /**
134
     * Get provider url to fetch user details
135
     *
136
     * @param AccessToken $token
137
     *
138
     * @return string
139
     * @throws Exception
140
     */
141
    public function getResourceOwnerDetailsUrl(AccessToken $token)
142
    {
143
        throw new Exception('No Apple ID REST API available yet!');
144
    }
145
146
    /**
147
     * Get the default scopes used by this provider.
148
     *
149
     * This should not be a complete list of all scopes, but the minimum
150
     * required for the provider user interface!
151
     *
152
     * @return array
153
     */
154 2
    protected function getDefaultScopes()
155
    {
156 2
        return $this->defaultScopes;
157
    }
158
159
    /**
160
     * Check a provider response for errors.
161
     *
162
     * @param  ResponseInterface $response
163
     * @param  array $data Parsed response data
164
     * @return void
165
     * @throws AppleAccessDeniedException
166
     */
167
    protected function checkResponse(ResponseInterface $response, $data)
168
    {
169
        if ($response->getStatusCode() >= 400) {
170
            throw new AppleAccessDeniedException(
171
                $data['error'] ?: $response->getReasonPhrase(),
172
                $data['code'] ?: $response->getStatusCode(),
173
                $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...
174
            );
175
        }
176
    }
177
178
    /**
179
     * Generate a user object from a successful user details request.
180
     *
181
     * @param array $response
182
     * @param Apple $token
183
     * @return AppleResourceOwner
184
     */
185
    protected function createResourceOwner(array $response, AccessToken $token)
186
    {
187
        return new AppleResourceOwner(array_merge($response, [
188
            'email' => isset($token->getValues()['email'])
189
                ? $token->getValues()['email'] : (isset($response['email']) ? $response['email'] : null)
190
        ]), $token->getResourceOwnerId());
191
    }
192
193
    /**
194
     * {@inheritDoc}
195
     */
196
    public function getAccessToken($grant, array $options = [])
197
    {
198
        $algorithmManager = new AlgorithmManager([new ES256()]);
199
        $jwsBuilder = new JWSBuilder($algorithmManager);
200
        $jws = $jwsBuilder
201
            ->create()
202
            ->withPayload(json_encode([
203
                'iat' => time(),
204
                'exp' => time() + 600,
205
                'iss' => $this->teamId,
206
                'aud' => 'https://appleid.apple.com',
207
                'sub' => $this->clientId
208
            ]))
209
            ->addSignature($this->getLocalKey(), [
210
                'alg' => 'ES256',
211
                'kid' => $this->keyFileId
212
            ])
213
            ->build();
214
215
        $serializer = new CompactSerializer();
216
        $token = $serializer->serialize($jws);
217
218
        $options += [
219
            'client_secret' => $token
220
        ];
221
222
        return parent::getAccessToken($grant, $options);
223
    }
224
225
    /**
226
     * @return \Jose\Component\Core\JWK
227
     */
228
    public function getLocalKey()
229
    {
230
        return JWKFactory::createFromKeyFile($this->keyFilePath);
231
    }
232
}
233