Skip to content
ecdsa.com
jose (JavaScript)JWT libraries

Error message

Key for the ES256 algorithm must be one of type CryptoKey, KeyObject, or JSON Web Key.

Also printed as

  • Key for the RS256 algorithm must be one of type CryptoKey, KeyObject, or JSON Web Key.
  • Key for the HS256 algorithm must be one of type CryptoKey, KeyObject, JSON Web Key, or Uint8Array.

The algorithm in the sentence follows your token (`ES256`, `RS256`, `EdDSA`…). For HMAC algorithms the list ends with "or Uint8Array".

jose (JavaScript) — TypeError from jwtVerify / SignJWT.sign

What it means

You passed jose a value it cannot use as a key — most often a PEM string or a plain string secret. Since version 4, jose accepts only key objects: a WebCrypto `CryptoKey`, a Node.js `KeyObject`, a JWK object, or (for HMAC algorithms) a `Uint8Array`. Text formats must be imported first; jose never guesses what a string contains.

Why it happens

How to fix it

  1. 1.

    Import PEM keys once, at startup

    `importSPKI` and `importPKCS8` take the PEM text and the algorithm the key will be used with, and return a `CryptoKey` you can reuse for every request.

    js
    import { importSPKI, importPKCS8, jwtVerify } from "jose";
    
    const publicKey = await importSPKI(process.env.JWT_PUBLIC_KEY_PEM, "ES256");   // -----BEGIN PUBLIC KEY-----
    await jwtVerify(token, publicKey, { algorithms: ["ES256"] });
    
    const privateKey = await importPKCS8(process.env.JWT_PRIVATE_KEY_PEM, "ES256"); // -----BEGIN PRIVATE KEY-----
  2. 2.

    Encode HMAC secrets as bytes

    Use a random secret of at least 32 bytes for HS256 and pass it as a `Uint8Array`. If the secret is stored base64-encoded, decode it to bytes instead of encoding the base64 text.

    js
    import { jwtVerify } from "jose";
    
    const secret = new TextEncoder().encode(process.env.JWT_SECRET); // 32+ random bytes for HS256
    await jwtVerify(token, secret, { algorithms: ["HS256"] });
  3. 3.

    Verify against a key set, not a URL

    For tokens from an identity provider, pass the function returned by `createRemoteJWKSet`; jose then picks the key by `kid` for each token.

    js
    import { createRemoteJWKSet, jwtVerify } from "jose";
    
    const jwks = createRemoteJWKSet(new URL("https://issuer.example.com/.well-known/jwks.json"));
    await jwtVerify(token, jwks, { algorithms: ["ES256"] });

Related errors

Browse the full signature error database