## Getting Started
### Minimal Setup
1. **Installation**:
```bash
composer require league/oauth2-server-bundle doctrine/doctrine-bundle doctrine/orm
Enable the bundle in config/bundles.php:
return [
// ...
League\OAuth2ServerBundle\OAuth2ServerBundle::class => ['all' => true],
];
Database Migration: Run Doctrine migrations to set up the required tables:
php bin/console doctrine:migrations:diff
php bin/console doctrine:migrations:migrate
First Use Case:
php bin/console make:oauth2-client --name="MyAppClient" --redirect-uri="https://yourapp.com/callback"
curl -X POST \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials&client_id=YOUR_CLIENT_ID&client_secret=YOUR_CLIENT_SECRET" \
http://yourapp.test/oauth/token
Key Files:
config/packages/league_oauth2_server.yaml: Main configuration.src/Entity/: Auto-generated OAuth entities (clients, scopes, etc.).src/Security/: Custom authentication logic (if extending).Token Generation:
/oauth/authorize, /oauth/token) for standard flows (authorization code, implicit, client credentials, etc.).TokenController:
// src/Controller/OAuthController.php
use League\OAuth2Server\ResourceServer;
class OAuthController extends AbstractController {
public function token(ResourceServer $server) {
$request = Request::createFromGlobals();
$server->validateAuthenticatedRequest($request);
// Custom logic (e.g., validate credentials)
$token = $server->issueAccessToken();
return new JsonResponse($token->jsonSerialize());
}
}
Resource Protection:
ResourceServer middleware:
# config/routes.yaml
protected_route:
path: /api/protected
methods: GET
controller: App\Controller\ProtectedController::index
requirements:
role: ROLE_USER
defaults:
_oauth: true # Adds ResourceServer middleware
use League\OAuth2Server\ResourceServer;
class ProtectedController {
public function index(ResourceServer $server) {
$request = Request::createFromGlobals();
$server->validateAuthenticatedRequest($request);
// Access user data via $server->getResourceOwner()
return new JsonResponse(['data' => 'protected']);
}
}
Custom Grant Types:
GrantType interface to support custom flows (e.g., JWT bearer):
use League\OAuth2Server\Grant\GrantTypeInterface;
class CustomGrant implements GrantTypeInterface {
public function respondToAccessTokenRequest() { /* ... */ }
}
league_oauth2_server:
grants:
custom_grant: League\OAuth2ServerBundle\Grant\CustomGrant
Scopes and Roles:
league_oauth2_server:
scopes:
read: Read access
write: Write access
User Authentication:
UserEntity:
use League\OAuth2Server\Entity\UserEntityInterface;
class AppUser implements UserEntityInterface {
public function getIdentifier() { /* ... */ }
public function getEmail() { /* ... */ }
// Implement other required methods
}
Laravel-Specific Adaptations:
HttpFoundation components (e.g., Request, Response) via Laravel’s bridge:
use Symfony\Component\HttpFoundation\Request as SymfonyRequest;
$request = SymfonyRequest::createFromGlobals();
Auth facade, create a bridge service:
class OAuthUserProvider implements UserProviderInterface {
public function retrieveByToken($tokenId, $type) {
$user = Auth::guard('api')->user();
return $user ? new AppUser($user) : null;
}
}
Testing:
League\OAuth2Server\Tests\TestCases\Traits for unit tests.ResourceOwnerRepositoryInterface and ClientRepositoryInterface:
$clientRepo = $this->createMock(ClientRepositoryInterface::class);
$clientRepo->method('getClientEntity')->willReturn($clientEntity);
$server = new Server($clientRepo, $accessTokenRepo, $scopeRepo, $privateKey, $encryptionKey);
Caching:
league_oauth2_server:
cache:
enabled: true
pool: cache.app
Logging:
league_oauth2_server:
debug: true
Database Schema Mismatches:
ClientEntity, AccessTokenEntity, etc.) may change between versions.php bin/console doctrine:migrations:diff --complete
CSRF Protection:
csrf_token in forms or add middleware:
$middleware->add(new \Symfony\Component\HttpKernel\EventListener\CsrfTokenListener());
Token Expiry:
league_oauth2_server:
access_token_lifetime: 7200 # 2 hours
null for infinite lifetime (not recommended for production).Scope Validation:
$scopes = $server->getRequestedScopes();
if (!in_array('read', $scopes)) {
throw new \League\OAuth2Server\Exception\OAuthServerException(
'insufficient_scope',
'The requested scope is invalid.',
\League\OAuth2Server\ResponseTypes\AccessTokenResponse::class
);
}
Client Secret Handling:
parameter_bag:
# config/packages/league_oauth2_server.yaml
league_oauth2_server:
private_key: '%env(OAUTH_PRIVATE_KEY)%'
encryption_key: '%env(OAUTH_ENCRYPTION_KEY)%'
CORS Issues:
/oauth/authorize, /oauth/token) are included in CORS allowed origins:
# config/packages/nelmio_cors.yaml
nelmio_cors:
defaults:
allow_origins: ['%env(APP_ALLOWED_ORIGINS)%']
allow_methods: ['POST', 'OPTIONS']
allow_headers: ['Authorization', 'Content-Type']
expose_headers: ['Authorization']
paths:
'^/oauth/':
allow_origins: ['*'] # Adjust for production
Enable Debug Mode:
league_oauth2_server:
debug: true
var/log/dev.log.Token Validation Errors:
error and error_description fields in failed token requests. Common errors:
invalid_client: Client ID/secret mismatch.invalid_grant: Authorization code or refresh token expired/invalid.unsupported_grant_type: Grant type not enabled for the client.Database Debugging:
doctrine:
dbal:
logging: true
profiling: true
Middleware Order:
ResourceServerMiddleware runs after FirewallMiddleware in your security config:How can I help you explore Laravel packages today?