## Getting Started
### Minimal Setup for Laravel Integration
Since this is a Symfony bundle, leverage **Laravel's Symfony Bridge** (`symfony/console`, `symfony/http-foundation`) for compatibility. Start by:
1. **Install via Composer** (Symfony-compatible packages require manual handling in Laravel):
```bash
composer require 20steps/oauth-server-bundle
Note: Use symfony/flex or manually configure autoload in composer.json to resolve Symfony dependencies.
Register the Bundle (Laravel’s config/bundles.php equivalent):
Add to config/app.php under providers:
'providers' => [
// ...
Symfony\Component\HttpKernel\Kernel::class,
League\OAuth2\Server\ResourceServer::class, // Core OAuth2 library
],
Configure the Bundle (Symfony-style config → Laravel’s config/oauth.php):
return [
'server' => [
'private_key' => storage_path('app/oauth_private.key'),
'encryption_key' => storage_path('app/oauth_encryption.key'),
'algorithm' => 'RS256',
'access_token_ttl' => 3600,
'refresh_token_ttl' => 86400,
],
'clients' => [
'alexa_account_linking' => [
'random_id' => 'client_id_here',
'random_secret' => 'client_secret_here',
'redirect_uri' => 'https://your-app.com/oauth/callback',
],
],
];
Generate Keys (Run via Artisan or Symfony CLI):
php artisan vendor:publish --provider="20steps\OAuthServerBundle\OAuthServerBundle" --tag="config"
openssl genpkey -algorithm RSA -out storage/app/oauth_private.key -pkeyopt rsa_keygen_bits:2048
openssl pkey -in storage/app/oauth_private.key -out storage/app/oauth_public.key -pubout
openssl enc -base64 -in storage/app/oauth_public.key -out storage/app/oauth_public.pem
First Use Case: Authenticate a User
Create a route (routes/web.php) to handle OAuth flow:
Route::get('/oauth/authorize', [OAuthController::class, 'authorize']);
Route::get('/oauth/callback', [OAuthController::class, 'callback']);
Controller Example (Symfony-style → Laravel-adapted):
use League\OAuth2\Server\AuthorizationServer;
use Symfony\Component\HttpFoundation\Request;
class OAuthController extends Controller {
public function authorize(Request $request) {
$server = new AuthorizationServer(
new \League\OAuth2\Server\Entities\ClientRepository(
config('oauth.clients')
),
new \League\OAuth2\Server\Entities\UserRepository(),
new \League\OAuth2\Server\Grant\AuthCodeGrant(),
new \League\OAuth2\Server\Storage\Memory(),
config('oauth.server')
);
return $server->validateAuthorizationRequest($request);
}
}
Client Requests Authorization:
Redirect user to /oauth/authorize?client_id=...&redirect_uri=...&response_type=code.
Laravel Tip: Use RedirectResponse with query params:
return redirect()->to('/oauth/authorize')
->withQueryString([
'client_id' => $clientId,
'redirect_uri' => $redirectUri,
'response_type' => 'code',
]);
User Approval: Implement a middleware or controller to validate user consent:
// Middleware: Check if user is authenticated
public function handle($request, Closure $next) {
if (!auth()->check()) {
return redirect()->route('login');
}
return $next($request);
}
Exchange Code for Tokens: Handle the callback to issue tokens:
public function callback(Request $request) {
$server = new AuthorizationServer(/* ... */);
$authRequest = $server->validateAuthorizationRequest($request);
$authRequest->setUser(auth()->user()); // Attach authenticated user
$authRequest->setRedirectUri($request->get('redirect_uri'));
$authRequest->setAuthorizationApproved(true);
$authCode = $server->issueAccessCode();
return redirect($request->get('redirect_uri'))
->withQueryString(['code' => $authCode->getId()]);
}
Token Endpoint: Create a route to exchange code for tokens:
Route::post('/oauth/token', [OAuthController::class, 'issueToken']);
Controller Logic:
public function issueToken(Request $request) {
$server = new \League\OAuth2\Server\ResourceServer(
new \League\OAuth2\Server\Entities\ClientRepository(config('oauth.clients')),
new \League\OAuth2\Server\Entities\UserRepository(),
new \League\OAuth2\Server\Storage\Memory(),
config('oauth.server')
);
$request = \League\OAuth2\Server\RequestServer::createFromGlobals();
$response = new \Symfony\Component\HttpFoundation\Response();
$server->handleTokenRequest($request, $response);
return $response;
}
Laravel Auth Integration:
Extend the UserRepository to fetch users from Laravel’s auth system:
use League\OAuth2\Server\Entities\UserEntityInterface;
class LaravelUserRepository implements \League\OAuth2\Server\Entities\UserRepositoryInterface {
public function getUserEntityByUserId($userId) {
$user = \App\Models\User::find($userId);
return new class($user) implements UserEntityInterface {
private $user;
public function __construct($user) { $this->user = $user; }
public function getIdentifier() { return $this->user->id; }
// Implement other UserEntityInterface methods...
};
}
}
Middleware for Protected Routes: Use Laravel’s middleware to validate access tokens:
class ValidateOAuthToken {
public function handle($request, Closure $next) {
$server = new \League\OAuth2\Server\ResourceServer(/* ... */);
$request = \League\OAuth2\Server\RequestServer::createFromGlobals();
try {
$server->validateAuthenticatedRequest($request);
} catch (\League\OAuth2\Server\Exception\OAuthServerException $e) {
return response()->json(['error' => $e->getMessage()], 401);
}
return $next($request);
}
}
Register in app/Http/Kernel.php:
protected $routeMiddleware = [
'oauth' => \App\Http\Middleware\ValidateOAuthToken::class,
];
Scopes and Claims:
Customize scopes in the AuthCodeGrant:
$grant = new \League\OAuth2\Server\Grant\AuthCodeGrant(
new \League\OAuth2\Server\Entities\ClientRepository(),
new \League\OAuth2\Server\Entities\UserRepository(),
new \League\OAuth2\Server\Storage\Memory(),
[
'scope_binder' => new class {
public function getDefaultScopes() { return ['read', 'write']; }
public function validateScopes($scopes) { /* ... */ }
}
]
);
Symfony vs. Laravel Compatibility:
HttpFoundation expects $_SERVER, $_GET, etc., directly. Laravel’s Request wraps these.RequestServer::createFromGlobals() to bridge the gap:
$request = \League\OAuth2\Server\RequestServer::createFromGlobals();
Token Storage:
Memory storage (in-memory only). Tokens are lost on server restart.Doctrine or Redis:
$storage = new \League\OAuth2\Server\Storage\Doctrine(
$entityManager,
new \League\OAuth2\Server\Entities\ClientEntity(),
new \League\OAuth2\Server\Entities\AccessTokenEntity(),
new \League\OAuth2\Server\Entities\RefreshTokenEntity(),
new \League\OAuth2\Server\Entities\AuthCodeEntity()
);
CSRF Protection:
How can I help you explore Laravel packages today?