BookStackApp/BookStack · error · OidcInvalidKeyException
Failed to load key from JWK parameters with error: {$excepti
Error message
Failed to load key from JWK parameters with error: {$exception->getMessage()} What it means
OidcJwtSigningKey::loadFromJwkArray builds an RSA public key from the JWK's 'e' and 'n' parameters via phpseclib's PublicKeyLoader. If phpseclib cannot construct a key from those parameters, it throws an OidcInvalidKeyException wrapping the underlying phpseclib message. This guards against malformed or unsupported JWK data rather than failing silently.
Source
Thrown at app/Access/Oidc/OidcJwtSigningKey.php:88
}
if (empty($jwk['e'])) {
throw new OidcInvalidKeyException('An "e" parameter on the provided key is expected');
}
if (empty($jwk['n'])) {
throw new OidcInvalidKeyException('A "n" parameter on the provided key is expected');
}
$n = strtr($jwk['n'], '-_', '+/');
try {
$key = PublicKeyLoader::load([
'e' => new BigInteger(base64_decode($jwk['e']), 256),
'n' => new BigInteger(base64_decode($n), 256),
]);
} catch (\Exception $exception) {
throw new OidcInvalidKeyException("Failed to load key from JWK parameters with error: {$exception->getMessage()}");
}
if (!$key instanceof RSA) {
throw new OidcInvalidKeyException('Key loaded from file path is not an RSA key as expected');
}
$this->key = $key->withPadding(RSA::SIGNATURE_PKCS1);
}
/**
* Use this key to sign the given content and return the signature.
*/
public function verify(string $content, string $signature): bool
{
return $this->key->verify($content, $signature);
}
/**View on GitHub (pinned to 18f8469a1c)
Solutions
- Inspect the wrapped phpseclib message for the exact parameter failure
- Verify the JWK contains non-empty 'e' and 'n' values and that they are valid base64url strings
- Re-fetch the JWKS from the issuer's /.well-known/jwks.json or jwks_uri and clear any stale cached keys
- Confirm the 'kty' of the JWK is RSA before passing it to OidcJwtSigningKey
Example fix
// before
$key = new OidcJwtSigningKey($jwk); // $jwk missing 'n'
// after
if (empty($jwk['n']) || empty($jwk['e'])) {
throw new \InvalidArgumentException('JWK is missing n/e parameters');
}
$key = new OidcJwtSigningKey($jwk); Defensive patterns
Strategy: try-catch
Validate before calling
function isValidRsaJwk(array $jwk): bool {
return ($jwk['kty'] ?? '') === 'RSA'
&& !empty($jwk['n']) && !empty($jwk['e'])
&& base64_decode($jwk['n'], true) !== false
&& base64_decode($jwk['e'], true) !== false;
} Type guard
function isRsaKey($key): bool { return $key instanceof \phpseclib3\Crypt\RSA; } Try / catch
try {
$key = new \BookStack\Access\Oidc\OidcJwtSigningKey($jwk);
} catch (\BookStack\Access\Oidc\OidcInvalidKeyException $e) {
logger()->warning('Invalid OIDC JWK: ' . $e->getMessage());
// skip key / refetch JWKS
} Prevention
- Validate kty/e/n presence and base64url decodability before constructing the key
- Refetch JWKS on key failure instead of relying on stale caches
- Log the wrapped phpseclib message to diagnose parameter issues quickly
When it happens
Trigger: Constructing OidcJwtSigningKey with a JWK array whose 'e' or 'n' is missing, empty, not valid base64, or does not decode into mathematically valid RSA modulus/exponent values.
Common situations: OIDC discovery/ JWKS endpoint returning truncated or corrupted keys; hand-copied JWK values; base64url vs standard base64 confusion; identity provider rotating keys and cache serving incomplete JWKS entries.
Related errors
- Failed to load key from file path with error: {$exception->g
- Key loaded from file path is not an RSA key as expected
- An "e" parameter on the provided key is expected
- A "n" parameter on the provided key is expected
- Unexpected type of key value provided
AI-assisted analysis of BookStackApp/BookStack@18f8469a1c (2026-09-02).
Data as JSON: /api/errors/210b669f5f0a3b0d.
Report an issue: GitHub.