DI and actions
The facade is the recommended default, not a requirement. There are three ways in, and all three reach the same code:
- The Jwt facade — shortest; recommended for most code.
- The manager, RoundlyConsulting\Jwt\JwtManager, injected through the constructor — it’s the facade root, a container singleton with exactly the same API, as an explicit dependency with no static calls.
- The contracts behind it. jwt is a stateless token codec plus a cache denylist, so instead of action classes it signs and verifies with container-bound service objects you can resolve directly.
Inject the manager
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use RoundlyConsulting\Jwt\JwtManager;
use RoundlyConsulting\Jwt\UserTokens\AccessTokenRequest;
final class TokenController
{
public function __construct(private JwtManager $jwt) {}
public function __invoke(Request $request): JsonResponse
{
$issued = $this->jwt->guard('users')->mintAccessToken(AccessTokenRequest::for($request->user()->id));
return response()->json(['access_token' => $issued->token]);
}
}Jwt::fake() swaps the manager behind the facade and in the container, so an injected JwtManager records too.
Resolve a contract
use RoundlyConsulting\Jwt\ServiceTokens\Contracts\ServiceTokenIssuer;
use RoundlyConsulting\Jwt\UserTokens\Contracts\UserTokenIssuer;
// The contracts behind it:
app(UserTokenIssuer::class)->mintAccessToken($request); // NativeUserTokenIssuer
app(ServiceTokenIssuer::class)->issue('billing'); // NativeServiceTokenServiceFacade method → contract
| Facade method | Contract | Default binding |
|---|---|---|
mintAccessToken() / mint() / mintChallengeToken() / mintEmailVerifyToken() | UserTokens\Contracts\UserTokenIssuer | NativeUserTokenIssuer |
verify() | UserTokens\Contracts\UserTokenVerifier | NativeUserTokenVerifier |
services()->issue() / request() / authenticate() | ServiceTokens\Contracts\ServiceTokenIssuer | NativeServiceTokenService |
services()->verify() | ServiceTokens\Contracts\ServiceTokenVerifier | NativeServiceTokenService |
denylist() / logout() / denyClaims() | Denylist\Contracts\Denylist | CacheDenylist |
A contract resolved directly bypasses the manager: it skips the TokenVerificationFailed event of Jwt::verify(), and Jwt::fake() doesn’t record it. Rebind any contract to change what the facade does — see Extending.
Show your open-source love
This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.
More ways to support, including cryptoBy donating, you agree to our donation terms.
Want this built into your product?
We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.