├── .gitignore ├── .scrutinizer.yml ├── .travis.yml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── composer.json ├── phpunit.xml ├── src └── Provider │ ├── Uber.php │ └── UberResourceOwner.php └── test └── src └── Provider └── UberTest.php /.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /vendor 3 | composer.phar 4 | composer.lock 5 | .DS_Store 6 | -------------------------------------------------------------------------------- /.scrutinizer.yml: -------------------------------------------------------------------------------- 1 | filter: 2 | excluded_paths: [test/*] 3 | checks: 4 | php: 5 | code_rating: true 6 | remove_extra_empty_lines: true 7 | remove_php_closing_tag: true 8 | remove_trailing_whitespace: true 9 | fix_use_statements: 10 | remove_unused: true 11 | preserve_multiple: false 12 | preserve_blanklines: true 13 | order_alphabetically: true 14 | fix_php_opening_tag: true 15 | fix_linefeed: true 16 | fix_line_ending: true 17 | fix_identation_4spaces: true 18 | fix_doc_comments: true 19 | tools: 20 | external_code_coverage: 21 | timeout: 600 22 | runs: 3 23 | php_analyzer: true 24 | php_code_coverage: false 25 | php_code_sniffer: 26 | config: 27 | standard: PSR2 28 | filter: 29 | paths: ['src'] 30 | php_loc: 31 | enabled: true 32 | excluded_dirs: [vendor, test] 33 | php_cpd: 34 | enabled: true 35 | excluded_dirs: [vendor, test] 36 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | 3 | sudo: false 4 | 5 | php: 6 | - 5.6 7 | - 7.0 8 | - 7.1 9 | - hhvm 10 | 11 | matrix: 12 | include: 13 | - php: 5.6 14 | env: 'COMPOSER_FLAGS="--prefer-stable --prefer-lowest"' 15 | 16 | before_script: 17 | - travis_retry composer self-update 18 | - travis_retry composer install --no-interaction --prefer-source --dev 19 | - travis_retry phpenv rehash 20 | 21 | script: 22 | - ./vendor/bin/phpcs --standard=psr2 src/ 23 | - ./vendor/bin/phpunit --coverage-text --coverage-clover=coverage.clover 24 | 25 | after_script: 26 | - wget https://scrutinizer-ci.com/ocular.phar 27 | - php ocular.phar code-coverage:upload --format=php-clover coverage.clover 28 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All Notable changes to `oauth2-uber` will be documented in this file 3 | 4 | ## 2.0.0 - 2017-01-25 5 | 6 | ### Added 7 | - PHP 7.1 Support 8 | 9 | ### Deprecated 10 | - Nothing 11 | 12 | ### Fixed 13 | - Nothing 14 | 15 | ### Removed 16 | - PHP 5.5 Support 17 | 18 | ### Security 19 | - Nothing 20 | 21 | ## 1.0.0 - 2017-01-25 22 | 23 | Bump for base package parity 24 | 25 | ## 0.2.1 - 2016-10-17 26 | 27 | ### Added 28 | - Nothing 29 | 30 | ### Deprecated 31 | - Nothing 32 | 33 | ### Fixed 34 | - Scope separator was incorrectly set to comma when space is required; it is now a space. 35 | 36 | ### Removed 37 | - Nothing 38 | 39 | ### Security 40 | - Nothing 41 | 42 | 43 | ## 0.2.0 - 2015-08-20 44 | 45 | ### Added 46 | - Upgrade to support version 1.0 release of core client 47 | 48 | ### Deprecated 49 | - Nothing 50 | 51 | ### Fixed 52 | - Nothing 53 | 54 | ### Removed 55 | - Nothing 56 | 57 | ### Security 58 | - Nothing 59 | 60 | ## 0.1.0 - 2015-03-21 61 | 62 | ### Added 63 | - Initial release! 64 | 65 | ### Deprecated 66 | - Nothing 67 | 68 | ### Fixed 69 | - Nothing 70 | 71 | ### Removed 72 | - Nothing 73 | 74 | ### Security 75 | - Nothing 76 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Contributions are **welcome** and will be fully **credited**. 4 | 5 | We accept contributions via Pull Requests on [Github](https://github.com/stevenmaguire/oauth2-uber). 6 | 7 | 8 | ## Pull Requests 9 | 10 | - **[PSR-2 Coding Standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)** - The easiest way to apply the conventions is to install [PHP Code Sniffer](http://pear.php.net/package/PHP_CodeSniffer). 11 | 12 | - **Add tests!** - Your patch won't be accepted if it doesn't have tests. 13 | 14 | - **Document any change in behaviour** - Make sure the README and any other relevant documentation are kept up-to-date. 15 | 16 | - **Consider our release cycle** - We try to follow SemVer. Randomly breaking public APIs is not an option. 17 | 18 | - **Create topic branches** - Don't ask us to pull from your master branch. 19 | 20 | - **One pull request per feature** - If you want to do more than one thing, send multiple pull requests. 21 | 22 | - **Send coherent history** - Make sure each individual commit in your pull request is meaningful. If you had to make multiple intermediate commits while developing, please squash them before submitting. 23 | 24 | - **Ensure tests pass!** - Please run the tests (see below) before submitting your pull request, and make sure they pass. We won't accept a patch until all tests pass. 25 | 26 | - **Ensure no coding standards violations** - Please run PHP Code Sniffer using the PSR-2 standard (see below) before submitting your pull request. A violation will cause the build to fail, so please make sure there are no violations. We can't accept a patch if the build fails. 27 | 28 | 29 | ## Running Tests 30 | 31 | ``` bash 32 | $ ./vendor/bin/phpunit 33 | ``` 34 | 35 | 36 | ## Running PHP Code Sniffer 37 | 38 | ``` bash 39 | $ ./vendor/bin/phpcs src --standard=psr2 -sp 40 | ``` 41 | 42 | **Happy coding**! 43 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Steven Maguire 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Uber Provider for OAuth 2.0 Client 2 | [![Latest Version](https://img.shields.io/github/release/stevenmaguire/oauth2-uber.svg?style=flat-square)](https://github.com/stevenmaguire/oauth2-uber/releases) 3 | [![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE.md) 4 | [![Build Status](https://img.shields.io/travis/stevenmaguire/oauth2-uber/master.svg?style=flat-square)](https://travis-ci.org/stevenmaguire/oauth2-uber) 5 | [![Coverage Status](https://img.shields.io/scrutinizer/coverage/g/stevenmaguire/oauth2-uber.svg?style=flat-square)](https://scrutinizer-ci.com/g/stevenmaguire/oauth2-uber/code-structure) 6 | [![Quality Score](https://img.shields.io/scrutinizer/g/stevenmaguire/oauth2-uber.svg?style=flat-square)](https://scrutinizer-ci.com/g/stevenmaguire/oauth2-uber) 7 | [![Total Downloads](https://img.shields.io/packagist/dt/stevenmaguire/oauth2-uber.svg?style=flat-square)](https://packagist.org/packages/stevenmaguire/oauth2-uber) 8 | 9 | This package provides Uber OAuth 2.0 support for the PHP League's [OAuth 2.0 Client](https://github.com/thephpleague/oauth2-client). 10 | 11 | ## Installation 12 | 13 | To install, use composer: 14 | 15 | ``` 16 | composer require stevenmaguire/oauth2-uber 17 | ``` 18 | 19 | ## Usage 20 | 21 | Usage is the same as The League's OAuth client, using `\Stevenmaguire\OAuth2\Client\Provider\Uber` as the provider. 22 | 23 | ### Authorization Code Flow 24 | 25 | ```php 26 | $provider = new Stevenmaguire\OAuth2\Client\Provider\Uber([ 27 | 'clientId' => '{uber-client-id}', 28 | 'clientSecret' => '{uber-client-secret}', 29 | 'redirectUri' => 'https://example.com/callback-url' 30 | ]); 31 | 32 | if (!isset($_GET['code'])) { 33 | 34 | // If we don't have an authorization code then get one 35 | $authUrl = $provider->getAuthorizationUrl(); 36 | $_SESSION['oauth2state'] = $provider->getState(); 37 | header('Location: '.$authUrl); 38 | exit; 39 | 40 | // Check given state against previously stored one to mitigate CSRF attack 41 | } elseif (empty($_GET['state']) || ($_GET['state'] !== $_SESSION['oauth2state'])) { 42 | 43 | unset($_SESSION['oauth2state']); 44 | exit('Invalid state'); 45 | 46 | } else { 47 | 48 | // Try to get an access token (using the authorization code grant) 49 | $token = $provider->getAccessToken('authorization_code', [ 50 | 'code' => $_GET['code'] 51 | ]); 52 | 53 | // Optional: Now you have a token you can look up a users profile data 54 | try { 55 | 56 | // We got an access token, let's now get the user's details 57 | $user = $provider->getResourceOwner($token); 58 | 59 | // Use these details to create a new profile 60 | printf('Hello %s!', $user->getFirstname()); 61 | 62 | } catch (Exception $e) { 63 | 64 | // Failed to get user details 65 | exit('Oh dear...'); 66 | } 67 | 68 | // Use this to interact with an API on the users behalf 69 | echo $token->getToken(); 70 | } 71 | ``` 72 | 73 | ### Managing Scopes 74 | 75 | When creating your Uber authorization URL, you can specify the state and scopes your application may authorize. 76 | 77 | ```php 78 | $options = [ 79 | 'state' => 'OPTIONAL_CUSTOM_CONFIGURED_STATE', 80 | 'scope' => ['profile','history_lite'] // array or string 81 | ]; 82 | 83 | $authorizationUrl = $provider->getAuthorizationUrl($options); 84 | ``` 85 | If neither are defined, the provider will utilize internal defaults. 86 | 87 | At the time of authoring this documentation, the following scopes are available. 88 | 89 | - profile 90 | - history 91 | - history_lite 92 | - request 93 | - request_receipt 94 | 95 | ### Refreshing a Token 96 | 97 | ```php 98 | $provider = new Stevenmaguire\OAuth2\Client\Provider\Uber([ 99 | 'clientId' => '{uber-client-id}', 100 | 'clientSecret' => '{uber-client-secret}', 101 | 'redirectUri' => 'https://example.com/callback-url' 102 | ]); 103 | 104 | $grant = new \League\OAuth2\Client\Grant\RefreshToken(); 105 | $token = $provider->getAccessToken($grant, ['refresh_token' => $refreshToken]); 106 | ``` 107 | 108 | ## Testing 109 | 110 | ``` bash 111 | $ ./vendor/bin/phpunit 112 | ``` 113 | 114 | ## Contributing 115 | 116 | Please see [CONTRIBUTING](https://github.com/stevenmaguire/oauth2-uber/blob/master/CONTRIBUTING.md) for details. 117 | 118 | 119 | ## Credits 120 | 121 | - [Steven Maguire](https://github.com/stevenmaguire) 122 | - [All Contributors](https://github.com/stevenmaguire/oauth2-uber/contributors) 123 | 124 | 125 | ## License 126 | 127 | The MIT License (MIT). Please see [License File](https://github.com/stevenmaguire/oauth2-uber/blob/master/LICENSE) for more information. 128 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "stevenmaguire/oauth2-uber", 3 | "description": "Uber OAuth 2.0 Client Provider for The PHP League OAuth2-Client", 4 | "license": "MIT", 5 | "authors": [ 6 | { 7 | "name": "Steven Maguire", 8 | "email": "stevenmaguire@gmail.com", 9 | "homepage": "https://github.com/stevenmaguire" 10 | } 11 | ], 12 | "keywords": [ 13 | "oauth", 14 | "oauth2", 15 | "client", 16 | "authorization", 17 | "authorisation", 18 | "uber" 19 | ], 20 | "require": { 21 | "league/oauth2-client": "^2.0" 22 | }, 23 | "require-dev": { 24 | "phpunit/phpunit": "~4.0", 25 | "mockery/mockery": "~0.9", 26 | "squizlabs/php_codesniffer": "~2.0" 27 | }, 28 | "autoload": { 29 | "psr-4": { 30 | "Stevenmaguire\\OAuth2\\Client\\": "src/" 31 | } 32 | }, 33 | "autoload-dev": { 34 | "psr-4": { 35 | "Stevenmaguire\\OAuth2\\Client\\Test\\": "test/src/" 36 | } 37 | }, 38 | "extra": { 39 | "branch-alias": { 40 | "dev-master": "1.0.x-dev" 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 13 | 14 | 20 | 22 | 23 | 24 | 25 | ./test/ 26 | 27 | 28 | 29 | 30 | ./ 31 | 32 | ./vendor 33 | ./test 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /src/Provider/Uber.php: -------------------------------------------------------------------------------- 1 | version.'/me'; 57 | } 58 | 59 | /** 60 | * Get the default scopes used by this provider. 61 | * 62 | * This should not be a complete list of all scopes, but the minimum 63 | * required for the provider user interface! 64 | * 65 | * @return array 66 | */ 67 | protected function getDefaultScopes() 68 | { 69 | return $this->defaultScopes; 70 | } 71 | 72 | /** 73 | * Returns the string that should be used to separate scopes when building 74 | * the URL for requesting an access token. 75 | * 76 | * @return string Scope separator, defaults to ' ' 77 | */ 78 | protected function getScopeSeparator() 79 | { 80 | return ' '; 81 | } 82 | 83 | /** 84 | * Check a provider response for errors. 85 | * 86 | * @link https://developer.uber.com/v1/api-reference/ 87 | * @throws IdentityProviderException 88 | * @param ResponseInterface $response 89 | * @param string $data Parsed response data 90 | * @return void 91 | */ 92 | protected function checkResponse(ResponseInterface $response, $data) 93 | { 94 | $acceptableStatuses = [200, 201]; 95 | 96 | if (!in_array($response->getStatusCode(), $acceptableStatuses)) { 97 | throw new IdentityProviderException( 98 | $data['message'] ?: $response->getReasonPhrase(), 99 | $response->getStatusCode(), 100 | $response 101 | ); 102 | } 103 | } 104 | 105 | /** 106 | * Generate a user object from a successful user details request. 107 | * 108 | * @param object $response 109 | * @param AccessToken $token 110 | * @return League\OAuth2\Client\Provider\ResourceOwnerInterface 111 | */ 112 | protected function createResourceOwner(array $response, AccessToken $token) 113 | { 114 | return new UberResourceOwner($response); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/Provider/UberResourceOwner.php: -------------------------------------------------------------------------------- 1 | response = $response; 22 | } 23 | 24 | /** 25 | * Get user email 26 | * 27 | * @return string|null 28 | */ 29 | public function getEmail() 30 | { 31 | return $this->response['email'] ?: null; 32 | } 33 | 34 | /** 35 | * Get user firstname 36 | * 37 | * @return string|null 38 | */ 39 | public function getFirstname() 40 | { 41 | return $this->response['first_name'] ?: null; 42 | } 43 | 44 | /** 45 | * Get user imageurl 46 | * 47 | * @return string|null 48 | */ 49 | public function getImageurl() 50 | { 51 | return $this->response['picture'] ?: null; 52 | } 53 | 54 | /** 55 | * Get user lastname 56 | * 57 | * @return string|null 58 | */ 59 | public function getLastname() 60 | { 61 | return $this->response['last_name'] ?: null; 62 | } 63 | 64 | /** 65 | * Get user userId 66 | * 67 | * @return string|null 68 | */ 69 | public function getId() 70 | { 71 | return $this->response['uuid'] ?: null; 72 | } 73 | 74 | /** 75 | * Return all of the owner details available as an array. 76 | * 77 | * @return array 78 | */ 79 | public function toArray() 80 | { 81 | return $this->response; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /test/src/Provider/UberTest.php: -------------------------------------------------------------------------------- 1 | provider = new \Stevenmaguire\OAuth2\Client\Provider\Uber([ 15 | 'clientId' => 'mock_client_id', 16 | 'clientSecret' => 'mock_secret', 17 | 'redirectUri' => 'none', 18 | ]); 19 | } 20 | 21 | public function tearDown() 22 | { 23 | m::close(); 24 | parent::tearDown(); 25 | } 26 | 27 | public function testAuthorizationUrl() 28 | { 29 | $url = $this->provider->getAuthorizationUrl(); 30 | $uri = parse_url($url); 31 | parse_str($uri['query'], $query); 32 | 33 | $this->assertArrayHasKey('client_id', $query); 34 | $this->assertArrayHasKey('redirect_uri', $query); 35 | $this->assertArrayHasKey('state', $query); 36 | $this->assertArrayHasKey('scope', $query); 37 | $this->assertArrayHasKey('response_type', $query); 38 | $this->assertArrayHasKey('approval_prompt', $query); 39 | $this->assertNotNull($this->provider->getState()); 40 | } 41 | 42 | 43 | public function testScopes() 44 | { 45 | $scopeSeparator = ' '; 46 | $options = ['scope' => [uniqid(), uniqid()]]; 47 | $query = ['scope' => implode($scopeSeparator, $options['scope'])]; 48 | $url = $this->provider->getAuthorizationUrl($options); 49 | $encodedScope = $this->buildQueryString($query); 50 | $this->assertContains($encodedScope, $url); 51 | } 52 | 53 | public function testGetAuthorizationUrl() 54 | { 55 | $url = $this->provider->getAuthorizationUrl(); 56 | $uri = parse_url($url); 57 | 58 | $this->assertEquals('/oauth/authorize', $uri['path']); 59 | } 60 | 61 | public function testGetBaseAccessTokenUrl() 62 | { 63 | $params = []; 64 | 65 | $url = $this->provider->getBaseAccessTokenUrl($params); 66 | $uri = parse_url($url); 67 | 68 | $this->assertEquals('/oauth/token', $uri['path']); 69 | } 70 | 71 | public function testGetAccessToken() 72 | { 73 | $response = m::mock('Psr\Http\Message\ResponseInterface'); 74 | $response->shouldReceive('getBody')->andReturn('{"access_token": "mock_access_token","token_type": "Bearer","expires_in": 3600,"refresh_token": "mock_refresh_token","scope": "profile history"}'); 75 | $response->shouldReceive('getHeader')->andReturn(['content-type' => 'json']); 76 | $response->shouldReceive('getStatusCode')->andReturn(200); 77 | 78 | $client = m::mock('GuzzleHttp\ClientInterface'); 79 | $client->shouldReceive('send')->times(1)->andReturn($response); 80 | $this->provider->setHttpClient($client); 81 | 82 | $token = $this->provider->getAccessToken('authorization_code', ['code' => 'mock_authorization_code']); 83 | 84 | $this->assertEquals('mock_access_token', $token->getToken()); 85 | $this->assertLessThanOrEqual(time() + 3600, $token->getExpires()); 86 | $this->assertGreaterThanOrEqual(time(), $token->getExpires()); 87 | $this->assertEquals('mock_refresh_token', $token->getRefreshToken()); 88 | $this->assertNull($token->getResourceOwnerId()); 89 | } 90 | 91 | public function testUserData() 92 | { 93 | $email = uniqid(); 94 | $userId = rand(1000,9999); 95 | $firstName = uniqid(); 96 | $lastName = uniqid(); 97 | $picture = uniqid(); 98 | $coupon = uniqid(); 99 | 100 | $postResponse = m::mock('Psr\Http\Message\ResponseInterface'); 101 | $postResponse->shouldReceive('getBody')->andReturn('{"access_token": "mock_access_token","token_type": "Bearer","expires_in": 3600,"refresh_token": "mock_refresh_token","scope": "profile history"}'); 102 | $postResponse->shouldReceive('getHeader')->andReturn(['content-type' => 'json']); 103 | $postResponse->shouldReceive('getStatusCode')->andReturn(200); 104 | 105 | $userResponse = m::mock('Psr\Http\Message\ResponseInterface'); 106 | $userResponse->shouldReceive('getBody')->andReturn('{"first_name": "'.$firstName.'","last_name": "'.$lastName.'","email": "'.$email.'","picture": "'.$picture.'","promo_code": "'.$coupon.'","uuid": "'.$userId.'"}'); 107 | $userResponse->shouldReceive('getHeader')->andReturn(['content-type' => 'json']); 108 | $userResponse->shouldReceive('getStatusCode')->andReturn(200); 109 | 110 | $client = m::mock('GuzzleHttp\ClientInterface'); 111 | $client->shouldReceive('send') 112 | ->times(2) 113 | ->andReturn($postResponse, $userResponse); 114 | $this->provider->setHttpClient($client); 115 | 116 | $token = $this->provider->getAccessToken('authorization_code', ['code' => 'mock_authorization_code']); 117 | $user = $this->provider->getResourceOwner($token); 118 | 119 | $this->assertEquals($email, $user->getEmail()); 120 | $this->assertEquals($email, $user->toArray()['email']); 121 | $this->assertEquals($userId, $user->getId()); 122 | $this->assertEquals($userId, $user->toArray()['uuid']); 123 | $this->assertEquals($firstName, $user->getFirstname()); 124 | $this->assertEquals($firstName, $user->toArray()['first_name']); 125 | $this->assertEquals($lastName, $user->getLastname()); 126 | $this->assertEquals($lastName, $user->toArray()['last_name']); 127 | $this->assertEquals($picture, $user->getImageurl()); 128 | $this->assertEquals($picture, $user->toArray()['picture']); 129 | } 130 | 131 | /** 132 | * @expectedException League\OAuth2\Client\Provider\Exception\IdentityProviderException 133 | **/ 134 | public function testExceptionThrownWhenErrorObjectReceived() 135 | { 136 | $message = uniqid(); 137 | $status = rand(400,600); 138 | $postResponse = m::mock('Psr\Http\Message\ResponseInterface'); 139 | $postResponse->shouldReceive('getBody')->andReturn('{"message": "'.$message.'","code": "invalid","fields": {"first_name": ["Required"]}}'); 140 | $postResponse->shouldReceive('getHeader')->andReturn(['content-type' => 'json']); 141 | $postResponse->shouldReceive('getStatusCode')->andReturn($status); 142 | 143 | $client = m::mock('GuzzleHttp\ClientInterface'); 144 | $client->shouldReceive('send') 145 | ->times(1) 146 | ->andReturn($postResponse); 147 | $this->provider->setHttpClient($client); 148 | $token = $this->provider->getAccessToken('authorization_code', ['code' => 'mock_authorization_code']); 149 | } 150 | } 151 | --------------------------------------------------------------------------------