gfreeau/get-jwt-bundle
A Symfony bundle that adds JSON Web Token authentication to your app, letting clients obtain and send JWTs to authenticate users against your existing user provider with minimal setup.
First, the honest framing: this is a Symfony bundle, not a Laravel package. There is no service provider, no facade, and nothing it registers will be picked up by Laravel's container. For a Laravel developer, day-to-day leverage comes in two forms: (1) if you maintain a legacy Symfony 2/3 app alongside your Laravel work, this is a drop-in "login returns a JWT" listener; (2) otherwise, treat it as a ~200-line reference implementation of the credential-exchange pattern and reproduce it with tymon/jwt-auth or firebase/php-jwt.
Minimal steps (Symfony side):
composer require gfreeau/get-jwt-bundle
openssl genrsa -out app/var/jwt/private.pem -aes256 4096
openssl rsa -pubout -in app/var/jwt/private.pem -out app/var/jwt/public.pem
Register new Gfreeau\Bundle\GetJWTBundle\GfreeauGetJWTBundle() in AppKernel, then configure the firewall:
gfreeau_get_jwt:
private_key: "%kernel.root_dir%/var/jwt/private.pem"
public_key: "%kernel.root_dir%/var/jwt/public.pem"
pass_phrase: "%jwt_key_pass_phrase%"
ttl: 3600
user_identity_field: username
security:
firewalls:
login:
pattern: ^/api/login
stateless: true
form_login:
check_path: /api/login_check
success_handler: gfreeau_get_jwt.security.authentication_success_handler
Where to look first: the README's config block, then DependencyInjection/Configuration.php (the authoritative list of keys), then Security/AuthenticationSuccessHandler.php — that one class is the whole bundle. It signs a token containing the identity field plus iat/exp and writes it into the response.
First use case: POST /api/login_check with credentials returns a JWT; the client stores it and sends Authorization: Bearer <token> on subsequent calls. The Laravel equivalent is one route and three lines:
$token = auth('api')->attempt($request->only('email', 'password'));
return response()->json(['token' => $token]);
If you are starting fresh in Laravel, skip the bundle entirely: composer require tymon/jwt-auth, php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider", php artisan jwt:secret, set the api guard driver to jwt.
The bundle only issues tokens — it never verifies them. This is the single most important architectural fact. You are expected to write the other half: a stateless firewall with a custom authenticator (or, in modern Symfony, a Guard/authenticator) that parses the Authorization header, validates the signature against the public key, checks exp, and loads the user. In Laravel terms, the bundle gives you JWTAuth::fromUser() but not the auth:api middleware.
Issue/verify split, mapped to Laravel:
| Bundle concept | Symfony mechanism | Laravel analogue |
|---|---|---|
| Sign on login | success_handler on form_login |
auth()->attempt() / JWTAuth::fromUser() |
| TTL | gfreeau_get_jwt.ttl |
config/jwt.php → ttl |
| Identity claim | user_identity_field |
JWTSubject::getJWTIdentifier() |
| Verify incoming token | you write it | auth:api middleware + JWTGuard |
| Refresh | not provided | JWTAuth::refresh() |
Workflow tips:
stateless: true. If sessions are enabled, Symfony may still write a session cookie and you lose the benefit of the token.parameters.yml / env vars), never in the repo. The bundle reads them at container compile time, so a bad path fails at cache warmup — that's actually a useful early signal.user_identity_field to the same property your user provider uses to load users (username, email, or id). A mismatch produces valid-looking tokens that fail to resolve a user.Access-Control-Expose-Headers for browser clients, otherwise fetch/XHR cannot read it.JWTAuth::claims(['role' => $user->role])->fromUser($user), and a jwt.auth middleware group on API routes. Use auth:api with 'driver' => 'jwt' as the default guard to avoid mixing session and token auth.Integration tip: the service id gfreeau_get_jwt.security.authentication_success_handler is public-ish and can be decorated or replaced. Compiler-pass decoration is the clean way to add claims (tenant id, scopes) without forking — the same role a JWTCustomClaims implementation plays in tymon/jwt-auth.
namshi/jose, which is abandoned. It will not install on PHP 8 / Symfony 5+ without patching, and there are zero known dependents — you will be the only person debugging your fork.exp regardless of password changes or user deactivation. If you need logout/blacklist semantics, you must add a token store (cache or DB) and check it in your authenticator — the Laravel blacklist_enabled equivalent, which tymon/jwt-auth gives you and this bundle does not.nbf/exp strictly on the verifying side, set a small leeway (a few seconds) or you'll see intermittent 401s between app servers.#, :, or quotes breaks YAML parsing and you get an OpenSSL "bad decrypt" buried in a container compile error. Quote it explicitly and verify openssl rsa -in private.pem -check.www-data must be able to read the private key; 600 with the wrong owner yields a failed to open stream at the worst moment. Prefer a dedicated key directory excluded from your deploy diff.debug:firewall <name> plus the Symfony profiler's Security panel to see which authenticator ran. A 401 with a valid token is almost always the identity claim not matching the user provider.gfreeau_get_jwt.*, so you can inject the public key into your own verifier instead of duplicating the config read.tymon/jwt-auth covers issuance, verification, refresh, and blacklist, and Sanctum/Passport are better choices if you also need SPA cookie auth. The bundle is worth 20 minutes of reading as a clean example of "authenticate once, hand back a signed credential," and nothing more.How can I help you explore Laravel packages today?