jwt-authentication
Skill35 starsChanged 5 months agoMITView on GitHub
Use when implementing stateless JWT authentication in Symfony with LexikJWTAuthenticationBundle — login endpoint, token issuance, refresh tokens, custom claims, and the JWT firewall. Use when the task mentions JWT, bearer token, access token, refresh token, or stateless API auth.
- Reads credentials
1 other project uses a version of this.
Adopted by
Most recently taken up by
- cohen-liel/hivemindtheir own version, first seen Mar 17, 2026
Other versions1 project uses a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 1
- cohen-liel/hivemind★ 108same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository16 other files it gives its agents.
- api-platform-resourcesSkill
- bundle-organizationSkill
- console-commandsSkill
- doctrine-migrationsSkill
- doctrine-ormSkill
- doctrine-query-optimizationSkill
- domain-driven-designSkill
- dto-and-validationSkill
- hexagonal-architectureSkill
- layered-architectureSkill
- messenger-asyncSkill
- problem-details-rfc9457Skill
- rest-controller-conventionsSkill
- security-authenticationSkill
- testing-pyramidSkill
- transactional-patternsSkill
The file
We split this skill into the rules it gives an agent
4 rules. About 0 are common, 3 are distinctive, and 1 is very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈3
Distinctive · estimate
This author's own choices. Worth a look.
1
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 90
Add claims (e.g. roles, tenant) at issuance via an event listener.
- Distinctive: line 109
Keep the payload small and never put secrets or sensitive data in a JWT — it's base64, not encrypted.
- Distinctive: line 123
Always serve over HTTPS — a bearer token on plain HTTP is a credential in the clear.
- Very specific: line 126
Don't store JWTs in localStorage for browser SPAs if you can use an HttpOnly cookie (XSS risk).
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
JWT Authentication (Symfony + LexikJWTAuthenticationBundle)
Setup
composer require lexik/jwt-authentication-bundle
php bin/console lexik:jwt:generate-keypair # writes config/jwt/private.pem + public.pem# config/packages/lexik_jwt_authentication.yaml
lexik_jwt_authentication:
secret_key: '%env(resolve:JWT_SECRET_KEY)%'
public_key: '%env(resolve:JWT_PUBLIC_KEY)%'
pass_phrase: '%env(JWT_PASSPHRASE)%'
token_ttl: 900 # 15 min access token — keep it SHORTThe private key and passphrase are secrets — store them in the Symfony secrets vault, never commit them.
Firewall
# config/packages/security.yaml
security:
firewalls:
login:
pattern: ^/api/login$
stateless: true
json_login:
check_path: /api/login # POST { "email": "...", "password": "..." }
username_path: email
password_path: password
success_handler: lexik_jwt_authentication.handler.authentication_success
failure_handler: lexik_jwt_authentication.handler.authentication_failure
api:
pattern: ^/api
stateless: true
jwt: ~ # validates the Bearer token
access_control:
- { path: ^/api/login, roles: PUBLIC_ACCESS }
- { path: ^/api/token/refresh, roles: PUBLIC_ACCESS }
- { path: ^/api, roles: ROLE_USER }json_login + the success handler issues the token; the jwt authenticator validates it on every subsequent request. Both firewalls are stateless — no session.
Login response
POST /api/login with credentials returns:
{ "token": "eyJ0eXAiOiJKV1Qi..." }The client sends it back as Authorization: Bearer <token> on protected requests.
Short access tokens + refresh tokens
Access tokens must be short-lived (5–15 min) because they can't be revoked before expiry. For "stay logged in", pair them with a refresh token (gesdinet/jwt-refresh-token-bundle):
# config/packages/gesdinet_jwt_refresh_token.yaml
gesdinet_jwt_refresh_token:
refresh_token_class: App\Entity\RefreshToken
ttl: 2592000 # 30 days
single_use: true # rotate on each refresh# routes
api_refresh_token:
path: /api/token/refresh
controller: gesdinet.jwtrefreshtoken::refreshRefresh tokens are stored server-side, so they can be revoked (logout, password change, compromise). single_use: true rotates them, limiting replay.
Custom claims
Add claims (e.g. roles, tenant) at issuance via an event listener; read them later without a DB hit.
#[AsEventListener(event: Events::JWT_CREATED)]
final class JwtCreatedListener
{
public function __invoke(JWTCreatedEvent $event): void
{
$user = $event->getUser();
if ($user instanceof User) {
$payload = $event->getData();
$payload['uid'] = $user->getId()->toRfc4122();
$payload['tenant'] = $user->getTenantId();
$event->setData($payload);
}
}
}Keep the payload small and never put secrets or sensitive data in a JWT — it's base64, not encrypted; anyone holding it can read it.
Reading the authenticated user
#[Route('/api/me', methods: ['GET'])]
public function me(#[CurrentUser] User $user): JsonResponse
{
return $this->json(UserResponse::fromEntity($user));
}Security conventions
- Always serve over HTTPS — a bearer token on plain HTTP is a credential in the clear.
- Short access TTL + rotating refresh tokens; revoke refresh tokens on logout/password change.
- Validate
exp/iat(Lexik does this) — don't disable signature/expiry checks. - Don't store JWTs in
localStoragefor browser SPAs if you can use anHttpOnlycookie (XSS risk). - 401 on missing/expired/invalid token; 403 on valid token lacking the role.
Gotchas
- Agent sets a long access-token TTL (hours/days) — keep access tokens short; use refresh tokens for longevity.
- Agent uses only access tokens and tries to "revoke" them — access JWTs can't be revoked before expiry; use server-side refresh tokens.
- Agent puts sensitive data in the JWT payload — it's readable; only put non-secret claims.
- Agent commits
private.pem/ the passphrase — store keys/passphrase in the secrets vault. - Agent makes the JWT firewall stateful — set
stateless: true. - Agent reads the user from a request field instead of
#[CurrentUser]/ the token. - Agent disables expiry or signature validation to "make it work" — never bypass token validation.
- Agent uses Lexik 2.x annotation/config — use the current bundle config +
json_login(Symfony 7).
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

