
# 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.

```ts
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:

```ts
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 `CryptoKey`s |
| `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.

```ts
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.

```ts
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.

```ts
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`.

```ts
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.

```ts
isPrivateJWK(esPrivate); // true
isPrivateJWK(esPublic); // false
isPrivateJWK(hs); // false
```

::warning
`!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.

```ts
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`.

```ts
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 `CryptoKey`s.

```ts
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`.

```ts
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); // false
```

::note
`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`.

```ts
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](/utils/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.

```ts
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](/jwk/sets): picking keys out of a set by `kid`, `alg` and `use`.
- [JWKS endpoint example](/examples/jwks-endpoint): publishing only public keys.
- [Errors](/errors): every `JWTError` code.
