@@ -64,278 +64,278 @@ |
||
| 64 | 64 | */ |
| 65 | 65 | class Apple extends OAuth2 |
| 66 | 66 | { |
| 67 | - /** |
|
| 68 | - * {@inheritdoc} |
|
| 69 | - */ |
|
| 70 | - protected $scope = 'name email'; |
|
| 71 | - |
|
| 72 | - /** |
|
| 73 | - * {@inheritdoc} |
|
| 74 | - */ |
|
| 75 | - protected $apiBaseUrl = 'https://appleid.apple.com/auth/'; |
|
| 76 | - |
|
| 77 | - /** |
|
| 78 | - * {@inheritdoc} |
|
| 79 | - */ |
|
| 80 | - protected $authorizeUrl = 'https://appleid.apple.com/auth/authorize'; |
|
| 81 | - |
|
| 82 | - /** |
|
| 83 | - * {@inheritdoc} |
|
| 84 | - */ |
|
| 85 | - protected $accessTokenUrl = 'https://appleid.apple.com/auth/token'; |
|
| 86 | - |
|
| 87 | - /** |
|
| 88 | - * {@inheritdoc} |
|
| 89 | - */ |
|
| 90 | - protected $apiDocumentation = 'https://developer.apple.com/documentation/sign_in_with_apple'; |
|
| 91 | - |
|
| 92 | - /** |
|
| 93 | - * {@inheritdoc} |
|
| 94 | - * The Sign in with Apple servers require percent encoding (or URL encoding) |
|
| 95 | - * for its query parameters. If you are using the Sign in with Apple REST API, |
|
| 96 | - * you must provide values with encoded spaces (`%20`) instead of plus (`+`) signs. |
|
| 97 | - */ |
|
| 98 | - protected $AuthorizeUrlParametersEncType = PHP_QUERY_RFC3986; |
|
| 99 | - |
|
| 100 | - /** |
|
| 101 | - * {@inheritdoc} |
|
| 102 | - */ |
|
| 103 | - protected function initialize() |
|
| 104 | - { |
|
| 105 | - parent::initialize(); |
|
| 106 | - $this->AuthorizeUrlParameters['response_mode'] = 'form_post'; |
|
| 107 | - |
|
| 108 | - if ($this->isRefreshTokenAvailable()) { |
|
| 109 | - $this->tokenRefreshParameters += [ |
|
| 110 | - 'client_id' => $this->clientId, |
|
| 111 | - 'client_secret' => $this->clientSecret, |
|
| 112 | - ]; |
|
| 113 | - } |
|
| 114 | - } |
|
| 115 | - |
|
| 116 | - /** |
|
| 117 | - * {@inheritdoc} |
|
| 118 | - * @throws InvalidApplicationCredentialsException |
|
| 119 | - */ |
|
| 120 | - protected function configure() |
|
| 121 | - { |
|
| 122 | - $keys = $this->config->get('keys'); |
|
| 123 | - $keys['secret'] = $this->getSecret(); |
|
| 124 | - $this->config->set('keys', $keys); |
|
| 125 | - parent::configure(); |
|
| 126 | - } |
|
| 127 | - |
|
| 128 | - /** |
|
| 129 | - * {@inheritdoc} |
|
| 130 | - * |
|
| 131 | - * include id_token $tokenNames |
|
| 132 | - */ |
|
| 133 | - public function getAccessToken() |
|
| 134 | - { |
|
| 135 | - $tokenNames = [ |
|
| 136 | - 'access_token', |
|
| 137 | - 'id_token', |
|
| 138 | - 'access_token_secret', |
|
| 139 | - 'token_type', |
|
| 140 | - 'refresh_token', |
|
| 141 | - 'expires_in', |
|
| 142 | - 'expires_at', |
|
| 143 | - ]; |
|
| 144 | - |
|
| 145 | - $tokens = []; |
|
| 146 | - |
|
| 147 | - foreach ($tokenNames as $name) { |
|
| 148 | - if ($this->getStoredData($name)) { |
|
| 149 | - $tokens[$name] = $this->getStoredData($name); |
|
| 150 | - } |
|
| 151 | - } |
|
| 152 | - |
|
| 153 | - return $tokens; |
|
| 154 | - } |
|
| 155 | - |
|
| 156 | - /** |
|
| 157 | - * {@inheritdoc} |
|
| 158 | - */ |
|
| 159 | - protected function validateAccessTokenExchange($response) |
|
| 160 | - { |
|
| 161 | - $collection = parent::validateAccessTokenExchange($response); |
|
| 162 | - |
|
| 163 | - $this->storeData('id_token', $collection->get('id_token')); |
|
| 164 | - |
|
| 165 | - return $collection; |
|
| 166 | - } |
|
| 167 | - |
|
| 168 | - /** |
|
| 169 | - * Get the user profile |
|
| 170 | - * |
|
| 171 | - * @throws HttpClientFailureException |
|
| 172 | - * @throws InvalidAccessTokenException |
|
| 173 | - * @throws UnexpectedValueException |
|
| 174 | - * @throws HttpRequestFailedException |
|
| 175 | - * @throws Exception |
|
| 176 | - */ |
|
| 177 | - public function getUserProfile() |
|
| 178 | - { |
|
| 179 | - $id_token = $this->getStoredData('id_token'); |
|
| 180 | - |
|
| 181 | - $verifyTokenSignature = |
|
| 182 | - $this->config->exists('verifyTokenSignature') ? $this->config->get('verifyTokenSignature') : true; |
|
| 183 | - |
|
| 184 | - if (!$verifyTokenSignature) { |
|
| 185 | - // payload extraction by https://github.com/omidborjian |
|
| 186 | - // https://github.com/hybridauth/hybridauth/issues/1095#issuecomment-626479263 |
|
| 187 | - // JWT splits the string to 3 components 1) first is header 2) is payload 3) is signature |
|
| 188 | - $payload = explode('.', $id_token)[1]; |
|
| 189 | - $payload = json_decode(base64_decode($payload)); |
|
| 190 | - } else { |
|
| 191 | - // validate the token signature and get the payload |
|
| 192 | - $publicKeys = $this->apiRequest('keys'); |
|
| 193 | - |
|
| 194 | - JWT::$leeway = 120; |
|
| 195 | - |
|
| 196 | - $error = false; |
|
| 197 | - $payload = null; |
|
| 198 | - |
|
| 199 | - foreach ($publicKeys->keys as $publicKey) { |
|
| 200 | - try { |
|
| 201 | - $jwk = (array)$publicKey; |
|
| 202 | - |
|
| 203 | - $key = PublicKeyLoader::load( |
|
| 204 | - [ |
|
| 205 | - 'e' => new BigInteger(base64_decode($jwk['e']), 256), |
|
| 206 | - 'n' => new BigInteger(base64_decode(strtr($jwk['n'], '-_', '+/'), true), 256) |
|
| 207 | - ] |
|
| 208 | - ) |
|
| 209 | - ->withHash('sha1') |
|
| 210 | - ->withMGFHash('sha1'); |
|
| 211 | - |
|
| 212 | - $pem = (string)$key; |
|
| 213 | - |
|
| 214 | - $payload = (version_compare($this->getJwtVersion(), '6.2') < 0) ? |
|
| 215 | - JWT::decode($id_token, $pem, ['RS256']) : |
|
| 216 | - JWT::decode($id_token, new Key($pem, 'RS256')); |
|
| 217 | - break; |
|
| 218 | - } catch (Exception $e) { |
|
| 219 | - $error = $e->getMessage(); |
|
| 220 | - if ($e instanceof ExpiredException) { |
|
| 221 | - break; |
|
| 222 | - } |
|
| 223 | - } |
|
| 224 | - } |
|
| 225 | - |
|
| 226 | - if ($error && !$payload) { |
|
| 227 | - throw new Exception($error); |
|
| 228 | - } |
|
| 229 | - } |
|
| 230 | - |
|
| 231 | - $data = new Data\Collection($payload); |
|
| 232 | - |
|
| 233 | - if (!$data->exists('sub')) { |
|
| 234 | - throw new UnexpectedValueException('Missing token payload.'); |
|
| 235 | - } |
|
| 236 | - |
|
| 237 | - $userProfile = new User\Profile(); |
|
| 238 | - $userProfile->identifier = $data->get('sub'); |
|
| 239 | - $userProfile->email = $data->get('email'); |
|
| 240 | - $this->storeData('expires_at', $data->get('exp')); |
|
| 241 | - |
|
| 242 | - if (!empty($_REQUEST['user'])) { |
|
| 243 | - $objUser = json_decode($_REQUEST['user']); |
|
| 244 | - $user = new Data\Collection($objUser); |
|
| 245 | - if (!$user->isEmpty()) { |
|
| 246 | - $name = $user->get('name'); |
|
| 247 | - if (!empty($name->firstName)) { |
|
| 248 | - $userProfile->firstName = $name->firstName; |
|
| 249 | - $userProfile->lastName = $name->lastName; |
|
| 250 | - $userProfile->displayName = join(' ', [$userProfile->firstName, $userProfile->lastName]); |
|
| 251 | - } |
|
| 252 | - } |
|
| 253 | - } |
|
| 254 | - |
|
| 255 | - return $userProfile; |
|
| 256 | - } |
|
| 257 | - |
|
| 258 | - /** |
|
| 259 | - * Get the Apple secret as a JWT token |
|
| 260 | - * |
|
| 261 | - * @return string secret token |
|
| 262 | - * @throws InvalidApplicationCredentialsException |
|
| 263 | - */ |
|
| 264 | - private function getSecret() |
|
| 265 | - { |
|
| 266 | - // Your 10-character Team ID |
|
| 267 | - $team_id = $this->config->filter('keys')->get('team_id'); |
|
| 268 | - |
|
| 269 | - if (!$team_id) { |
|
| 270 | - throw new InvalidApplicationCredentialsException( |
|
| 271 | - 'Missing parameter team_id: your team id is required to generate the JWS token.' |
|
| 272 | - ); |
|
| 273 | - } |
|
| 274 | - |
|
| 275 | - // Your Services ID, e.g. com.aaronparecki.services |
|
| 276 | - $client_id = $this->config->filter('keys')->get('id') ?: $this->config->filter('keys')->get('key'); |
|
| 277 | - |
|
| 278 | - if (!$client_id) { |
|
| 279 | - throw new InvalidApplicationCredentialsException( |
|
| 280 | - 'Missing parameter id: your client id is required to generate the JWS token.' |
|
| 281 | - ); |
|
| 282 | - } |
|
| 283 | - |
|
| 284 | - // Find the 10-char Key ID value from the portal |
|
| 285 | - $key_id = $this->config->filter('keys')->get('key_id'); |
|
| 286 | - |
|
| 287 | - if (!$key_id) { |
|
| 288 | - throw new InvalidApplicationCredentialsException( |
|
| 289 | - 'Missing parameter key_id: your key id is required to generate the JWS token.' |
|
| 290 | - ); |
|
| 291 | - } |
|
| 292 | - |
|
| 293 | - // Find the 10-char Key ID value from the portal |
|
| 294 | - $key_content = $this->config->filter('keys')->get('key_content'); |
|
| 295 | - |
|
| 296 | - // Save your private key from Apple in a file called `key.txt` |
|
| 297 | - if (!$key_content) { |
|
| 298 | - $key_file = $this->config->filter('keys')->get('key_file'); |
|
| 299 | - |
|
| 300 | - if (!$key_file) { |
|
| 301 | - throw new InvalidApplicationCredentialsException( |
|
| 302 | - 'Missing parameter key_content or key_file: your key is required to generate the JWS token.' |
|
| 303 | - ); |
|
| 304 | - } |
|
| 305 | - |
|
| 306 | - if (!file_exists($key_file)) { |
|
| 307 | - throw new InvalidApplicationCredentialsException( |
|
| 308 | - "Your key file $key_file does not exist." |
|
| 309 | - ); |
|
| 310 | - } |
|
| 311 | - |
|
| 312 | - $key_content = file_get_contents($key_file); |
|
| 313 | - } |
|
| 314 | - |
|
| 315 | - $data = [ |
|
| 316 | - 'iat' => time(), |
|
| 317 | - 'exp' => time() + 86400 * 180, |
|
| 318 | - 'iss' => $team_id, |
|
| 319 | - 'aud' => 'https://appleid.apple.com', |
|
| 320 | - 'sub' => $client_id |
|
| 321 | - ]; |
|
| 322 | - |
|
| 323 | - return JWT::encode($data, $key_content, 'ES256', $key_id); |
|
| 324 | - } |
|
| 325 | - |
|
| 326 | - /** |
|
| 327 | - * Try to get the installed JWT version |
|
| 328 | - * |
|
| 329 | - * If composer 2 is installed use InstalledVersions::getVersion, |
|
| 330 | - * otherwise return an empty string because no version check is available |
|
| 331 | - * |
|
| 332 | - * @return string|null |
|
| 333 | - */ |
|
| 334 | - private function getJwtVersion() |
|
| 335 | - { |
|
| 336 | - // assume old JWT version if no version check is possible because composer 1 is installed |
|
| 337 | - return class_exists('Composer\InstalledVersions') ? |
|
| 338 | - InstalledVersions::getVersion('firebase/php-jwt') : |
|
| 339 | - ''; |
|
| 340 | - } |
|
| 67 | + /** |
|
| 68 | + * {@inheritdoc} |
|
| 69 | + */ |
|
| 70 | + protected $scope = 'name email'; |
|
| 71 | + |
|
| 72 | + /** |
|
| 73 | + * {@inheritdoc} |
|
| 74 | + */ |
|
| 75 | + protected $apiBaseUrl = 'https://appleid.apple.com/auth/'; |
|
| 76 | + |
|
| 77 | + /** |
|
| 78 | + * {@inheritdoc} |
|
| 79 | + */ |
|
| 80 | + protected $authorizeUrl = 'https://appleid.apple.com/auth/authorize'; |
|
| 81 | + |
|
| 82 | + /** |
|
| 83 | + * {@inheritdoc} |
|
| 84 | + */ |
|
| 85 | + protected $accessTokenUrl = 'https://appleid.apple.com/auth/token'; |
|
| 86 | + |
|
| 87 | + /** |
|
| 88 | + * {@inheritdoc} |
|
| 89 | + */ |
|
| 90 | + protected $apiDocumentation = 'https://developer.apple.com/documentation/sign_in_with_apple'; |
|
| 91 | + |
|
| 92 | + /** |
|
| 93 | + * {@inheritdoc} |
|
| 94 | + * The Sign in with Apple servers require percent encoding (or URL encoding) |
|
| 95 | + * for its query parameters. If you are using the Sign in with Apple REST API, |
|
| 96 | + * you must provide values with encoded spaces (`%20`) instead of plus (`+`) signs. |
|
| 97 | + */ |
|
| 98 | + protected $AuthorizeUrlParametersEncType = PHP_QUERY_RFC3986; |
|
| 99 | + |
|
| 100 | + /** |
|
| 101 | + * {@inheritdoc} |
|
| 102 | + */ |
|
| 103 | + protected function initialize() |
|
| 104 | + { |
|
| 105 | + parent::initialize(); |
|
| 106 | + $this->AuthorizeUrlParameters['response_mode'] = 'form_post'; |
|
| 107 | + |
|
| 108 | + if ($this->isRefreshTokenAvailable()) { |
|
| 109 | + $this->tokenRefreshParameters += [ |
|
| 110 | + 'client_id' => $this->clientId, |
|
| 111 | + 'client_secret' => $this->clientSecret, |
|
| 112 | + ]; |
|
| 113 | + } |
|
| 114 | + } |
|
| 115 | + |
|
| 116 | + /** |
|
| 117 | + * {@inheritdoc} |
|
| 118 | + * @throws InvalidApplicationCredentialsException |
|
| 119 | + */ |
|
| 120 | + protected function configure() |
|
| 121 | + { |
|
| 122 | + $keys = $this->config->get('keys'); |
|
| 123 | + $keys['secret'] = $this->getSecret(); |
|
| 124 | + $this->config->set('keys', $keys); |
|
| 125 | + parent::configure(); |
|
| 126 | + } |
|
| 127 | + |
|
| 128 | + /** |
|
| 129 | + * {@inheritdoc} |
|
| 130 | + * |
|
| 131 | + * include id_token $tokenNames |
|
| 132 | + */ |
|
| 133 | + public function getAccessToken() |
|
| 134 | + { |
|
| 135 | + $tokenNames = [ |
|
| 136 | + 'access_token', |
|
| 137 | + 'id_token', |
|
| 138 | + 'access_token_secret', |
|
| 139 | + 'token_type', |
|
| 140 | + 'refresh_token', |
|
| 141 | + 'expires_in', |
|
| 142 | + 'expires_at', |
|
| 143 | + ]; |
|
| 144 | + |
|
| 145 | + $tokens = []; |
|
| 146 | + |
|
| 147 | + foreach ($tokenNames as $name) { |
|
| 148 | + if ($this->getStoredData($name)) { |
|
| 149 | + $tokens[$name] = $this->getStoredData($name); |
|
| 150 | + } |
|
| 151 | + } |
|
| 152 | + |
|
| 153 | + return $tokens; |
|
| 154 | + } |
|
| 155 | + |
|
| 156 | + /** |
|
| 157 | + * {@inheritdoc} |
|
| 158 | + */ |
|
| 159 | + protected function validateAccessTokenExchange($response) |
|
| 160 | + { |
|
| 161 | + $collection = parent::validateAccessTokenExchange($response); |
|
| 162 | + |
|
| 163 | + $this->storeData('id_token', $collection->get('id_token')); |
|
| 164 | + |
|
| 165 | + return $collection; |
|
| 166 | + } |
|
| 167 | + |
|
| 168 | + /** |
|
| 169 | + * Get the user profile |
|
| 170 | + * |
|
| 171 | + * @throws HttpClientFailureException |
|
| 172 | + * @throws InvalidAccessTokenException |
|
| 173 | + * @throws UnexpectedValueException |
|
| 174 | + * @throws HttpRequestFailedException |
|
| 175 | + * @throws Exception |
|
| 176 | + */ |
|
| 177 | + public function getUserProfile() |
|
| 178 | + { |
|
| 179 | + $id_token = $this->getStoredData('id_token'); |
|
| 180 | + |
|
| 181 | + $verifyTokenSignature = |
|
| 182 | + $this->config->exists('verifyTokenSignature') ? $this->config->get('verifyTokenSignature') : true; |
|
| 183 | + |
|
| 184 | + if (!$verifyTokenSignature) { |
|
| 185 | + // payload extraction by https://github.com/omidborjian |
|
| 186 | + // https://github.com/hybridauth/hybridauth/issues/1095#issuecomment-626479263 |
|
| 187 | + // JWT splits the string to 3 components 1) first is header 2) is payload 3) is signature |
|
| 188 | + $payload = explode('.', $id_token)[1]; |
|
| 189 | + $payload = json_decode(base64_decode($payload)); |
|
| 190 | + } else { |
|
| 191 | + // validate the token signature and get the payload |
|
| 192 | + $publicKeys = $this->apiRequest('keys'); |
|
| 193 | + |
|
| 194 | + JWT::$leeway = 120; |
|
| 195 | + |
|
| 196 | + $error = false; |
|
| 197 | + $payload = null; |
|
| 198 | + |
|
| 199 | + foreach ($publicKeys->keys as $publicKey) { |
|
| 200 | + try { |
|
| 201 | + $jwk = (array)$publicKey; |
|
| 202 | + |
|
| 203 | + $key = PublicKeyLoader::load( |
|
| 204 | + [ |
|
| 205 | + 'e' => new BigInteger(base64_decode($jwk['e']), 256), |
|
| 206 | + 'n' => new BigInteger(base64_decode(strtr($jwk['n'], '-_', '+/'), true), 256) |
|
| 207 | + ] |
|
| 208 | + ) |
|
| 209 | + ->withHash('sha1') |
|
| 210 | + ->withMGFHash('sha1'); |
|
| 211 | + |
|
| 212 | + $pem = (string)$key; |
|
| 213 | + |
|
| 214 | + $payload = (version_compare($this->getJwtVersion(), '6.2') < 0) ? |
|
| 215 | + JWT::decode($id_token, $pem, ['RS256']) : |
|
| 216 | + JWT::decode($id_token, new Key($pem, 'RS256')); |
|
| 217 | + break; |
|
| 218 | + } catch (Exception $e) { |
|
| 219 | + $error = $e->getMessage(); |
|
| 220 | + if ($e instanceof ExpiredException) { |
|
| 221 | + break; |
|
| 222 | + } |
|
| 223 | + } |
|
| 224 | + } |
|
| 225 | + |
|
| 226 | + if ($error && !$payload) { |
|
| 227 | + throw new Exception($error); |
|
| 228 | + } |
|
| 229 | + } |
|
| 230 | + |
|
| 231 | + $data = new Data\Collection($payload); |
|
| 232 | + |
|
| 233 | + if (!$data->exists('sub')) { |
|
| 234 | + throw new UnexpectedValueException('Missing token payload.'); |
|
| 235 | + } |
|
| 236 | + |
|
| 237 | + $userProfile = new User\Profile(); |
|
| 238 | + $userProfile->identifier = $data->get('sub'); |
|
| 239 | + $userProfile->email = $data->get('email'); |
|
| 240 | + $this->storeData('expires_at', $data->get('exp')); |
|
| 241 | + |
|
| 242 | + if (!empty($_REQUEST['user'])) { |
|
| 243 | + $objUser = json_decode($_REQUEST['user']); |
|
| 244 | + $user = new Data\Collection($objUser); |
|
| 245 | + if (!$user->isEmpty()) { |
|
| 246 | + $name = $user->get('name'); |
|
| 247 | + if (!empty($name->firstName)) { |
|
| 248 | + $userProfile->firstName = $name->firstName; |
|
| 249 | + $userProfile->lastName = $name->lastName; |
|
| 250 | + $userProfile->displayName = join(' ', [$userProfile->firstName, $userProfile->lastName]); |
|
| 251 | + } |
|
| 252 | + } |
|
| 253 | + } |
|
| 254 | + |
|
| 255 | + return $userProfile; |
|
| 256 | + } |
|
| 257 | + |
|
| 258 | + /** |
|
| 259 | + * Get the Apple secret as a JWT token |
|
| 260 | + * |
|
| 261 | + * @return string secret token |
|
| 262 | + * @throws InvalidApplicationCredentialsException |
|
| 263 | + */ |
|
| 264 | + private function getSecret() |
|
| 265 | + { |
|
| 266 | + // Your 10-character Team ID |
|
| 267 | + $team_id = $this->config->filter('keys')->get('team_id'); |
|
| 268 | + |
|
| 269 | + if (!$team_id) { |
|
| 270 | + throw new InvalidApplicationCredentialsException( |
|
| 271 | + 'Missing parameter team_id: your team id is required to generate the JWS token.' |
|
| 272 | + ); |
|
| 273 | + } |
|
| 274 | + |
|
| 275 | + // Your Services ID, e.g. com.aaronparecki.services |
|
| 276 | + $client_id = $this->config->filter('keys')->get('id') ?: $this->config->filter('keys')->get('key'); |
|
| 277 | + |
|
| 278 | + if (!$client_id) { |
|
| 279 | + throw new InvalidApplicationCredentialsException( |
|
| 280 | + 'Missing parameter id: your client id is required to generate the JWS token.' |
|
| 281 | + ); |
|
| 282 | + } |
|
| 283 | + |
|
| 284 | + // Find the 10-char Key ID value from the portal |
|
| 285 | + $key_id = $this->config->filter('keys')->get('key_id'); |
|
| 286 | + |
|
| 287 | + if (!$key_id) { |
|
| 288 | + throw new InvalidApplicationCredentialsException( |
|
| 289 | + 'Missing parameter key_id: your key id is required to generate the JWS token.' |
|
| 290 | + ); |
|
| 291 | + } |
|
| 292 | + |
|
| 293 | + // Find the 10-char Key ID value from the portal |
|
| 294 | + $key_content = $this->config->filter('keys')->get('key_content'); |
|
| 295 | + |
|
| 296 | + // Save your private key from Apple in a file called `key.txt` |
|
| 297 | + if (!$key_content) { |
|
| 298 | + $key_file = $this->config->filter('keys')->get('key_file'); |
|
| 299 | + |
|
| 300 | + if (!$key_file) { |
|
| 301 | + throw new InvalidApplicationCredentialsException( |
|
| 302 | + 'Missing parameter key_content or key_file: your key is required to generate the JWS token.' |
|
| 303 | + ); |
|
| 304 | + } |
|
| 305 | + |
|
| 306 | + if (!file_exists($key_file)) { |
|
| 307 | + throw new InvalidApplicationCredentialsException( |
|
| 308 | + "Your key file $key_file does not exist." |
|
| 309 | + ); |
|
| 310 | + } |
|
| 311 | + |
|
| 312 | + $key_content = file_get_contents($key_file); |
|
| 313 | + } |
|
| 314 | + |
|
| 315 | + $data = [ |
|
| 316 | + 'iat' => time(), |
|
| 317 | + 'exp' => time() + 86400 * 180, |
|
| 318 | + 'iss' => $team_id, |
|
| 319 | + 'aud' => 'https://appleid.apple.com', |
|
| 320 | + 'sub' => $client_id |
|
| 321 | + ]; |
|
| 322 | + |
|
| 323 | + return JWT::encode($data, $key_content, 'ES256', $key_id); |
|
| 324 | + } |
|
| 325 | + |
|
| 326 | + /** |
|
| 327 | + * Try to get the installed JWT version |
|
| 328 | + * |
|
| 329 | + * If composer 2 is installed use InstalledVersions::getVersion, |
|
| 330 | + * otherwise return an empty string because no version check is available |
|
| 331 | + * |
|
| 332 | + * @return string|null |
|
| 333 | + */ |
|
| 334 | + private function getJwtVersion() |
|
| 335 | + { |
|
| 336 | + // assume old JWT version if no version check is possible because composer 1 is installed |
|
| 337 | + return class_exists('Composer\InstalledVersions') ? |
|
| 338 | + InstalledVersions::getVersion('firebase/php-jwt') : |
|
| 339 | + ''; |
|
| 340 | + } |
|
| 341 | 341 | } |
@@ -181,7 +181,7 @@ discard block |
||
| 181 | 181 | $verifyTokenSignature = |
| 182 | 182 | $this->config->exists('verifyTokenSignature') ? $this->config->get('verifyTokenSignature') : true; |
| 183 | 183 | |
| 184 | - if (!$verifyTokenSignature) { |
|
| 184 | + if ( ! $verifyTokenSignature) { |
|
| 185 | 185 | // payload extraction by https://github.com/omidborjian |
| 186 | 186 | // https://github.com/hybridauth/hybridauth/issues/1095#issuecomment-626479263 |
| 187 | 187 | // JWT splits the string to 3 components 1) first is header 2) is payload 3) is signature |
@@ -198,7 +198,7 @@ discard block |
||
| 198 | 198 | |
| 199 | 199 | foreach ($publicKeys->keys as $publicKey) { |
| 200 | 200 | try { |
| 201 | - $jwk = (array)$publicKey; |
|
| 201 | + $jwk = (array) $publicKey; |
|
| 202 | 202 | |
| 203 | 203 | $key = PublicKeyLoader::load( |
| 204 | 204 | [ |
@@ -209,11 +209,10 @@ discard block |
||
| 209 | 209 | ->withHash('sha1') |
| 210 | 210 | ->withMGFHash('sha1'); |
| 211 | 211 | |
| 212 | - $pem = (string)$key; |
|
| 212 | + $pem = (string) $key; |
|
| 213 | 213 | |
| 214 | 214 | $payload = (version_compare($this->getJwtVersion(), '6.2') < 0) ? |
| 215 | - JWT::decode($id_token, $pem, ['RS256']) : |
|
| 216 | - JWT::decode($id_token, new Key($pem, 'RS256')); |
|
| 215 | + JWT::decode($id_token, $pem, ['RS256']) : JWT::decode($id_token, new Key($pem, 'RS256')); |
|
| 217 | 216 | break; |
| 218 | 217 | } catch (Exception $e) { |
| 219 | 218 | $error = $e->getMessage(); |
@@ -223,14 +222,14 @@ discard block |
||
| 223 | 222 | } |
| 224 | 223 | } |
| 225 | 224 | |
| 226 | - if ($error && !$payload) { |
|
| 225 | + if ($error && ! $payload) { |
|
| 227 | 226 | throw new Exception($error); |
| 228 | 227 | } |
| 229 | 228 | } |
| 230 | 229 | |
| 231 | 230 | $data = new Data\Collection($payload); |
| 232 | 231 | |
| 233 | - if (!$data->exists('sub')) { |
|
| 232 | + if ( ! $data->exists('sub')) { |
|
| 234 | 233 | throw new UnexpectedValueException('Missing token payload.'); |
| 235 | 234 | } |
| 236 | 235 | |
@@ -239,12 +238,12 @@ discard block |
||
| 239 | 238 | $userProfile->email = $data->get('email'); |
| 240 | 239 | $this->storeData('expires_at', $data->get('exp')); |
| 241 | 240 | |
| 242 | - if (!empty($_REQUEST['user'])) { |
|
| 241 | + if ( ! empty($_REQUEST['user'])) { |
|
| 243 | 242 | $objUser = json_decode($_REQUEST['user']); |
| 244 | 243 | $user = new Data\Collection($objUser); |
| 245 | - if (!$user->isEmpty()) { |
|
| 244 | + if ( ! $user->isEmpty()) { |
|
| 246 | 245 | $name = $user->get('name'); |
| 247 | - if (!empty($name->firstName)) { |
|
| 246 | + if ( ! empty($name->firstName)) { |
|
| 248 | 247 | $userProfile->firstName = $name->firstName; |
| 249 | 248 | $userProfile->lastName = $name->lastName; |
| 250 | 249 | $userProfile->displayName = join(' ', [$userProfile->firstName, $userProfile->lastName]); |
@@ -266,7 +265,7 @@ discard block |
||
| 266 | 265 | // Your 10-character Team ID |
| 267 | 266 | $team_id = $this->config->filter('keys')->get('team_id'); |
| 268 | 267 | |
| 269 | - if (!$team_id) { |
|
| 268 | + if ( ! $team_id) { |
|
| 270 | 269 | throw new InvalidApplicationCredentialsException( |
| 271 | 270 | 'Missing parameter team_id: your team id is required to generate the JWS token.' |
| 272 | 271 | ); |
@@ -275,7 +274,7 @@ discard block |
||
| 275 | 274 | // Your Services ID, e.g. com.aaronparecki.services |
| 276 | 275 | $client_id = $this->config->filter('keys')->get('id') ?: $this->config->filter('keys')->get('key'); |
| 277 | 276 | |
| 278 | - if (!$client_id) { |
|
| 277 | + if ( ! $client_id) { |
|
| 279 | 278 | throw new InvalidApplicationCredentialsException( |
| 280 | 279 | 'Missing parameter id: your client id is required to generate the JWS token.' |
| 281 | 280 | ); |
@@ -284,7 +283,7 @@ discard block |
||
| 284 | 283 | // Find the 10-char Key ID value from the portal |
| 285 | 284 | $key_id = $this->config->filter('keys')->get('key_id'); |
| 286 | 285 | |
| 287 | - if (!$key_id) { |
|
| 286 | + if ( ! $key_id) { |
|
| 288 | 287 | throw new InvalidApplicationCredentialsException( |
| 289 | 288 | 'Missing parameter key_id: your key id is required to generate the JWS token.' |
| 290 | 289 | ); |
@@ -294,16 +293,16 @@ discard block |
||
| 294 | 293 | $key_content = $this->config->filter('keys')->get('key_content'); |
| 295 | 294 | |
| 296 | 295 | // Save your private key from Apple in a file called `key.txt` |
| 297 | - if (!$key_content) { |
|
| 296 | + if ( ! $key_content) { |
|
| 298 | 297 | $key_file = $this->config->filter('keys')->get('key_file'); |
| 299 | 298 | |
| 300 | - if (!$key_file) { |
|
| 299 | + if ( ! $key_file) { |
|
| 301 | 300 | throw new InvalidApplicationCredentialsException( |
| 302 | 301 | 'Missing parameter key_content or key_file: your key is required to generate the JWS token.' |
| 303 | 302 | ); |
| 304 | 303 | } |
| 305 | 304 | |
| 306 | - if (!file_exists($key_file)) { |
|
| 305 | + if ( ! file_exists($key_file)) { |
|
| 307 | 306 | throw new InvalidApplicationCredentialsException( |
| 308 | 307 | "Your key file $key_file does not exist." |
| 309 | 308 | ); |
@@ -335,7 +334,6 @@ discard block |
||
| 335 | 334 | { |
| 336 | 335 | // assume old JWT version if no version check is possible because composer 1 is installed |
| 337 | 336 | return class_exists('Composer\InstalledVersions') ? |
| 338 | - InstalledVersions::getVersion('firebase/php-jwt') : |
|
| 339 | - ''; |
|
| 337 | + InstalledVersions::getVersion('firebase/php-jwt') : ''; |
|
| 340 | 338 | } |
| 341 | 339 | } |