Server IP : 213.176.29.180 / Your IP : 18.221.25.133 Web Server : Apache System : Linux 213.176.29.180.hostiran.name 4.18.0-553.22.1.el8_10.x86_64 #1 SMP Tue Sep 24 05:16:59 EDT 2024 x86_64 User : webtaragh ( 1001) PHP Version : 8.3.14 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : ON Directory (0750) : /home/webtaragh/public_html/ |
[ Home ] | [ C0mmand ] | [ Upload File ] |
---|
oauth2-client/CONTRIBUTING.md 0000644 00000003556 14736103263 0011471 0 ustar 00 # Contributing Contributions are **welcome** and will be fully **credited**. We accept contributions via Pull Requests on [Github](https://github.com/thephpleague/oauth2-client). ## Pull Requests - **[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). - **Add tests!** - Your patch won't be accepted if it doesn't have tests. - **Document any change in behaviour** - Make sure the README and any other relevant documentation are kept up-to-date. - **Consider our release cycle** - We try to follow SemVer. Randomly breaking public APIs is not an option. - **Create topic branches** - Don't ask us to pull from your master branch. - **One pull request per feature** - If you want to do more than one thing, send multiple pull requests. - **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. - **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. - **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. ## Testing The following tests must pass for a build to be considered successful. If contributing, please ensure these pass before submitting a pull request. ``` bash $ ./vendor/bin/parallel-lint src test $ ./vendor/bin/phpunit --coverage-text $ ./vendor/bin/phpcs src --standard=psr2 -sp ``` **Happy coding**! oauth2-client/CREDITS.md 0000644 00000001073 14736103263 0010647 0 ustar 00 # OAuth 2.0 Client ## Authors Also see <https://github.com/thephpleague/oauth2-client/contributors>. ### Current Maintainer - [Ben Ramsey](https://github.com/ramsey) ### Contributors - [Alex Bilbie](https://github.com/alexbilbie) - [Ben Corlett](https://github.com/bencorlett) - [Ben Ramsey](https://github.com/ramsey) - [James Mills](https://github.com/jamesmills) - [Phil Sturgeon](https://github.com/philsturgeon) - [Rudi Theunissen](https://github.com/rtheunissen) - [Tom Anderson](https://github.com/TomHAnderson) - [Woody Gilk](https://github.com/shadowhand) oauth2-client/CODE_OF_CONDUCT.md 0000644 00000006444 14736103263 0012036 0 ustar 00 # Contributor Covenant Code of Conduct ## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: * Using welcoming and inclusive language * Being respectful of differing viewpoints and experiences * Gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery and unwelcome sexual attention or advances * Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or electronic address, without explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. ## Scope This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at https://twitter.com/thephpleague. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html [homepage]: https://www.contributor-covenant.org For answers to common questions about this code of conduct, see https://www.contributor-covenant.org/faq oauth2-client/src/Provider/AbstractProvider.php 0000644 00000056325 14736103263 0015612 0 ustar 00 <?php /** * This file is part of the league/oauth2-client library * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * @copyright Copyright (c) Alex Bilbie <hello@alexbilbie.com> * @license http://opensource.org/licenses/MIT MIT * @link http://thephpleague.com/oauth2-client/ Documentation * @link https://packagist.org/packages/league/oauth2-client Packagist * @link https://github.com/thephpleague/oauth2-client GitHub */ namespace League\OAuth2\Client\Provider; use GuzzleHttp\Client as HttpClient; use GuzzleHttp\ClientInterface as HttpClientInterface; use GuzzleHttp\Exception\BadResponseException; use League\OAuth2\Client\Grant\AbstractGrant; use League\OAuth2\Client\Grant\GrantFactory; use League\OAuth2\Client\OptionProvider\OptionProviderInterface; use League\OAuth2\Client\OptionProvider\PostAuthOptionProvider; use League\OAuth2\Client\Provider\Exception\IdentityProviderException; use League\OAuth2\Client\Token\AccessToken; use League\OAuth2\Client\Token\AccessTokenInterface; use League\OAuth2\Client\Tool\ArrayAccessorTrait; use League\OAuth2\Client\Tool\GuardedPropertyTrait; use League\OAuth2\Client\Tool\QueryBuilderTrait; use League\OAuth2\Client\Tool\RequestFactory; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; use UnexpectedValueException; /** * Represents a service provider (authorization server). * * @link http://tools.ietf.org/html/rfc6749#section-1.1 Roles (RFC 6749, §1.1) */ abstract class AbstractProvider { use ArrayAccessorTrait; use GuardedPropertyTrait; use QueryBuilderTrait; /** * @var string Key used in a token response to identify the resource owner. */ const ACCESS_TOKEN_RESOURCE_OWNER_ID = null; /** * @var string HTTP method used to fetch access tokens. */ const METHOD_GET = 'GET'; /** * @var string HTTP method used to fetch access tokens. */ const METHOD_POST = 'POST'; /** * @var string */ protected $clientId; /** * @var string */ protected $clientSecret; /** * @var string */ protected $redirectUri; /** * @var string */ protected $state; /** * @var GrantFactory */ protected $grantFactory; /** * @var RequestFactory */ protected $requestFactory; /** * @var HttpClientInterface */ protected $httpClient; /** * @var OptionProviderInterface */ protected $optionProvider; /** * Constructs an OAuth 2.0 service provider. * * @param array $options An array of options to set on this provider. * Options include `clientId`, `clientSecret`, `redirectUri`, and `state`. * Individual providers may introduce more options, as needed. * @param array $collaborators An array of collaborators that may be used to * override this provider's default behavior. Collaborators include * `grantFactory`, `requestFactory`, and `httpClient`. * Individual providers may introduce more collaborators, as needed. */ public function __construct(array $options = [], array $collaborators = []) { // We'll let the GuardedPropertyTrait handle mass assignment of incoming // options, skipping any blacklisted properties defined in the provider $this->fillProperties($options); if (empty($collaborators['grantFactory'])) { $collaborators['grantFactory'] = new GrantFactory(); } $this->setGrantFactory($collaborators['grantFactory']); if (empty($collaborators['requestFactory'])) { $collaborators['requestFactory'] = new RequestFactory(); } $this->setRequestFactory($collaborators['requestFactory']); if (empty($collaborators['httpClient'])) { $client_options = $this->getAllowedClientOptions($options); $collaborators['httpClient'] = new HttpClient( array_intersect_key($options, array_flip($client_options)) ); } $this->setHttpClient($collaborators['httpClient']); if (empty($collaborators['optionProvider'])) { $collaborators['optionProvider'] = new PostAuthOptionProvider(); } $this->setOptionProvider($collaborators['optionProvider']); } /** * Returns the list of options that can be passed to the HttpClient * * @param array $options An array of options to set on this provider. * Options include `clientId`, `clientSecret`, `redirectUri`, and `state`. * Individual providers may introduce more options, as needed. * @return array The options to pass to the HttpClient constructor */ protected function getAllowedClientOptions(array $options) { $client_options = ['timeout', 'proxy']; // Only allow turning off ssl verification if it's for a proxy if (!empty($options['proxy'])) { $client_options[] = 'verify'; } return $client_options; } /** * Sets the grant factory instance. * * @param GrantFactory $factory * @return self */ public function setGrantFactory(GrantFactory $factory) { $this->grantFactory = $factory; return $this; } /** * Returns the current grant factory instance. * * @return GrantFactory */ public function getGrantFactory() { return $this->grantFactory; } /** * Sets the request factory instance. * * @param RequestFactory $factory * @return self */ public function setRequestFactory(RequestFactory $factory) { $this->requestFactory = $factory; return $this; } /** * Returns the request factory instance. * * @return RequestFactory */ public function getRequestFactory() { return $this->requestFactory; } /** * Sets the HTTP client instance. * * @param HttpClientInterface $client * @return self */ public function setHttpClient(HttpClientInterface $client) { $this->httpClient = $client; return $this; } /** * Returns the HTTP client instance. * * @return HttpClientInterface */ public function getHttpClient() { return $this->httpClient; } /** * Sets the option provider instance. * * @param OptionProviderInterface $provider * @return self */ public function setOptionProvider(OptionProviderInterface $provider) { $this->optionProvider = $provider; return $this; } /** * Returns the option provider instance. * * @return OptionProviderInterface */ public function getOptionProvider() { return $this->optionProvider; } /** * Returns the current value of the state parameter. * * This can be accessed by the redirect handler during authorization. * * @return string */ public function getState() { return $this->state; } /** * Returns the base URL for authorizing a client. * * Eg. https://oauth.service.com/authorize * * @return string */ abstract public function getBaseAuthorizationUrl(); /** * Returns the base URL for requesting an access token. * * Eg. https://oauth.service.com/token * * @param array $params * @return string */ abstract public function getBaseAccessTokenUrl(array $params); /** * Returns the URL for requesting the resource owner's details. * * @param AccessToken $token * @return string */ abstract public function getResourceOwnerDetailsUrl(AccessToken $token); /** * Returns a new random string to use as the state parameter in an * authorization flow. * * @param int $length Length of the random string to be generated. * @return string */ protected function getRandomState($length = 32) { // Converting bytes to hex will always double length. Hence, we can reduce // the amount of bytes by half to produce the correct length. return bin2hex(random_bytes($length / 2)); } /** * Returns the default scopes used by this provider. * * This should only be the scopes that are required to request the details * of the resource owner, rather than all the available scopes. * * @return array */ abstract protected function getDefaultScopes(); /** * Returns the string that should be used to separate scopes when building * the URL for requesting an access token. * * @return string Scope separator, defaults to ',' */ protected function getScopeSeparator() { return ','; } /** * Returns authorization parameters based on provided options. * * @param array $options * @return array Authorization parameters */ protected function getAuthorizationParameters(array $options) { if (empty($options['state'])) { $options['state'] = $this->getRandomState(); } if (empty($options['scope'])) { $options['scope'] = $this->getDefaultScopes(); } $options += [ 'response_type' => 'code', 'approval_prompt' => 'auto' ]; if (is_array($options['scope'])) { $separator = $this->getScopeSeparator(); $options['scope'] = implode($separator, $options['scope']); } // Store the state as it may need to be accessed later on. $this->state = $options['state']; // Business code layer might set a different redirect_uri parameter // depending on the context, leave it as-is if (!isset($options['redirect_uri'])) { $options['redirect_uri'] = $this->redirectUri; } $options['client_id'] = $this->clientId; return $options; } /** * Builds the authorization URL's query string. * * @param array $params Query parameters * @return string Query string */ protected function getAuthorizationQuery(array $params) { return $this->buildQueryString($params); } /** * Builds the authorization URL. * * @param array $options * @return string Authorization URL */ public function getAuthorizationUrl(array $options = []) { $base = $this->getBaseAuthorizationUrl(); $params = $this->getAuthorizationParameters($options); $query = $this->getAuthorizationQuery($params); return $this->appendQuery($base, $query); } /** * Redirects the client for authorization. * * @param array $options * @param callable|null $redirectHandler * @return mixed */ public function authorize( array $options = [], callable $redirectHandler = null ) { $url = $this->getAuthorizationUrl($options); if ($redirectHandler) { return $redirectHandler($url, $this); } // @codeCoverageIgnoreStart header('Location: ' . $url); exit; // @codeCoverageIgnoreEnd } /** * Appends a query string to a URL. * * @param string $url The URL to append the query to * @param string $query The HTTP query string * @return string The resulting URL */ protected function appendQuery($url, $query) { $query = trim($query, '?&'); if ($query) { $glue = strstr($url, '?') === false ? '?' : '&'; return $url . $glue . $query; } return $url; } /** * Returns the method to use when requesting an access token. * * @return string HTTP method */ protected function getAccessTokenMethod() { return self::METHOD_POST; } /** * Returns the key used in the access token response to identify the resource owner. * * @return string|null Resource owner identifier key */ protected function getAccessTokenResourceOwnerId() { return static::ACCESS_TOKEN_RESOURCE_OWNER_ID; } /** * Builds the access token URL's query string. * * @param array $params Query parameters * @return string Query string */ protected function getAccessTokenQuery(array $params) { return $this->buildQueryString($params); } /** * Checks that a provided grant is valid, or attempts to produce one if the * provided grant is a string. * * @param AbstractGrant|string $grant * @return AbstractGrant */ protected function verifyGrant($grant) { if (is_string($grant)) { return $this->grantFactory->getGrant($grant); } $this->grantFactory->checkGrant($grant); return $grant; } /** * Returns the full URL to use when requesting an access token. * * @param array $params Query parameters * @return string */ protected function getAccessTokenUrl(array $params) { $url = $this->getBaseAccessTokenUrl($params); if ($this->getAccessTokenMethod() === self::METHOD_GET) { $query = $this->getAccessTokenQuery($params); return $this->appendQuery($url, $query); } return $url; } /** * Returns a prepared request for requesting an access token. * * @param array $params Query string parameters * @return RequestInterface */ protected function getAccessTokenRequest(array $params) { $method = $this->getAccessTokenMethod(); $url = $this->getAccessTokenUrl($params); $options = $this->optionProvider->getAccessTokenOptions($this->getAccessTokenMethod(), $params); return $this->getRequest($method, $url, $options); } /** * Requests an access token using a specified grant and option set. * * @param mixed $grant * @param array $options * @throws IdentityProviderException * @return AccessTokenInterface */ public function getAccessToken($grant, array $options = []) { $grant = $this->verifyGrant($grant); $params = [ 'client_id' => $this->clientId, 'client_secret' => $this->clientSecret, 'redirect_uri' => $this->redirectUri, ]; $params = $grant->prepareRequestParameters($params, $options); $request = $this->getAccessTokenRequest($params); $response = $this->getParsedResponse($request); if (false === is_array($response)) { throw new UnexpectedValueException( 'Invalid response received from Authorization Server. Expected JSON.' ); } $prepared = $this->prepareAccessTokenResponse($response); $token = $this->createAccessToken($prepared, $grant); return $token; } /** * Returns a PSR-7 request instance that is not authenticated. * * @param string $method * @param string $url * @param array $options * @return RequestInterface */ public function getRequest($method, $url, array $options = []) { return $this->createRequest($method, $url, null, $options); } /** * Returns an authenticated PSR-7 request instance. * * @param string $method * @param string $url * @param AccessTokenInterface|string $token * @param array $options Any of "headers", "body", and "protocolVersion". * @return RequestInterface */ public function getAuthenticatedRequest($method, $url, $token, array $options = []) { return $this->createRequest($method, $url, $token, $options); } /** * Creates a PSR-7 request instance. * * @param string $method * @param string $url * @param AccessTokenInterface|string|null $token * @param array $options * @return RequestInterface */ protected function createRequest($method, $url, $token, array $options) { $defaults = [ 'headers' => $this->getHeaders($token), ]; $options = array_merge_recursive($defaults, $options); $factory = $this->getRequestFactory(); return $factory->getRequestWithOptions($method, $url, $options); } /** * Sends a request instance and returns a response instance. * * WARNING: This method does not attempt to catch exceptions caused by HTTP * errors! It is recommended to wrap this method in a try/catch block. * * @param RequestInterface $request * @return ResponseInterface */ public function getResponse(RequestInterface $request) { return $this->getHttpClient()->send($request); } /** * Sends a request and returns the parsed response. * * @param RequestInterface $request * @throws IdentityProviderException * @return mixed */ public function getParsedResponse(RequestInterface $request) { try { $response = $this->getResponse($request); } catch (BadResponseException $e) { $response = $e->getResponse(); } $parsed = $this->parseResponse($response); $this->checkResponse($response, $parsed); return $parsed; } /** * Attempts to parse a JSON response. * * @param string $content JSON content from response body * @return array Parsed JSON data * @throws UnexpectedValueException if the content could not be parsed */ protected function parseJson($content) { $content = json_decode($content, true); if (json_last_error() !== JSON_ERROR_NONE) { throw new UnexpectedValueException(sprintf( "Failed to parse JSON response: %s", json_last_error_msg() )); } return $content; } /** * Returns the content type header of a response. * * @param ResponseInterface $response * @return string Semi-colon separated join of content-type headers. */ protected function getContentType(ResponseInterface $response) { return join(';', (array) $response->getHeader('content-type')); } /** * Parses the response according to its content-type header. * * @throws UnexpectedValueException * @param ResponseInterface $response * @return array */ protected function parseResponse(ResponseInterface $response) { $content = (string) $response->getBody(); $type = $this->getContentType($response); if (strpos($type, 'urlencoded') !== false) { parse_str($content, $parsed); return $parsed; } // Attempt to parse the string as JSON regardless of content type, // since some providers use non-standard content types. Only throw an // exception if the JSON could not be parsed when it was expected to. try { return $this->parseJson($content); } catch (UnexpectedValueException $e) { if (strpos($type, 'json') !== false) { throw $e; } if ($response->getStatusCode() == 500) { throw new UnexpectedValueException( 'An OAuth server error was encountered that did not contain a JSON body', 0, $e ); } return $content; } } /** * Checks a provider response for errors. * * @throws IdentityProviderException * @param ResponseInterface $response * @param array|string $data Parsed response data * @return void */ abstract protected function checkResponse(ResponseInterface $response, $data); /** * Prepares an parsed access token response for a grant. * * Custom mapping of expiration, etc should be done here. Always call the * parent method when overloading this method. * * @param mixed $result * @return array */ protected function prepareAccessTokenResponse(array $result) { if ($this->getAccessTokenResourceOwnerId() !== null) { $result['resource_owner_id'] = $this->getValueByKey( $result, $this->getAccessTokenResourceOwnerId() ); } return $result; } /** * Creates an access token from a response. * * The grant that was used to fetch the response can be used to provide * additional context. * * @param array $response * @param AbstractGrant $grant * @return AccessTokenInterface */ protected function createAccessToken(array $response, AbstractGrant $grant) { return new AccessToken($response); } /** * Generates a resource owner object from a successful resource owner * details request. * * @param array $response * @param AccessToken $token * @return ResourceOwnerInterface */ abstract protected function createResourceOwner(array $response, AccessToken $token); /** * Requests and returns the resource owner of given access token. * * @param AccessToken $token * @return ResourceOwnerInterface */ public function getResourceOwner(AccessToken $token) { $response = $this->fetchResourceOwnerDetails($token); return $this->createResourceOwner($response, $token); } /** * Requests resource owner details. * * @param AccessToken $token * @return mixed */ protected function fetchResourceOwnerDetails(AccessToken $token) { $url = $this->getResourceOwnerDetailsUrl($token); $request = $this->getAuthenticatedRequest(self::METHOD_GET, $url, $token); $response = $this->getParsedResponse($request); if (false === is_array($response)) { throw new UnexpectedValueException( 'Invalid response received from Authorization Server. Expected JSON.' ); } return $response; } /** * Returns the default headers used by this provider. * * Typically this is used to set 'Accept' or 'Content-Type' headers. * * @return array */ protected function getDefaultHeaders() { return []; } /** * Returns the authorization headers used by this provider. * * Typically this is "Bearer" or "MAC". For more information see: * http://tools.ietf.org/html/rfc6749#section-7.1 * * No default is provided, providers must overload this method to activate * authorization headers. * * @param mixed|null $token Either a string or an access token instance * @return array */ protected function getAuthorizationHeaders($token = null) { return []; } /** * Returns all headers used by this provider for a request. * * The request will be authenticated if an access token is provided. * * @param mixed|null $token object or string * @return array */ public function getHeaders($token = null) { if ($token) { return array_merge( $this->getDefaultHeaders(), $this->getAuthorizationHeaders($token) ); } return $this->getDefaultHeaders(); } } oauth2-client/src/Provider/GenericResourceOwner.php 0000644 00000002655 14736103263 0016430 0 ustar 00 <?php /** * This file is part of the league/oauth2-client library * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * @copyright Copyright (c) Alex Bilbie <hello@alexbilbie.com> * @license http://opensource.org/licenses/MIT MIT * @link http://thephpleague.com/oauth2-client/ Documentation * @link https://packagist.org/packages/league/oauth2-client Packagist * @link https://github.com/thephpleague/oauth2-client GitHub */ namespace League\OAuth2\Client\Provider; /** * Represents a generic resource owner for use with the GenericProvider. */ class GenericResourceOwner implements ResourceOwnerInterface { /** * @var array */ protected $response; /** * @var string */ protected $resourceOwnerId; /** * @param array $response * @param string $resourceOwnerId */ public function __construct(array $response, $resourceOwnerId) { $this->response = $response; $this->resourceOwnerId = $resourceOwnerId; } /** * Returns the identifier of the authorized resource owner. * * @return mixed */ public function getId() { return $this->response[$this->resourceOwnerId]; } /** * Returns the raw resource owner response. * * @return array */ public function toArray() { return $this->response; } } oauth2-client/src/Provider/ResourceOwnerInterface.php 0000644 00000001763 14736103263 0016753 0 ustar 00 <?php /** * This file is part of the league/oauth2-client library * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * @copyright Copyright (c) Alex Bilbie <hello@alexbilbie.com> * @license http://opensource.org/licenses/MIT MIT * @link http://thephpleague.com/oauth2-client/ Documentation * @link https://packagist.org/packages/league/oauth2-client Packagist * @link https://github.com/thephpleague/oauth2-client GitHub */ namespace League\OAuth2\Client\Provider; /** * Classes implementing `ResourceOwnerInterface` may be used to represent * the resource owner authenticated with a service provider. */ interface ResourceOwnerInterface { /** * Returns the identifier of the authorized resource owner. * * @return mixed */ public function getId(); /** * Return all of the owner details available as an array. * * @return array */ public function toArray(); } oauth2-client/src/Provider/GenericProvider.php 0000644 00000012565 14736103263 0015421 0 ustar 00 <?php /** * This file is part of the league/oauth2-client library * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * @copyright Copyright (c) Alex Bilbie <hello@alexbilbie.com> * @license http://opensource.org/licenses/MIT MIT * @link http://thephpleague.com/oauth2-client/ Documentation * @link https://packagist.org/packages/league/oauth2-client Packagist * @link https://github.com/thephpleague/oauth2-client GitHub */ namespace League\OAuth2\Client\Provider; use InvalidArgumentException; use League\OAuth2\Client\Provider\Exception\IdentityProviderException; use League\OAuth2\Client\Token\AccessToken; use League\OAuth2\Client\Tool\BearerAuthorizationTrait; use Psr\Http\Message\ResponseInterface; /** * Represents a generic service provider that may be used to interact with any * OAuth 2.0 service provider, using Bearer token authentication. */ class GenericProvider extends AbstractProvider { use BearerAuthorizationTrait; /** * @var string */ private $urlAuthorize; /** * @var string */ private $urlAccessToken; /** * @var string */ private $urlResourceOwnerDetails; /** * @var string */ private $accessTokenMethod; /** * @var string */ private $accessTokenResourceOwnerId; /** * @var array|null */ private $scopes = null; /** * @var string */ private $scopeSeparator; /** * @var string */ private $responseError = 'error'; /** * @var string */ private $responseCode; /** * @var string */ private $responseResourceOwnerId = 'id'; /** * @param array $options * @param array $collaborators */ public function __construct(array $options = [], array $collaborators = []) { $this->assertRequiredOptions($options); $possible = $this->getConfigurableOptions(); $configured = array_intersect_key($options, array_flip($possible)); foreach ($configured as $key => $value) { $this->$key = $value; } // Remove all options that are only used locally $options = array_diff_key($options, $configured); parent::__construct($options, $collaborators); } /** * Returns all options that can be configured. * * @return array */ protected function getConfigurableOptions() { return array_merge($this->getRequiredOptions(), [ 'accessTokenMethod', 'accessTokenResourceOwnerId', 'scopeSeparator', 'responseError', 'responseCode', 'responseResourceOwnerId', 'scopes', ]); } /** * Returns all options that are required. * * @return array */ protected function getRequiredOptions() { return [ 'urlAuthorize', 'urlAccessToken', 'urlResourceOwnerDetails', ]; } /** * Verifies that all required options have been passed. * * @param array $options * @return void * @throws InvalidArgumentException */ private function assertRequiredOptions(array $options) { $missing = array_diff_key(array_flip($this->getRequiredOptions()), $options); if (!empty($missing)) { throw new InvalidArgumentException( 'Required options not defined: ' . implode(', ', array_keys($missing)) ); } } /** * @inheritdoc */ public function getBaseAuthorizationUrl() { return $this->urlAuthorize; } /** * @inheritdoc */ public function getBaseAccessTokenUrl(array $params) { return $this->urlAccessToken; } /** * @inheritdoc */ public function getResourceOwnerDetailsUrl(AccessToken $token) { return $this->urlResourceOwnerDetails; } /** * @inheritdoc */ public function getDefaultScopes() { return $this->scopes; } /** * @inheritdoc */ protected function getAccessTokenMethod() { return $this->accessTokenMethod ?: parent::getAccessTokenMethod(); } /** * @inheritdoc */ protected function getAccessTokenResourceOwnerId() { return $this->accessTokenResourceOwnerId ?: parent::getAccessTokenResourceOwnerId(); } /** * @inheritdoc */ protected function getScopeSeparator() { return $this->scopeSeparator ?: parent::getScopeSeparator(); } /** * @inheritdoc */ protected function checkResponse(ResponseInterface $response, $data) { if (!empty($data[$this->responseError])) { $error = $data[$this->responseError]; if (!is_string($error)) { $error = var_export($error, true); } $code = $this->responseCode && !empty($data[$this->responseCode])? $data[$this->responseCode] : 0; if (!is_int($code)) { $code = intval($code); } throw new IdentityProviderException($error, $code, $data); } } /** * @inheritdoc */ protected function createResourceOwner(array $response, AccessToken $token) { return new GenericResourceOwner($response, $this->responseResourceOwnerId); } } oauth2-client/src/Provider/Exception/IdentityProviderException.php 0000644 00000002274 14736103263 0021447 0 ustar 00 <?php /** * This file is part of the league/oauth2-client library * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * @copyright Copyright (c) Alex Bilbie <hello@alexbilbie.com> * @license http://opensource.org/licenses/MIT MIT * @link http://thephpleague.com/oauth2-client/ Documentation * @link https://packagist.org/packages/league/oauth2-client Packagist * @link https://github.com/thephpleague/oauth2-client GitHub */ namespace League\OAuth2\Client\Provider\Exception; /** * Exception thrown if the provider response contains errors. */ class IdentityProviderException extends \Exception { /** * @var mixed */ protected $response; /** * @param string $message * @param int $code * @param array|string $response The response body */ public function __construct($message, $code, $response) { $this->response = $response; parent::__construct($message, $code); } /** * Returns the exception's response body. * * @return array|string */ public function getResponseBody() { return $this->response; } } oauth2-client/src/Token/ResourceOwnerAccessTokenInterface.php 0000644 00000001414 14736103263 0020355 0 ustar 00 <?php /** * This file is part of the league/oauth2-client library * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * @copyright Copyright (c) Alex Bilbie <hello@alexbilbie.com> * @license http://opensource.org/licenses/MIT MIT * @link http://thephpleague.com/oauth2-client/ Documentation * @link https://packagist.org/packages/league/oauth2-client Packagist * @link https://github.com/thephpleague/oauth2-client GitHub */ namespace League\OAuth2\Client\Token; interface ResourceOwnerAccessTokenInterface extends AccessTokenInterface { /** * Returns the resource owner identifier, if defined. * * @return string|null */ public function getResourceOwnerId(); } oauth2-client/src/Token/AccessToken.php 0000644 00000012306 14736103263 0014013 0 ustar 00 <?php /** * This file is part of the league/oauth2-client library * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * @copyright Copyright (c) Alex Bilbie <hello@alexbilbie.com> * @license http://opensource.org/licenses/MIT MIT * @link http://thephpleague.com/oauth2-client/ Documentation * @link https://packagist.org/packages/league/oauth2-client Packagist * @link https://github.com/thephpleague/oauth2-client GitHub */ namespace League\OAuth2\Client\Token; use InvalidArgumentException; use RuntimeException; /** * Represents an access token. * * @link http://tools.ietf.org/html/rfc6749#section-1.4 Access Token (RFC 6749, §1.4) */ class AccessToken implements AccessTokenInterface, ResourceOwnerAccessTokenInterface { /** * @var string */ protected $accessToken; /** * @var int */ protected $expires; /** * @var string */ protected $refreshToken; /** * @var string */ protected $resourceOwnerId; /** * @var array */ protected $values = []; /** * Constructs an access token. * * @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`. */ public function __construct(array $options = []) { if (empty($options['access_token'])) { throw new InvalidArgumentException('Required option not passed: "access_token"'); } $this->accessToken = $options['access_token']; if (!empty($options['resource_owner_id'])) { $this->resourceOwnerId = $options['resource_owner_id']; } if (!empty($options['refresh_token'])) { $this->refreshToken = $options['refresh_token']; } // We need to know when the token expires. Show preference to // 'expires_in' since it is defined in RFC6749 Section 5.1. // Defer to 'expires' if it is provided instead. if (isset($options['expires_in'])) { if (!is_numeric($options['expires_in'])) { throw new \InvalidArgumentException('expires_in value must be an integer'); } $this->expires = $options['expires_in'] != 0 ? time() + $options['expires_in'] : 0; } elseif (!empty($options['expires'])) { // Some providers supply the seconds until expiration rather than // the exact timestamp. Take a best guess at which we received. $expires = $options['expires']; if (!$this->isExpirationTimestamp($expires)) { $expires += time(); } $this->expires = $expires; } // Capture any additional values that might exist in the token but are // not part of the standard response. Vendors will sometimes pass // additional user data this way. $this->values = array_diff_key($options, array_flip([ 'access_token', 'resource_owner_id', 'refresh_token', 'expires_in', 'expires', ])); } /** * Check if a value is an expiration timestamp or second value. * * @param integer $value * @return bool */ protected function isExpirationTimestamp($value) { // If the given value is larger than the original OAuth 2 draft date, // assume that it is meant to be a (possible expired) timestamp. $oauth2InceptionDate = 1349067600; // 2012-10-01 return ($value > $oauth2InceptionDate); } /** * @inheritdoc */ public function getToken() { return $this->accessToken; } /** * @inheritdoc */ public function getRefreshToken() { return $this->refreshToken; } /** * @inheritdoc */ public function getExpires() { return $this->expires; } /** * @inheritdoc */ public function getResourceOwnerId() { return $this->resourceOwnerId; } /** * @inheritdoc */ public function hasExpired() { $expires = $this->getExpires(); if (empty($expires)) { throw new RuntimeException('"expires" is not set on the token'); } return $expires < time(); } /** * @inheritdoc */ public function getValues() { return $this->values; } /** * @inheritdoc */ public function __toString() { return (string) $this->getToken(); } /** * @inheritdoc */ public function jsonSerialize() { $parameters = $this->values; if ($this->accessToken) { $parameters['access_token'] = $this->accessToken; } if ($this->refreshToken) { $parameters['refresh_token'] = $this->refreshToken; } if ($this->expires) { $parameters['expires'] = $this->expires; } if ($this->resourceOwnerId) { $parameters['resource_owner_id'] = $this->resourceOwnerId; } return $parameters; } } oauth2-client/src/Token/AccessTokenInterface.php 0000644 00000003351 14736103263 0015634 0 ustar 00 <?php /** * This file is part of the league/oauth2-client library * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * @copyright Copyright (c) Alex Bilbie <hello@alexbilbie.com> * @license http://opensource.org/licenses/MIT MIT * @link http://thephpleague.com/oauth2-client/ Documentation * @link https://packagist.org/packages/league/oauth2-client Packagist * @link https://github.com/thephpleague/oauth2-client GitHub */ namespace League\OAuth2\Client\Token; use JsonSerializable; use RuntimeException; interface AccessTokenInterface extends JsonSerializable { /** * Returns the access token string of this instance. * * @return string */ public function getToken(); /** * Returns the refresh token, if defined. * * @return string|null */ public function getRefreshToken(); /** * Returns the expiration timestamp, if defined. * * @return integer|null */ public function getExpires(); /** * Checks if this token has expired. * * @return boolean true if the token has expired, false otherwise. * @throws RuntimeException if 'expires' is not set on the token. */ public function hasExpired(); /** * Returns additional vendor values stored in the token. * * @return array */ public function getValues(); /** * Returns a string representation of the access token * * @return string */ public function __toString(); /** * Returns an array of parameters to serialize when this is serialized with * json_encode(). * * @return array */ public function jsonSerialize(); } oauth2-client/src/Tool/GuardedPropertyTrait.php 0000644 00000003414 14736103263 0015572 0 ustar 00 <?php /** * This file is part of the league/oauth2-client library * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * @copyright Copyright (c) Alex Bilbie <hello@alexbilbie.com> * @license http://opensource.org/licenses/MIT MIT * @link http://thephpleague.com/oauth2-client/ Documentation * @link https://packagist.org/packages/league/oauth2-client Packagist * @link https://github.com/thephpleague/oauth2-client GitHub */ namespace League\OAuth2\Client\Tool; /** * Provides support for blacklisting explicit properties from the * mass assignment behavior. */ trait GuardedPropertyTrait { /** * The properties that aren't mass assignable. * * @var array */ protected $guarded = []; /** * Attempts to mass assign the given options to explicitly defined properties, * skipping over any properties that are defined in the guarded array. * * @param array $options * @return mixed */ protected function fillProperties(array $options = []) { if (isset($options['guarded'])) { unset($options['guarded']); } foreach ($options as $option => $value) { if (property_exists($this, $option) && !$this->isGuarded($option)) { $this->{$option} = $value; } } } /** * Returns current guarded properties. * * @return array */ public function getGuarded() { return $this->guarded; } /** * Determines if the given property is guarded. * * @param string $property * @return bool */ public function isGuarded($property) { return in_array($property, $this->getGuarded()); } } oauth2-client/src/Tool/ProviderRedirectTrait.php 0000644 00000006146 14736103263 0015733 0 ustar 00 <?php namespace League\OAuth2\Client\Tool; use GuzzleHttp\Exception\BadResponseException; use GuzzleHttp\Psr7\Uri; use InvalidArgumentException; use Psr\Http\Message\RequestInterface; use Psr\Http\Message\ResponseInterface; trait ProviderRedirectTrait { /** * Maximum number of times to follow provider initiated redirects * * @var integer */ protected $redirectLimit = 2; /** * Retrieves a response for a given request and retrieves subsequent * responses, with authorization headers, if a redirect is detected. * * @param RequestInterface $request * @return ResponseInterface * @throws BadResponseException */ protected function followRequestRedirects(RequestInterface $request) { $response = null; $attempts = 0; while ($attempts < $this->redirectLimit) { $attempts++; $response = $this->getHttpClient()->send($request, [ 'allow_redirects' => false ]); if ($this->isRedirect($response)) { $redirectUrl = new Uri($response->getHeader('Location')[0]); $request = $request->withUri($redirectUrl); } else { break; } } return $response; } /** * Returns the HTTP client instance. * * @return GuzzleHttp\ClientInterface */ abstract public function getHttpClient(); /** * Retrieves current redirect limit. * * @return integer */ public function getRedirectLimit() { return $this->redirectLimit; } /** * Determines if a given response is a redirect. * * @param ResponseInterface $response * * @return boolean */ protected function isRedirect(ResponseInterface $response) { $statusCode = $response->getStatusCode(); return $statusCode > 300 && $statusCode < 400 && $response->hasHeader('Location'); } /** * Sends a request instance and returns a response instance. * * WARNING: This method does not attempt to catch exceptions caused by HTTP * errors! It is recommended to wrap this method in a try/catch block. * * @param RequestInterface $request * @return ResponseInterface */ public function getResponse(RequestInterface $request) { try { $response = $this->followRequestRedirects($request); } catch (BadResponseException $e) { $response = $e->getResponse(); } return $response; } /** * Updates the redirect limit. * * @param integer $limit * @return League\OAuth2\Client\Provider\AbstractProvider * @throws InvalidArgumentException */ public function setRedirectLimit($limit) { if (!is_int($limit)) { throw new InvalidArgumentException('redirectLimit must be an integer.'); } if ($limit < 1) { throw new InvalidArgumentException('redirectLimit must be greater than or equal to one.'); } $this->redirectLimit = $limit; return $this; } } oauth2-client/src/Tool/RequestFactory.php 0000644 00000004427 14736103263 0014433 0 ustar 00 <?php /** * This file is part of the league/oauth2-client library * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * @copyright Copyright (c) Alex Bilbie <hello@alexbilbie.com> * @license http://opensource.org/licenses/MIT MIT * @link http://thephpleague.com/oauth2-client/ Documentation * @link https://packagist.org/packages/league/oauth2-client Packagist * @link https://github.com/thephpleague/oauth2-client GitHub */ namespace League\OAuth2\Client\Tool; use GuzzleHttp\Psr7\Request; /** * Used to produce PSR-7 Request instances. * * @link https://github.com/guzzle/guzzle/pull/1101 */ class RequestFactory { /** * Creates a PSR-7 Request instance. * * @param null|string $method HTTP method for the request. * @param null|string $uri URI for the request. * @param array $headers Headers for the message. * @param string|resource|StreamInterface $body Message body. * @param string $version HTTP protocol version. * * @return Request */ public function getRequest( $method, $uri, array $headers = [], $body = null, $version = '1.1' ) { return new Request($method, $uri, $headers, $body, $version); } /** * Parses simplified options. * * @param array $options Simplified options. * * @return array Extended options for use with getRequest. */ protected function parseOptions(array $options) { // Should match default values for getRequest $defaults = [ 'headers' => [], 'body' => null, 'version' => '1.1', ]; return array_merge($defaults, $options); } /** * Creates a request using a simplified array of options. * * @param null|string $method * @param null|string $uri * @param array $options * * @return Request */ public function getRequestWithOptions($method, $uri, array $options = []) { $options = $this->parseOptions($options); return $this->getRequest( $method, $uri, $options['headers'], $options['body'], $options['version'] ); } } oauth2-client/src/Tool/ArrayAccessorTrait.php 0000644 00000002637 14736103263 0015221 0 ustar 00 <?php /** * This file is part of the league/oauth2-client library * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * @copyright Copyright (c) Alex Bilbie <hello@alexbilbie.com> * @license http://opensource.org/licenses/MIT MIT * @link http://thephpleague.com/oauth2-client/ Documentation * @link https://packagist.org/packages/league/oauth2-client Packagist * @link https://github.com/thephpleague/oauth2-client GitHub */ namespace League\OAuth2\Client\Tool; /** * Provides generic array navigation tools. */ trait ArrayAccessorTrait { /** * Returns a value by key using dot notation. * * @param array $data * @param string $key * @param mixed|null $default * @return mixed */ private function getValueByKey(array $data, $key, $default = null) { if (!is_string($key) || empty($key) || !count($data)) { return $default; } if (strpos($key, '.') !== false) { $keys = explode('.', $key); foreach ($keys as $innerKey) { if (!is_array($data) || !array_key_exists($innerKey, $data)) { return $default; } $data = $data[$innerKey]; } return $data; } return array_key_exists($key, $data) ? $data[$key] : $default; } } oauth2-client/src/Tool/MacAuthorizationTrait.php 0000644 00000005012 14736103263 0015727 0 ustar 00 <?php /** * This file is part of the league/oauth2-client library * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * @copyright Copyright (c) Alex Bilbie <hello@alexbilbie.com> * @license http://opensource.org/licenses/MIT MIT * @link http://thephpleague.com/oauth2-client/ Documentation * @link https://packagist.org/packages/league/oauth2-client Packagist * @link https://github.com/thephpleague/oauth2-client GitHub */ namespace League\OAuth2\Client\Tool; use League\OAuth2\Client\Token\AccessToken; use League\OAuth2\Client\Token\AccessTokenInterface; /** * Enables `MAC` header authorization for providers. * * @link http://tools.ietf.org/html/draft-ietf-oauth-v2-http-mac-05 Message Authentication Code (MAC) Tokens */ trait MacAuthorizationTrait { /** * Returns the id of this token for MAC generation. * * @param AccessToken $token * @return string */ abstract protected function getTokenId(AccessToken $token); /** * Returns the MAC signature for the current request. * * @param string $id * @param integer $ts * @param string $nonce * @return string */ abstract protected function getMacSignature($id, $ts, $nonce); /** * Returns a new random string to use as the state parameter in an * authorization flow. * * @param int $length Length of the random string to be generated. * @return string */ abstract protected function getRandomState($length = 32); /** * Returns the authorization headers for the 'mac' grant. * * @param AccessTokenInterface|string|null $token Either a string or an access token instance * @return array * @codeCoverageIgnore * * @todo This is currently untested and provided only as an example. If you * complete the implementation, please create a pull request for * https://github.com/thephpleague/oauth2-client */ protected function getAuthorizationHeaders($token = null) { if ($token === null) { return []; } $ts = time(); $id = $this->getTokenId($token); $nonce = $this->getRandomState(16); $mac = $this->getMacSignature($id, $ts, $nonce); $parts = []; foreach (compact('id', 'ts', 'nonce', 'mac') as $key => $value) { $parts[] = sprintf('%s="%s"', $key, $value); } return ['Authorization' => 'MAC ' . implode(', ', $parts)]; } } oauth2-client/src/Tool/BearerAuthorizationTrait.php 0000644 00000002134 14736103263 0016431 0 ustar 00 <?php /** * This file is part of the league/oauth2-client library * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * @copyright Copyright (c) Alex Bilbie <hello@alexbilbie.com> * @license http://opensource.org/licenses/MIT MIT * @link http://thephpleague.com/oauth2-client/ Documentation * @link https://packagist.org/packages/league/oauth2-client Packagist * @link https://github.com/thephpleague/oauth2-client GitHub */ namespace League\OAuth2\Client\Tool; use League\OAuth2\Client\Token\AccessTokenInterface; /** * Enables `Bearer` header authorization for providers. * * @link http://tools.ietf.org/html/rfc6750 Bearer Token Usage (RFC 6750) */ trait BearerAuthorizationTrait { /** * Returns authorization headers for the 'bearer' grant. * * @param AccessTokenInterface|string|null $token Either a string or an access token instance * @return array */ protected function getAuthorizationHeaders($token = null) { return ['Authorization' => 'Bearer ' . $token]; } } oauth2-client/src/Tool/QueryBuilderTrait.php 0000644 00000001611 14736103263 0015063 0 ustar 00 <?php /** * This file is part of the league/oauth2-client library * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * @copyright Copyright (c) Alex Bilbie <hello@alexbilbie.com> * @license http://opensource.org/licenses/MIT MIT * @link http://thephpleague.com/oauth2-client/ Documentation * @link https://packagist.org/packages/league/oauth2-client Packagist * @link https://github.com/thephpleague/oauth2-client GitHub */ namespace League\OAuth2\Client\Tool; /** * Provides a standard way to generate query strings. */ trait QueryBuilderTrait { /** * Build a query string from an array. * * @param array $params * * @return string */ protected function buildQueryString(array $params) { return http_build_query($params, null, '&', \PHP_QUERY_RFC3986); } } oauth2-client/src/Tool/RequiredParameterTrait.php 0000644 00000002776 14736103263 0016105 0 ustar 00 <?php /** * This file is part of the league/oauth2-client library * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * @copyright Copyright (c) Alex Bilbie <hello@alexbilbie.com> * @license http://opensource.org/licenses/MIT MIT * @link http://thephpleague.com/oauth2-client/ Documentation * @link https://packagist.org/packages/league/oauth2-client Packagist * @link https://github.com/thephpleague/oauth2-client GitHub */ namespace League\OAuth2\Client\Tool; use BadMethodCallException; /** * Provides functionality to check for required parameters. */ trait RequiredParameterTrait { /** * Checks for a required parameter in a hash. * * @throws BadMethodCallException * @param string $name * @param array $params * @return void */ private function checkRequiredParameter($name, array $params) { if (!isset($params[$name])) { throw new BadMethodCallException(sprintf( 'Required parameter not passed: "%s"', $name )); } } /** * Checks for multiple required parameters in a hash. * * @throws InvalidArgumentException * @param array $names * @param array $params * @return void */ private function checkRequiredParameters(array $names, array $params) { foreach ($names as $name) { $this->checkRequiredParameter($name, $params); } } } oauth2-client/src/Grant/ClientCredentials.php 0000644 00000001737 14736103263 0015206 0 ustar 00 <?php /** * This file is part of the league/oauth2-client library * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * @copyright Copyright (c) Alex Bilbie <hello@alexbilbie.com> * @license http://opensource.org/licenses/MIT MIT * @link http://thephpleague.com/oauth2-client/ Documentation * @link https://packagist.org/packages/league/oauth2-client Packagist * @link https://github.com/thephpleague/oauth2-client GitHub */ namespace League\OAuth2\Client\Grant; /** * Represents a client credentials grant. * * @link http://tools.ietf.org/html/rfc6749#section-1.3.4 Client Credentials (RFC 6749, §1.3.4) */ class ClientCredentials extends AbstractGrant { /** * @inheritdoc */ protected function getName() { return 'client_credentials'; } /** * @inheritdoc */ protected function getRequiredRequestParameters() { return []; } } oauth2-client/src/Grant/Password.php 0000644 00000002047 14736103263 0013407 0 ustar 00 <?php /** * This file is part of the league/oauth2-client library * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * @copyright Copyright (c) Alex Bilbie <hello@alexbilbie.com> * @license http://opensource.org/licenses/MIT MIT * @link http://thephpleague.com/oauth2-client/ Documentation * @link https://packagist.org/packages/league/oauth2-client Packagist * @link https://github.com/thephpleague/oauth2-client GitHub */ namespace League\OAuth2\Client\Grant; /** * Represents a resource owner password credentials grant. * * @link http://tools.ietf.org/html/rfc6749#section-1.3.3 Resource Owner Password Credentials (RFC 6749, §1.3.3) */ class Password extends AbstractGrant { /** * @inheritdoc */ protected function getName() { return 'password'; } /** * @inheritdoc */ protected function getRequiredRequestParameters() { return [ 'username', 'password', ]; } } oauth2-client/src/Grant/AuthorizationCode.php 0000644 00000001775 14736103263 0015247 0 ustar 00 <?php /** * This file is part of the league/oauth2-client library * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * @copyright Copyright (c) Alex Bilbie <hello@alexbilbie.com> * @license http://opensource.org/licenses/MIT MIT * @link http://thephpleague.com/oauth2-client/ Documentation * @link https://packagist.org/packages/league/oauth2-client Packagist * @link https://github.com/thephpleague/oauth2-client GitHub */ namespace League\OAuth2\Client\Grant; /** * Represents an authorization code grant. * * @link http://tools.ietf.org/html/rfc6749#section-1.3.1 Authorization Code (RFC 6749, §1.3.1) */ class AuthorizationCode extends AbstractGrant { /** * @inheritdoc */ protected function getName() { return 'authorization_code'; } /** * @inheritdoc */ protected function getRequiredRequestParameters() { return [ 'code', ]; } } oauth2-client/src/Grant/GrantFactory.php 0000644 00000005144 14736103263 0014211 0 ustar 00 <?php /** * This file is part of the league/oauth2-client library * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * @copyright Copyright (c) Alex Bilbie <hello@alexbilbie.com> * @license http://opensource.org/licenses/MIT MIT * @link http://thephpleague.com/oauth2-client/ Documentation * @link https://packagist.org/packages/league/oauth2-client Packagist * @link https://github.com/thephpleague/oauth2-client GitHub */ namespace League\OAuth2\Client\Grant; use League\OAuth2\Client\Grant\Exception\InvalidGrantException; /** * Represents a factory used when retrieving an authorization grant type. */ class GrantFactory { /** * @var array */ protected $registry = []; /** * Defines a grant singleton in the registry. * * @param string $name * @param AbstractGrant $grant * @return self */ public function setGrant($name, AbstractGrant $grant) { $this->registry[$name] = $grant; return $this; } /** * Returns a grant singleton by name. * * If the grant has not be registered, a default grant will be loaded. * * @param string $name * @return AbstractGrant */ public function getGrant($name) { if (empty($this->registry[$name])) { $this->registerDefaultGrant($name); } return $this->registry[$name]; } /** * Registers a default grant singleton by name. * * @param string $name * @return self */ protected function registerDefaultGrant($name) { // PascalCase the grant. E.g: 'authorization_code' becomes 'AuthorizationCode' $class = str_replace(' ', '', ucwords(str_replace(['-', '_'], ' ', $name))); $class = 'League\\OAuth2\\Client\\Grant\\' . $class; $this->checkGrant($class); return $this->setGrant($name, new $class); } /** * Determines if a variable is a valid grant. * * @param mixed $class * @return boolean */ public function isGrant($class) { return is_subclass_of($class, AbstractGrant::class); } /** * Checks if a variable is a valid grant. * * @throws InvalidGrantException * @param mixed $class * @return void */ public function checkGrant($class) { if (!$this->isGrant($class)) { throw new InvalidGrantException(sprintf( 'Grant "%s" must extend AbstractGrant', is_object($class) ? get_class($class) : $class )); } } } oauth2-client/src/Grant/Exception/InvalidGrantException.php 0000644 00000001424 14736103263 0020002 0 ustar 00 <?php /** * This file is part of the league/oauth2-client library * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * @copyright Copyright (c) Alex Bilbie <hello@alexbilbie.com> * @license http://opensource.org/licenses/MIT MIT * @link http://thephpleague.com/oauth2-client/ Documentation * @link https://packagist.org/packages/league/oauth2-client Packagist * @link https://github.com/thephpleague/oauth2-client GitHub */ namespace League\OAuth2\Client\Grant\Exception; use InvalidArgumentException; /** * Exception thrown if the grant does not extend from AbstractGrant. * * @see League\OAuth2\Client\Grant\AbstractGrant */ class InvalidGrantException extends InvalidArgumentException { } oauth2-client/src/Grant/RefreshToken.php 0000644 00000001766 14736103263 0014213 0 ustar 00 <?php /** * This file is part of the league/oauth2-client library * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * @copyright Copyright (c) Alex Bilbie <hello@alexbilbie.com> * @license http://opensource.org/licenses/MIT MIT * @link http://thephpleague.com/oauth2-client/ Documentation * @link https://packagist.org/packages/league/oauth2-client Packagist * @link https://github.com/thephpleague/oauth2-client GitHub */ namespace League\OAuth2\Client\Grant; /** * Represents a refresh token grant. * * @link http://tools.ietf.org/html/rfc6749#section-6 Refreshing an Access Token (RFC 6749, §6) */ class RefreshToken extends AbstractGrant { /** * @inheritdoc */ protected function getName() { return 'refresh_token'; } /** * @inheritdoc */ protected function getRequiredRequestParameters() { return [ 'refresh_token', ]; } } oauth2-client/src/Grant/AbstractGrant.php 0000644 00000004645 14736103263 0014352 0 ustar 00 <?php /** * This file is part of the league/oauth2-client library * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * @copyright Copyright (c) Alex Bilbie <hello@alexbilbie.com> * @license http://opensource.org/licenses/MIT MIT * @link http://thephpleague.com/oauth2-client/ Documentation * @link https://packagist.org/packages/league/oauth2-client Packagist * @link https://github.com/thephpleague/oauth2-client GitHub */ namespace League\OAuth2\Client\Grant; use League\OAuth2\Client\Tool\RequiredParameterTrait; /** * Represents a type of authorization grant. * * An authorization grant is a credential representing the resource * owner's authorization (to access its protected resources) used by the * client to obtain an access token. OAuth 2.0 defines four * grant types -- authorization code, implicit, resource owner password * credentials, and client credentials -- as well as an extensibility * mechanism for defining additional types. * * @link http://tools.ietf.org/html/rfc6749#section-1.3 Authorization Grant (RFC 6749, §1.3) */ abstract class AbstractGrant { use RequiredParameterTrait; /** * Returns the name of this grant, eg. 'grant_name', which is used as the * grant type when encoding URL query parameters. * * @return string */ abstract protected function getName(); /** * Returns a list of all required request parameters. * * @return array */ abstract protected function getRequiredRequestParameters(); /** * Returns this grant's name as its string representation. This allows for * string interpolation when building URL query parameters. * * @return string */ public function __toString() { return $this->getName(); } /** * Prepares an access token request's parameters by checking that all * required parameters are set, then merging with any given defaults. * * @param array $defaults * @param array $options * @return array */ public function prepareRequestParameters(array $defaults, array $options) { $defaults['grant_type'] = $this->getName(); $required = $this->getRequiredRequestParameters(); $provided = array_merge($defaults, $options); $this->checkRequiredParameters($required, $provided); return $provided; } } oauth2-client/src/OptionProvider/HttpBasicAuthOptionProvider.php 0000644 00000002646 14736103263 0021131 0 ustar 00 <?php /** * This file is part of the league/oauth2-client library * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * @copyright Copyright (c) Alex Bilbie <hello@alexbilbie.com> * @license http://opensource.org/licenses/MIT MIT * @link http://thephpleague.com/oauth2-client/ Documentation * @link https://packagist.org/packages/league/oauth2-client Packagist * @link https://github.com/thephpleague/oauth2-client GitHub */ namespace League\OAuth2\Client\OptionProvider; use InvalidArgumentException; /** * Add http basic auth into access token request options * @link https://tools.ietf.org/html/rfc6749#section-2.3.1 */ class HttpBasicAuthOptionProvider extends PostAuthOptionProvider { /** * @inheritdoc */ public function getAccessTokenOptions($method, array $params) { if (empty($params['client_id']) || empty($params['client_secret'])) { throw new InvalidArgumentException('clientId and clientSecret are required for http basic auth'); } $encodedCredentials = base64_encode(sprintf('%s:%s', $params['client_id'], $params['client_secret'])); unset($params['client_id'], $params['client_secret']); $options = parent::getAccessTokenOptions($method, $params); $options['headers']['Authorization'] = 'Basic ' . $encodedCredentials; return $options; } } oauth2-client/src/OptionProvider/OptionProviderInterface.php 0000644 00000001573 14736103263 0020324 0 ustar 00 <?php /** * This file is part of the league/oauth2-client library * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * @copyright Copyright (c) Alex Bilbie <hello@alexbilbie.com> * @license http://opensource.org/licenses/MIT MIT * @link http://thephpleague.com/oauth2-client/ Documentation * @link https://packagist.org/packages/league/oauth2-client Packagist * @link https://github.com/thephpleague/oauth2-client GitHub */ namespace League\OAuth2\Client\OptionProvider; /** * Interface for access token options provider */ interface OptionProviderInterface { /** * Builds request options used for requesting an access token. * * @param string $method * @param array $params * @return array */ public function getAccessTokenOptions($method, array $params); } oauth2-client/src/OptionProvider/PostAuthOptionProvider.php 0000644 00000002622 14736103263 0020167 0 ustar 00 <?php /** * This file is part of the league/oauth2-client library * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * @copyright Copyright (c) Alex Bilbie <hello@alexbilbie.com> * @license http://opensource.org/licenses/MIT MIT * @link http://thephpleague.com/oauth2-client/ Documentation * @link https://packagist.org/packages/league/oauth2-client Packagist * @link https://github.com/thephpleague/oauth2-client GitHub */ namespace League\OAuth2\Client\OptionProvider; use League\OAuth2\Client\Provider\AbstractProvider; use League\OAuth2\Client\Tool\QueryBuilderTrait; /** * Provide options for access token */ class PostAuthOptionProvider implements OptionProviderInterface { use QueryBuilderTrait; /** * @inheritdoc */ public function getAccessTokenOptions($method, array $params) { $options = ['headers' => ['content-type' => 'application/x-www-form-urlencoded']]; if ($method === AbstractProvider::METHOD_POST) { $options['body'] = $this->getAccessTokenBody($params); } return $options; } /** * Returns the request body for requesting an access token. * * @param array $params * @return string */ protected function getAccessTokenBody(array $params) { return $this->buildQueryString($params); } } oauth2-client/README.md 0000644 00000034677 14736103263 0010527 0 ustar 00 # OAuth 2.0 Client This package makes it simple to integrate your application with [OAuth 2.0](http://oauth.net/2/) service providers. [![Gitter Chat](https://img.shields.io/badge/gitter-join_chat-brightgreen.svg?style=flat-square)](https://gitter.im/thephpleague/oauth2-client) [![Source Code](http://img.shields.io/badge/source-thephpleague/oauth2--client-blue.svg?style=flat-square)](https://github.com/thephpleague/oauth2-client) [![Latest Version](https://img.shields.io/github/release/thephpleague/oauth2-client.svg?style=flat-square)](https://github.com/thephpleague/oauth2-client/releases) [![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](https://github.com/thephpleague/oauth2-client/blob/master/LICENSE) [![Build Status](https://img.shields.io/travis/thephpleague/oauth2-client/master.svg?style=flat-square)](https://travis-ci.org/thephpleague/oauth2-client) [![Scrutinizer](https://img.shields.io/scrutinizer/g/thephpleague/oauth2-client/master.svg?style=flat-square)](https://scrutinizer-ci.com/g/thephpleague/oauth2-client/) [![Coverage Status](https://img.shields.io/coveralls/thephpleague/oauth2-client/master.svg?style=flat-square)](https://coveralls.io/r/thephpleague/oauth2-client?branch=master) [![Total Downloads](https://img.shields.io/packagist/dt/league/oauth2-client.svg?style=flat-square)](https://packagist.org/packages/league/oauth2-client) --- We are all used to seeing those "Connect with Facebook/Google/etc." buttons around the internet, and social network integration is an important feature of most web applications these days. Many of these sites use an authentication and authorization standard called OAuth 2.0 ([RFC 6749](http://tools.ietf.org/html/rfc6749)). This OAuth 2.0 client library will work with any OAuth provider that conforms to the OAuth 2.0 standard. Out-of-the-box, we provide a `GenericProvider` that may be used to connect to any service provider that uses [Bearer tokens](http://tools.ietf.org/html/rfc6750) (see example below). Many service providers provide additional functionality above and beyond the OAuth 2.0 standard. For this reason, this library may be easily extended and wrapped to support this additional behavior. We provide links to [all known provider clients extending this library](docs/providers/thirdparty.md) (i.e. Facebook, GitHub, Google, Instagram, LinkedIn, etc.). If your provider isn't in the list, feel free to add it. This package is compliant with [PSR-1][], [PSR-2][], [PSR-4][], and [PSR-7][]. If you notice compliance oversights, please send a patch via pull request. If you're interesting in contributing to this library, please take a look at our [contributing guidelines](CONTRIBUTING.md). ## Requirements The following versions of PHP are supported. * PHP 5.6 * PHP 7.0 * PHP 7.1 * PHP 7.2 * PHP 7.3 ## Providers A list of official PHP League providers, as well as third-party providers, may be found in the [providers list README](docs/providers/thirdparty.md). To build your own provider, please refer to the [provider guide README](README.PROVIDER-GUIDE.md). ## Usage **In most cases, you'll want to use a specific provider client library rather than this base library.** Take a look at [providers list README](docs/providers/thirdparty.md) to see a list of provider client libraries. If using Composer to require a specific provider client library, you **do not need to also require this library**. Composer will handle the dependencies for you. ### Authorization Code Grant The following example uses the out-of-the-box `GenericProvider` provided by this library. If you're looking for a specific provider (i.e. Facebook, Google, GitHub, etc.), take a look at our [list of provider client libraries](docs/providers/thirdparty.md). **HINT: You're probably looking for a specific provider.** The authorization code grant type is the most common grant type used when authenticating users with a third-party service. This grant type utilizes a client (this library), a server (the service provider), and a resource owner (the user with credentials to a protected—or owned—resource) to request access to resources owned by the user. This is often referred to as _3-legged OAuth_, since there are three parties involved. The following example illustrates this using [Brent Shaffer's](https://github.com/bshaffer) demo OAuth 2.0 application named **Lock'd In**. When running this code, you will be redirected to Lock'd In, where you'll be prompted to authorize the client to make requests to a resource on your behalf. Now, you don't really have an account on Lock'd In, but for the sake of this example, imagine that you are already logged in on Lock'd In when you are redirected there. ```php $provider = new \League\OAuth2\Client\Provider\GenericProvider([ 'clientId' => 'demoapp', // The client ID assigned to you by the provider 'clientSecret' => 'demopass', // The client password assigned to you by the provider 'redirectUri' => 'http://example.com/your-redirect-url/', 'urlAuthorize' => 'http://brentertainment.com/oauth2/lockdin/authorize', 'urlAccessToken' => 'http://brentertainment.com/oauth2/lockdin/token', 'urlResourceOwnerDetails' => 'http://brentertainment.com/oauth2/lockdin/resource' ]); // If we don't have an authorization code then get one if (!isset($_GET['code'])) { // Fetch the authorization URL from the provider; this returns the // urlAuthorize option and generates and applies any necessary parameters // (e.g. state). $authorizationUrl = $provider->getAuthorizationUrl(); // Get the state generated for you and store it to the session. $_SESSION['oauth2state'] = $provider->getState(); // Redirect the user to the authorization URL. header('Location: ' . $authorizationUrl); exit; // Check given state against previously stored one to mitigate CSRF attack } elseif (empty($_GET['state']) || (isset($_SESSION['oauth2state']) && $_GET['state'] !== $_SESSION['oauth2state'])) { if (isset($_SESSION['oauth2state'])) { unset($_SESSION['oauth2state']); } exit('Invalid state'); } else { try { // Try to get an access token using the authorization code grant. $accessToken = $provider->getAccessToken('authorization_code', [ 'code' => $_GET['code'] ]); // We have an access token, which we may use in authenticated // requests against the service provider's API. echo 'Access Token: ' . $accessToken->getToken() . "<br>"; echo 'Refresh Token: ' . $accessToken->getRefreshToken() . "<br>"; echo 'Expired in: ' . $accessToken->getExpires() . "<br>"; echo 'Already expired? ' . ($accessToken->hasExpired() ? 'expired' : 'not expired') . "<br>"; // Using the access token, we may look up details about the // resource owner. $resourceOwner = $provider->getResourceOwner($accessToken); var_export($resourceOwner->toArray()); // The provider provides a way to get an authenticated API request for // the service, using the access token; it returns an object conforming // to Psr\Http\Message\RequestInterface. $request = $provider->getAuthenticatedRequest( 'GET', 'http://brentertainment.com/oauth2/lockdin/resource', $accessToken ); } catch (\League\OAuth2\Client\Provider\Exception\IdentityProviderException $e) { // Failed to get the access token or user details. exit($e->getMessage()); } } ``` ### Refreshing a Token Once your application is authorized, you can refresh an expired token using a refresh token rather than going through the entire process of obtaining a brand new token. To do so, simply reuse this refresh token from your data store to request a refresh. _This example uses [Brent Shaffer's](https://github.com/bshaffer) demo OAuth 2.0 application named **Lock'd In**. See authorization code example above, for more details._ ```php $provider = new \League\OAuth2\Client\Provider\GenericProvider([ 'clientId' => 'demoapp', // The client ID assigned to you by the provider 'clientSecret' => 'demopass', // The client password assigned to you by the provider 'redirectUri' => 'http://example.com/your-redirect-url/', 'urlAuthorize' => 'http://brentertainment.com/oauth2/lockdin/authorize', 'urlAccessToken' => 'http://brentertainment.com/oauth2/lockdin/token', 'urlResourceOwnerDetails' => 'http://brentertainment.com/oauth2/lockdin/resource' ]); $existingAccessToken = getAccessTokenFromYourDataStore(); if ($existingAccessToken->hasExpired()) { $newAccessToken = $provider->getAccessToken('refresh_token', [ 'refresh_token' => $existingAccessToken->getRefreshToken() ]); // Purge old access token and store new access token to your data store. } ``` ### Resource Owner Password Credentials Grant Some service providers allow you to skip the authorization code step to exchange a user's credentials (username and password) for an access token. This is referred to as the "resource owner password credentials" grant type. According to [section 1.3.3](http://tools.ietf.org/html/rfc6749#section-1.3.3) of the OAuth 2.0 standard (emphasis added): > The credentials **should only be used when there is a high degree of trust** > between the resource owner and the client (e.g., the client is part of the > device operating system or a highly privileged application), and when other > authorization grant types are not available (such as an authorization code). **We do not advise using this grant type if the service provider supports the authorization code grant type (see above), as this reinforces the [password anti-pattern](https://agentile.com/the-password-anti-pattern) by allowing users to think it's okay to trust third-party applications with their usernames and passwords.** That said, there are use-cases where the resource owner password credentials grant is acceptable and useful. Here's an example using it with [Brent Shaffer's](https://github.com/bshaffer) demo OAuth 2.0 application named **Lock'd In**. See authorization code example above, for more details about the Lock'd In demo application. ``` php $provider = new \League\OAuth2\Client\Provider\GenericProvider([ 'clientId' => 'demoapp', // The client ID assigned to you by the provider 'clientSecret' => 'demopass', // The client password assigned to you by the provider 'redirectUri' => 'http://example.com/your-redirect-url/', 'urlAuthorize' => 'http://brentertainment.com/oauth2/lockdin/authorize', 'urlAccessToken' => 'http://brentertainment.com/oauth2/lockdin/token', 'urlResourceOwnerDetails' => 'http://brentertainment.com/oauth2/lockdin/resource' ]); try { // Try to get an access token using the resource owner password credentials grant. $accessToken = $provider->getAccessToken('password', [ 'username' => 'demouser', 'password' => 'testpass' ]); } catch (\League\OAuth2\Client\Provider\Exception\IdentityProviderException $e) { // Failed to get the access token exit($e->getMessage()); } ``` ### Client Credentials Grant When your application is acting on its own behalf to access resources it controls/owns in a service provider, it may use the client credentials grant type. This is best used when the credentials for your application are stored privately and never exposed (e.g. through the web browser, etc.) to end-users. This grant type functions similarly to the resource owner password credentials grant type, but it does not request a user's username or password. It uses only the client ID and secret issued to your client by the service provider. Unlike earlier examples, the following does not work against a functioning demo service provider. It is provided for the sake of example only. ``` php // Note: the GenericProvider requires the `urlAuthorize` option, even though // it's not used in the OAuth 2.0 client credentials grant type. $provider = new \League\OAuth2\Client\Provider\GenericProvider([ 'clientId' => 'XXXXXX', // The client ID assigned to you by the provider 'clientSecret' => 'XXXXXX', // The client password assigned to you by the provider 'redirectUri' => 'http://my.example.com/your-redirect-url/', 'urlAuthorize' => 'http://service.example.com/authorize', 'urlAccessToken' => 'http://service.example.com/token', 'urlResourceOwnerDetails' => 'http://service.example.com/resource' ]); try { // Try to get an access token using the client credentials grant. $accessToken = $provider->getAccessToken('client_credentials'); } catch (\League\OAuth2\Client\Provider\Exception\IdentityProviderException $e) { // Failed to get the access token exit($e->getMessage()); } ``` ### Using a proxy It is possible to use a proxy to debug HTTP calls made to a provider. All you need to do is set the `proxy` and `verify` options when creating your Provider instance. Make sure you enable SSL proxying in your proxy. ``` php $provider = new \League\OAuth2\Client\Provider\GenericProvider([ 'clientId' => 'XXXXXX', // The client ID assigned to you by the provider 'clientSecret' => 'XXXXXX', // The client password assigned to you by the provider 'redirectUri' => 'http://my.example.com/your-redirect-url/', 'urlAuthorize' => 'http://service.example.com/authorize', 'urlAccessToken' => 'http://service.example.com/token', 'urlResourceOwnerDetails' => 'http://service.example.com/resource', 'proxy' => '192.168.0.1:8888', 'verify' => false ]); ``` ## Install Via Composer ``` bash $ composer require league/oauth2-client ``` ## Contributing Please see [CONTRIBUTING](https://github.com/thephpleague/oauth2-client/blob/master/CONTRIBUTING.md) for details. ## License The MIT License (MIT). Please see [License File](https://github.com/thephpleague/oauth2-client/blob/master/LICENSE) for more information. [PSR-1]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-1-basic-coding-standard.md [PSR-2]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md [PSR-4]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-4-autoloader.md [PSR-7]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-7-http-message.md oauth2-client/README.PROVIDER-GUIDE.md 0000644 00000010215 14736103263 0012571 0 ustar 00 # OAuth 2.0 Client ## Provider Guide New providers may be created by copying the layout of an existing package. See the [list of providers](docs/providers/thirdparty.md) for good examples. When choosing a name for your package, please don’t use the `league` vendor prefix, as this implies that it is officially supported. You should use your own username as the vendor prefix, and prepend `oauth2-` to the package name to make it clear that your package works with OAuth2 Client. For example, if your GitHub username was "santa," and you were implementing the "giftpay" OAuth2 library, a good name for your composer package would be `santa/oauth2-giftpay`. ### Implementing your own provider If you are working with an oauth2 service not supported out-of-the-box or by an existing package, it is quite simple to implement your own. Simply extend [`League\OAuth2\Client\Provider\AbstractProvider`](src/Provider/AbstractProvider.php) and implement the required abstract methods: ```php abstract public function getBaseAuthorizationUrl(); abstract public function getBaseAccessTokenUrl(array $params); abstract public function getResourceOwnerDetailsUrl(AccessToken $token); abstract protected function getDefaultScopes(); abstract protected function checkResponse(ResponseInterface $response, $data); abstract protected function createResourceOwner(array $response, AccessToken $token); ``` Each of these abstract methods contain a docblock defining their expectations and typical behavior. Once you have extended this class, you can simply follow the [usage example in the README](README.md#usage) using your new `Provider`. If you wish to use the `Provider` to make authenticated requests to the service, you will also need to define how you provide the token to the service. If this is done via headers, you should override this method: ```php protected function getAuthorizationHeaders($token = null); ``` This package comes with a trait for implementing `Bearer` authorization. To use this, you just need to include the trait in your `Provider` class: ```php <?php class SomeProvider extends AbstractProvider { use League\OAuth2\Client\Tool\BearerAuthorizationTrait; /** ... **/ } ``` ### Resource owner identifiers in access token responses In services where the resource owner is a person, the resource owner is sometimes referred to as an end-user. We have decided to abstract away as much of the resource owner details as possible, since these are not part of the OAuth 2.0 specification and are very specific to each service provider. This provides greater flexibility to each provider, allowing them to handle the implementation details for resource owners. The `AbstractProvider` does not specify an access token resource owner identifier. It is the responsibility of the provider class to set the `ACCESS_TOKEN_RESOURCE_OWNER_ID` constant to the string value of the key used in the access token response to identify the resource owner. ```php /** * @var string Key used in the access token response to identify the resource owner. */ const ACCESS_TOKEN_RESOURCE_OWNER_ID = null; ``` Once this is set on your provider, when calling `AbstractProvider::getAccessToken()`, the `AccessToken` returned will have its `$resourceOwnerId` property set, which you may retrieve by calling `AccessToken::getResourceOwnerId()`. The next step is to implement the `AbstractProvider::createResourceOwner()` method. This method accepts as parameters a response array and an `AccessToken`. You may use this information in order to request resource owner details from your service and construct and return an object that implements [`League\OAuth2\Client\Provider\ResourceOwnerInterface`](src/Provider/ResourceOwnerInterface.php). This object is returned when calling `AbstractProvider::getResourceOwner()`. ### Make your gateway official If you want to transfer your provider to the `thephpleague` GitHub organization and add it to the list of officially supported providers, please open a pull request on the thephpleague/oauth2-client package. Before new providers will be accepted, they must have 100% unit test code coverage, and follow the conventions and code style used in other OAuth2 Client providers. oauth2-client/CHANGELOG.md 0000644 00000026247 14736103263 0011053 0 ustar 00 # OAuth 2.0 Client Changelog ## 2.5.0 _Released: 2020-07-18_ * Allow Guzzle 7.x to be used [#847](https://github.com/thephpleague/oauth2-client/pull/847) ## 2.4.1 _Released: 2018-11-22_ * Revert to use of `AccessToken` in type hints to preserve backwards compatibility; this fixes the issue reported in [#752](https://github.com/thephpleague/oauth2-client/issues/752) and [#753](https://github.com/thephpleague/oauth2-client/issues/753) ## 2.4.0 _Released: 2018-11-21_ * Add `HttpBasicAuthOptionProvider` to ease implementation for providers requiring HTTP basic auth * Add `GuardedPropertyTrait` to allow providers the ability to specify properties that may not be overridden by user-defined values passed to the provider constructor * Add `AccessTokenInterface` and `ResourceOwnerAccessTokenInterface` to allow providers the ability to override the default `AccessToken` ## 2.3.1 _Released: 2018-11-19_ * Allow paragonie/random_compat's empty 9.99.99 placeholder * Throw an `UnexpectedValueException` on non-JSON responses from access token request (when calling `AbstractProvider::getAccessToken()`) ## 2.3.0 _Released: 2018-01-13_ * Add `ProviderRedirectTrait` tool for 3rd-party provider libraries to use when handling provider redirections * Fix TypeError thrown because `getResourceOwner()` receives a non-JSON Response * Gracefully handle non-standard errors received from providers * Update README to reflect official support of PHP 7.2 ## 2.2.1 _Released: 2017-04-25_ * Fix potential type error when HTTP 500 errors are encountered * Allow broader range of `random_compat` versions ## 2.2.0 _Released: 2017-02-01_ * Allow base URLs to contain query parameters * Protect against `+` being improperly encoded in URL parameters * Remove misleading `state` option from authorization parameters * Stop generating more random bytes than necessary ## 2.1.0 _Released: 2017-01-24_ * Allow `expires_in` with a value of `0` ## 2.0.0 _Released: 2017-01-12_ * Rename `getResponse()` to `getParsedResponse()` * Add `getResponse()` method that returns the unparsed PSR-7 `Response` instance * Removed `RandomFactory`, switched to native random functions ## 1.4.1 _Released: 2016-04-29_ * Add `QueryBuilderTrait` to standardize query string generation. ## 1.4.0 _Released: 2016-04-19_ * Add `AccessToken::getValues()` to access additional vendor data provided with tokens. ## 1.3.0 _Released: 2016-02-13_ * Enable dynamic parameters being passed into the authorization URL. * Minor documentation updates. ## 1.2.0 _Released: 2016-01-23_ * Add `resource_owner_id` to the JSON-serialized representation of the access token. * Minor documentation updates and improved test coverage. ## 1.1.0 _Released: 2015-11-13_ * Add `ArrayAccessorTrait`, update `AbstractProvider` to utilize. * Use `expires` to serialize access tokens. * Documentation updates. ## 1.0.2 _Released: 2015-09-22_ * Allow access tokens to be created from storage (see #431). * Minor fixes and documentation updates. ## 1.0.1 _Released: 2015-08-26_ * Allow required parameters checked using the `RequiredParameterTrait` to be set as `false`, `null`, `"0"`, etc. ## 1.0.0 _Released: 2015-08-19_ * We are running code-quality builds through Scrutinizer, and we are running unit test builds on the new Travis CI container-based infrastructure. * Cleaned up code, as recommended by Scrutinizer. * Documentation updates. ## 1.0.0-beta2 _Released: 2015-08-12_ * BREAK: Add toArray() to ResourceOwnerInterface. * Always attempt to parse responses as JSON and fallback on failure. * Add dot notation support to access token resource owner ID. * Use the Bearer authorization header for the generic provider. * Documentation updates. ## 1.0.0-beta1 _Released: 2015-07-16_ * API for 1.0 is now frozen! * BREAK: Convert all uses of "User" to "ResourceOwner" to more closely match the OAuth 2.0 specification. * BREAK: Rename `StandardProvider` to `GenericProvider`. * BREAK: Move access token creation to the `AbstractProvider`. It was previously handled in the `AbstractGrant`. * FIX: Add `Content-Type` header with value of `application/x-www-form-urlencoded` to the request header when retrieving access tokens. This adheres to the OAuth 2.0 specification and fixes issues where certain OAuth servers expect this header. * Enhanced `json_encode()` serialization of AccessToken; when using `json_encode()` on an AccessToken, it will return a JSON object with these properties: `access_token`, `refresh_token`, and `expires_in`. ## 1.0.0-alpha2 _Released: 2015-07-04_ * BREAK: Renamed `AbstractProvider::ACCESS_TOKEN_METHOD_GET` to `AbstractProvider::METHOD_GET`. * BREAK: Renamed `AbstractProvider::ACCESS_TOKEN_METHOD_POST` to `AbstractProvider::METHOD_POST`. * BREAK: Renamed `AbstractProvider::prepareUserDetails()` to `AbstractProvider::createUser()`. * BREAK: Renamed `AbstractProvider::getUserDetails()` to `AbstractProvider::getUser()`. * BREAK: Removed `$token` parameter from `AbstractProvider::getDefaultHeaders()`. * BREAK: Modify `AbstractProvider::getBaseAccessTokenUrl()` to accept a required array of parameters, allowing providers the ability to vary the access token URL, based on the parameters. * Removed newline characters from MAC Authorization header. * Documentation updates, notably: - Moved list of providers to `README.PROVIDERS.md`. - Moved provider creation notes to `README.PROVIDER-GUIDE.md`. ## 1.0.0-alpha1 _Released: 2015-06-25_ This release contains numerous BC breaks from the 0.x series. Please note these breaks and refer to the [upgrade guide](GUIDE-UPGRADING.md). * BREAK: Requires PHP 5.5.0 and greater. * BREAK: All providers have been moved to separate repositories, one for each provider. * BREAK: All `public` properties have been set as `protected` or `private` and getters/setters have been introduced for access to these properties. * BREAK: The `Provider\ProviderInterface` has been removed. Please extend from and override `Provider\AbstractProvider`. * BREAK: The `Entity\User` has been removed. Providers should implement the `Provider\UserInterface` and provide user functionality instead of expecting it in this base library. * BREAK: The `Grant\GrantInterface` has been removed. Providers needing to provide a new grant type should extend from and override `Grant\AbstractGrant`. * A generic `Provider\StandardProvider` has been introduced, which may be used as a client to integrate with most OAuth 2.0 compatible servers. * A `Grant\GrantFactory` has been introduced as a means to register and retrieve singleton grants from a registry. * Introduced traits for bearer and MAC authorization (`Tool\BearerAuthorizationTrait` and `Tool\MacAuthorizationTrait`), which providers may use to enable these header authorization types. ## 0.12.1 _Released: 2015-06-20_ * FIX: Scope separators for LinkedIn and Instagram are now correctly a single space ## 0.12.0 _Released: 2015-06-15_ * BREAK: LinkedIn Provider: Default scopes removed from LinkedIn Provider. See "[Managing LinkedIn Scopes](https://github.com/thephpleague/oauth2-client/blob/9cea9864c2e89bce1b922d1e37ba5378b3b0b264/README.md#managing-linkedin-scopes)" in the README for information on how to set scopes. See [#327](https://github.com/thephpleague/oauth2-client/pull/327) and [#307](https://github.com/thephpleague/oauth2-client/pull/307) for details on this change. * FIX: LinkedIn Provider: A scenario existed in which `publicProfileUrl` was not set, generating a PHP notice; this has been fixed. * FIX: Instagram Provider: Fixed scope separator. * Documentation updates and corrections. ## 0.11.0 _Released: 2015-04-25_ * Identity Provider: Better handling of error responses * Documentation updates ## 0.10.1 _Released: 2015-04-02_ * FIX: Invalid JSON triggering fatal error * FIX: Sending headers along with auth `getAccessToken()` requests * Now running Travis CI tests on PHP 7 * Documentation updates ## 0.10.0 _Released: 2015-03-10_ * Providers: Added `getHeaders()` to ProviderInterface and updated AbstractProvider to provide the method * Providers: Updated all bundled providers to support new `$authorizationHeader` property * Identity Provider: Update IDPException to account for empty strings * Identity Provider: Added `getResponseBody()` method to IDPException * Documentation updates, minor bug fixes, and coding standards fixes ## 0.9.0 _Released: 2015-02-24_ * Add `AbstractProvider::prepareAccessTokenResult()` to provide additional token response preparation to providers * Remove custom provider code from AccessToken * Add links to README for Dropbox and Square providers ## 0.8.1 _Released: 2015-02-12_ * Allow `approval_prompt` to be set by providers. This fixes an issue where some providers have problems if the `approval_prompt` is present in the query string. ## 0.8.0 _Released: 2015-02-10_ * Facebook Provider: Upgrade to Graph API v2.2 * Google Provider: Add `access_type` parameter for Google authorization URL * Get a more reliable response body on errors ## 0.7.2 _Released: 2015-02-03_ * GitHub Provider: Fix regression * Documentation updates ## 0.7.1 _Released: 2015-01-06_ * Google Provider: fixed issue where Google API was not returning the user ID ## 0.7.0 _Released: 2014-12-29_ * Improvements to Provider\AbstractProvider (addition of `userUid()`, `userEmail()`, and `userScreenName()`) * GitHub Provider: Support for GitHub Enterprise * GitHub Provider: Methods to allow fetching user email addresses * Google Provider: Updated scopes and endpoints to remove deprecated values * Documentation updates, minor bug fixes, and coding standards fixes ## 0.6.0 _Released: 2014-12-03_ * Added ability to specify a redirect handler for providers through use of a callback (see [Provider\AbstractProvider::setRedirectHandler()](https://github.com/thephpleague/oauth2-client/blob/55de45401eaa21f53c0b2414091da6f3b0f3fcb7/src/Provider/AbstractProvider.php#L314-L317)) * Updated authorize and token URLs for the Microsoft provider; the old URLs had been phased out and were no longer working (see #146) * Increased test coverage * Documentation updates, minor bug fixes, and coding standards fixes ## 0.5.0 _Released: 2014-11-28_ * Added `ClientCredentials` and `Password` grants * Added support for providers to set their own `uid` parameter key name * Added support for Google's `hd` (hosted domain) parameter * Added support for providing a custom `state` parameter to the authorization URL * LinkedIn `pictureUrl` is now an optional response element * Added Battle.net provider package link to README * Added Meetup provider package link to README * Added `.gitattributes` file * Increased test coverage * A number of documentation fixes, minor bug fixes, and coding standards fixes ## 0.4.0 _Released: 2014-10-28_ * Added `ProviderInterface` and removed `IdentityProvider`. * Expose generated state to allow for CSRF validation. * Renamed `League\OAuth2\Client\Provider\User` to `League\OAuth2\Client\Entity\User`. * Entity: User: added `gender` and `locale` properties * Updating logic for populating the token expiration time. ## 0.3.0 _Released: 2014-04-26_ * This release made some huge leaps forward, including 100% unit-coverage and a bunch of new features. ## 0.2.0 _Released: 2013-05-28_ * No release notes available. ## 0.1.0 _Released: 2013-05-25_ * Initial release. oauth2-client/LICENSE 0000644 00000002122 14736103263 0010231 0 ustar 00 The MIT License (MIT) Copyright (c) 2013-2018 Alex Bilbie <hello@alexbilbie.com> Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. oauth2-google/CONTRIBUTING.md 0000644 00000003251 14736103263 0011457 0 ustar 00 # Contributing Contributions are **welcome** and will be fully **credited**. We accept contributions via Pull Requests on [Github](https://github.com/thephpleague/oauth2-google). ## Pull Requests - **[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). - **Add tests!** - Your patch won't be accepted if it doesn't have tests. - **Document any change in behaviour** - Make sure the README and any other relevant documentation are kept up-to-date. - **Consider our release cycle** - We try to follow SemVer. Randomly breaking public APIs is not an option. - **Create topic branches** - Don't ask us to pull from your master branch. - **One pull request per feature** - If you want to do more than one thing, send multiple pull requests. - **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. - **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. - **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. ## Running Tests ```sh composer test ``` ## Running PHP Code Sniffer ```sh composer check ``` **Happy coding**! oauth2-google/src/Provider/GoogleUser.php 0000644 00000003623 14736103263 0014376 0 ustar 00 <?php namespace League\OAuth2\Client\Provider; class GoogleUser implements ResourceOwnerInterface { /** * @var array */ protected $response; /** * @param array $response */ public function __construct(array $response) { $this->response = $response; } public function getId() { return $this->response['sub']; } /** * Get preferred display name. * * @return string */ public function getName() { return $this->response['name']; } /** * Get preferred first name. * * @return string|null */ public function getFirstName() { return $this->getResponseValue('given_name'); } /** * Get preferred last name. * * @return string|null */ public function getLastName() { return $this->getResponseValue('family_name'); } /** * Get locale. * * @return string|null */ public function getLocale() { return $this->getResponseValue('locale'); } /** * Get email address. * * @return string|null */ public function getEmail() { return $this->getResponseValue('email'); } /** * Get hosted domain. * * @return string|null */ public function getHostedDomain() { return $this->getResponseValue('hd'); } /** * Get avatar image URL. * * @return string|null */ public function getAvatar() { return $this->getResponseValue('picture'); } /** * Get user data as an array. * * @return array */ public function toArray() { return $this->response; } private function getResponseValue($key) { if (array_key_exists($key, $this->response)) { return $this->response[$key]; } return null; } } oauth2-google/src/Provider/Google.php 0000644 00000010517 14736103263 0013537 0 ustar 00 <?php namespace League\OAuth2\Client\Provider; use League\OAuth2\Client\Exception\HostedDomainException; use League\OAuth2\Client\Provider\Exception\IdentityProviderException; use League\OAuth2\Client\Token\AccessToken; use League\OAuth2\Client\Tool\BearerAuthorizationTrait; use Psr\Http\Message\ResponseInterface; class Google extends AbstractProvider { use BearerAuthorizationTrait; /** * @var string If set, this will be sent to google as the "access_type" parameter. * @link https://developers.google.com/identity/protocols/OpenIDConnect#authenticationuriparameters */ protected $accessType; /** * @var string If set, this will be sent to google as the "hd" parameter. * @link https://developers.google.com/identity/protocols/OpenIDConnect#authenticationuriparameters */ protected $hostedDomain; /** * @var string If set, this will be sent to google as the "prompt" parameter. * @link https://developers.google.com/identity/protocols/OpenIDConnect#authenticationuriparameters */ protected $prompt; /** * @var array List of scopes that will be used for authentication. * @link https://developers.google.com/identity/protocols/googlescopes */ protected $scopes = []; public function getBaseAuthorizationUrl() { return 'https://accounts.google.com/o/oauth2/v2/auth'; } public function getBaseAccessTokenUrl(array $params) { return 'https://www.googleapis.com/oauth2/v4/token'; } public function getResourceOwnerDetailsUrl(AccessToken $token) { return 'https://openidconnect.googleapis.com/v1/userinfo'; } protected function getAuthorizationParameters(array $options) { if (empty($options['hd']) && $this->hostedDomain) { $options['hd'] = $this->hostedDomain; } if (empty($options['access_type']) && $this->accessType) { $options['access_type'] = $this->accessType; } if (empty($options['prompt']) && $this->prompt) { $options['prompt'] = $this->prompt; } // Default scopes MUST be included for OpenID Connect. // Additional scopes MAY be added by constructor or option. $scopes = array_merge($this->getDefaultScopes(), $this->scopes); if (!empty($options['scope'])) { $scopes = array_merge($scopes, $options['scope']); } $options['scope'] = array_unique($scopes); $options = parent::getAuthorizationParameters($options); // The "approval_prompt" MUST be removed as it is not supported by Google, use "prompt" instead: // https://developers.google.com/identity/protocols/oauth2/openid-connect#prompt unset($options['approval_prompt']); return $options; } protected function getDefaultScopes() { // "openid" MUST be the first scope in the list. return [ 'openid', 'email', 'profile', ]; } protected function getScopeSeparator() { return ' '; } protected function checkResponse(ResponseInterface $response, $data) { // @codeCoverageIgnoreStart if (empty($data['error'])) { return; } // @codeCoverageIgnoreEnd $code = 0; $error = $data['error']; if (is_array($error)) { $code = $error['code']; $error = $error['message']; } throw new IdentityProviderException($error, $code, $data); } protected function createResourceOwner(array $response, AccessToken $token) { $user = new GoogleUser($response); $this->assertMatchingDomain($user->getHostedDomain()); return $user; } /** * @throws HostedDomainException If the domain does not match the configured domain. */ protected function assertMatchingDomain($hostedDomain) { if ($this->hostedDomain === null) { // No hosted domain configured. return; } if ($this->hostedDomain === '*' && $hostedDomain) { // Any hosted domain is allowed. return; } if ($this->hostedDomain === $hostedDomain) { // Hosted domain is correct. return; } throw HostedDomainException::notMatchingDomain($this->hostedDomain); } } oauth2-google/src/Exception/HostedDomainException.php 0000644 00000000565 14736103263 0016726 0 ustar 00 <?php namespace League\OAuth2\Client\Exception; /** * Exception thrown if the Google Provider is configured with a hosted domain that the user doesn't belong to */ class HostedDomainException extends \Exception { public static function notMatchingDomain($configuredDomain) { return new static("User is not part of domain '$configuredDomain'"); } } oauth2-google/README.md 0000644 00000016545 14736103263 0010517 0 ustar 00 # Google Provider for OAuth 2.0 Client [![Join the chat](https://img.shields.io/badge/gitter-join-1DCE73.svg)](https://gitter.im/thephpleague/oauth2-google) [![Build Status](https://img.shields.io/travis/thephpleague/oauth2-google.svg)](https://travis-ci.org/thephpleague/oauth2-google) [![Code Coverage](https://img.shields.io/coveralls/thephpleague/oauth2-google.svg)](https://coveralls.io/r/thephpleague/oauth2-google) [![Code Quality](https://img.shields.io/scrutinizer/g/thephpleague/oauth2-google.svg)](https://scrutinizer-ci.com/g/thephpleague/oauth2-google/) [![License](https://img.shields.io/packagist/l/league/oauth2-google.svg)](https://github.com/thephpleague/oauth2-google/blob/master/LICENSE) [![Latest Stable Version](https://img.shields.io/packagist/v/league/oauth2-google.svg)](https://packagist.org/packages/league/oauth2-google) This package provides Google OAuth 2.0 support for the PHP League's [OAuth 2.0 Client](https://github.com/thephpleague/oauth2-client). This package is compliant with [PSR-1][], [PSR-2][] and [PSR-4][]. If you notice compliance oversights, please send a patch via pull request. [PSR-1]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-1-basic-coding-standard.md [PSR-2]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md [PSR-4]: https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-4-autoloader.md ## Requirements The following versions of PHP are supported. * PHP 7.0 * PHP 7.1 * PHP 7.2 * PHP 7.3 * PHP 7.4 This package uses [OpenID Connect][openid-connect] to authenticate users with Google accounts. To use this package, it will be necessary to have a Google client ID and client secret. These are referred to as `{google-client-id}` and `{google-client-secret}` in the documentation. Please follow the [Google instructions][oauth-setup] to create the required credentials. [openid-connect]: https://developers.google.com/identity/protocols/OpenIDConnect [oauth-setup]: https://developers.google.com/identity/protocols/OpenIDConnect#registeringyourapp ## Installation To install, use composer: ```sh composer require league/oauth2-google ``` ## Usage ### Authorization Code Flow ```php use League\OAuth2\Client\Provider\Google; $provider = new Google([ 'clientId' => '{google-client-id}', 'clientSecret' => '{google-client-secret}', 'redirectUri' => 'https://example.com/callback-url', 'hostedDomain' => 'example.com', // optional; used to restrict access to users on your G Suite/Google Apps for Business accounts ]); if (!empty($_GET['error'])) { // Got an error, probably user denied access exit('Got error: ' . htmlspecialchars($_GET['error'], ENT_QUOTES, 'UTF-8')); } elseif (empty($_GET['code'])) { // If we don't have an authorization code then get one $authUrl = $provider->getAuthorizationUrl(); $_SESSION['oauth2state'] = $provider->getState(); header('Location: ' . $authUrl); exit; } elseif (empty($_GET['state']) || ($_GET['state'] !== $_SESSION['oauth2state'])) { // State is invalid, possible CSRF attack in progress unset($_SESSION['oauth2state']); exit('Invalid state'); } else { // Try to get an access token (using the authorization code grant) $token = $provider->getAccessToken('authorization_code', [ 'code' => $_GET['code'] ]); // Optional: Now you have a token you can look up a users profile data try { // We got an access token, let's now get the owner details $ownerDetails = $provider->getResourceOwner($token); // Use these details to create a new profile printf('Hello %s!', $ownerDetails->getFirstName()); } catch (Exception $e) { // Failed to get user details exit('Something went wrong: ' . $e->getMessage()); } // Use this to interact with an API on the users behalf echo $token->getToken(); // Use this to get a new access token if the old one expires echo $token->getRefreshToken(); // Unix timestamp at which the access token expires echo $token->getExpires(); } ``` #### Available Options The `Google` provider has the following [options][auth-params]: - `accessType` to use online or offline access - `hostedDomain` to authenticate G Suite users - `prompt` to modify the prompt that the user will see - `scopes` to request access to additional user information [auth-params]: https://developers.google.com/identity/protocols/OpenIDConnect#authenticationuriparameters #### Accessing Token JWT Google provides a [JSON Web Token][jwt] (JWT) with all access tokens. This token [contains basic information][openid-jwt] about the authenticated user. The JWT can be accessed from the `id_token` value of the access token: ```php /** @var League\OAuth2\Client\Token\AccessToken $token */ $values = $token->getValues(); /** @var string */ $jwt = $values['id_token']; ``` Parsing the JWT will require a [JWT parser][jwt-parsers]. Refer to parser documentation for instructions. [jwt]: https://jwt.io/ [openid-jwt]: https://developers.google.com/identity/protocols/OpenIDConnect#obtainuserinfo [jwt-parsers]: https://packagist.org/search/?q=jwt ### Refreshing a Token Refresh tokens are only provided to applications which request offline access. You can specify offline access by setting the `accessType` option in your provider: ```php use League\OAuth2\Client\Provider\Google; $provider = new Google([ 'clientId' => '{google-client-id}', 'clientSecret' => '{google-client-secret}', 'redirectUri' => 'https://example.com/callback-url', 'accessType' => 'offline', ]); ``` It is important to note that the refresh token is only returned on the first request after this it will be `null`. You should securely store the refresh token when it is returned: ```php $token = $provider->getAccessToken('authorization_code', [ 'code' => $code ]); // persist the token in a database $refreshToken = $token->getRefreshToken(); ``` If you ever need to get a new refresh token you can request one by forcing the consent prompt: ```php $authUrl = $provider->getAuthorizationUrl(['prompt' => 'consent']); ``` Now you have everything you need to refresh an access token using a refresh token: ```php use League\OAuth2\Client\Provider\Google; use League\OAuth2\Client\Grant\RefreshToken; $provider = new Google([ 'clientId' => '{google-client-id}', 'clientSecret' => '{google-client-secret}', 'redirectUri' => 'https://example.com/callback-url', ]); $grant = new RefreshToken(); $token = $provider->getAccessToken($grant, ['refresh_token' => $refreshToken]); ``` ## Scopes Additional [scopes][scopes] can be set by using the `scope` parameter when generating the authorization URL: ```php $authorizationUrl = $provider->getAuthorizationUrl([ 'scope' => [ 'scope-url-here' ], ]); ``` [scopes]: https://developers.google.com/identity/protocols/googlescopes ## Testing Tests can be run with: ```sh composer test ``` Style checks can be run with: ```sh composer check ``` ## Contributing Please see [CONTRIBUTING](https://github.com/thephpleague/oauth2-google/blob/master/CONTRIBUTING.md) for details. ## Credits - [Woody Gilk](https://github.com/shadowhand) - [All Contributors](https://github.com/thephpleague/oauth2-google/contributors) ## License The MIT License (MIT). Please see [License File](https://github.com/thephpleague/oauth2-google/blob/master/LICENSE) for more information. oauth2-google/CHANGELOG.md 0000644 00000002026 14736103263 0011036 0 ustar 00 OAuth 2.0 Google Provider Changelog ## 3.0.3 - 2020-07-24 ### Fixed - Remove the `approval_prompt` from default parameters, #90 ## 3.0.2 - 2019-11-16 ### Fixed - Allow for `family_name` to be undefined in user information, #79 by @majkel89 ## 3.0.1 - 2018-12-28 ### Fixed - Correct conflict handling for prompt option, #69 by @mxdpeep ## 3.0.0 - 2018-12-23 ### Changed - Update to latest version of Google OAuth - Use only OpenID Connect for user details ### Fixed - Correct handling of selecting from multiple user accounts, #45 - Prevent conflict when using prompt option, #42 ### Added - Add "locale" to user details, #60 - Support additional scopes at construction ### Removed - Dropped support for Google+ user details, #34 and #63 ## 2.2.0 - 2018-03-19 ### Added - Hosted domain validation, #54 by @pradtke ## 2.1.0 - 2018-03-09 ### Added - OpenID Connect support, #48 by @pradtke ## 2.0.0 - 2017-01-24 ### Added - PHP 7.1 support ### Removed - Dropped PHP 5.5 support ## 1.0.0 - 2015-08-12 - Initial release oauth2-google/LICENSE 0000644 00000002114 14736103263 0010230 0 ustar 00 The MIT License (MIT) Copyright (c) 2015 Woody Gilk <woody.gilk@gmail.com> Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. plates/CONTRIBUTING.md 0000644 00000002236 14736103263 0010275 0 ustar 00 # Contributing Contributions are **welcome** and will be fully **credited**. We accept contributions via Pull Requests on [Github](https://github.com/thephpleague/plates). ## Pull Requests - **[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). - **Add tests!** - Your patch won't be accepted if it doesn't have tests. - **Document any change in behaviour** - Make sure the README and any other relevant documentation are kept up-to-date. - **Consider our release cycle** - We try to follow semver. Randomly breaking public APIs is not an option. - **Create topic branches** - Don't ask us to pull from your master branch. - **One pull request per feature** - If you want to do more than one thing, send multiple pull requests. - **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. ## Running Tests ``` bash $ phpunit ``` **Happy coding**! plates/src/Extension/URI.php 0000644 00000005427 14736103263 0011764 0 ustar 00 <?php namespace League\Plates\Extension; use League\Plates\Engine; use League\Plates\Template\Template; use LogicException; /** * Extension that adds a number of URI checks. */ class URI implements ExtensionInterface { /** * Instance of the current template. * @var Template */ public $template; /** * The request URI. * @var string */ protected $uri; /** * The request URI as an array. * @var array */ protected $parts; /** * Create new URI instance. * @param string $uri */ public function __construct($uri) { $this->uri = $uri; $this->parts = explode('/', $this->uri); } /** * Register extension functions. * @param Engine $engine * @return null */ public function register(Engine $engine) { $engine->registerFunction('uri', array($this, 'runUri')); } /** * Perform URI check. * @param null|integer|string $var1 * @param mixed $var2 * @param mixed $var3 * @param mixed $var4 * @return mixed */ public function runUri($var1 = null, $var2 = null, $var3 = null, $var4 = null) { if (is_null($var1)) { return $this->uri; } if (is_numeric($var1) and is_null($var2)) { return array_key_exists($var1, $this->parts) ? $this->parts[$var1] : null; } if (is_numeric($var1) and is_string($var2)) { return $this->checkUriSegmentMatch($var1, $var2, $var3, $var4); } if (is_string($var1)) { return $this->checkUriRegexMatch($var1, $var2, $var3); } throw new LogicException('Invalid use of the uri function.'); } /** * Perform a URI segment match. * @param integer $key * @param string $string * @param mixed $returnOnTrue * @param mixed $returnOnFalse * @return mixed */ protected function checkUriSegmentMatch($key, $string, $returnOnTrue = null, $returnOnFalse = null) { if (array_key_exists($key, $this->parts) && $this->parts[$key] === $string) { return is_null($returnOnTrue) ? true : $returnOnTrue; } return is_null($returnOnFalse) ? false : $returnOnFalse; } /** * Perform a regular express match. * @param string $regex * @param mixed $returnOnTrue * @param mixed $returnOnFalse * @return mixed */ protected function checkUriRegexMatch($regex, $returnOnTrue = null, $returnOnFalse = null) { if (preg_match('#^' . $regex . '$#', $this->uri) === 1) { return is_null($returnOnTrue) ? true : $returnOnTrue; } return is_null($returnOnFalse) ? false : $returnOnFalse; } } plates/src/Extension/ExtensionInterface.php 0000644 00000000303 14736103263 0015106 0 ustar 00 <?php namespace League\Plates\Extension; use League\Plates\Engine; /** * A common interface for extensions. */ interface ExtensionInterface { public function register(Engine $engine); } plates/src/Extension/Asset.php 0000644 00000004010 14736103263 0012367 0 ustar 00 <?php namespace League\Plates\Extension; use League\Plates\Engine; use League\Plates\Template\Template; use LogicException; /** * Extension that adds the ability to create "cache busted" asset URLs. */ class Asset implements ExtensionInterface { /** * Instance of the current template. * @var Template */ public $template; /** * Path to asset directory. * @var string */ public $path; /** * Enables the filename method. * @var boolean */ public $filenameMethod; /** * Create new Asset instance. * @param string $path * @param boolean $filenameMethod */ public function __construct($path, $filenameMethod = false) { $this->path = rtrim($path, '/'); $this->filenameMethod = $filenameMethod; } /** * Register extension function. * @param Engine $engine * @return null */ public function register(Engine $engine) { $engine->registerFunction('asset', array($this, 'cachedAssetUrl')); } /** * Create "cache busted" asset URL. * @param string $url * @return string */ public function cachedAssetUrl($url) { $filePath = $this->path . '/' . ltrim($url, '/'); if (!file_exists($filePath)) { throw new LogicException( 'Unable to locate the asset "' . $url . '" in the "' . $this->path . '" directory.' ); } $lastUpdated = filemtime($filePath); $pathInfo = pathinfo($url); if ($pathInfo['dirname'] === '.') { $directory = ''; } elseif ($pathInfo['dirname'] === '/') { $directory = '/'; } else { $directory = $pathInfo['dirname'] . '/'; } if ($this->filenameMethod) { return $directory . $pathInfo['filename'] . '.' . $lastUpdated . '.' . $pathInfo['extension']; } return $directory . $pathInfo['filename'] . '.' . $pathInfo['extension'] . '?v=' . $lastUpdated; } } plates/src/Engine.php 0000644 00000013505 14736103263 0010552 0 ustar 00 <?php namespace League\Plates; use League\Plates\Extension\ExtensionInterface; use League\Plates\Template\Data; use League\Plates\Template\Directory; use League\Plates\Template\FileExtension; use League\Plates\Template\Folders; use League\Plates\Template\Func; use League\Plates\Template\Functions; use League\Plates\Template\Name; use League\Plates\Template\Template; /** * Template API and environment settings storage. */ class Engine { /** * Default template directory. * @var Directory */ protected $directory; /** * Template file extension. * @var FileExtension */ protected $fileExtension; /** * Collection of template folders. * @var Folders */ protected $folders; /** * Collection of template functions. * @var Functions */ protected $functions; /** * Collection of preassigned template data. * @var Data */ protected $data; /** * Create new Engine instance. * @param string $directory * @param string $fileExtension */ public function __construct($directory = null, $fileExtension = 'php') { $this->directory = new Directory($directory); $this->fileExtension = new FileExtension($fileExtension); $this->folders = new Folders(); $this->functions = new Functions(); $this->data = new Data(); } /** * Set path to templates directory. * @param string|null $directory Pass null to disable the default directory. * @return Engine */ public function setDirectory($directory) { $this->directory->set($directory); return $this; } /** * Get path to templates directory. * @return string */ public function getDirectory() { return $this->directory->get(); } /** * Set the template file extension. * @param string|null $fileExtension Pass null to manually set it. * @return Engine */ public function setFileExtension($fileExtension) { $this->fileExtension->set($fileExtension); return $this; } /** * Get the template file extension. * @return string */ public function getFileExtension() { return $this->fileExtension->get(); } /** * Add a new template folder for grouping templates under different namespaces. * @param string $name * @param string $directory * @param boolean $fallback * @return Engine */ public function addFolder($name, $directory, $fallback = false) { $this->folders->add($name, $directory, $fallback); return $this; } /** * Remove a template folder. * @param string $name * @return Engine */ public function removeFolder($name) { $this->folders->remove($name); return $this; } /** * Get collection of all template folders. * @return Folders */ public function getFolders() { return $this->folders; } /** * Add preassigned template data. * @param array $data; * @param null|string|array $templates; * @return Engine */ public function addData(array $data, $templates = null) { $this->data->add($data, $templates); return $this; } /** * Get all preassigned template data. * @param null|string $template; * @return array */ public function getData($template = null) { return $this->data->get($template); } /** * Register a new template function. * @param string $name; * @param callback $callback; * @return Engine */ public function registerFunction($name, $callback) { $this->functions->add($name, $callback); return $this; } /** * Remove a template function. * @param string $name; * @return Engine */ public function dropFunction($name) { $this->functions->remove($name); return $this; } /** * Get a template function. * @param string $name * @return Func */ public function getFunction($name) { return $this->functions->get($name); } /** * Check if a template function exists. * @param string $name * @return boolean */ public function doesFunctionExist($name) { return $this->functions->exists($name); } /** * Load an extension. * @param ExtensionInterface $extension * @return Engine */ public function loadExtension(ExtensionInterface $extension) { $extension->register($this); return $this; } /** * Load multiple extensions. * @param array $extensions * @return Engine */ public function loadExtensions(array $extensions = array()) { foreach ($extensions as $extension) { $this->loadExtension($extension); } return $this; } /** * Get a template path. * @param string $name * @return string */ public function path($name) { $name = new Name($this, $name); return $name->getPath(); } /** * Check if a template exists. * @param string $name * @return boolean */ public function exists($name) { $name = new Name($this, $name); return $name->doesPathExist(); } /** * Create a new template. * @param string $name * @return Template */ public function make($name) { return new Template($this, $name); } /** * Create a new template and render it. * @param string $name * @param array $data * @return string */ public function render($name, array $data = array()) { return $this->make($name)->render($data); } } plates/src/Template/Folders.php 0000644 00000003036 14736103263 0012514 0 ustar 00 <?php namespace League\Plates\Template; use LogicException; /** * A collection of template folders. */ class Folders { /** * Array of template folders. * @var array */ protected $folders = array(); /** * Add a template folder. * @param string $name * @param string $path * @param boolean $fallback * @return Folders */ public function add($name, $path, $fallback = false) { if ($this->exists($name)) { throw new LogicException('The template folder "' . $name . '" is already being used.'); } $this->folders[$name] = new Folder($name, $path, $fallback); return $this; } /** * Remove a template folder. * @param string $name * @return Folders */ public function remove($name) { if (!$this->exists($name)) { throw new LogicException('The template folder "' . $name . '" was not found.'); } unset($this->folders[$name]); return $this; } /** * Get a template folder. * @param string $name * @return Folder */ public function get($name) { if (!$this->exists($name)) { throw new LogicException('The template folder "' . $name . '" was not found.'); } return $this->folders[$name]; } /** * Check if a template folder exists. * @param string $name * @return boolean */ public function exists($name) { return isset($this->folders[$name]); } } plates/src/Template/FileExtension.php 0000644 00000001451 14736103263 0013671 0 ustar 00 <?php namespace League\Plates\Template; /** * Template file extension. */ class FileExtension { /** * Template file extension. * @var string */ protected $fileExtension; /** * Create new FileExtension instance. * @param null|string $fileExtension */ public function __construct($fileExtension = 'php') { $this->set($fileExtension); } /** * Set the template file extension. * @param null|string $fileExtension * @return FileExtension */ public function set($fileExtension) { $this->fileExtension = $fileExtension; return $this; } /** * Get the template file extension. * @return string */ public function get() { return $this->fileExtension; } } plates/src/Template/Name.php 0000644 00000010172 14736103263 0011775 0 ustar 00 <?php namespace League\Plates\Template; use League\Plates\Engine; use LogicException; /** * A template name. */ class Name { /** * Instance of the template engine. * @var Engine */ protected $engine; /** * The original name. * @var string */ protected $name; /** * The parsed template folder. * @var Folder */ protected $folder; /** * The parsed template filename. * @var string */ protected $file; /** * Create a new Name instance. * @param Engine $engine * @param string $name */ public function __construct(Engine $engine, $name) { $this->setEngine($engine); $this->setName($name); } /** * Set the engine. * @param Engine $engine * @return Name */ public function setEngine(Engine $engine) { $this->engine = $engine; return $this; } /** * Get the engine. * @return Engine */ public function getEngine() { return $this->engine; } /** * Set the original name and parse it. * @param string $name * @return Name */ public function setName($name) { $this->name = $name; $parts = explode('::', $this->name); if (count($parts) === 1) { $this->setFile($parts[0]); } elseif (count($parts) === 2) { $this->setFolder($parts[0]); $this->setFile($parts[1]); } else { throw new LogicException( 'The template name "' . $this->name . '" is not valid. ' . 'Do not use the folder namespace separator "::" more than once.' ); } return $this; } /** * Get the original name. * @return string */ public function getName() { return $this->name; } /** * Set the parsed template folder. * @param string $folder * @return Name */ public function setFolder($folder) { $this->folder = $this->engine->getFolders()->get($folder); return $this; } /** * Get the parsed template folder. * @return string */ public function getFolder() { return $this->folder; } /** * Set the parsed template file. * @param string $file * @return Name */ public function setFile($file) { if ($file === '') { throw new LogicException( 'The template name "' . $this->name . '" is not valid. ' . 'The template name cannot be empty.' ); } $this->file = $file; if (!is_null($this->engine->getFileExtension())) { $this->file .= '.' . $this->engine->getFileExtension(); } return $this; } /** * Get the parsed template file. * @return string */ public function getFile() { return $this->file; } /** * Resolve template path. * @return string */ public function getPath() { if (is_null($this->folder)) { return $this->getDefaultDirectory() . DIRECTORY_SEPARATOR . $this->file; } $path = $this->folder->getPath() . DIRECTORY_SEPARATOR . $this->file; if (!is_file($path) and $this->folder->getFallback() and is_file($this->getDefaultDirectory() . DIRECTORY_SEPARATOR . $this->file)) { $path = $this->getDefaultDirectory() . DIRECTORY_SEPARATOR . $this->file; } return $path; } /** * Check if template path exists. * @return boolean */ public function doesPathExist() { return is_file($this->getPath()); } /** * Get the default templates directory. * @return string */ protected function getDefaultDirectory() { $directory = $this->engine->getDirectory(); if (is_null($directory)) { throw new LogicException( 'The template name "' . $this->name . '" is not valid. '. 'The default directory has not been defined.' ); } return $directory; } } plates/src/Template/Directory.php 0000644 00000001677 14736103263 0013073 0 ustar 00 <?php namespace League\Plates\Template; use LogicException; /** * Default template directory. */ class Directory { /** * Template directory path. * @var string */ protected $path; /** * Create new Directory instance. * @param string $path */ public function __construct($path = null) { $this->set($path); } /** * Set path to templates directory. * @param string|null $path Pass null to disable the default directory. * @return Directory */ public function set($path) { if (!is_null($path) and !is_dir($path)) { throw new LogicException( 'The specified path "' . $path . '" does not exist.' ); } $this->path = $path; return $this; } /** * Get path to templates directory. * @return string */ public function get() { return $this->path; } } plates/src/Template/Folder.php 0000644 00000003524 14736103263 0012333 0 ustar 00 <?php namespace League\Plates\Template; use LogicException; /** * A template folder. */ class Folder { /** * The folder name. * @var string */ protected $name; /** * The folder path. * @var string */ protected $path; /** * The folder fallback status. * @var boolean */ protected $fallback; /** * Create a new Folder instance. * @param string $name * @param string $path * @param boolean $fallback */ public function __construct($name, $path, $fallback = false) { $this->setName($name); $this->setPath($path); $this->setFallback($fallback); } /** * Set the folder name. * @param string $name * @return Folder */ public function setName($name) { $this->name = $name; return $this; } /** * Get the folder name. * @return string */ public function getName() { return $this->name; } /** * Set the folder path. * @param string $path * @return Folder */ public function setPath($path) { if (!is_dir($path)) { throw new LogicException('The specified directory path "' . $path . '" does not exist.'); } $this->path = $path; return $this; } /** * Get the folder path. * @return string */ public function getPath() { return $this->path; } /** * Set the folder fallback status. * @param boolean $fallback * @return Folder */ public function setFallback($fallback) { $this->fallback = $fallback; return $this; } /** * Get the folder fallback status. * @return boolean */ public function getFallback() { return $this->fallback; } } plates/src/Template/Data.php 0000644 00000004254 14736103263 0011772 0 ustar 00 <?php namespace League\Plates\Template; use LogicException; /** * Preassigned template data. */ class Data { /** * Variables shared by all templates. * @var array */ protected $sharedVariables = array(); /** * Specific template variables. * @var array */ protected $templateVariables = array(); /** * Add template data. * @param array $data; * @param null|string|array $templates; * @return Data */ public function add(array $data, $templates = null) { if (is_null($templates)) { return $this->shareWithAll($data); } if (is_array($templates)) { return $this->shareWithSome($data, $templates); } if (is_string($templates)) { return $this->shareWithSome($data, array($templates)); } throw new LogicException( 'The templates variable must be null, an array or a string, ' . gettype($templates) . ' given.' ); } /** * Add data shared with all templates. * @param array $data; * @return Data */ public function shareWithAll($data) { $this->sharedVariables = array_merge($this->sharedVariables, $data); return $this; } /** * Add data shared with some templates. * @param array $data; * @param array $templates; * @return Data */ public function shareWithSome($data, array $templates) { foreach ($templates as $template) { if (isset($this->templateVariables[$template])) { $this->templateVariables[$template] = array_merge($this->templateVariables[$template], $data); } else { $this->templateVariables[$template] = $data; } } return $this; } /** * Get template data. * @param null|string $template; * @return array */ public function get($template = null) { if (isset($template, $this->templateVariables[$template])) { return array_merge($this->sharedVariables, $this->templateVariables[$template]); } return $this->sharedVariables; } } plates/src/Template/Functions.php 0000644 00000003127 14736103263 0013067 0 ustar 00 <?php namespace League\Plates\Template; use LogicException; /** * A collection of template functions. */ class Functions { /** * Array of template functions. * @var array */ protected $functions = array(); /** * Add a new template function. * @param string $name; * @param callback $callback; * @return Functions */ public function add($name, $callback) { if ($this->exists($name)) { throw new LogicException( 'The template function name "' . $name . '" is already registered.' ); } $this->functions[$name] = new Func($name, $callback); return $this; } /** * Remove a template function. * @param string $name; * @return Functions */ public function remove($name) { if (!$this->exists($name)) { throw new LogicException( 'The template function "' . $name . '" was not found.' ); } unset($this->functions[$name]); return $this; } /** * Get a template function. * @param string $name * @return Func */ public function get($name) { if (!$this->exists($name)) { throw new LogicException('The template function "' . $name . '" was not found.'); } return $this->functions[$name]; } /** * Check if a template function exists. * @param string $name * @return boolean */ public function exists($name) { return isset($this->functions[$name]); } } plates/src/Template/Template.php 0000644 00000017241 14736103263 0012674 0 ustar 00 <?php namespace League\Plates\Template; use Exception; use League\Plates\Engine; use LogicException; use Throwable; /** * Container which holds template data and provides access to template functions. */ class Template { /** * Instance of the template engine. * @var Engine */ protected $engine; /** * The name of the template. * @var Name */ protected $name; /** * The data assigned to the template. * @var array */ protected $data = array(); /** * An array of section content. * @var array */ protected $sections = array(); /** * The name of the section currently being rendered. * @var string */ protected $sectionName; /** * Whether the section should be appended or not. * @var boolean */ protected $appendSection; /** * The name of the template layout. * @var string */ protected $layoutName; /** * The data assigned to the template layout. * @var array */ protected $layoutData; /** * Create new Template instance. * @param Engine $engine * @param string $name */ public function __construct(Engine $engine, $name) { $this->engine = $engine; $this->name = new Name($engine, $name); $this->data($this->engine->getData($name)); } /** * Magic method used to call extension functions. * @param string $name * @param array $arguments * @return mixed */ public function __call($name, $arguments) { return $this->engine->getFunction($name)->call($this, $arguments); } /** * Alias for render() method. * @throws \Throwable * @throws \Exception * @return string */ public function __toString() { return $this->render(); } /** * Assign or get template data. * @param array $data * @return mixed */ public function data(array $data = null) { if (is_null($data)) { return $this->data; } $this->data = array_merge($this->data, $data); } /** * Check if the template exists. * @return boolean */ public function exists() { return $this->name->doesPathExist(); } /** * Get the template path. * @return string */ public function path() { return $this->name->getPath(); } /** * Render the template and layout. * @param array $data * @throws \Throwable * @throws \Exception * @return string */ public function render(array $data = array()) { $this->data($data); unset($data); extract($this->data); if (!$this->exists()) { throw new LogicException( 'The template "' . $this->name->getName() . '" could not be found at "' . $this->path() . '".' ); } try { $level = ob_get_level(); ob_start(); include $this->path(); $content = ob_get_clean(); if (isset($this->layoutName)) { $layout = $this->engine->make($this->layoutName); $layout->sections = array_merge($this->sections, array('content' => $content)); $content = $layout->render($this->layoutData); } return $content; } catch (Throwable $e) { while (ob_get_level() > $level) { ob_end_clean(); } throw $e; } catch (Exception $e) { while (ob_get_level() > $level) { ob_end_clean(); } throw $e; } } /** * Set the template's layout. * @param string $name * @param array $data * @return null */ public function layout($name, array $data = array()) { $this->layoutName = $name; $this->layoutData = $data; } /** * Start a new section block. * @param string $name * @return null */ public function start($name) { if ($name === 'content') { throw new LogicException( 'The section name "content" is reserved.' ); } if ($this->sectionName) { throw new LogicException('You cannot nest sections within other sections.'); } $this->sectionName = $name; ob_start(); } /** * Start a new append section block. * @param string $name * @return null */ public function push($name) { $this->appendSection = true; $this->start($name); } /** * Stop the current section block. * @return null */ public function stop() { if (is_null($this->sectionName)) { throw new LogicException( 'You must start a section before you can stop it.' ); } if (!isset($this->sections[$this->sectionName])) { $this->sections[$this->sectionName] = ''; } $this->sections[$this->sectionName] = $this->appendSection ? $this->sections[$this->sectionName] . ob_get_clean() : ob_get_clean(); $this->sectionName = null; $this->appendSection = false; } /** * Alias of stop(). * @return null */ public function end() { $this->stop(); } /** * Returns the content for a section block. * @param string $name Section name * @param string $default Default section content * @return string|null */ public function section($name, $default = null) { if (!isset($this->sections[$name])) { return $default; } return $this->sections[$name]; } /** * Fetch a rendered template. * @param string $name * @param array $data * @return string */ public function fetch($name, array $data = array()) { return $this->engine->render($name, $data); } /** * Output a rendered template. * @param string $name * @param array $data * @return null */ public function insert($name, array $data = array()) { echo $this->engine->render($name, $data); } /** * Apply multiple functions to variable. * @param mixed $var * @param string $functions * @return mixed */ public function batch($var, $functions) { foreach (explode('|', $functions) as $function) { if ($this->engine->doesFunctionExist($function)) { $var = call_user_func(array($this, $function), $var); } elseif (is_callable($function)) { $var = call_user_func($function, $var); } else { throw new LogicException( 'The batch function could not find the "' . $function . '" function.' ); } } return $var; } /** * Escape string. * @param string $string * @param null|string $functions * @return string */ public function escape($string, $functions = null) { static $flags; if (!isset($flags)) { $flags = ENT_QUOTES | (defined('ENT_SUBSTITUTE') ? ENT_SUBSTITUTE : 0); } if ($functions) { $string = $this->batch($string, $functions); } return htmlspecialchars($string, $flags, 'UTF-8'); } /** * Alias to escape function. * @param string $string * @param null|string $functions * @return string */ public function e($string, $functions = null) { return $this->escape($string, $functions); } } plates/src/Template/Func.php 0000644 00000004137 14736103263 0012014 0 ustar 00 <?php namespace League\Plates\Template; use League\Plates\Extension\ExtensionInterface; use LogicException; /** * A template function. */ class Func { /** * The function name. * @var string */ protected $name; /** * The function callback. * @var callable */ protected $callback; /** * Create new Func instance. * @param string $name * @param callable $callback */ public function __construct($name, $callback) { $this->setName($name); $this->setCallback($callback); } /** * Set the function name. * @param string $name * @return Func */ public function setName($name) { if (preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/', $name) !== 1) { throw new LogicException( 'Not a valid function name.' ); } $this->name = $name; return $this; } /** * Get the function name. * @return string */ public function getName() { return $this->name; } /** * Set the function callback * @param callable $callback * @return Func */ public function setCallback($callback) { if (!is_callable($callback, true)) { throw new LogicException( 'Not a valid function callback.' ); } $this->callback = $callback; return $this; } /** * Get the function callback. * @return callable */ public function getCallback() { return $this->callback; } /** * Call the function. * @param Template $template * @param array $arguments * @return mixed */ public function call(Template $template = null, $arguments = array()) { if (is_array($this->callback) and isset($this->callback[0]) and $this->callback[0] instanceof ExtensionInterface ) { $this->callback[0]->template = $template; } return call_user_func_array($this->callback, $arguments); } } plates/README.md 0000644 00000006305 14736103263 0007324 0 ustar 00 Plates ====== [![Author](http://img.shields.io/badge/author-@reinink-blue.svg?style=flat-square)](https://twitter.com/reinink) [![Source Code](http://img.shields.io/badge/source-league/plates-blue.svg?style=flat-square)](https://github.com/thephpleague/plates) [![Latest Version](https://img.shields.io/github/release/thephpleague/plates.svg?style=flat-square)](https://github.com/thephpleague/plates/releases) [![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE) [![Build Status](https://img.shields.io/travis/thephpleague/plates/master.svg?style=flat-square)](https://travis-ci.org/thephpleague/plates) [![Coverage Status](https://img.shields.io/scrutinizer/coverage/g/thephpleague/plates.svg?style=flat-square)](https://scrutinizer-ci.com/g/thephpleague/plates/code-structure) [![Quality Score](https://img.shields.io/scrutinizer/g/thephpleague/plates.svg?style=flat-square)](https://scrutinizer-ci.com/g/thephpleague/plates) [![Total Downloads](https://img.shields.io/packagist/dt/league/plates.svg?style=flat-square)](https://packagist.org/packages/league/plates) Plates is a native PHP template system that's fast, easy to use and easy to extend. It's inspired by the excellent [Twig](http://twig.sensiolabs.org/) template engine and strives to bring modern template language functionality to native PHP templates. Plates is designed for developers who prefer to use native PHP templates over compiled template languages, such as Twig or Smarty. ### Highlights - Native PHP templates, no new [syntax](http://platesphp.com/templates/syntax/) to learn - Plates is a template system, not a template language - Plates encourages the use of existing PHP functions - Increase code reuse with template [layouts](http://platesphp.com/templates/layouts/) and [inheritance](http://platesphp.com/templates/inheritance/) - Template [folders](http://platesphp.com/engine/folders/) for grouping templates into namespaces - [Data](http://platesphp.com/templates/data/#preassigned-and-shared-data) sharing across templates - Preassign [data](http://platesphp.com/templates/data/#preassigned-and-shared-data) to specific templates - Built-in [escaping](http://platesphp.com/templates/escaping/) helpers - Easy to extend using [functions](http://platesphp.com/engine/functions/) and [extensions](http://platesphp.com/engine/extensions/) - Framework-agnostic, will work with any project - Decoupled design makes templates easy to test - Composer ready and PSR-2 compliant ## Installation Plates is available via Composer: ``` composer require league/plates ``` ## Documentation Full documentation can be found at [platesphp.com](http://platesphp.com/). ## Testing ```bash phpunit ``` ## Contributing Please see [CONTRIBUTING](https://github.com/thephpleague/plates/blob/master/CONTRIBUTING.md) for details. ## Security If you discover any security related issues, please email jonathan@reinink.ca instead of using the issue tracker. ## Credits - [Jonathan Reinink](https://github.com/reinink) - [All Contributors](https://github.com/thephpleague/plates/contributors) ## License The MIT License (MIT). Please see [License File](https://github.com/thephpleague/plates/blob/master/LICENSE) for more information. plates/example/example.php 0000644 00000000461 14736103263 0011641 0 ustar 00 <?php include '../vendor/autoload.php'; // Create new Plates instance $templates = new League\Plates\Engine('templates'); // Preassign data to the layout $templates->addData(['company' => 'The Company Name'], 'layout'); // Render a template echo $templates->render('profile', ['name' => 'Jonathan']); plates/example/templates/profile.php 0000644 00000000410 14736103263 0013636 0 ustar 00 <?php $this->layout('layout', ['title' => 'User Profile']) ?> <h1>User Profile</h1> <p>Hello, <?=$this->e($name)?>!</p> <?php $this->insert('sidebar') ?> <?php $this->push('scripts') ?> <script> // Some JavaScript </script> <?php $this->end() ?>