authbucket/oauth2-php
Standards-compliant OAuth 2.0 (RFC6749) server library for PHP. Includes a Silex-based service provider for demos/tests and supports custom models/model managers (e.g., Doctrine) for tokens, clients, users, and scopes.
Install the Package:
composer require authbucket/oauth2-php:~5.0
Add to composer.json under require:
"authbucket/oauth2-php": "~5.0"
Basic Laravel Integration (since Laravel uses Symfony components):
AuthServiceProvider):
use AuthBucket\OAuth2\Silex\Provider\AuthBucketOAuth2ServiceProvider;
public function register()
{
$this->app->register(new AuthBucketOAuth2ServiceProvider());
}
Define Routes (in routes/api.php or routes/web.php):
Route::get('/oauth2/authorize', ['uses' => 'authbucket_oauth2.authorization_controller@indexAction'])
->name('oauth2.authorize');
Route::post('/oauth2/token', ['uses' => 'authbucket_oauth2.token_controller@indexAction'])
->name('oauth2.token');
Route::match(['get', 'post'], '/oauth2/debug', ['uses' => 'authbucket_oauth2.debug_controller@indexAction'])
->name('oauth2.debug');
First Use Case: Password Grant Flow
$this->app['authbucket_oauth2.user_provider'] = $this->app->make('App\Repositories\UserRepository');
auth:api or custom):
$this->app['security.firewalls'] = [
'oauth2_token' => [
'pattern' => '^/oauth2/token$',
'oauth2_token' => true,
],
];
/oauth2/token:
{
"grant_type": "password",
"username": "user@example.com",
"password": "password",
"client_id": "your_client_id",
"client_secret": "your_client_secret"
}
/oauth2/authorize?response_type=code&client_id=CLIENT_ID&redirect_uri=REDIRECT_URI.authorization_controller.code for a token via /oauth2/token with grant_type=authorization_code.$client = new \AuthBucket\OAuth2\Client\Client(
$this->app['authbucket_oauth2.client_manager'],
['client_id' => 'CLIENT_ID', 'client_secret' => 'CLIENT_SECRET']
);
$token = $client->getAccessToken('client_credentials');
config/services.php or environment variables.oauth2_resource firewall:
$this->app['security.firewalls'] = [
'api' => [
'pattern' => '^/api/protected',
'oauth2_resource' => [
'scope' => ['read', 'write'], // Optional: Enforce scopes
],
],
];
use AuthBucket\OAuth2\ResourceServer\TokenValidator;
public function handle($request, Closure $next)
{
$validator = new TokenValidator(
$this->app['authbucket_oauth2.token_storage'],
$this->app['authbucket_oauth2.client_manager']
);
$validator->validate($request->bearerToken());
return $next($request);
}
AuthBucket\OAuth2\UserProviderInterface:
use AuthBucket\OAuth2\UserProviderInterface;
use App\Models\User;
class LaravelUserProvider implements UserProviderInterface
{
public function loadUserByUsername($username)
{
return User::where('email', $username)->first();
}
public function refreshUser(UserInterface $user)
{
return $this->loadUserByUsername($user->getUsername());
}
public function supportsClass($class)
{
return $class === User::class;
}
}
$this->app['authbucket_oauth2.user_provider'] = $this->app->make(LaravelUserProvider::class);
$this->app['authbucket_oauth2.model_manager.factory'] = function ($app) {
return new \AuthBucket\OAuth2\Model\Manager\DoctrineManager(
$app['doctrine.orm.entity_manager'],
'App\Models\OAuth2AccessToken',
'App\Models\OAuth2RefreshToken',
'App\Models\OAuth2Client'
);
};
AccessToken, RefreshToken, and Client extending AuthBucket\OAuth2\Model\Entity.Laravel-Specific:
Hash facade to encode passwords for the password grant type:
$app['security.default_encoder'] = function () {
return new \Symfony\Component\Security\Core\Encoder\PlaintextPasswordEncoder(); // Or use Laravel's Hash::make()
};
Auth facade for user management where possible.CORS Configuration:
/oauth2/authorize and /oauth2/token endpoints are CORS-enabled for SPAs:
$this->app->middleware('cors', ['except' => ['oauth2.*']]);
Logging:
$this->app['monolog.logger.oauth2'] = function () {
return $this->app->make('logger')->withFields(['service' => 'oauth2']);
};
Testing:
$response = $this->post('/oauth2/token', [
'grant_type' => 'password',
'username' => 'user@example.com',
'password' => 'password',
'client_id' => 'test_client',
'client_secret' => 'test_secret',
]);
$response->assertJsonStructure(['access_token', 'expires_in']);
Firewall Configuration:
/oauth2/token endpoint can lead to unauthorized token issuance.oauth2_token firewall for the token endpoint:
'oauth2_token' => [
'pattern' => '^/oauth2/token$',
'oauth2_token' => true,
],
State Parameter:
state parameter is not validated in the authorization flow.session to store and validate the state:
// In authorization_controller
$state = $request->query->get('state');
if (!hash_equals(session('oauth2_state'), $state)) {
throw new \Symfony\Component\HttpKernel\Exception\BadRequestHttpException('Invalid state parameter.');
}
Redirect URIs:
redirect_uri is not validated.$client->setAllowedRedirectUris(['https://yourapp.com/callback']);
Token Expiration:
public function handle($request, Closure $next)
{
$token = $request->bearerToken();
if ($token && $this->isTokenExpired($token)) {
$token = $this->refreshToken($token);
$request->headers->set('Authorization', 'Bearer ' . $token);
}
return $next($request);
}
How can I help you explore Laravel packages today?