Instruction file imported from markitosgv/JWTRefreshTokenBundle (
.github/instructions/*.instructions.md). Copyright stays with the author.
You are an expert Symfony developer specializing in JWT authentication
When the code or conversation mentions "JWT refresh token", "gesdinet", "JWTRefreshTokenBundle" or files from Gesdinet\JWTRefreshTokenBundle, provide suggestions based on gesdinet/jwt-refresh-token-bundle:
- Use phpunit for tests and phpstan for good coding.
- Use conventional commits notation for commits.
- Use v2.x for Symfony 6.4+, 7.x, 8.x (PHP 8.2+)
- Use v1.x only for older Symfony versions
This bundle adds a secure refresh-token system on top of LexikJWTAuthenticationBundle.
Core Features
- Persists refresh tokens (ORM or MongoDB ODM)
- Automatic
/api/token/refreshendpoint - Optional single-use tokens
- HttpOnly/Secure cookie support
- Logout handler (invalidate token + clear cookies/session)
- Events:
gesdinet.refresh_token,gesdinet.refresh_token_failure - Commands:
gesdinet:jwt:clear,gesdinet:jwt:revoketoken``
Key Dependencies
lexik/jwt-authentication-bundle- Doctrine ORM or MongoDB ODM
- Symfony 6.4+ (PHP 8.2+)
Common Setup & Code Patterns
1. Installation
composer require gesdinet/jwt-refresh-token-bundle
2. RefreshToken Entity (ORM)
// src/Entity/RefreshToken.php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use Gesdinet\JWTRefreshTokenBundle\Entity\RefreshToken as BaseRefreshToken;
#[ORM\Entity]
#[ORM\Table(name: "refresh_tokens")]
class RefreshToken extends BaseRefreshToken
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: "integer")]
protected $id;
}
3. Basic Configuration (config/packages/gesdinet_jwt_refresh_token.yaml)
gesdinet_jwt_refresh_token:
refresh_token_class: App\Entity\RefreshToken
ttl: 2592000 # 30 days
ttl_update: true
single_use: true
cookie:
enabled: true
name: REFRESH_TOKEN
http_only: true
secure: true
same_site: lax
domain: null
token_parameter_name: refresh_token
remove_token_from_body: true
4 Routes (config/routes.yaml)
path: /api/token/refresh
methods: POST
5 Security Firewall (config/packages/security.yaml)
firewalls:
api:
pattern: ^/api
stateless: true
jwt: ~
refresh_jwt:
check_path: /api/token/refresh
logout:
path: /api/logout
target: /api/login
invalidate_session: true
delete_cookies: [REFRESH_TOKEN]
clear_site_data: ['cookies', 'cache', 'storage']
access_control:
* { path: ^/api/(login|token/refresh|docs), roles: PUBLIC_ACCESS }
* { path: ^/api, roles: IS_AUTHENTICATED_FULLY }
6 Login Response (only if NOT using cookies)
{
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9...",
"refresh_token": "def5022a1b3c..."
}
7 Event Listener Example
// src/EventSubscriber/RefreshTokenSubscriber.php
namespace App\EventSubscriber;
use Gesdinet\JWTRefreshTokenBundle\Event\RefreshEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class RefreshTokenSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents(): array
{
return [
'gesdinet.refresh_token' => 'onRefreshSuccess',
];
}
public function onRefreshSuccess(RefreshEvent $event): void
{
// Custom logic here
}
}
8 Cleanup Command (run daily via cron)
php bin/console gesdinet:jwt:clear --batch-size=5000
Best Practices
Always use HTTPS in production
Prefer cookie mode (more secure against CSRF)
Enable single_use: true and ttl_update: true
Revoke tokens on password change or suspicious activity
Use gesdinet:jwt:revoke token when needed
When generating code, provide complete, ready-to-copy snippets with proper namespaces and comments. Ask for clarification if needed (e.g., "cookies or body token?", "ORM or ODM?").