BookStackApp/BookStack · error · OidcInvalidKeyException
Only RS256 keys are currently supported. Found key using {$a
Error message
Only RS256 keys are currently supported. Found key using {$alg} What it means
loadFromJwkArray validates that the JWK is an RSA signing key usable with RS256. It throws when kty is not 'RSA', or when an 'alg' is present and is anything other than 'RS256' (null alg is tolerated and presumed compatible).
Source
Thrown at app/Access/Oidc/OidcJwtSigningKey.php:62
}
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);
}
/**
* @throws OidcInvalidKeyException
*/
protected function loadFromJwkArray(array $jwk): void
{
// 'alg' is optional for a JWK, but we will still attempt to validate if
// it exists otherwise presume it will be compatible.
$alg = $jwk['alg'] ?? null;
if ($jwk['kty'] !== 'RSA' || !(is_null($alg) || $alg === 'RS256')) {
throw new OidcInvalidKeyException("Only RS256 keys are currently supported. Found key using {$alg}");
}
// 'use' is optional for a JWK but we assume 'sig' where no value exists since that's what
// the OIDC discovery spec infers since 'sig' MUST be set if encryption keys come into play.
$use = $jwk['use'] ?? 'sig';
if ($use !== 'sig') {
throw new OidcInvalidKeyException("Only signature keys are currently supported. Found key for use {$jwk['use']}");
}
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'], '-_', '+/');View on GitHub (pinned to 18f8469a1c)
Solutions
- Select a JWK from the JWKS with kty=RSA (and alg RS256 or absent)
- Reconfigure the IdP to sign with RS256
- Filter discovery keys: only use entries where kty === 'RSA' and (alg is null or 'RS256')
- If you control the JWK fixture for tests, add/set 'kty' => 'RSA'
Example fix
// before $jwk = $jwks['keys'][0]; // may be EC // after $jwk = current(array_filter($jwks['keys'], fn($k) => ($k['kty'] ?? null) === 'RSA' && in_array($k['alg'] ?? null, [null, 'RS256'], true)));
Defensive patterns
Strategy: validation
Validate before calling
if (($jwk['kty'] ?? null) !== 'RSA' || !in_array($jwk['alg'] ?? null, [null, 'RS256'], true)) { throw new \RuntimeException('JWK is not RS256/RSA'); } Type guard
function isRs256Jwk(array $jwk): bool { return ($jwk['kty'] ?? null) === 'RSA' && in_array($jwk['alg'] ?? null, [null, 'RS256'], true); } Try / catch
try { $key = new OidcJwtSigningKey($jwk); } catch (OidcInvalidKeyException $e) { if (str_contains($e->getMessage(), 'Only RS256')) { /* select another key or fix IdP alg */ } throw $e; } Prevention
- Filter jwks_uri keys by kty=RSA and alg RS256/absent before constructing
- Confirm IdP signing algorithm is RS256 in discovery metadata (id_token_signing_alg_values_supported)
- Match kid from the token header to the correct JWK instead of taking keys[0]
When it happens
Trigger: new OidcJwtSigningKey($jwkArray) where $jwkArray['kty'] !== 'RSA' (e.g. 'EC', 'OKP', 'oct') or $jwkArray['alg'] is 'ES256', 'RS384', 'RS512', etc.
Common situations: IdP configured for ES256/RS512 while the app only supports RS256; jwks_uri returns EC keys; picking the wrong key from a multi-key JWKS; missing 'kty' key in the JWK array.
Related errors
- Token audience value has ' . count($aud) . ' values, Expecte
- Token authorized party exists but does not match the expecte
- Missing token expiration time value
- Token has expired
- Missing token issued at time value
AI-assisted analysis of BookStackApp/BookStack@18f8469a1c (2026-09-02).
Data as JSON: /api/errors/7f793ee0a8e5fea1.
Report an issue: GitHub.