mirror of
https://github.com/code-rhapsodie/oauth2-apple.git
synced 2026-04-29 06:33:12 +02:00
Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 95cc38cd0c | |||
| a08ded7b3a | |||
| 31c61d334b | |||
| d649df8313 | |||
| 6702931e2e | |||
| 5598a94a36 | |||
| e191fd0988 | |||
| 8c49a59451 | |||
| 45903a3f90 | |||
| 06ef0a50d1 | |||
| a6b002a062 | |||
| 1b47b8e9d3 | |||
| 3f23b1d156 | |||
| 81164c4b8a | |||
| a45b7cdc9b |
+7
-2
@@ -7,6 +7,8 @@ matrix:
|
||||
- php: 7.1
|
||||
- php: 7.2
|
||||
- php: 7.3
|
||||
- php: 7.4
|
||||
- php: 8.0
|
||||
- php: nightly
|
||||
- php: hhvm-3.6
|
||||
sudo: required
|
||||
@@ -47,5 +49,8 @@ script:
|
||||
- ./vendor/bin/phpunit --coverage-text --coverage-clover=coverage.clover
|
||||
|
||||
after_script:
|
||||
- if [ "$TRAVIS_PHP_VERSION" == "7.1" ]; then wget https://scrutinizer-ci.com/ocular.phar; fi
|
||||
- if [ "$TRAVIS_PHP_VERSION" == "7.1" ]; then php ocular.phar code-coverage:upload --format=php-clover coverage.clover; fi
|
||||
- if [ "$TRAVIS_PHP_VERSION" == "7.4" ]; then wget https://scrutinizer-ci.com/ocular.phar; fi
|
||||
- if [ "$TRAVIS_PHP_VERSION" == "7.4" ]; then php ocular.phar code-coverage:upload --format=php-clover coverage.clover; fi
|
||||
|
||||
after_success:
|
||||
- bash <(curl -s https://codecov.io/bash)
|
||||
|
||||
@@ -18,6 +18,24 @@ All Notable changes to `oauth2-apple` will be documented in this file
|
||||
### Security
|
||||
- Nothing
|
||||
|
||||
## 0.2.4 - 2021-01-17
|
||||
|
||||
### Added
|
||||
- Codecov for Code Coverage
|
||||
|
||||
### Fixed
|
||||
- Few compatibility issues with PHP 8 and PHP 5.6 (Read [#16](https://github.com/patrickbussmann/oauth2-apple/pull/16) for more details)
|
||||
|
||||
## 0.2.3 - 2021-01-05
|
||||
|
||||
### Added
|
||||
- Using guzzle http instead of file_get_contents [#14](https://github.com/patrickbussmann/oauth2-apple/pull/14)/[#17](https://github.com/patrickbussmann/oauth2-apple/pull/17) (thanks to [jmalinens](https://github.com/jmalinens) and [williamxsp](https://github.com/williamxsp))
|
||||
- README no scope instruction [#15](https://github.com/patrickbussmann/oauth2-apple/pull/15) (thanks to [NgSekLong](https://github.com/NgSekLong))
|
||||
- README leeway usage [#18](https://github.com/patrickbussmann/oauth2-apple/issues/18) (thanks to [lukequinnell](https://github.com/lukequinnell))
|
||||
|
||||
### Fixed
|
||||
- Fixed getting first and last name issues [#13](https://github.com/patrickbussmann/oauth2-apple/pull/13) (thanks to [bogdandovgopol](https://github.com/bogdandovgopol))
|
||||
|
||||
## 0.2.1 - 2020-02-13
|
||||
|
||||
### Added
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
# Sign in with Apple ID Provider for OAuth 2.0 Client
|
||||
[](https://github.com/patrickbussmann/oauth2-apple/releases)
|
||||
[](LICENSE.md)
|
||||
[](https://travis-ci.org/patrickbussmann/oauth2-apple)
|
||||
[](https://travis-ci.org/patrickbussmann/oauth2-apple)
|
||||
[](https://scrutinizer-ci.com/g/patrickbussmann/oauth2-apple/code-structure)
|
||||
[](https://scrutinizer-ci.com/g/patrickbussmann/oauth2-apple)
|
||||
[](https://codecov.io/gh/patrickbussmann/oauth2-apple)
|
||||
[](https://packagist.org/packages/patrickbussmann/oauth2-apple)
|
||||
|
||||
This package provides Apple ID OAuth 2.0 support for the PHP League's [OAuth 2.0 Client](https://github.com/thephpleague/oauth2-client).
|
||||
@@ -32,11 +33,14 @@ Usage is the same as The League's OAuth client, using `\League\OAuth2\Client\Pro
|
||||
### Authorization Code Flow
|
||||
|
||||
```php
|
||||
// $leeway is needed for clock skew
|
||||
Firebase\JWT\JWT::$leeway = 60;
|
||||
|
||||
$provider = new League\OAuth2\Client\Provider\Apple([
|
||||
'clientId' => '{apple-client-id}',
|
||||
'teamId' => '{apple-team-id}', // 1A234BFK46 https://developer.apple.com/account/#/membership/ (Team ID)
|
||||
'keyFileId' => '{apple-key-file-id}', // 1ABC6523AA https://developer.apple.com/account/resources/authkeys/list (Key ID)
|
||||
'keyFilePath' => '{apple-key-file-path}', // __DIR__ . '/AuthKey_1ABC6523AA.p8' -> Download key above
|
||||
'keyFilePath' => '{apple-key-file-path}', // __DIR__ . '/AuthKey_1ABC6523AA.p8' -> Download key above
|
||||
'redirectUri' => 'https://example.com/callback-url',
|
||||
]);
|
||||
|
||||
@@ -107,6 +111,7 @@ At the time of authoring this documentation, the following scopes are available.
|
||||
Please note that you will get this informations only at the first log in of the user!
|
||||
In the following log ins you'll get only the user id!
|
||||
|
||||
If you only want to get the user id, you can set the `scope` as ` `, then change all the `$_POST` to `$_GET`.
|
||||
|
||||
### Refresh Tokens
|
||||
|
||||
@@ -125,15 +130,15 @@ $ ./vendor/bin/phpunit
|
||||
|
||||
## Contributing
|
||||
|
||||
Please see [CONTRIBUTING](https://github.com/patrickbussmann/oauth2-apple/blob/master/CONTRIBUTING.md) for details.
|
||||
Please see [CONTRIBUTING](https://github.com/patrickbussmann/oauth2-apple/blob/main/CONTRIBUTING.md) for details.
|
||||
|
||||
|
||||
## Credits
|
||||
|
||||
- [All Contributors](https://github.com/patrickbussmann/oauth2-apple/contributors)
|
||||
|
||||
Template for this repository was the [LinkedIn](https://github.com/thephpleague/oauth2-linkedin).
|
||||
Template for this repository was the [LinkedIn](https://github.com/thephpleague/oauth2-linkedin).
|
||||
|
||||
## License
|
||||
|
||||
The MIT License (MIT). Please see [License File](https://github.com/patrickbussmann/oauth2-apple/blob/master/LICENSE) for more information.
|
||||
The MIT License (MIT). Please see [License File](https://github.com/patrickbussmann/oauth2-apple/blob/main/LICENSE) for more information.
|
||||
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
codecov:
|
||||
require_ci_to_pass: yes
|
||||
|
||||
coverage:
|
||||
precision: 2
|
||||
round: down
|
||||
range: "70...100"
|
||||
status:
|
||||
project:
|
||||
default:
|
||||
target: auto
|
||||
threshold: 0%
|
||||
patch:
|
||||
default:
|
||||
target: auto
|
||||
threshold: 0%
|
||||
|
||||
parsers:
|
||||
gcov:
|
||||
branch_detection:
|
||||
conditional: yes
|
||||
loop: yes
|
||||
method: no
|
||||
macro: no
|
||||
|
||||
comment:
|
||||
layout: "reach,diff,flags,tree"
|
||||
behavior: default
|
||||
require_changes: false
|
||||
+4
-4
@@ -22,12 +22,12 @@
|
||||
"league/oauth2-client": "^2.0",
|
||||
"ext-json": "*",
|
||||
"firebase/php-jwt": "^5.2",
|
||||
"lcobucci/jwt": "^3.3"
|
||||
"lcobucci/jwt": "^3.4 || ^4.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "~4.0",
|
||||
"mockery/mockery": "~0.9",
|
||||
"squizlabs/php_codesniffer": "~2.0",
|
||||
"phpunit/phpunit": "^5.7 || ^6.0 || ^9.3",
|
||||
"mockery/mockery": "^1.3",
|
||||
"squizlabs/php_codesniffer": "^2.3 || ^3.0",
|
||||
"ext-json": "*"
|
||||
},
|
||||
"autoload": {
|
||||
|
||||
+20
-35
@@ -1,37 +1,22 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit backupGlobals="false"
|
||||
backupStaticAttributes="false"
|
||||
bootstrap="vendor/autoload.php"
|
||||
colors="true"
|
||||
convertErrorsToExceptions="true"
|
||||
convertNoticesToExceptions="true"
|
||||
convertWarningsToExceptions="true"
|
||||
processIsolation="false"
|
||||
stopOnFailure="false"
|
||||
syntaxCheck="false"
|
||||
>
|
||||
<logging>
|
||||
<log type="coverage-html"
|
||||
target="./build/coverage/html"
|
||||
charset="UTF-8"
|
||||
highlight="false"
|
||||
lowUpperBound="35"
|
||||
highLowerBound="70"/>
|
||||
<log type="coverage-clover"
|
||||
target="./build/coverage/log/coverage.xml"/>
|
||||
</logging>
|
||||
<testsuites>
|
||||
<testsuite name="Package Test Suite">
|
||||
<directory suffix=".php">./test/</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<filter>
|
||||
<whitelist>
|
||||
<directory suffix=".php">./</directory>
|
||||
<exclude>
|
||||
<directory suffix=".php">./vendor</directory>
|
||||
<directory suffix=".php">./test</directory>
|
||||
</exclude>
|
||||
</whitelist>
|
||||
</filter>
|
||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" backupGlobals="false" backupStaticAttributes="false" bootstrap="vendor/autoload.php" colors="true" convertErrorsToExceptions="true" convertNoticesToExceptions="true" convertWarningsToExceptions="true" processIsolation="false" stopOnFailure="false" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd">
|
||||
<coverage>
|
||||
<include>
|
||||
<directory suffix=".php">./</directory>
|
||||
</include>
|
||||
<exclude>
|
||||
<directory suffix=".php">./vendor</directory>
|
||||
<directory suffix=".php">./test</directory>
|
||||
</exclude>
|
||||
<report>
|
||||
<clover outputFile="./build/coverage/log/coverage.xml"/>
|
||||
<html outputDirectory="./build/coverage/html" lowUpperBound="35" highLowerBound="70"/>
|
||||
</report>
|
||||
</coverage>
|
||||
<logging/>
|
||||
<testsuites>
|
||||
<testsuite name="Package Test Suite">
|
||||
<directory suffix=".php">./test/</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
</phpunit>
|
||||
|
||||
+25
-11
@@ -4,8 +4,9 @@ namespace League\OAuth2\Client\Provider;
|
||||
|
||||
use Exception;
|
||||
use InvalidArgumentException;
|
||||
use Lcobucci\JWT\Builder;
|
||||
use Lcobucci\JWT\Signer\Ecdsa\Sha256;
|
||||
use Lcobucci\JWT\Configuration;
|
||||
use Lcobucci\JWT\Signer\Key\LocalFileReference;
|
||||
use Lcobucci\JWT\Signer;
|
||||
use Lcobucci\JWT\Signer\Key;
|
||||
use League\OAuth2\Client\Grant\AbstractGrant;
|
||||
use League\OAuth2\Client\Provider\Exception\AppleAccessDeniedException;
|
||||
@@ -76,7 +77,7 @@ class Apple extends AbstractProvider
|
||||
*/
|
||||
protected function createAccessToken(array $response, AbstractGrant $grant)
|
||||
{
|
||||
return new AppleAccessToken($response);
|
||||
return new AppleAccessToken($this->getHttpClient(), $response);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -208,32 +209,45 @@ class Apple extends AbstractProvider
|
||||
*/
|
||||
public function getAccessToken($grant, array $options = [])
|
||||
{
|
||||
$signer = new Sha256();
|
||||
$time = time();
|
||||
$configuration = $this->getConfiguration();
|
||||
$time = new \DateTimeImmutable();
|
||||
$time = $time->setTime($time->format('H'), $time->format('i'), $time->format('s'));
|
||||
$expiresAt = $time->modify('+1 Hour');
|
||||
$expiresAt = $expiresAt->setTime($expiresAt->format('H'), $expiresAt->format('i'), $expiresAt->format('s'));
|
||||
|
||||
$token = (new Builder())
|
||||
$token = $configuration->builder()
|
||||
->issuedBy($this->teamId)
|
||||
->permittedFor('https://appleid.apple.com')
|
||||
->issuedAt($time)
|
||||
->expiresAt($time + 600)
|
||||
->expiresAt($expiresAt)
|
||||
->relatedTo($this->clientId)
|
||||
->withClaim('sub', $this->clientId)
|
||||
->withHeader('alg', 'ES256')
|
||||
->withHeader('kid', $this->keyFileId)
|
||||
->getToken($signer, $this->getLocalKey());
|
||||
->getToken($configuration->signer(), $configuration->signingKey());
|
||||
|
||||
$options += [
|
||||
'client_secret' => (string) $token
|
||||
'client_secret' => $token->toString()
|
||||
];
|
||||
|
||||
return parent::getAccessToken($grant, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Configuration
|
||||
*/
|
||||
public function getConfiguration()
|
||||
{
|
||||
return Configuration::forSymmetricSigner(
|
||||
Signer\Ecdsa\Sha256::create(),
|
||||
$this->getLocalKey()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Key
|
||||
*/
|
||||
public function getLocalKey()
|
||||
{
|
||||
return new Key('file://' . $this->keyFilePath);
|
||||
return LocalFileReference::file($this->keyFilePath);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,7 +46,11 @@ class AppleResourceOwner extends GenericResourceOwner
|
||||
*/
|
||||
public function getFirstName()
|
||||
{
|
||||
return $this->getAttribute('name')['firstName'];
|
||||
$name = $this->getAttribute('name');
|
||||
if (isset($name)) {
|
||||
return $name['firstName'];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -66,7 +70,11 @@ class AppleResourceOwner extends GenericResourceOwner
|
||||
*/
|
||||
public function getLastName()
|
||||
{
|
||||
return $this->getAttribute('name')['lastName'];
|
||||
$name = $this->getAttribute('name');
|
||||
if (isset($name)) {
|
||||
return $name['lastName'];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace League\OAuth2\Client\Token;
|
||||
|
||||
use Firebase\JWT\JWK;
|
||||
use Firebase\JWT\JWT;
|
||||
use GuzzleHttp\ClientInterface;
|
||||
use InvalidArgumentException;
|
||||
|
||||
class AppleAccessToken extends AccessToken
|
||||
@@ -23,17 +24,25 @@ class AppleAccessToken extends AccessToken
|
||||
*/
|
||||
protected $isPrivateEmail;
|
||||
|
||||
/**
|
||||
* @var ClientInterface
|
||||
*/
|
||||
protected $httpClient;
|
||||
|
||||
/**
|
||||
* Constructs an access token.
|
||||
*
|
||||
* @param ClientInterface $httpClient the http client to use
|
||||
* @param array $options An array of options returned by the service provider
|
||||
* in the access token request. The `access_token` option is required.
|
||||
* @throws InvalidArgumentException if `access_token` is not provided in `$options`.
|
||||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function __construct(array $options = [])
|
||||
public function __construct($httpClient, array $options = [])
|
||||
{
|
||||
$this->httpClient = $httpClient;
|
||||
|
||||
if (array_key_exists('refresh_token', $options)) {
|
||||
if (empty($options['id_token'])) {
|
||||
throw new InvalidArgumentException('Required option not passed: "id_token"');
|
||||
@@ -84,7 +93,13 @@ class AppleAccessToken extends AccessToken
|
||||
*/
|
||||
protected function getAppleKey()
|
||||
{
|
||||
return JWK::parseKeySet(json_decode(file_get_contents('https://appleid.apple.com/auth/keys'), true));
|
||||
$response = $this->httpClient->request('GET', 'https://appleid.apple.com/auth/keys');
|
||||
|
||||
if ($response) {
|
||||
return JWK::parseKeySet(json_decode($response->getBody()->getContents(), true));
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+157
-163
@@ -2,28 +2,26 @@
|
||||
|
||||
namespace League\OAuth2\Client\Test\Provider;
|
||||
|
||||
use Exception;
|
||||
use GuzzleHttp\ClientInterface;
|
||||
use GuzzleHttp\Psr7\Response;
|
||||
use InvalidArgumentException;
|
||||
use Lcobucci\JWT\Builder;
|
||||
use Lcobucci\JWT\Configuration;
|
||||
use League\OAuth2\Client\Provider\Apple;
|
||||
use League\OAuth2\Client\Test\Provider\TestApple;
|
||||
use League\OAuth2\Client\Provider\AppleResourceOwner;
|
||||
use League\OAuth2\Client\Provider\Exception\AppleAccessDeniedException;
|
||||
use League\OAuth2\Client\Token\AccessToken;
|
||||
use League\OAuth2\Client\Tool\QueryBuilderTrait;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Mockery as m;
|
||||
|
||||
class AppleTest extends \PHPUnit_Framework_TestCase
|
||||
class AppleTest extends TestCase
|
||||
{
|
||||
use QueryBuilderTrait;
|
||||
|
||||
/** @var Apple|\Mockery\MockInterface */
|
||||
protected $provider;
|
||||
|
||||
protected function setUp()
|
||||
/**
|
||||
* @return Apple
|
||||
*/
|
||||
private function getProvider()
|
||||
{
|
||||
$this->provider = new \League\OAuth2\Client\Provider\Apple([
|
||||
return new Apple([
|
||||
'clientId' => 'mock.example',
|
||||
'teamId' => 'mock.team.id',
|
||||
'keyFileId' => 'mock.file.id',
|
||||
@@ -32,62 +30,49 @@ class AppleTest extends \PHPUnit_Framework_TestCase
|
||||
]);
|
||||
}
|
||||
|
||||
public function tearDown()
|
||||
public function testMissingTeamIdDuringInstantiationThrowsException()
|
||||
{
|
||||
m::close();
|
||||
parent::tearDown();
|
||||
$this->expectException('InvalidArgumentException');
|
||||
new Apple([
|
||||
'clientId' => 'mock.example',
|
||||
'keyFileId' => 'mock.file.id',
|
||||
'keyFilePath' => __DIR__ . '/p256-private-key.p8',
|
||||
'redirectUri' => 'none'
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException InvalidArgumentException
|
||||
*/
|
||||
public function testMissingTeamIdDuringInstantiationThrowsException()
|
||||
{
|
||||
new \League\OAuth2\Client\Provider\Apple([
|
||||
'clientId' => 'mock.example',
|
||||
'keyFileId' => 'mock.file.id',
|
||||
'keyFilePath' => __DIR__ . '/p256-private-key.p8',
|
||||
'redirectUri' => 'none'
|
||||
]);
|
||||
}
|
||||
public function testMissingKeyFileIdDuringInstantiationThrowsException()
|
||||
{
|
||||
$this->expectException('InvalidArgumentException');
|
||||
new Apple([
|
||||
'clientId' => 'mock.example',
|
||||
'teamId' => 'mock.team.id',
|
||||
'keyFilePath' => __DIR__ . '/p256-private-key.p8',
|
||||
'redirectUri' => 'none'
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException InvalidArgumentException
|
||||
*/
|
||||
public function testMissingKeyFileIdDuringInstantiationThrowsException()
|
||||
{
|
||||
new \League\OAuth2\Client\Provider\Apple([
|
||||
'clientId' => 'mock.example',
|
||||
'teamId' => 'mock.team.id',
|
||||
'keyFilePath' => __DIR__ . '/p256-private-key.p8',
|
||||
'redirectUri' => 'none'
|
||||
]);
|
||||
}
|
||||
public function testMissingKeyFilePathDuringInstantiationThrowsException()
|
||||
{
|
||||
$this->expectException('InvalidArgumentException');
|
||||
new Apple([
|
||||
'clientId' => 'mock.example',
|
||||
'teamId' => 'mock.team.id',
|
||||
'keyFileId' => 'mock.file.id',
|
||||
'redirectUri' => 'none'
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException InvalidArgumentException
|
||||
*/
|
||||
public function testMissingKeyFilePathDuringInstantiationThrowsException()
|
||||
{
|
||||
new \League\OAuth2\Client\Provider\Apple([
|
||||
'clientId' => 'mock.example',
|
||||
'teamId' => 'mock.team.id',
|
||||
'keyFileId' => 'mock.file.id',
|
||||
'redirectUri' => 'none'
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException InvalidArgumentException
|
||||
*/
|
||||
public function testMissingKeyDuringInstantiationThrowsException()
|
||||
{
|
||||
$this->provider->getLocalKey();
|
||||
}
|
||||
public function testMissingKeyDuringInstantiationThrowsException()
|
||||
{
|
||||
$this->expectException('InvalidArgumentException');
|
||||
$this->getProvider()->getLocalKey();
|
||||
}
|
||||
|
||||
public function testAuthorizationUrl()
|
||||
{
|
||||
$url = $this->provider->getAuthorizationUrl();
|
||||
$provider = $this->getProvider();
|
||||
$url = $provider->getAuthorizationUrl();
|
||||
$uri = parse_url($url);
|
||||
parse_str($uri['query'], $query);
|
||||
|
||||
@@ -97,22 +82,24 @@ class AppleTest extends \PHPUnit_Framework_TestCase
|
||||
$this->assertArrayHasKey('scope', $query);
|
||||
$this->assertArrayHasKey('response_type', $query);
|
||||
$this->assertArrayHasKey('response_mode', $query);
|
||||
$this->assertNotNull($this->provider->getState());
|
||||
$this->assertNotNull($provider->getState());
|
||||
}
|
||||
|
||||
public function testScopes()
|
||||
{
|
||||
$provider = $this->getProvider();
|
||||
$scopeSeparator = ' ';
|
||||
$options = ['scope' => [uniqid(), uniqid()]];
|
||||
$query = ['scope' => implode($scopeSeparator, $options['scope'])];
|
||||
$url = $this->provider->getAuthorizationUrl($options);
|
||||
$url = $provider->getAuthorizationUrl($options);
|
||||
$encodedScope = $this->buildQueryString($query);
|
||||
$this->assertContains($encodedScope, $url);
|
||||
$this->assertNotFalse(strpos($url, $encodedScope));
|
||||
}
|
||||
|
||||
public function testGetAuthorizationUrl()
|
||||
{
|
||||
$url = $this->provider->getAuthorizationUrl();
|
||||
$provider = $this->getProvider();
|
||||
$url = $provider->getAuthorizationUrl();
|
||||
$uri = parse_url($url);
|
||||
|
||||
$this->assertEquals('/auth/authorize', $uri['path']);
|
||||
@@ -120,135 +107,142 @@ class AppleTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
public function testGetBaseAccessTokenUrl()
|
||||
{
|
||||
$provider = $this->getProvider();
|
||||
$params = [];
|
||||
|
||||
$url = $this->provider->getBaseAccessTokenUrl($params);
|
||||
$url = $provider->getBaseAccessTokenUrl($params);
|
||||
$uri = parse_url($url);
|
||||
|
||||
$this->assertEquals('/auth/token', $uri['path']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Firebase\JWT\SignatureInvalidException
|
||||
*/
|
||||
public function testGetAccessToken()
|
||||
{
|
||||
$provider = new TestApple([
|
||||
'clientId' => 'mock.example',
|
||||
'teamId' => 'mock.team.id',
|
||||
'keyFileId' => 'mock.file.id',
|
||||
'keyFilePath' => __DIR__ . '/../../resources/p256-private-key.p8',
|
||||
'redirectUri' => 'none'
|
||||
]);
|
||||
$this->expectException('UnexpectedValueException');
|
||||
$provider = new TestApple([
|
||||
'clientId' => 'mock.example',
|
||||
'teamId' => 'mock.team.id',
|
||||
'keyFileId' => 'mock.file.id',
|
||||
'keyFilePath' => __DIR__ . '/../../resources/p256-private-key.p8',
|
||||
'redirectUri' => 'none'
|
||||
]);
|
||||
$provider = m::mock($provider);
|
||||
|
||||
$time = time();
|
||||
$token = (new Builder())
|
||||
->issuedBy('test-team-id')
|
||||
->permittedFor('https://appleid.apple.com')
|
||||
->issuedAt($time)
|
||||
->expiresAt($time + 600)
|
||||
->relatedTo('test-client')
|
||||
->withClaim('sub', 'test')
|
||||
->withHeader('alg', 'RS256')
|
||||
->withHeader('kid', 'test')
|
||||
->getToken();
|
||||
|
||||
$client = m::mock('GuzzleHttp\ClientInterface');
|
||||
$client->shouldReceive('send')
|
||||
->times(1)
|
||||
->andReturn(new Response(200, [], json_encode([
|
||||
'access_token' => 'aad897dee58fe4f66bf220c181adaf82b.0.mrwxq.hmiE0djj1vJqoNisKmF-pA',
|
||||
'token_type' => 'Bearer',
|
||||
'expires_in' => 3600,
|
||||
'refresh_token' => 'r4a6e8b9c50104b78bc86b0d2649353fa.0.mrwxq.54joUj40j0cpuMANRtRjfg',
|
||||
'id_token' => (string) $token
|
||||
])));
|
||||
$provider->setHttpClient($client);
|
||||
$configuration = Configuration::forUnsecuredSigner();
|
||||
|
||||
$provider->getAccessToken('authorization_code', [
|
||||
'code' => 'hello-world'
|
||||
]);
|
||||
$time = new \DateTimeImmutable();
|
||||
$expiresAt = $time->modify('+1 Hour');
|
||||
$token = $configuration->builder()
|
||||
->issuedBy('test-team-id')
|
||||
->permittedFor('https://appleid.apple.com')
|
||||
->issuedAt($time)
|
||||
->expiresAt($expiresAt)
|
||||
->relatedTo('test-client')
|
||||
->withHeader('alg', 'RS256')
|
||||
->withHeader('kid', 'test')
|
||||
->getToken($configuration->signer(), $configuration->signingKey());
|
||||
|
||||
$client = m::mock(ClientInterface::class);
|
||||
$client->shouldReceive('request')
|
||||
->times(1)
|
||||
->andReturn(new Response(200, [], file_get_contents('https://appleid.apple.com/auth/keys')));
|
||||
$client->shouldReceive('send')
|
||||
->times(1)
|
||||
->andReturn(new Response(200, [], json_encode([
|
||||
'access_token' => 'aad897dee58fe4f66bf220c181adaf82b.0.mrwxq.hmiE0djj1vJqoNisKmF-pA',
|
||||
'token_type' => 'Bearer',
|
||||
'expires_in' => 3600,
|
||||
'refresh_token' => 'r4a6e8b9c50104b78bc86b0d2649353fa.0.mrwxq.54joUj40j0cpuMANRtRjfg',
|
||||
'id_token' => $token->toString()
|
||||
])));
|
||||
$provider->setHttpClient($client);
|
||||
|
||||
$provider->getAccessToken('authorization_code', [
|
||||
'code' => 'hello-world'
|
||||
]);
|
||||
}
|
||||
|
||||
public function testFetchingOwnerDetails()
|
||||
{
|
||||
$class = new \ReflectionClass($this->provider);
|
||||
$method = $class->getMethod('fetchResourceOwnerDetails');
|
||||
$method->setAccessible(true);
|
||||
public function testFetchingOwnerDetails()
|
||||
{
|
||||
$provider = $this->getProvider();
|
||||
$class = new \ReflectionClass($provider);
|
||||
$method = $class->getMethod('fetchResourceOwnerDetails');
|
||||
$method->setAccessible(true);
|
||||
|
||||
$arr = [
|
||||
'name' => 'John Doe'
|
||||
];
|
||||
$_POST['user'] = json_encode($arr);
|
||||
$data = $method->invokeArgs($this->provider, [new AccessToken(['access_token' => 'hello'])]);
|
||||
$arr = [
|
||||
'name' => 'John Doe'
|
||||
];
|
||||
$_POST['user'] = json_encode($arr);
|
||||
$data = $method->invokeArgs($provider, [new AccessToken(['access_token' => 'hello'])]);
|
||||
|
||||
$this->assertEquals($arr, $data);
|
||||
}
|
||||
$this->assertEquals($arr, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see https://github.com/patrickbussmann/oauth2-apple/issues/12
|
||||
*/
|
||||
public function testFetchingOwnerDetailsIssue12()
|
||||
{
|
||||
$class = new \ReflectionClass($this->provider);
|
||||
$method = $class->getMethod('fetchResourceOwnerDetails');
|
||||
$method->setAccessible(true);
|
||||
public function testFetchingOwnerDetailsIssue12()
|
||||
{
|
||||
$provider = $this->getProvider();
|
||||
$class = new \ReflectionClass($provider);
|
||||
$method = $class->getMethod('fetchResourceOwnerDetails');
|
||||
$method->setAccessible(true);
|
||||
|
||||
$_POST['user'] = '';
|
||||
$data = $method->invokeArgs($this->provider, [new AccessToken(['access_token' => 'hello'])]);
|
||||
$data = $method->invokeArgs($provider, [new AccessToken(['access_token' => 'hello'])]);
|
||||
|
||||
$this->assertEquals([], $data);
|
||||
}
|
||||
$this->assertEquals([], $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException Exception
|
||||
*/
|
||||
public function testNotImplementedGetResourceOwnerDetailsUrl()
|
||||
{
|
||||
$this->provider->getResourceOwnerDetailsUrl(new AccessToken(['access_token' => 'hello']));
|
||||
}
|
||||
public function testNotImplementedGetResourceOwnerDetailsUrl()
|
||||
{
|
||||
$this->expectException('Exception');
|
||||
$provider = $this->getProvider();
|
||||
$provider->getResourceOwnerDetailsUrl(new AccessToken(['access_token' => 'hello']));
|
||||
}
|
||||
|
||||
public function testCheckResponse()
|
||||
{
|
||||
$this->setExpectedException(AppleAccessDeniedException::class, 'invalid_client', 400);
|
||||
public function testCheckResponse()
|
||||
{
|
||||
$this->expectException('\League\OAuth2\Client\Provider\Exception\AppleAccessDeniedException');
|
||||
$provider = $this->getProvider();
|
||||
$class = new \ReflectionClass($provider);
|
||||
$method = $class->getMethod('checkResponse');
|
||||
$method->setAccessible(true);
|
||||
|
||||
$class = new \ReflectionClass($this->provider);
|
||||
$method = $class->getMethod('checkResponse');
|
||||
$method->setAccessible(true);
|
||||
$method->invokeArgs($provider, [new Response(400, []), [
|
||||
'error' => 'invalid_client',
|
||||
'code' => 400
|
||||
]]);
|
||||
}
|
||||
|
||||
$method->invokeArgs($this->provider, [new Response(400, []), [
|
||||
'error' => 'invalid_client',
|
||||
'code' => 400
|
||||
]]);
|
||||
}
|
||||
public function testCreationOfResourceOwner()
|
||||
{
|
||||
$provider = $this->getProvider();
|
||||
$class = new \ReflectionClass($provider);
|
||||
$method = $class->getMethod('createResourceOwner');
|
||||
$method->setAccessible(true);
|
||||
|
||||
public function testCreationOfResourceOwner()
|
||||
{
|
||||
$class = new \ReflectionClass($this->provider);
|
||||
$method = $class->getMethod('createResourceOwner');
|
||||
$method->setAccessible(true);
|
||||
|
||||
/** @var AppleResourceOwner $data */
|
||||
$data = $method->invokeArgs($this->provider, [
|
||||
[
|
||||
'email' => 'john@doe.com',// <- Fake E-Mail from user input
|
||||
'name' => [
|
||||
'firstName' => 'John',
|
||||
'lastName' => 'Doe'
|
||||
]
|
||||
],
|
||||
new AccessToken([
|
||||
'access_token' => 'hello',
|
||||
'email' => 'john@doe.de',
|
||||
'resource_owner_id' => '123.4.567'
|
||||
])
|
||||
]);
|
||||
$this->assertEquals('john@doe.de', $data->getEmail());
|
||||
$this->assertEquals('Doe', $data->getLastName());
|
||||
$this->assertEquals('John', $data->getFirstName());
|
||||
$this->assertEquals('123.4.567', $data->getId());
|
||||
/** @var AppleResourceOwner $data */
|
||||
$data = $method->invokeArgs($provider, [
|
||||
[
|
||||
'email' => 'john@doe.com',// <- Fake E-Mail from user input
|
||||
'name' => [
|
||||
'firstName' => 'John',
|
||||
'lastName' => 'Doe'
|
||||
]
|
||||
],
|
||||
new AccessToken([
|
||||
'access_token' => 'hello',
|
||||
'email' => 'john@doe.de',
|
||||
'resource_owner_id' => '123.4.567'
|
||||
])
|
||||
]);
|
||||
$this->assertEquals('john@doe.de', $data->getEmail());
|
||||
$this->assertEquals('Doe', $data->getLastName());
|
||||
$this->assertEquals('John', $data->getFirstName());
|
||||
$this->assertEquals('123.4.567', $data->getId());
|
||||
$this->assertFalse($data->isPrivateEmail());
|
||||
$this->assertArrayHasKey('name', $data->toArray());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace League\OAuth2\Client\Test\Provider;
|
||||
|
||||
use Lcobucci\JWT\Configuration;
|
||||
use League\OAuth2\Client\Provider\Apple;
|
||||
|
||||
/**
|
||||
@@ -12,7 +13,15 @@ use League\OAuth2\Client\Provider\Apple;
|
||||
class TestApple extends Apple
|
||||
{
|
||||
/**
|
||||
* @return \Lcobucci\JWT\Signer\Key|null
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getConfiguration()
|
||||
{
|
||||
return Configuration::forUnsecuredSigner();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getLocalKey()
|
||||
{
|
||||
|
||||
@@ -2,18 +2,13 @@
|
||||
|
||||
namespace League\OAuth2\Client\Test\Token;
|
||||
|
||||
use GuzzleHttp\Psr7\Response;
|
||||
use League\OAuth2\Client\Token\AppleAccessToken;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Mockery as m;
|
||||
|
||||
class AppleAccessTokenTest extends TestCase
|
||||
{
|
||||
public function tearDown()
|
||||
{
|
||||
m::close();
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
/**
|
||||
* @runInSeparateProcess
|
||||
* @preserveGlobalState disabled
|
||||
@@ -33,7 +28,7 @@ class AppleAccessTokenTest extends TestCase
|
||||
->once()
|
||||
->andReturn(['examplekey']);
|
||||
|
||||
$accessToken = new AppleAccessToken([
|
||||
$accessToken = new AppleAccessToken($this->getClient(1), [
|
||||
'access_token' => 'access_token',
|
||||
'token_type' => 'Bearer',
|
||||
'expires_in' => 3600,
|
||||
@@ -47,11 +42,25 @@ class AppleAccessTokenTest extends TestCase
|
||||
|
||||
public function testCreatingRefreshToken()
|
||||
{
|
||||
$refreshToken = new AppleAccessToken([
|
||||
$refreshToken = new AppleAccessToken($this->getClient(0), [
|
||||
'access_token' => 'access_token',
|
||||
'token_type' => 'Bearer',
|
||||
'expires_in' => 3600
|
||||
]);
|
||||
$this->assertEquals('access_token', $refreshToken->getToken());
|
||||
}
|
||||
|
||||
private function getClient($times)
|
||||
{
|
||||
$client = m::mock('GuzzleHttp\ClientInterface');
|
||||
if ($times > 0) {
|
||||
$client->shouldReceive('request')
|
||||
->times($times)
|
||||
->withArgs(['GET', 'https://appleid.apple.com/auth/keys'])
|
||||
->andReturn(new Response())
|
||||
;
|
||||
}
|
||||
|
||||
return $client;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user