BookStackApp/BookStack · error · OidcInvalidKeyException

Failed to load key from JWK parameters with error

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.

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.

Appendix: 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)