An easy-to-use helper for Laravel HTTP Client to make manage API requests with a two-step auth flow.
For example, OAuth2 or refresh tokens to get a new short-lived access token.
This helper takes care of all the headaches and boilerplate code with a simple and easy-to-use interface.
- Automatically fetches new access tokens when needed.
- Automatically caches the access tokens for their lifetime.
- Extends the Laravel HTTP Client to make it straightforward to use. Se the usage section below for examples.
- Supports common auth flows like OAuth2 and refresh tokens with most grant types.
- No configuration and no boilerplate code required. Just require and use with a simple and consise API.
- Supports callbacks to customize the behaviour when needed.
I want to support as many common auth flows as possible.
If you have a use case that is not super obscure,
please open an issue where you provide as much detail as possible,
or submit a PR with a working solution.
Plans for 1.1 - Milestone
- Helper for automatically refresh tokens in the background before they expire.
- Add support for authorization_code and implicit OAuth2 grants.
- Tokens owned by a model. For example, tokens associated with a user model rather than the whole application.
- Requirements
- Vision, roadmap & plans for the future
- Contributing
- Installation
- Options reference
- Usage
- PHP 8.2 or higher
- Laravel 10
I want to support as many common auth flows as possible.
If you have a use case that is not super obscure,
please open an issue where you provide as much detail as possible,
or submit a PR.
See Contribution Guide before sending pull requests.
When you are submitting issues, I appreciate if you could provide a failing test case. That makes my job a lot easier.
I will try to fix reported issues as soon as possible, but I do this in my spare time, so I might not be able to do it immediately.
composer require pelmered/laravel-http-client-auth-helper
Scopes to send when requesting an access token.
Typically only used for OAuth2 flows.
Possible options: array with strings
Default: []
The type of authorization for the refresh token request.
Possible options: Credentials::AUTH_TYPE_BEARER
, Credentials::AUTH_TYPE_BODY
, Credentials::AUTH_TYPE_QUERY
, Credentials::AUTH_TYPE_BASIC
, Credentials::AUTH_TYPE_CUSTOM
,
Default: Credentials::AUTH_TYPE_BEARER
(='Bearer'
)
Grant type for OAuth2 flows.
Possible options: Credentials::GRANT_TYPE_CLIENT_CREDENTIALS
, Credentials::GRANT_TYPE_PASSWORD_CREDENTIALS
(authorization_code and implicit grants are not yet supported. See issue #3)
Default: Credentials::GRANT_TYPE_CLIENT_CREDENTIALS
(='client_credentials'
)
How the access token should be applied to all subsequent requests.
Possible options: AccessToken::TOKEN_TYPE_BEARER
, AccessToken::TOKEN_TYPE_QUERY
, AccessToken::TOKEN_TYPE_CUSTOM
Default: AccessToken::TOKEN_TYPE_BEARER
(='Bearer'
)
The name of the token field. This only applies for when the token is applied as a query parameter or to the body of the request.
Possible options: Any string
Default: 'token'
This determines when the access token expires.
Possible options:
integer - for how long until expiry in seconds)
string - Can be key of the field in response that contains the expiry of the token. Can also be a string with a date. This is then parsed by Carbon::parse so any format that Carbon can parse is acceptable.
Closure - A closure that receives the refresh response and can return any other acceptable value (integer, string or Carbon object).
Carbon - A Carbon object with the time of the expiry.
Default: 3600
This is where the access token can be found on the refresh response.
Possible options:
string - The key of the access token in the refresh response.
Closure - A closure that receives the refresh response and should return the token as a string.
Default: 'access_token'
A callback for giving dull control of how the authentication should be applied.
The closure receives the Http client and should return a new Http Client where the auth information has been appended.
Possible options:\ Any closure that returns a Http Client (Illuminate\Http\Client\PendingRequest
).
Default: null
The cache key that should be used to save the access tokens.
If left empty, it will be generated based on the refresh URL.
Possible options:
Default: null
The cache driver/store that should be used for storing the access tokens.
If left empty, the Laravel default will be used.
Possible options:
Default: null
It's really simple to use. Just add the withRefreshToken
method to your HTTP request and provide the necessary parameters. No configuration needed.
$response = Http::withRefreshToken(
'https://example.com/token.oauth2',
[
'client_id',
'client_secret',
]
)->get(
'https://example.com/api',
);
$response = Http::withRefreshToken(
'https://example.com/token.oauth2',
[
'client_id',
'client_secret',
],
[
'scopes' => [],
'expires' => 'expires_in', // When token should be considered expired. A string key in the response JSON for the expiration. We try to parse different formats and then remove 1 minute to be on the safe side.
'auth_type' => 'body', // 'body' or 'header'
'access_token' => 'access_token', // Key for the access token in the response JSON
],
'Bearer'
)->get(
'https://example.com/api',
);
use Pelmered\LaravelHttpOAuthHelper\AccessToken;
use Pelmered\LaravelHttpOAuthHelper\Credentials;
use Pelmered\LaravelHttpOAuthHelper\Options;
use Pelmered\LaravelHttpOAuthHelper\RefreshToken;
$response = Http::withRefreshToken(
'https://example.com/token.oauth2',
new Credentials(
clientId: 'client_id',
clientSecret: 'client_secret',
),
new Options(
scopes: ['scope1', 'scope2'],
expires: 3600,
grantType: 'password_credentials',
authType: Credentials::AUTH_TYPE_BODY,
tokenType: AccessToken::TOKEN_TYPE_BEARER,
),
)->get(
'https://example.com/api',
);
You can also provide callbacks for expires
, auth_type
, and access_token
to customize the behavior.
$response = Http::withRefreshToken(
'https://example.com/token.oauth2',
[
'client_id',
'client_secret',
],
[
'expires' => fn($response) => $response->json()['expires_in'] - 300, // Should return the ttl in seconds that has been parsed from the response and can be manipulated as you want.
'access_token' => fn($response) => $response->access_token, // Should return the access token that has been parsed from the response.
],
'Bearer'
)->get(
'https://example.com/api',
);
Custom auth for refreshing token:
use Illuminate\Http\Client\PendingRequest;
$response = Http::withRefreshToken(
'https://example.com/token.oauth2',
[
'client_id',
'client_secret',
],
[
'expires' => fn($response) => $response->json()['expires_in'] - 300, // Should return the ttl in seconds that has been parsed from the response and can be manipulated as you want.
'access_token' => fn($response) => $response->access_token, // Should return the access token that has been parsed from the response.
'auth_type' => 'custom',
'apply_auth_token' => fn(PendingRequest $httpClient) => $request->withHeader('Authorization', 'Bearer ' . $token),
)->get(
'https://example.com/api',
);
For more examples, check out the Macro tests.
If you use the same token in multiple places, you can create the client only once and save it. For example:
$this->client = Http::withRefreshToken(
'https://example.com/token.oauth2',
[
'client_id',
'client_secret',
],
[
'scopes' => ['read:posts', 'write:posts', 'read:comments'],
]
)->baseUrl('https://example.com/api');
to use it later like this:
$this->client->get('posts');
$this->client->get('comments');
$this->client->post('posts', [
'title' => 'My post',
'content' => 'My content',
]);
You can also resolve it in the container if you want. In your service provider:
$this->app->singleton('my-oauth-client', function ($app) {
return Http::withRefreshToken(
'https://example.com/token.oauth2',
[
'client_id',
'client_secret',
],
[
'scopes' => ['read:posts', 'write:posts', 'read:comments'],
]
)->baseUrl('https://example.com/api');
});
and then use it anywhere like this:
app('my-oauth-client')->get('posts');