yasser-elgammal / green-auth-jwt
Optional JWT authentication adapter for Green Framework v3.0
Requires
- php: ^8.3
- doctrine/dbal: ^4.4
- firebase/php-jwt: ^7.0
- yasser-elgammal/green-core: ^3.0
Requires (Dev)
- phpunit/phpunit: ^12.5
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is not auto-updated.
Last update: 2026-10-25 13:25:34 UTC
README
An official, modular, and production-ready JWT (JSON Web Token) authentication adapter for the Green Framework.
This package implements Green Core's Authenticator contract, allowing you to easily add stateless token-based API authentication and optional database-backed refresh token rotation to your application.
Features
- Decoupled Architecture: Plugs into Green Core's authentication contracts (
Authenticator) without bloating the core framework. - Strict Cryptographic Security:
- Supports HMAC algorithms (
HS256,HS384,HS512) with minimum key length enforcement (32/48/64 bytes). - Supports RSA asymmetric pairs (
RS256,RS384,RS512) with minimum 2048-bit keys and verification-only support. - Enforces mandatory claims:
exp(expiration),iat(issued-at),nbf(not-before),iss(issuer), andaud(audience).
- Supports HMAC algorithms (
- Identity Resolution: Decoupled user retrieval via
IdentityResolverInterface(supports numeric IDs, UUIDs, or custom string IDs). - Optional Refresh Token Management:
- Secure SHA-256 hashed storage in database (raw tokens are never stored).
- Automatic token rotation with replay attack detection.
- Multi-device support (track and revoke per device).
- Schema publishing via CLI command (
jwt:install-refresh).
Requirements
- PHP:
^8.3 - Green Core:
^3.1 - OpenSSL Extension enabled
Installation
Install the package via Composer:
composer require yasser-elgammal/green-auth-jwt
Configuration
Add your JWT configuration in config/jwt.php:
<?php return [ /* |-------------------------------------------------------------------------- | Algorithm & Keys |-------------------------------------------------------------------------- | Supported: HS256, HS384, HS512, RS256, RS384, RS512 */ 'algorithm' => env('JWT_ALGO', 'HS256'), // For HMAC (minimum 32 characters for HS256): 'secret' => env('JWT_SECRET'), // For RSA (pass the key string or file path): 'signing_key' => env('JWT_PRIVATE_KEY'), 'verification_key' => env('JWT_PUBLIC_KEY'), /* |-------------------------------------------------------------------------- | Token Expiry & Claims |-------------------------------------------------------------------------- */ 'ttl' => (int) env('JWT_TTL', 3600), // Access token lifetime in seconds 'issuer' => env('APP_URL', 'https://example.com'), 'audience' => env('APP_URL', 'https://example.com'), /* |-------------------------------------------------------------------------- | Refresh Tokens (Optional) |-------------------------------------------------------------------------- */ 'refresh_ttl' => (int) env('JWT_REFRESH_TTL', 2592000), // 30 days 'refresh_owner_type' => 'user', ];
Quick Start
1. Implement the Identity Resolver
Create an adapter that tells the package how to resolve your application's user model from the token subject (sub):
namespace App\Auth; use App\Models\User; use YasserElgammal\Green\AuthJwt\IdentityResolverInterface; class UserIdentityResolver implements IdentityResolverInterface { public function findById(int|string $id): ?object { return User::query()->find($id); } }
2. Register Service Providers
In your application bootstrap or app/Providers/AuthServiceProvider.php:
use App\Auth\UserIdentityResolver; use YasserElgammal\Green\AuthJwt\IdentityResolverInterface; use YasserElgammal\Green\AuthJwt\JwtServiceProvider; // Bind the resolver $app->bind(IdentityResolverInterface::class, UserIdentityResolver::class); // Register the JWT provider $app->register(new JwtServiceProvider($app));
Usage
Issuing Access Tokens
Inject JwtService into your controller or login service:
use YasserElgammal\Green\AuthJwt\JwtService; class AuthController { public function login(Request $request, JwtService $jwt) { // 1. Validate credentials... $user = ...; // 2. Issue JWT Access Token $accessToken = $jwt->issueAccessToken( subject: (string) $user->id, customClaims: ['role' => $user->role] // optional ); return response()->json([ 'access_token' => $accessToken, 'token_type' => 'Bearer', 'expires_in' => 3600, ]); } }
Protecting API Routes
Use the standard Green Authenticate middleware configured with JwtAuthenticator:
use YasserElgammal\Green\AuthJwt\JwtAuthenticator; use YasserElgammal\Green\Http\Middleware\Authenticate; // In your router: $router->group(['middleware' => [new Authenticate(app(JwtAuthenticator::class), 'api')]], function ($router) { $router->get('/api/profile', [ProfileController::class, 'show']); });
Accessing Authenticated User
Inside protected routes, you can access the authenticated user through the request or the Auth facade:
// Via Request: $user = $request->user(); // Via Auth Facade: $user = \YasserElgammal\Green\Auth\Auth::user(); $userId = \YasserElgammal\Green\Auth\Auth::id();
Optional: Refresh Tokens & Rotation
If your application needs long-lived sessions with secure, rotatable refresh tokens:
1. Publish the Migration
Run the console command to copy the refresh tokens table migration into your project:
php green jwt:install-refresh
This creates a migration inside database/migrations/ creating the refresh_tokens table.
2. Run Migrations
php green migrate
3. Register the Refresh Token Service Provider
use YasserElgammal\Green\AuthJwt\Refresh\RefreshTokenServiceProvider; $app->register(new RefreshTokenServiceProvider($app));
4. Issue and Rotate Refresh Tokens
use YasserElgammal\Green\AuthJwt\Refresh\RefreshTokenManager; class TokenController { public function issue(RefreshTokenManager $refreshManager, $userId) { // Returns the unhashed raw token string to send to the client $rawRefreshToken = $refreshManager->issue( ownerId: (string) $userId, deviceName: 'iPhone 15 Pro' ); return $rawRefreshToken; } public function refresh(Request $request, RefreshTokenManager $refreshManager, JwtService $jwt) { $refreshToken = $request->input('refresh_token'); // Atomically rotates old token and issues a new one (mitigates replay attacks) $newRawToken = $refreshManager->rotate($refreshToken, 'iPhone 15 Pro'); if (!$newRawToken) { return response()->json(['error' => 'Invalid or expired refresh token'], 401); } $record = $refreshManager->verify($newRawToken); $newAccessToken = $jwt->issueAccessToken($record->modelableId); return response()->json([ 'access_token' => $newAccessToken, 'refresh_token' => $newRawToken, ]); } public function logout(Request $request, RefreshTokenManager $refreshManager) { $refreshManager->revoke($request->input('refresh_token')); return response()->json(['message' => 'Logged out successfully']); } }
Security Architecture
- No Plaintext Storage: Refresh tokens are stored strictly as
SHA-256hashes in the database. - Strict Claim Validation: Signature, expiration (
exp), not-before (nbf), issuer (iss), and audience (aud) are strictly verified on every request. - Separate Operations: Token signing and token verification use distinct paths, enabling public-key-only verification in microservices.
- Replay Protection: The moment a refresh token is rotated or verified as compromised, its family can be invalidated.
Migration from Green Core 3.0
If you are upgrading from previous versions where JWT was bundled in green-core:
- Update class imports:
YasserElgammal\Green\Security\Jwt\JwtConfig→YasserElgammal\Green\AuthJwt\JwtConfigYasserElgammal\Green\Security\Jwt\JwtService→YasserElgammal\Green\AuthJwt\JwtService
- Remove any legacy references to
users.refresh_tokendatabase columns. Use the newrefresh_tokenstable viaphp green jwt:install-refresh.
Testing
Run tests with PHPUnit:
composer test
License
This package is open-sourced software licensed under the MIT License.