bshaffer/oauth2-server-bundle
Install the Bundle
composer require bshaffer/oauth2-server-bundle
Add to config/bundles.php:
return [
// ...
Bshaffer\OAuth2ServerBundle\OAuth2ServerBundle::class => ['all' => true],
];
Configure Database & Clients
Run migrations (if using Doctrine) to create oauth_clients, oauth_access_tokens, and oauth_auth_codes tables.
Example client creation via CLI:
php bin/console doctrine:fixtures:load --append --no-interaction
(Use Fixtures/OAuthClientFixtures.php or create your own.)
First Token Request
Test the /token endpoint with curl:
curl -X POST http://your-app.com/token \
-d "grant_type=client_credentials" \
-d "client_id=your_client_id" \
-d "client_secret=your_client_secret"
Expected response: {"access_token":"...", "token_type":"Bearer", "expires_in":3600}
Key Configuration Files
config/packages/oauth2_server.yaml (default paths, grant types, etc.)config/packages/security.yaml (for user credentials grant, if enabled).Grant Type Handling
$server->addGrantType(new \Bshaffer\OAuth2\Grant\ClientCredentials());
/authorize).
$server->addGrantType(new \Bshaffer\OAuth2\Grant\AuthorizationCode());
$server->addGrantType(new \Bshaffer\OAuth2\Grant\UserCredentials());
$server->addGrantType(new \Bshaffer\OAuth2\Grant\RefreshToken());
Customizing the /authorize Endpoint
Extend the controller to add UI or logic:
// src/Controller/OAuthController.php
namespace App\Controller;
use Bshaffer\OAuth2ServerBundle\Controller\TokenController;
use Symfony\Component\HttpFoundation\Request;
class OAuthController extends TokenController
{
public function authorizeAction(Request $request)
{
// Custom logic (e.g., redirect to login if unauthenticated)
return parent::authorizeAction($request);
}
}
Update routing (config/routes.yaml):
oauth_authorize:
path: /authorize
defaults: { _controller: App\Controller\OAuthController::authorizeAction }
Token Storage & Validation Implement custom storage for tokens/authorizations:
// src/OAuth/Storage/DoctrineTokenStorage.php
use Bshaffer\OAuth2\Storage\TokenStorageInterface;
class DoctrineTokenStorage implements TokenStorageInterface
{
public function getAccessToken($token)
{
// Query your DB for the token
}
public function setAccessToken($tokenEntity)
{
// Save to DB
}
}
Register in services.yaml:
services:
oauth2.storage.token:
class: App\OAuth\Storage\DoctrineTokenStorage
tags:
- { name: oauth2.storage.token }
Scopes & Permissions
Define scopes in the client table (e.g., read write). Enforce them in your API:
// src/EventListener/OAuthScopeListener.php
use Symfony\Component\HttpKernel\Event\ControllerEvent;
class OAuthScopeListener
{
public function onKernelController(ControllerEvent $event)
{
$request = $event->getRequest();
$token = $request->attributes->get('oauth_token');
if (!$token->getScope()->hasScope('read')) {
throw new \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException();
}
}
}
Integration with Symfony Security Use OAuth tokens for API authentication:
# config/packages/security.yaml
firewalls:
api:
pattern: ^/api
stateless: true
provider: oauth_token
oauth_token:
check_path: /login_check
services:
- App\Security\OAuthTokenAuthenticator
Laravel-Specific Adaptation: While this bundle is Symfony-focused, leverage it in Laravel via:
laravel/symfony-bundle to integrate Symfony components.oauth2-server-php library and adapt its classes to Laravel’s DI container.// app/Http/Middleware/OAuthValidate.php
public function handle($request, Closure $next)
{
$server = new \Bshaffer\OAuth2\Server();
$server->addStorage(new \Bshaffer\OAuth2\Storage\Pdo());
$request = $server->validateAuthenticatedRequest($request);
return $next($request);
}
Testing Tokens:
Use HttpFoundation to mock requests in PHPUnit:
$request = new Request([], [
'grant_type' => 'client_credentials',
'client_id' => 'test_client',
'client_secret' => 'test_secret',
]);
$response = $this->get('oauth2.response');
$server = $this->get('oauth2.server');
$server->handleTokenRequest($request, $response);
$this->assertTrue($response->isSuccessful());
Deprecated Grant Types
password) is deprecated in OAuth2 (RFC 6749). Avoid using it in production unless absolutely necessary.CSRF in Authorization Code Flow
/authorize endpoint by default.// src/EventListener/OAuthCsrfListener.php
public function onKernelRequest(GetResponseEvent $event)
{
$request = $event->getRequest();
if ($request->getPathInfo() === '/authorize' && $request->isMethod('GET')) {
if (!$request->query->has('_csrf_token')) {
throw new \Symfony\Component\HttpKernel\Exception\BadRequestHttpException();
}
}
}
Token Expiration Handling
401 Unauthorized and refresh tokens.expires_in field in the response to inform clients. Example:
$response->setAccessToken($token);
$response->setExpiresIn($token->getExpiresAt()->getTimestamp() - time());
Database Schema Mismatches
oauth_clients, oauth_access_tokens). Custom schemas may break functionality.CORS Issues
/token endpoint may block cross-origin requests if CORS headers are missing.nelmio/cors-bundle) and configure it for /token:
# config/packages/nelmio_cors.yaml
nelmio_cors:
defaults:
allow_origin: ["*"]
allow_methods: ["POST"]
allow_headers: ["Authorization", "Content-Type"]
expose_headers: ["Authorization"]
paths:
'^/token':
allow_origin: ["*"]
Enable Verbose Logging Configure Monolog to log OAuth2 events:
# config/packages/monolog.yaml
handlers:
oauth:
type: stream
path: "%kernel.logs_dir%/%kernel.environment%.oauth.log"
level: debug
channels: ["oauth2"]
Then enable the channel in oauth2_server.yaml:
oauth2_server:
logging: true
Inspect Request/Response Dump the OAuth2 request/response objects in a controller:
public function tokenAction()
{
$request = $this->get('oauth2.request
How can I help you explore Laravel packages today?