Type guards
Functions that answer "what is this value?" about keys, key sets, headers and errors, and narrow its TypeScript type when the answer is yes.
import { isPublicJWK } from "unjwt/utils";
const shareable = jwks.keys.filter(isPublicJWK);A JWK is a plain JSON object, so at runtime nothing tells a public key from a private one except the fields it carries. Each guard checks those fields and returns a boolean. In TypeScript it also narrows the value, so code after the check sees the precise key type.
The examples on this page use these keys:
import { generateJWK, generateKey } from "unjwt/jwk";
const hs = await generateJWK("HS256"); // { kty: "oct", k: "cSc6O7FW…", alg: "HS256", … }
const { privateKey: esPrivate, publicKey: esPublic } = await generateJWK("ES256");
const cryptoKey = await generateKey("HS256"); // CryptoKey
const pair = await generateKey("ES256"); // CryptoKeyPair#At a glance
| Guard | Narrows to | True when |
|---|---|---|
isJWK(value) | JWK | an object whose kty is oct, RSA, EC or OKP |
isJWKSet(value) | JWKSet | an object with a keys array |
isSymmetricJWK(value) | JWK_oct | a JWK with kty: "oct" and a string k |
isAsymmetricJWK(value) | Exclude<JWK, JWK_oct> | a JWK whose kty is RSA, EC or OKP |
isPrivateJWK(value) | JWK_Private | an RSA, EC or OKP JWK with a string d |
isPublicJWK(value) | JWK_Public | an RSA, EC or OKP JWK with its public fields and no d |
isCryptoKey(value) | CryptoKey | a Web Crypto key, from any realm |
isCryptoKeyPair(value) | CryptoKeyPair | an object whose publicKey and privateKey are both CryptoKeys |
assertCryptoKey(value) | asserts value is CryptoKey | throws ERR_JWK_INVALID otherwise |
isJWTContent(header) | boolean, no narrowing | typ names JWT, or cty names JSON |
isJWTError(error, code?) | JWTError | an unjwt error, optionally with that code |
isUnsecureError(error, code?) | UnsecureError | an error from the bundled unsecure primitives |
#isJWK
Recognises the four key types this library models. A JWK with any other kty is refused, so the narrowed type is always one unjwt can use.
isJWK(esPrivate); // true
isJWK({ kty: "foo" }); // false
isJWK("secret"); // false#isJWKSet
Checks the shape { keys: [...] } and nothing more. It does not look inside the array, so filter the entries with isJWK before you trust them.
isJWKSet({ keys: [esPublic] }); // true
isJWKSet({ keys: [] }); // true
isJWKSet(esPublic); // false#isSymmetricJWK
True for oct keys that carry k. Their k is the shared secret, so a symmetric JWK is always secret.
isSymmetricJWK(hs); // true
isSymmetricJWK(esPrivate); // false
isSymmetricJWK({ kty: "oct" }); // false#isAsymmetricJWK
True for an RSA, EC or OKP JWK, public or private. Anything that is not a JWK, and every oct key, is false.
isAsymmetricJWK(esPublic); // true
isAsymmetricJWK(esPrivate); // true
isAsymmetricJWK(hs); // false
isAsymmetricJWK("x"); // false#isPrivateJWK
True for an RSA, EC or OKP key with a d member. It returns false for every oct key, even though an oct key is secret.
isPrivateJWK(esPrivate); // true
isPrivateJWK(esPublic); // false
isPrivateJWK(hs); // falseWarning
!isPrivateJWK(key) is no "safe to publish" check, because it lets every oct key through. Use isPublicJWK to decide what goes into a JWKS endpoint.
#isPublicJWK
The "safe to publish" check. True only for an asymmetric key that has no d and carries the public fields its type needs: x and y for EC, x for OKP, n and e for RSA.
isPublicJWK(esPublic); // true
isPublicJWK(esPrivate); // false
isPublicJWK(hs); // false
isPublicJWK({ kty: "EC", crv: "P-256", x: "abc" }); // false, no y#isCryptoKey and assertCryptoKey
isCryptoKey reads Symbol.toStringTag, so it also recognises a CryptoKey created in another realm, such as a worker or an iframe. assertCryptoKey throws instead of returning false.
isCryptoKey(cryptoKey); // true
isCryptoKey(hs); // false
assertCryptoKey(cryptoKey); // passes
assertCryptoKey(hs);
// JWTError ERR_JWK_INVALID: CryptoKey instance expected#isCryptoKeyPair
True when both halves are present and both are CryptoKeys.
isCryptoKeyPair(pair); // true
isCryptoKeyPair({ publicKey: pair.publicKey }); // false#isJWTContent
Reports what a header claims the content is: true when typ contains jwt (case-insensitive) or cty is json, application/json or ends in +json. A non-string typ or cty returns false.
isJWTContent({ typ: "JWT" }); // true
isJWTContent({ typ: "at+jwt" }); // true
isJWTContent({ cty: "application/vnd.example+json" }); // true
isJWTContent({ typ: "text", cty: "text/plain" }); // false
isJWTContent(undefined); // falseNote
typ and cty are chosen by whoever made the token. verify() and decrypt() never read them to decide how to parse a payload; you declare that with the payload option. Treat isJWTContent as information, not as a security check.
#isJWTError
Narrows to JWTError. With a code, it also checks the code, and a misspelled code fails to type-check. For ERR_JWT_EXPIRED it also types cause as { jti?, iat?, exp? }. It is exported from unjwt, unjwt/jws, unjwt/jwe and unjwt/jwk, not from unjwt/utils.
import { isJWTError, verify } from "unjwt/jws";
const error = await verify("not.a.token", hs).catch((e) => e);
// JWTError ERR_JWS_INVALID: Invalid JWS: Protected header could not be decoded.
isJWTError(error); // true
isJWTError(error, "ERR_JWS_INVALID"); // true
isJWTError(error, "ERR_JWT_EXPIRED"); // false
isJWTError(new Error("x")); // false#isUnsecureError
Narrows to the UnsecureError class that unjwt bundles. The encoding helpers throw it directly, and a JWTError from a token that did not decode carries it as cause. Import the guard from unjwt: a class from your own unsecure install is a different class.
import { base64UrlParse, isUnsecureError } from "unjwt/utils";
isUnsecureError(error.cause, "MALFORMED"); // true, the error from above
isUnsecureError(error); // false, it is a JWTError
try {
base64UrlParse("a=");
} catch (e) {
isUnsecureError(e, "MALFORMED"); // true
}#See also
- JWK sets: picking keys out of a set by
kid,alganduse. - JWKS endpoint example: publishing only public keys.
- Errors: every
JWTErrorcode.