{"record":{"id":"ee2f7588b7830913","repo":"tymondesigns/jwt-auth","slug":"public-key-is-not-set","errorCode":null,"errorMessage":"Public key is not set.","messagePattern":"Public key is not set\\.","errorType":"exception","errorClass":"Tymon\\JWTAuth\\Exceptions\\JWTException","httpStatus":null,"severity":"critical","filePath":"src/Providers/JWT/Lcobucci.php","lineNumber":255,"sourceCode":"        if (! $secret = $this->getSecret()) {\n            throw new JWTException('Secret is not set.');\n        }\n\n        return $this->getKey($secret);\n    }\n\n    /**\n     * {@inheritdoc}\n     *\n     * @return \\Lcobucci\\JWT\\Signer\\Key\n     *\n     * @throws \\Tymon\\JWTAuth\\Exceptions\\JWTException\n     */\n    protected function getVerificationKey()\n    {\n        if ($this->isAsymmetric()) {\n            if (! $public = $this->getPublicKey()) {\n                throw new JWTException('Public key is not set.');\n            }\n\n            return $this->getKey($public);\n        }\n\n        if (! $secret = $this->getSecret()) {\n            throw new JWTException('Secret is not set.');\n        }\n\n        return $this->getKey($secret);\n    }\n\n    /**\n     * Get the signing key instance.\n     */\n    protected function getKey(string $contents, string $passphrase = ''): Key\n    {\n        return InMemory::plainText($contents, $passphrase);","sourceCodeStart":237,"sourceCodeEnd":273,"githubUrl":"https://github.com/tymondesigns/jwt-auth/blob/6c70930a92710d97e8e52b182fca2176097f33be/src/Providers/JWT/Lcobucci.php#L237-L273","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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.'"],"exampleFix":"# before\nJWT_ALGO=RS256\nJWT_PRIVATE_KEY=\"-----BEGIN PRIVATE KEY-----\\n...\\n-----END PRIVATE KEY-----\\n\"\n# JWT_PUBLIC_KEY unset -> JWTException: Public key is not set.\n\n# after\nopenssl rsa -in private.pem -pubout -out public.pem\nJWT_PUBLIC_KEY=\"-----BEGIN PUBLIC KEY-----\\nMIIBIjANBg...\\n-----END PUBLIC KEY-----\\n\"","handlingStrategy":"validation","validationCode":"// Boot guard: asymmetric algorithms need BOTH keys at construction time\npublic function boot(): void\n{\n    $asymmetric = in_array(config('jwt.algo'), ['RS256', 'RS384', 'RS512', 'ES256', 'ES384', 'ES512'], true);\n\n    if ($asymmetric && empty(config('jwt.keys.public'))) {\n        throw new RuntimeException('jwt.keys.public must be set for algorithm '.config('jwt.algo'));\n    }\n}","typeGuard":"function hasCompleteAsymmetricKeys(array $keysConfig): bool\n{\n    return !empty($keysConfig['private'])\n        && !empty($keysConfig['public'])\n        && openssl_pkey_get_public($keysConfig['public']) !== false;\n}","tryCatchPattern":"use Tymon\\JWTAuth\\Exceptions\\JWTException;\n\ntry {\n    auth('api')->parseToken()->authenticate();\n} catch (JWTException $e) {\n    // 'Public key is not set.' fires at service construction - treat as 500 config failure\n    report($e);\n    abort(500, 'token service unavailable');\n}","preventionTips":["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"],"tags":["jwt","php","laravel","configuration","rsa","ecdsa","missing-key"],"backgroundTag":"jwt-missing-signing-key","analyzedSha":"6c70930a92710d97e8e52b182fca2176097f33be","analyzedAt":"2026-08-21T02:16:37.040Z","schemaVersion":2},"datasetVersion":"2026-08-21T03:17:12.404Z"}