digitaldream/symfony-access-token
Installation
composer require digitaldream/symfony-access-token
Ensure your project uses Symfony 5.4+ (Laravel developers: this is a Symfony bundle, but we’ll adapt concepts).
Copy Configs
Copy config/packages/access_token.yaml and config/routes/access_token.yaml from the vendor to your project’s config/ directory.
Environment Setup
Add these to .env (adjust values as needed):
JWT_SECRET="your_secure_random_string_here"
JWT_ISSUER="https://your-app-domain.com"
JWT_ALGORITHM=HS256
JWT_EXPIRE_AT="+1 hour" # Shorter for testing
Configure Security
Update config/packages/security.yaml:
security:
firewalls:
api:
pattern: ^/api
stateless: true
provider: app_user_provider # Your Symfony user provider (e.g., `users:` in `security.yaml`)
user_checker: AccessToken\Security\UserChecker
access_token:
token_handler: AccessToken\Security\AccessTokenHandler
failure_handler: AccessToken\Security\AuthenticationFailureHandler
access_control:
- { path: ^/api, roles: ROLE_USER }
First Use Case
Test the default /api/login endpoint:
curl -X POST http://localhost:8000/api/login \
-H "Content-Type: application/json" \
-d '{"username":"test@example.com","password":"password"}'
Expect a JSON response with a token field.
/api/login with username/password.Authorization header:
Authorization: Bearer <token>
CreateAccessTokenService to issue new tokens.Override the default login route by injecting CreateAccessTokenService:
// src/Controller/CustomLoginController.php
use AccessToken\Services\CreateAccessTokenService;
use AccessToken\Services\UserCredentialsRequest;
class CustomLoginController extends AbstractController {
public function login(CreateAccessTokenService $tokenService, Request $request) {
$credentials = new UserCredentialsRequest(
$request->request->get('username'),
$request->request->get('password')
);
$token = $tokenService->create($credentials);
return $this->json(['token' => $token]);
}
}
Register the route in config/routes.yaml:
app_custom_login:
path: /api/custom-login
controller: App\Controller\CustomLoginController::login
methods: POST
While this is a Symfony bundle, Laravel developers can adapt the logic:
CreateAccessTokenService.AccessTokenHandler.Reuse the bundle’s AccessTokenHandler to validate tokens in Laravel middleware:
// app/Http/Middleware/AuthenticateToken.php
public function handle($request, Closure $next) {
$token = $request->bearerToken();
if (!$token || !$this->validateToken($token)) {
return response()->json(['error' => 'Unauthorized'], 401);
}
return $next($request);
}
private function validateToken(string $token): bool {
// Adapt AccessTokenHandler::validate() logic here
return true/false;
}
Environment Variables
JWT_SECRET must be long and random (e.g., openssl rand -hex 32). Hardcoded secrets in .env are unsafe in production.JWT_ISSUER should match your production domain (e.g., https://api.yourdomain.com). Mismatches cause validation failures.User Provider Mismatch
Ensure app_user_provider in security.yaml matches your Symfony user provider (e.g., users: config). Laravel equivalent: Ensure your User model is properly configured in Auth::provider().
Token Expiration
Default JWT_EXPIRE_AT is +24 hours. For APIs, shorten this (e.g., +1 hour) and implement refresh tokens via a custom endpoint.
Stateless Firewall
The api firewall is stateless. If you need session-like behavior, add:
access_token:
stateless: false # Only if you handle sessions manually
CORS Issues
The bundle doesn’t handle CORS. Add this to your Symfony config or Laravel’s HandleCors middleware:
# config/packages/nelmio_cors.yaml
nelmio_cors:
defaults:
allow_origin: ["*"]
allow_methods: ["GET", "POST", "PUT", "DELETE"]
allow_headers: ["Authorization", "Content-Type"]
expose_headers: ["Authorization"]
Token Validation Errors
Check the AccessTokenHandler logs or Symfony’s security.event.authentication_failure event for details.
UserChecker Failures If authentication fails silently, enable debug mode and check:
bin/console debug:event-dispatcher security.event.authentication_failure
Route Conflicts
If /api/login conflicts with existing routes, override it as shown in the Custom Login Logic section.
Custom Token Claims
Extend AccessToken\Entity\AccessToken to add metadata:
// src/Entity/CustomAccessToken.php
class CustomAccessToken extends AccessToken {
private ?string $customClaim;
public function setCustomClaim(string $claim): self {
$this->customClaim = $claim;
return $this;
}
}
Update CreateAccessTokenService to use your custom class.
Token Storage Save tokens to a database for revocation:
// src/Repository/AccessTokenRepository.php
class AccessTokenRepository extends ServiceEntityRepository {
public function saveToken(AccessToken $token): void {
$entityManager = $this->getEntityManager();
$entityManager->persist($token);
$entityManager->flush();
}
}
Algorithm Flexibility
Change JWT_ALGORITHM to RS256 for asymmetric encryption (requires JWT_KEY as a private/public key pair). Example:
JWT_ALGORITHM=RS256
JWT_KEY="file:///path/to/private.pem"
Laravel Adaptation
For Laravel, create a facade to wrap CreateAccessTokenService:
// app/Facades/TokenGenerator.php
public static function generateToken(string $username, string $password): string {
$service = app(CreateAccessTokenService::class);
$credentials = new UserCredentialsRequest($username, $password);
return $service->create($credentials);
}
How can I help you explore Laravel packages today?