tymondesigns/jwt-auth · critical · Tymon\JWTAuth\Exceptions\JWTException
Public key is not set.
Error message
Public key is not set.
What it means
Thrown by getVerificationKey() when an asymmetric algorithm (RS*/ES*) is configured but the keys.public config value is null or empty. The provider needs the public key at construction time both to build the SignedWith validation constraint and for verification, so the service fails to instantiate on first use.
Source
Thrown at src/Providers/JWT/Lcobucci.php:255
if (! $secret = $this->getSecret()) {
throw new JWTException('Secret is not set.');
}
return $this->getKey($secret);
}
/**
* {@inheritdoc}
*
* @return \Lcobucci\JWT\Signer\Key
*
* @throws \Tymon\JWTAuth\Exceptions\JWTException
*/
protected function getVerificationKey()
{
if ($this->isAsymmetric()) {
if (! $public = $this->getPublicKey()) {
throw new JWTException('Public key is not set.');
}
return $this->getKey($public);
}
if (! $secret = $this->getSecret()) {
throw new JWTException('Secret is not set.');
}
return $this->getKey($secret);
}
/**
* Get the signing key instance.
*/
protected function getKey(string $contents, string $passphrase = ''): Key
{
return InMemory::plainText($contents, $passphrase);View on GitHub (pinned to 6c70930a92)
Solutions
- Export the public half of your existing private key: openssl rsa -in private.pem -pubout -out public.pem (use -passin if encrypted)
- Set JWT_PUBLIC_KEY in every environment, value being the full PEM contents including BEGIN/END lines
- Run php artisan config:clear to drop the stale cached config, then re-cache if used
- Make sure the public key belongs to the same pair as JWT_PRIVATE_KEY, otherwise signing works but every verification fails with 'Token Signature could not be verified.'
Example fix
# before JWT_ALGO=RS256 JWT_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n" # JWT_PUBLIC_KEY unset -> JWTException: Public key is not set. # after openssl rsa -in private.pem -pubout -out public.pem JWT_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----\nMIIBIjANBg...\n-----END PUBLIC KEY-----\n"
Defensive patterns
Strategy: validation
Validate before calling
// Boot guard: asymmetric algorithms need BOTH keys at construction time
public function boot(): void
{
$asymmetric = in_array(config('jwt.algo'), ['RS256', 'RS384', 'RS512', 'ES256', 'ES384', 'ES512'], true);
if ($asymmetric && empty(config('jwt.keys.public'))) {
throw new RuntimeException('jwt.keys.public must be set for algorithm '.config('jwt.algo'));
}
} Type guard
function hasCompleteAsymmetricKeys(array $keysConfig): bool
{
return !empty($keysConfig['private'])
&& !empty($keysConfig['public'])
&& openssl_pkey_get_public($keysConfig['public']) !== false;
} Try / catch
use Tymon\JWTAuth\Exceptions\JWTException;
try {
auth('api')->parseToken()->authenticate();
} catch (JWTException $e) {
// 'Public key is not set.' fires at service construction - treat as 500 config failure
report($e);
abort(500, 'token service unavailable');
} Prevention
- Deploy public and private keys together from the same key pair; verify with openssl rsa -pubout that the public half matches
- Assert both keys exist in every environment in a deploy-time smoke test that resolves the auth service
- Remember creation also fails without the public key (the constructor registers the SignedWith constraint) - do not skip it on token-issuing-only services
When it happens
Trigger: JWT_ALGO set to RS256/RS384/RS512/ES256/ES384/ES512 with JWT_PUBLIC_KEY unset or empty. Even token *creation* fails, because buildConfig() registers a SignedWith(verification) constraint at construction; triggered by auth('api') resolution, auth:api middleware, or resolving 'tymon.jwt' from the container.
Common situations: Developer sets only JWT_PRIVATE_KEY (assuming verification needs nothing), deploys with the private key but forgets the public key in production secrets, or regenerates the key pair and only updates one side; config cached before the env value existed.
Related errors
- Private key is not set.
- Could not create token: {exception message}
- Token Signature could not be verified.
- The given algorithm could not be found
- Secret is not set.
AI-assisted analysis of tymondesigns/jwt-auth@6c70930a92 (2026-08-21).
Data as JSON: /api/errors/ee2f7588b7830913.
Report an issue: GitHub.