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

  1. Inspect the wrapped phpseclib message for the exact parameter failure
  2. Verify the JWK contains non-empty 'e' and 'n' values and that they are valid base64url strings
  3. Re-fetch the JWKS from the issuer's /.well-known/jwks.json or jwks_uri and clear any stale cached keys
  4. 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

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


AI-assisted analysis of BookStackApp/BookStack@18f8469a1c (2026-09-02). Data as JSON: /api/errors/210b669f5f0a3b0d. Report an issue: GitHub.