- PHP 7.2+
- the Sodium extension for token encryption
- cURL, PHP's stream wrapper or a HTTP client library of your choice
- see
chillerlan/php-oauth
In order to instance an OAuthInterface you you'll need to invoke a HTTPClientInterface, OAuthStorageInterface and OAuthOptions (a ContainerInterface) objects first:
use chillerlan\OAuth\Providers\<PROVIDER_NAMESPACE>\<PROVIDER>;
use chillerlan\OAuth\{
OAuthOptions, Storage\SessionTokenStorage
};
use chillerlan\HTTP\CurlClient;
// OAuthOptions
$options = new OAuthOptions([
// OAuthOptions
'key' => '<API_KEY>',
'secret' => '<API_SECRET>',
'callbackURL' => '<API_CALLBACK_URL>',
// HTTPOptions
'ca_info' => '/path/to/cacert.pem', // https://curl.haxx.se/ca/cacert.pem
'userAgent' => 'my-awesome-oauth-app',
]);
// HTTPClientInterface
$http = new CurlClient($options);
// OAuthStorageInterface
// a persistent storage is required for authentication!
$storage = new SessionTokenStorage($options);
// optional scopes for OAuth2 providers
$scopes = [
Provider::SCOPE_WHATEVER,
];
// an optional \Psr\LoggerInterface logger
$logger = new Logger;
// invoke and use the OAuthInterface
$provider = new Provider($http, $storage, $options, $logger, $scopes);The application flow may differ slightly depending on the provider; there's a working authentication example in the provider repository.
Display a login link and provide the user with information what kind of data you're about to access; ask them for permission to save the access token if needed.
echo '<a href="?login='.$provider->serviceName.'">connect with '.$provider->serviceName.'!</a>';Redirect to the provider's login screen with optional arguments in the authentication URL, like permissions etc.
if(isset($_GET['login']) && $_GET['login'] === $provider->serviceName){
header('Location: '.$provider->getAuthURL(['extra-param' => 'val']));
}Receive the access token, save it, do whatever you need to do, then redirect to step 4
if(isset($_GET['oauth_token']) && isset($_GET['oauth_verifier'])){
$token = $provider->getAccessToken($_GET['oauth_token'], $_GET['oauth_verifier']);
// save & redirect...
}usage of the <state> parameter depends on the provider
if(isset($_GET['code'])){
$token = $provider->getAccessToken($_GET['code'], $_GET['state'] ?? null);
// save & redirect...
}After receiving the access token, go on and verify it then use the API.
if(isset($_GET['granted']) && $_GET['granted'] === $provider->serviceName){
$response = $provider->doStuff();
// ...
}After successfully receiving the Token, we're ready to make API requests:
// import a token to the OAuth token storage if needed
$storage->storeAccessToken($provider->serviceName, new AccessToken->__fromJSON($token_json));
// make a request
$response = $provider->request(
'/some/endpoint',
['q' => 'param'],
'POST',
['data' => 'content'],
['content-type' => 'whatever']
);
// use the data
$headers = $response->headers;
$data = $response->json;In order to use a provider or storage, that is not yet supported, you'll need to implement the respective interfaces:
The OAuth1 implementation is close to Twitter's specs and should work for most other OAuth1 services.
use chillerlan\OAuth\Providers\OAuth1Provider;
class MyOauth1Provider extends Oauth1Provider{
protected $apiURL = 'https://api.example.com';
protected $requestTokenURL = 'https://example.com/oauth/request_token';
protected $authURL = 'https://example.com/oauth/authorize';
protected $accessTokenURL = 'https://example.com/oauth/access_token';
}OAuth2 is a very straightforward... mess. Please refer to your provider's docs for implementation details.
use chillerlan\OAuth\Providers\OAuth2Provider;
class MyOauth2Provider extends Oauth2Provider implements ClientCredentials, CSRFToken, TokenExpires, TokenRefresh{
use OAuth2ClientCredentialsTrait, CSRFTokenTrait, OAuth2TokenRefreshTrait;
public const SCOPE_WHATEVER = 'whatever';
protected $apiURL = 'https://api.example.com';
protected $authURL = 'https://example.com/oauth2/authorize';
protected $accessTokenURL = 'https://example.com/oauth2/token';
protected $clientCredentialsTokenURL = 'https://example.com/oauth2/client_credentials';
protected $authMethod = self::HEADER_BEARER;
protected $authHeaders = ['Accept' => 'application/json'];
protected $apiHeaders = ['Accept' => 'application/json'];
protected $scopesDelimiter = ',';
}There are currently 3 different OAuthStorageInterface, 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.DBStorage: persistent, multi purpose database driven storage with encryption support (via ext-sodium)
| method | return |
|---|---|
__construct(HTTPClientInterface $http, OAuthStorageInterface $storage, ContainerInterface $options, LoggerInterface $logger = null) |
- |
getAuthURL(array $params = null) |
string |
getStorageInterface() |
OAuthStorageInterface |
request(string $path, array $params = null, string $method = null, $body = null, array $headers = null) |
HTTPResponseInterface |
| 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 |
| method | return |
|---|---|
getAccessToken(string $token, string $verifier, string $tokenSecret = null) |
AccessToken |
getRequestToken() |
AccessToken |
getSignature(string $url, array $params, string $method = null) |
string |
| method | return |
|---|---|
__construct(HTTPClientInterface $http, OAuthStorageInterface $storage, ContainerInterface $options, LoggerInterface $logger = null, array $scopes = null) |
- |
getAccessToken(string $code, string $state = null) |
AccessToken |
implemented by OAuth2ClientCredentialsTrait
| method | return |
|---|---|
getClientCredentialsToken(array $scopes = null) |
AccessToken |
(protected) getClientCredentialsTokenBody(array $scopes) |
array |
(protected) getClientCredentialsTokenHeaders() |
array |
implemented by CSRFTokenTrait
| method | return |
|---|---|
(protected) checkState(string $state = null) |
OAuth2Interface |
(protected) setState(array $params) |
array |
implemented by OAuth2TokenRefreshTrait
| method | return |
|---|---|
refreshAccessToken(AccessToken $token = null) |
AccessToken |
| method | return |
|---|
| method | return |
|---|---|
storeAccessToken(string $service, AccessToken $token) |
OAuthStorageInterface |
getAccessToken(string $service) |
AccessToken |
hasAccessToken(string $service) |
AccessToken |
clearAccessToken(string$service) |
OAuthStorageInterface |
clearAllAccessTokens() |
OAuthStorageInterface |
storeCSRFState(string $service, string $state) |
OAuthStorageInterface |
getCSRFState(string $service) |
string |
hasCSRFState(string $service) |
bool |
clearCSRFState(string $service) |
OAuthStorageInterface |
clearAllCSRFStates() |
OAuthStorageInterface |
toStorage(AccessToken $token) |
string |
fromStorage(string $data) |
AccessToken |
| method | return | description |
|---|---|---|
__construct(array $properties = null) |
- | |
__set(string $property, $value) |
void | overrides chillerlan\Traits\Container |
__toArray() |
array | from chillerlan\Traits\Container |
setExpiry(int $expires = null) |
AccessToken |
|
isExpired() |
bool |
| property | type | default | allowed | description |
|---|---|---|---|---|
$requestToken |
string | null | * | OAuth1 only |
$requestTokenSecret |
string | null | * | OAuth1 only |
$accessTokenSecret |
string | null | * | OAuth1 only |
$accessToken |
string | null | * | |
$refreshToken |
string | null | * | |
$extraParams |
array | [] |
||
$expires |
int | AccessToken::EOL_UNKNOWN |
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.