chillerlan / php-oauth-core
A PHP7.2+ OAuth client core library
Fund package maintenance!
Ko Fi
Installs: 1 129
Dependents: 2
Suggesters: 0
Security: 0
Stars: 3
Watchers: 2
Forks: 0
Open Issues: 0
Requires
- php: ^7.2
- ext-json: *
- ext-simplexml: *
- chillerlan/php-httpinterface: ^4.0
- chillerlan/php-magic-apiclient: ^3.0
- chillerlan/php-settings-container: ^1.2
- psr/http-client: ^1.0
- psr/http-message: ^1.0
- psr/log: ^1.1
Requires (Dev)
- chillerlan/php-dotenv: ^1.0
- phpunit/phpunit: ^8.3
README
A PHP7.2+ OAuth1/2 client with an integrated API wrapper, loosely based on Lusitanian/PHPoAuthLib.
Documentation
Requirements
- PHP 7.2+
- a PSR-18 compatible HTTP client library of your choice (there is one included, though)
- optional PSR-17 compatible Request-, Response- and UriFactories
- see
chillerlan/php-oauth-providers
for already implemented providers
Installation
requires composer
composer.json
(note: replace dev-master
with a version boundary)
{ "require": { "php": "^7.2", "chillerlan/php-oauth-core": "dev-master" } }
Profit!
API
OAuthInterface
The OAuthInterface
implements the PSR-18 ClientInterface
(a PSR-18 compatible http client is still required),
the PSR-3LoggerAwareInterface
as well as the
ApiClientInterface
and offers basic methods that are common to the OAuth 1/2 interfaces, all supplied in the abstract class OAuthProvider
.
method | return | description |
---|---|---|
__construct(ClientInterface $http, OAuthStorageInterface $storage, SettingsContainerInterface $options, LoggerInterface $logger = null) |
- | |
__call(string $name, array $arguments) |
PSR-7 ResponseInterface |
magic API |
getAuthURL(array $params = null) |
PSR-7 UriInterface |
|
request(string $path, array $params = null, string $method = null, $body = null, array $headers = null) |
PSR-7 ResponseInterface |
|
setStorage(OAuthStorageInterface $storage) |
OAuthInterface |
|
setLogger(LoggerInterface $logger) |
void | PSR-3 |
setRequestFactory(RequestFactoryInterface $requestFactory) |
OAuthInterface |
PSR-17 |
setStreamFactory(StreamFactoryInterface $streamFactory) |
OAuthInterface |
PSR-17 |
setUriFactory(UriFactoryInterface $uriFactory) |
OAuthInterface |
PSR-17 |
getRequestAuthorization(RequestInterface $request, AccessToken $token) |
PSR-7 RequestInterface |
The following public properties are available:
property | description |
---|---|
$serviceName |
the classname for the current provider |
$userRevokeURL |
an optional link to the provider's user control panel where they can revoke the current token |
$apiDocs |
a link to the provider's API docs |
$applicationURL |
a link to the API/application credential generation page |
$endpoints |
an EndpointMapInterface for the current provider |
The following internal (protected) properties affect a provider's functionality
property | description |
---|---|
$authURL |
URL to the the provider's consent screen |
$accessTokenURL |
the provider's token exchange URL |
$apiURL |
the base URL of the provider's API to access |
$revokeURL |
(optional) an URL to revoke the given access token via the provider's API |
$endpointMap |
(optional) a class FQCN of an EndpointMapInterface for the provider's API |
$authHeaders |
(optional) additional headers to use during authentication |
$apiHeaders |
(optional) additional headers to use during API access |
OAuth1Interface
method | return |
---|---|
getRequestToken() |
AccessToken |
getAccessToken(string $token, string $verifier) |
AccessToken |
Protected properties:
property | description |
---|---|
$requestTokenURL |
the OAuth1 request token excange URL |
A minimal OAuth1 provider
use chillerlan\OAuth\Core\OAuth1Provider; class MyOauth1Provider extends Oauth1Provider{ protected $requestTokenURL = 'https://example.com/oauth/request_token'; protected $authURL = 'https://example.com/oauth/authorize'; protected $accessTokenURL = 'https://example.com/oauth/access_token'; protected $apiURL = 'https://api.example.com'; }
OAuth2Interface
method | return |
---|---|
getAccessToken(string $code, string $state = null) |
AccessToken |
getAuthURL(array $params = null, $scopes = null) |
PSR-7 UriInterface |
Protected properties:
property | description |
---|---|
$authMethod |
the authentication method, OAuth2Interface::AUTH_METHOD_HEADER (default) or OAuth2Interface::AUTH_METHOD_QUERY |
$authMethodHeader |
the name of the Authorization header in case OAuth2Interface::AUTH_METHOD_HEADER is used, defaults to Bearer |
$authMethodQuery |
the name of the querystring in case OAuth2Interface::AUTH_METHOD_QUERY is used, defaults to access_token |
$scopesDelimiter |
(optional) a delimiter string for the OAuth2 scopes, defaults to ' ' (space) |
$refreshTokenURL |
(optional) a refresh token exchange URL, in case it differs from $accessTokenURL |
$clientCredentialsTokenURL |
(optional) a client credentials token exchange URL, in case it differs from $accessTokenURL |
ClientCredentials
The ClientCredentials
interface indicates that the provider supports the client credentials grant type.
method | return |
---|---|
getClientCredentialsToken(array $scopes = null) |
AccessToken |
CSRFToken
The CSRFToken
interface enables usage of the <state>
parameter to mitigate cross-site request forgery and automatically enforces it during authorization requests.
method | return |
---|---|
(protected) checkState(string $state = null) |
OAuth2Interface |
(protected) setState(array $params) |
array |
TokenRefresh
The TokenRefresh
interface indicates if a provider supports usage of refresh tokens.
The option setting $tokenAutoRefresh
enables automatic refresh of expired tokens when using the OAuthInterface::request()
or the PSR-18 OAuthInterface::sendRequest()
methods to call the provider's API.
method | return |
---|---|
refreshAccessToken(AccessToken $token = null) |
AccessToken |
A minimal OAuth2 provider
use chillerlan\OAuth\Core\OAuth2Provider; class MyOauth2Provider extends Oauth2Provider implements ClientCredentials, CSRFToken, TokenRefresh{ protected $authURL = 'https://example.com/oauth2/authorize'; protected $accessTokenURL = 'https://example.com/oauth2/token'; protected $apiURL = 'https://api.example.com'; // optional protected $clientCredentialsTokenURL = 'https://example.com/oauth2/client_credentials'; protected $authMethod = self::AUTH_METHOD_HEADER; protected $authMethodHeader = 'OAuth'; protected $scopesDelimiter = ','; }
OAuthStorageInterface
The OAuthStorageInterface
serves for storing access tokens and auth states (CSRF) on a per-user basis.
The included implementations are intended for throwaway use during authentication or script runtime, please refer to these for implementation details (extend OAuthStorageAbstract
):
MemoryStorage
: non-persistent, to store an existing token during script runtime and then discard it.SessionStorage
: (half-)persistent, stores a token for as long a user's session is alive, e.g. while authenticating.
An example implementation for a persistent database storage with token encryption can be found over here: DBStorage
.
method | return |
---|---|
storeAccessToken(string $service, AccessToken $token) |
bool |
getAccessToken(string $service) |
AccessToken |
hasAccessToken(string $service) |
bool |
clearAccessToken(string$service) |
bool |
clearAllAccessTokens() |
bool |
storeCSRFState(string $service, string $state) |
bool |
getCSRFState(string $service) |
string |
hasCSRFState(string $service) |
bool |
clearCSRFState(string $service) |
bool |
clearAllCSRFStates() |
bool |
toStorage(AccessToken $token) |
mixed |
fromStorage($data) |
AccessToken |
AccessToken
The AccessToken
is a container to keep any token related data in one place. It extends the
SettingsContainerInterface
and therefore offers all of its methods.
method | return | description |
---|---|---|
__construct(iterable $properties = null) |
- | |
setExpiry(int $expires = null) |
AccessToken |
|
isExpired() |
bool |
inherited from SettingsContainerInterface
:
method | return | description |
---|---|---|
__get(string $property) |
mixed | calls $this->{'get_'.$property}($value) if such a method exists |
__set(string $property, $value) |
void | calls $this->{'set_'.$property}($value) if such a method exists |
__isset(string $property) |
bool | |
__unset(string $property) |
void | |
__toString() |
string | a JSON string |
toArray() |
array | |
fromIterable(iterable $properties) |
SettingsContainerInterface |
|
toJSON(int $jsonOptions = null) |
string | accepts JSON options constants |
fromJSON(string $json) |
SettingsContainerInterface |
property | type | default | description |
---|---|---|---|
$accessTokenSecret |
string | null |
OAuth1 only |
$accessToken |
string | null |
|
$refreshToken |
string | null |
|
$extraParams |
array | [] |
|
$expires |
int | AccessToken::EOL_UNKNOWN |
|
$provider |
string | null |
OAuthOptions
OAuthOptions
is a SettingsContainerInterface
that uses the plug-in traits OAuthOptionsTrait
and HTTPOptionsTrait
to provide settings for a provider.
property | type | default | description |
---|---|---|---|
$key |
string | null |
The application key (or id) given by your provider (see supported providers) |
$secret |
string | null |
The application secret given by your provider |
$callbackURL |
string | null |
The callback URL associated with your application |
$sessionStart |
bool | true |
Whether or not to start the session when session storage is used |
$sessionTokenVar |
string | 'chillerlan-oauth-token' | The session array key for token storage |
$sessionStateVar |
string | 'chillerlan-oauth-state' | The session array key for storage (OAuth2) |
$tokenAutoRefresh |
bool | true |
Whether or not to automatically refresh access tokens (OAuth2) |
from HTTPOptionsTrait
:
property | type | default | description |
---|---|---|---|
$user_agent |
string | ||
$curl_options |
array | [] |
https://php.net/manual/function.curl-setopt.php |
$ca_info |
string | null |
https://curl.haxx.se/docs/caextract.html |
$ssl_verifypeer |
bool | true |
see CURLOPT_SSL_VERIFYPEER |
$curlHandle |
string | CurlHandle::class |
|
$windowSize |
int | 5 | |
$sleep |
int/float | null |
|
$timeout |
int | 10 | |
$retries |
int | 3 | |
$curl_multi_options |
array | [] |
Testing
If you just wrote your own provider implementation, you also might want to test it.
This library brings several abstract tests that you can use.
In order to use them, you need to add the PSR-4 namespaces to your project's composer.json
like so:
{ ... "autoload": { "psr-4": { "my\\project\\source\\": "src/" } }, "autoload-dev": { "psr-4": { "my\\project\\test\\": "tests/", ... "chillerlan\\HTTPTest\\MagicAPI\\": "vendor/chillerlan/php-magic-apiclient/tests", "chillerlan\\OAuthTest\\": "vendor/chillerlan/php-oauth-core/tests" } } }
The basic unit test looks as follows:
namespace my\project\test; use my\project\source\MyProvider; use chillerlan\OAuthTest\Providers\OAuth2ProviderTestAbstract; /** * @property \my\project\source\MyProvider $provider */ class MyProviderTest extends OAuth2ProviderTestAbstract{ protected $CFG = __DIR__.'/../config'; // specifies the path for .env, cacert.pem and oauth tokens protected $FQN = MyProvider::class; }
That is all! You'll only need to add/overload methods in case you need to cover edge cases or additional features of your provider implementation.
In case you want to run tests against a live API, you can use the abstract API tests.
The live API tests are disabled on (Travis) CI and you need to enable them explicit by changing the line IS_CI=TRUE
in you project's .env.
namespace my\project\test; use my\project\source\MyProvider; use chillerlan\OAuthTest\Providers\OAuth2APITestAbstract; /** * @property \my\project\source\MyProvider $provider */ class MyProviderAPITest extends OAuth2APITestAbstract{ protected $CFG = __DIR__.'/../config'; protected $FQN = MyProvider::class; protected $ENV = 'MYPROVIDER'; // the prefix for the provider's name in .env // your live API tests here public function testIdentity(){ $r = $this->provider->identity(); $this->assertSame($this->testuser, $this->responseJson($r)->id); } }
Disclaimer
OAuth tokens are secrets and should be treated as such. Store them in a safe place,
consider encryption.
I won't take responsibility for stolen auth tokens. Use at your own risk.