update
This commit is contained in:
@@ -17,3 +17,7 @@ This project uses [semantic versioning](https://semver.org).
|
||||
- Fix a license mismatch (#4)
|
||||
- Add user profile image to the default user (#5)
|
||||
- Update README to reflect state of Twitter API and this library
|
||||
|
||||
### 1.2.0
|
||||
|
||||
- Add user email to the default user
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
This package provides Twitter OAuth 2.0 support for the PHP League's [OAuth 2.0 Client](https://github.com/thephpleague/oauth2-client).
|
||||
|
||||
**As I (Evan) have closed all my X accounts, I can no longer verify this library's features myself. If you are willing to take maintenance of this library, please get in touch!**
|
||||
|
||||
## Installation
|
||||
|
||||
To install, use composer:
|
||||
@@ -31,14 +33,15 @@ $provider = new Smolblog\OAuth2\Client\Provider\Twitter([
|
||||
if (!isset($_GET['code'])) {
|
||||
unset($_SESSION['oauth2state']);
|
||||
unset($_SESSION['oauth2verifier']);
|
||||
|
||||
|
||||
// Optional: The default scopes are ‘tweet.read’, ‘users.read’,
|
||||
// and ‘offline.access’. You can change them like this:
|
||||
// ‘users.email’ and ‘offline.access’. You can change them like this:
|
||||
$options = [
|
||||
‘scope’ => [
|
||||
‘tweet.read’,
|
||||
‘tweet.write’,
|
||||
‘tweet.moderate.write’,
|
||||
‘users.email’,
|
||||
‘users.read’,
|
||||
‘follows.read’,
|
||||
‘follows.write’,
|
||||
@@ -55,8 +58,8 @@ if (!isset($_GET['code'])) {
|
||||
‘bookmark.read’,
|
||||
‘bookmark.write’,
|
||||
],
|
||||
];
|
||||
|
||||
];
|
||||
|
||||
|
||||
// If we don't have an authorization code then get one
|
||||
$authUrl = $provider->getAuthorizationUrl($options);
|
||||
@@ -90,7 +93,7 @@ if (!isset($_GET['code'])) {
|
||||
$user = $provider->getResourceOwner($token);
|
||||
|
||||
// Use these details to create a new profile
|
||||
printf('Hello %s!', $user->getName());
|
||||
printf('Hello %s (%s)!', $user->getName(), $user->getEmail());
|
||||
|
||||
} catch (Exception $e) {
|
||||
echo '<pre>';
|
||||
@@ -125,4 +128,4 @@ touch._
|
||||
|
||||
## License
|
||||
|
||||
The Modified 3-clause BSD License (BSD). Please see [License File](https://github.com/smolblog/oauth2-twitter/blob/main/LICENSE.md) for more information.
|
||||
The Modified 3-clause BSD License (BSD). Please see [License File](https://github.com/smolblog/oauth2-twitter/blob/main/LICENSE.md) for more information.
|
||||
|
||||
@@ -1,48 +1,47 @@
|
||||
{
|
||||
"name": "smolblog\/oauth2-twitter",
|
||||
"description": "Twitter OAuth 2.0 Client Provider for The PHP League OAuth2-Client",
|
||||
"license": "BSD-3-Clause",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Smolblog",
|
||||
"email": "dev@smolblog.org"
|
||||
},
|
||||
{
|
||||
"name": "Evan Hildreth",
|
||||
"email": "me@eph.me"
|
||||
}
|
||||
],
|
||||
"keywords": [
|
||||
"oauth",
|
||||
"oauth2",
|
||||
"client",
|
||||
"authorization",
|
||||
"authorisation",
|
||||
"twitter"
|
||||
],
|
||||
"require": {
|
||||
"php": "^7.3 || ^8.0",
|
||||
"league\/oauth2-client": "^2.0",
|
||||
"paragonie\/random-lib": "^2.0"
|
||||
"name": "smolblog/oauth2-twitter",
|
||||
"description": "Twitter OAuth 2.0 Client Provider for The PHP League OAuth2-Client",
|
||||
"license": "BSD-3-Clause",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Smolblog",
|
||||
"email": "dev@smolblog.org"
|
||||
},
|
||||
"require-dev": {
|
||||
"eloquent\/phony-phpunit": "^6.0 || ^7.1",
|
||||
"phpunit\/phpunit": ">=8.0",
|
||||
"squizlabs\/php_codesniffer": "^3.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Pshowsso\\Scope68f5e85e9608b\\Smolblog\\OAuth2\\Client\\Provider\\": "src\/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Pshowsso\\Scope68f5e85e9608b\\Smolblog\\OAuth2\\Client\\Provider\\Test\\": "test\/src\/"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"test": "phpunit --testdox test\/src\/",
|
||||
"lint": ".\/vendor\/squizlabs\/php_codesniffer\/bin\/phpcs",
|
||||
"lintfix": ".\/vendor\/squizlabs\/php_codesniffer\/bin\/phpcbf"
|
||||
{
|
||||
"name": "Evan Hildreth",
|
||||
"email": "me@eph.me"
|
||||
}
|
||||
}
|
||||
],
|
||||
"keywords": [
|
||||
"oauth",
|
||||
"oauth2",
|
||||
"client",
|
||||
"authorization",
|
||||
"authorisation",
|
||||
"twitter"
|
||||
],
|
||||
"require": {
|
||||
"php": "^7.3 || ^8.0",
|
||||
"league/oauth2-client": "^2.0",
|
||||
"paragonie/random-lib": "^2.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": ">=8.0",
|
||||
"squizlabs/php_codesniffer": "^3.0"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Smolblog\\OAuth2\\Client\\Provider\\": "src/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Smolblog\\OAuth2\\Client\\Provider\\Test\\": "test/src/"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"test": "phpunit --testdox test/src/",
|
||||
"lint": "./vendor/squizlabs/php_codesniffer/bin/phpcs",
|
||||
"lintfix": "./vendor/squizlabs/php_codesniffer/bin/phpcbf"
|
||||
}
|
||||
}
|
||||
|
||||
1175
modules/pshowsso/vendor/smolblog/oauth2-twitter/composer.lock
generated
vendored
1175
modules/pshowsso/vendor/smolblog/oauth2-twitter/composer.lock
generated
vendored
File diff suppressed because it is too large
Load Diff
@@ -2,77 +2,117 @@
|
||||
|
||||
namespace Smolblog\OAuth2\Client\Test\Provider;
|
||||
|
||||
use Eloquent\Phony\Phpunit\Phony;
|
||||
use Pshowsso\Scope68f5e85e9608b\League\OAuth2\Client\Provider\Exception\IdentityProviderException;
|
||||
use Pshowsso\Scope68f5e85e9608b\League\OAuth2\Client\Provider\ResourceOwnerInterface;
|
||||
use Pshowsso\Scope68f5e85e9608b\League\OAuth2\Client\Token\AccessToken;
|
||||
use League\OAuth2\Client\Provider\Exception\IdentityProviderException;
|
||||
use League\OAuth2\Client\Provider\ResourceOwnerInterface;
|
||||
use League\OAuth2\Client\Token\AccessToken;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Pshowsso\Scope68f5e85e9608b\Smolblog\OAuth2\Client\Provider\Twitter as TwitterProvider;
|
||||
use Smolblog\OAuth2\Client\Provider\Twitter as TwitterProvider;
|
||||
|
||||
class TwitterTest extends TestCase
|
||||
{
|
||||
/** @var TwitterProvider */
|
||||
protected $provider;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->provider = new TwitterProvider(['clientId' => 'mock_client_id', 'clientSecret' => 'mock_secret', 'redirectUri' => 'none', 'pkceVerifier' => 'ENuF7brJJNM5v-dEROtJf.Uee3kTO-GqNQ33fyuY33oixZXo9Vxiomml8-~3ulU9xu4xr_rj1weIer9UYu1JEzK_ZuDUtXe-zHi_2b6Eu41c~HEhzIlV6_QOQWeuvlyh']);
|
||||
$this->provider = new TwitterProvider([
|
||||
'clientId' => 'mock_client_id',
|
||||
'clientSecret' => 'mock_secret',
|
||||
'redirectUri' => 'none',
|
||||
'pkceVerifier' => 'ENuF7brJJNM5v-dEROtJf.Uee3kTO-GqNQ33fyuY33oixZXo9Vxiomml8-~3ulU9xu4xr_rj1weIer9UYu1JEzK_ZuDUtXe-zHi_2b6Eu41c~HEhzIlV6_QOQWeuvlyh',
|
||||
]);
|
||||
}
|
||||
/**
|
||||
* @link https://developer.twitter.com/en/docs/authentication/oauth-2-0/authorization-code
|
||||
*/
|
||||
|
||||
/**
|
||||
* @link https://developer.twitter.com/en/docs/authentication/oauth-2-0/authorization-code
|
||||
*/
|
||||
public function testSmipleAuthorizationUrl(): void
|
||||
{
|
||||
$url = $this->provider->getAuthorizationUrl();
|
||||
$uri = parse_url($url);
|
||||
parse_str($uri['query'], $query);
|
||||
|
||||
self::assertArrayHasKey('response_type', $query);
|
||||
self::assertArrayHasKey('client_id', $query);
|
||||
self::assertArrayHasKey('redirect_uri', $query);
|
||||
self::assertArrayHasKey('state', $query);
|
||||
self::assertArrayHasKey('code_challenge', $query);
|
||||
self::assertArrayHasKey('code_challenge_method', $query);
|
||||
|
||||
self::assertEquals('code', $query['response_type']);
|
||||
self::assertEquals('mock_client_id', $query['client_id']);
|
||||
self::assertEquals('none', $query['redirect_uri']);
|
||||
self::assertEquals('Q7tD_xw-1L6mtr1RgNQ6-ZHCqA2mRg8_5_OqERLrJtE', $query['code_challenge']);
|
||||
self::assertEquals('S256', $query['code_challenge_method']);
|
||||
|
||||
self::assertStringContainsString('tweet.read', $query['scope']);
|
||||
self::assertStringContainsString('users.read', $query['scope']);
|
||||
self::assertStringContainsString('offline.access', $query['scope']);
|
||||
|
||||
self::assertNotEmpty($this->provider->getState());
|
||||
}
|
||||
|
||||
public function testBaseAccessTokenUrl(): void
|
||||
{
|
||||
$url = $this->provider->getBaseAccessTokenUrl([]);
|
||||
$uri = parse_url($url);
|
||||
|
||||
self::assertEquals('/2/oauth2/token', $uri['path']);
|
||||
}
|
||||
|
||||
public function testResourceOwnerDetailsUrl(): void
|
||||
{
|
||||
$token = $this->mockAccessToken();
|
||||
|
||||
$url = $this->provider->getResourceOwnerDetailsUrl($token);
|
||||
self::assertEquals('https://api.twitter.com/2/users/me', $url);
|
||||
|
||||
self::assertStringStartsWith('https://api.twitter.com/2/users/me', $url);
|
||||
}
|
||||
|
||||
public function testUserData(): void
|
||||
{
|
||||
// Mock
|
||||
$response = ["data" => ["id" => "1132750396936589312", "name" => "Smolblog", "username" => "_smolblog"]];
|
||||
$response = [
|
||||
"data" => [
|
||||
"id" => "1132750396936589312",
|
||||
"name" => "Smolblog",
|
||||
"username" => "_smolblog",
|
||||
"confirmed_email" => "some@email.test",
|
||||
]
|
||||
];
|
||||
|
||||
$token = $this->mockAccessToken();
|
||||
$provider = Phony::partialMock(TwitterProvider::class);
|
||||
$provider->fetchResourceOwnerDetails->returns($response);
|
||||
$google = $provider->get();
|
||||
|
||||
$provider = $this
|
||||
->getMockBuilder(TwitterProvider::class)
|
||||
->onlyMethods(['fetchResourceOwnerDetails'])
|
||||
->setConstructorArgs([[
|
||||
'clientId' => 'mock_client_id',
|
||||
'clientSecret' => 'mock_secret',
|
||||
'redirectUri' => 'none',
|
||||
'pkceVerifier' => 'ENuF7brJJNM5v-dEROtJf.Uee3kTO-GqNQ33fyuY33oixZXo9Vxiomml8-~3ulU9xu4xr_rj1weIer9UYu1JEzK_ZuDUtXe-zHi_2b6Eu41c~HEhzIlV6_QOQWeuvlyh',
|
||||
]])
|
||||
->getMock();
|
||||
$provider->expects($this->once())->method('fetchResourceOwnerDetails')->willReturn($response);
|
||||
|
||||
// Execute
|
||||
$user = $google->getResourceOwner($token);
|
||||
// Verify
|
||||
Phony::inOrder($provider->fetchResourceOwnerDetails->called());
|
||||
$user = $provider->getResourceOwner($token);
|
||||
|
||||
self::assertInstanceOf(ResourceOwnerInterface::class, $user);
|
||||
|
||||
self::assertEquals(1132750396936589312, $user->getId());
|
||||
self::assertEquals('Smolblog', $user->getName());
|
||||
self::assertEquals('_smolblog', $user->getUsername());
|
||||
self::assertEquals('some@email.test', $user->getEmail());
|
||||
|
||||
$user = $user->toArray();
|
||||
|
||||
self::assertArrayHasKey('id', $user);
|
||||
self::assertArrayHasKey('name', $user);
|
||||
self::assertArrayHasKey('username', $user);
|
||||
self::assertArrayHasKey('confirmed_email', $user);
|
||||
}
|
||||
|
||||
public function testErrorResponse(): void
|
||||
{
|
||||
// Mock
|
||||
@@ -82,37 +122,68 @@ class TwitterTest extends TestCase
|
||||
"status": 401,
|
||||
"detail": "Unauthorized"
|
||||
}';
|
||||
$stream = Phony::mock('Pshowsso\Scope68f5e85e9608b\GuzzleHttp\Psr7\Stream');
|
||||
$stream->__toString->returns($error_json);
|
||||
$response = Phony::mock('Pshowsso\Scope68f5e85e9608b\GuzzleHttp\Psr7\Response');
|
||||
$response->getHeader->returns(['application/json']);
|
||||
$response->getBody->returns($stream);
|
||||
$provider = Phony::partialMock(TwitterProvider::class);
|
||||
$provider->getResponse->returns($response);
|
||||
$google = $provider->get();
|
||||
|
||||
$stream = $this->createMock('GuzzleHttp\Psr7\Stream');
|
||||
$stream->method('__toString')->willReturn($error_json);
|
||||
|
||||
$response = $this->createMock('GuzzleHttp\Psr7\Response');
|
||||
$response->expects($this->once())->method('getHeader')->willReturn(['application/json']);
|
||||
$response->expects($this->once())->method('getBody')->willReturn($stream);
|
||||
|
||||
$provider = $this
|
||||
->getMockBuilder(TwitterProvider::class)
|
||||
->onlyMethods(['getResponse'])
|
||||
->setConstructorArgs([[
|
||||
'clientId' => 'mock_client_id',
|
||||
'clientSecret' => 'mock_secret',
|
||||
'redirectUri' => 'none',
|
||||
'pkceVerifier' => 'ENuF7brJJNM5v-dEROtJf.Uee3kTO-GqNQ33fyuY33oixZXo9Vxiomml8-~3ulU9xu4xr_rj1weIer9UYu1JEzK_ZuDUtXe-zHi_2b6Eu41c~HEhzIlV6_QOQWeuvlyh',
|
||||
]])
|
||||
->getMock();
|
||||
$provider->expects($this->once())->method('getResponse')->willReturn($response);
|
||||
|
||||
|
||||
$token = $this->mockAccessToken();
|
||||
|
||||
// Expect
|
||||
$this->expectException(IdentityProviderException::class);
|
||||
|
||||
// Execute
|
||||
$user = $google->getResourceOwner($token);
|
||||
// Verify
|
||||
Phony::inOrder($provider->getResponse->calledWith($this->instanceOf('Pshowsso\Scope68f5e85e9608b\GuzzleHttp\Psr7\Request')), $response->getHeader->called(), $response->getBody->called());
|
||||
$user = $provider->getResourceOwner($token);
|
||||
}
|
||||
|
||||
public function testVerifierGeneration(): void
|
||||
{
|
||||
$verifier = $this->provider->generatePkceVerifier();
|
||||
$match_result = preg_match('/^[A-Za-z0-9\-._~]{43,128}$/', $verifier);
|
||||
|
||||
self::assertEquals(1, $match_result);
|
||||
}
|
||||
|
||||
public function testChallengeGeneration(): void
|
||||
{
|
||||
$tests = ['g0sseWY2Gp772L_Xu7T1tHkeqRGAOk_9JnU9gFYCmKkVbkFUHu5izyZEivpxDsZU-r40geolIbX64zEvQ7Y4SOYwKL9drG9OF2g1kTB.PJ7nHPbVLFJFL-ziSv6KclSK' => 'hzRLCtPmWN3w_EVqGW19ARrMaXZBwYrpnTMkelrYIv4', 'd_O4i_N0nDZdsjl6JGE.vYoIi-Yr8lXcEYWUKXbjwojf8VtMaTmOSwJJYQ5n5NYz2BrdKSQFkLei3sSzP0dygP8vUkH3rP-dEBl9l5rvFAUXtjsTXUusxwRTisOUPe~Y' => 'Lk5oLe4qImaZKgQbT4ICB9rfD5Hy4ozjydlCP_9nPlo', 'H5MmPYr8-j.GHXGzaN.Ck8LFh-kmeK_Q6xgUZfOSYkYJHKObUJgtP0xcLCkAySnMBQ~-L-RUUfdNr7r2kT1-9Mpabf5wmoBbPRft.T8HFUiyuVCd4KcX2wRGfc1evspn' => 'e5KT8_NuYwqcBGkdv3t1Wk-QnbozLkjSaFXKfvDp0nU', 'D4R-xl8r_6slynxksZhCSbwj5fDB2Hdk8ZzfdW8iWqqbOx7A0oP_XCffIatxBR~J0JYAddxcpIBshuNOTxwUTXhm~24OZWAzmnn-s5FOnOK~mnetlfvDeH6cjhHg~H0-' => 'NA7eMVS9lXYsvSWA1T2wFXfxNK8Yx-RttVo9iwmQ2FM', 'Fk0SY30MvDDXCfwO8TiHz0cFADb3sP8-DqCDysiH7iY4NI_sVHW8Bbyl1sypVY61m4fGv4VzEX.ASdir4BRfcD..I70mINH~_L-g0_Y9xLXD9Di0fYu0psevbxm0yh~w' => 'VPKX0gnLeTzjM-UJ5Mc5ZR5VGQzh8ukr_RbFzbfYJ30'];
|
||||
$tests = [
|
||||
'g0sseWY2Gp772L_Xu7T1tHkeqRGAOk_9JnU9gFYCmKkVbkFUHu5izyZEivpxDsZU-r40geolIbX64zEvQ7Y4SOYwKL9drG9OF2g1kTB.PJ7nHPbVLFJFL-ziSv6KclSK'
|
||||
=> 'hzRLCtPmWN3w_EVqGW19ARrMaXZBwYrpnTMkelrYIv4',
|
||||
'd_O4i_N0nDZdsjl6JGE.vYoIi-Yr8lXcEYWUKXbjwojf8VtMaTmOSwJJYQ5n5NYz2BrdKSQFkLei3sSzP0dygP8vUkH3rP-dEBl9l5rvFAUXtjsTXUusxwRTisOUPe~Y'
|
||||
=> 'Lk5oLe4qImaZKgQbT4ICB9rfD5Hy4ozjydlCP_9nPlo',
|
||||
'H5MmPYr8-j.GHXGzaN.Ck8LFh-kmeK_Q6xgUZfOSYkYJHKObUJgtP0xcLCkAySnMBQ~-L-RUUfdNr7r2kT1-9Mpabf5wmoBbPRft.T8HFUiyuVCd4KcX2wRGfc1evspn'
|
||||
=> 'e5KT8_NuYwqcBGkdv3t1Wk-QnbozLkjSaFXKfvDp0nU',
|
||||
'D4R-xl8r_6slynxksZhCSbwj5fDB2Hdk8ZzfdW8iWqqbOx7A0oP_XCffIatxBR~J0JYAddxcpIBshuNOTxwUTXhm~24OZWAzmnn-s5FOnOK~mnetlfvDeH6cjhHg~H0-'
|
||||
=> 'NA7eMVS9lXYsvSWA1T2wFXfxNK8Yx-RttVo9iwmQ2FM',
|
||||
'Fk0SY30MvDDXCfwO8TiHz0cFADb3sP8-DqCDysiH7iY4NI_sVHW8Bbyl1sypVY61m4fGv4VzEX.ASdir4BRfcD..I70mINH~_L-g0_Y9xLXD9Di0fYu0psevbxm0yh~w'
|
||||
=> 'VPKX0gnLeTzjM-UJ5Mc5ZR5VGQzh8ukr_RbFzbfYJ30',
|
||||
];
|
||||
|
||||
foreach ($tests as $verifier => $expected) {
|
||||
self::assertEquals($expected, $this->provider->generatePkceChallenge($verifier));
|
||||
}
|
||||
}
|
||||
|
||||
private function mockAccessToken(): AccessToken
|
||||
{
|
||||
return new AccessToken(['access_token' => 'mock_access_token']);
|
||||
return new AccessToken([
|
||||
'access_token' => 'mock_access_token',
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user