# Core concepts

> 

The JOSE specs (short for **J**avaScript **O**bject **S**igning and **E**ncryption) use several acronyms that sound alike. This page is the shortest explanation of each that still makes sense.

## JWT — JSON Web Token

A **JWT** is the end product: a small string that carries some JSON data, and is either signed or encrypted so the receiver can trust it.

```text
eyJhbGciOi...eyJzdWIiOi...SflKxwRJSM...
<header>   .<payload>   .<signature>
```

Three base64url segments joined by dots. That's the "Compact Serialization" — by far the most common form.

Defined in [RFC 7519](https://www.rfc-editor.org/rfc/rfc7519).

<note>

"JWT" specifically refers to tokens whose **payload is a JSON object of claims** (`sub`, `exp`, `iss`, …). The broader term for the same dotted format is **JOSE token**, and its payload can be any bytes. unjwt handles both — if you pass a `string` or `Uint8Array` to `sign()`/`encrypt()`, you get a valid JOSE token that isn't technically a JWT.

</note>

## JWS — JSON Web Signature

A **JWS** is a JWT whose payload is **signed**. Anyone can read the payload; only the holder of the signing key could have produced the signature.

Use when:

- You want the client to read the data (user ID, role, expiration) but trust it came from you.
- The data is non-sensitive.

Defined in [RFC 7515](https://www.rfc-editor.org/rfc/rfc7515). → [JWS in unjwt](/jwt/jws).

## JWE — JSON Web Encryption

A **JWE** is a JWT whose payload is **encrypted**. Only the holder of the decryption key can read the data. Anyone else sees opaque ciphertext.

Use when:

- The payload contains sensitive data the client (or intermediary) should not see.
- You want to keep the self-contained, signed-like shape of a JWT but with confidentiality.

Defined in [RFC 7516](https://www.rfc-editor.org/rfc/rfc7516). → [JWE in unjwt](/jwt/jwe).

<tip>

**JWS or JWE?** Signed is the default choice. Encrypt only when you have a concrete reason — encrypted tokens can't be inspected in logs, shown in DevTools, or debugged by the frontend team. You can also nest a JWS inside a JWE if you want to make sure the sender is trusted and the payload is confidential, but that's a more complex workflow.

</tip>

## JWK — JSON Web Key

A **JWK** is a way to represent a cryptographic key as a JSON object. Every signing, verifying, encrypting, and decrypting operation involves a key; a JWK is unjwt's preferred way to pass keys around.

```json
{
  "kty": "oct",
  "k": "GawgguFyGrWKav7AX4VKUg",
  "alg": "HS256",
  "kid": "4c3d..."
}
```

A **JWK Set** (or JWKS) is simply a JSON document with a `keys` array — typically published at `/.well-known/jwks.json` by OAuth/OIDC providers.

Defined in [RFC 7517](https://www.rfc-editor.org/rfc/rfc7517). → [JWK in unjwt](/jwk).

## How they fit together

```text
┌─────────────────────────────┐
      │             JWT             │  ← the portable string
      └──────────────┬──────────────┘
                     │
        ┌────────────┴────────────┐
        ▼                         ▼
       JWS                       JWE
     (signed)                (encrypted)
        │                         │
        └────────────┬────────────┘
                     ▼
                    JWK
  (the key used to produce or consume the above)
```

- A **JWT** is either a **JWS** or a **JWE**.
- Both use a **JWK** as the key (or a `CryptoKey` / password / raw bytes, which unjwt normalizes internally).

## The claims you'll actually see

Inside a JWT payload, certain claim names are standardized. unjwt validates the ones that affect security automatically:

<table>
<thead>
  <tr>
    <th>
      Claim
    </th>
    
    <th>
      Meaning
    </th>
    
    <th>
      Validated by <code>
        verify()
      </code>
      
       / <code>
        decrypt()
      </code>
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        iss
      </code>
    </td>
    
    <td>
      Issuer
    </td>
    
    <td>
      Only if <code>
        options.issuer
      </code>
      
       is set
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        sub
      </code>
    </td>
    
    <td>
      Subject
    </td>
    
    <td>
      Only if <code>
        options.subject
      </code>
      
       is set
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        aud
      </code>
    </td>
    
    <td>
      Audience
    </td>
    
    <td>
      Only if <code>
        options.audience
      </code>
      
       is set
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        exp
      </code>
    </td>
    
    <td>
      Expires at (seconds)
    </td>
    
    <td>
      Always — rejects expired tokens
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        nbf
      </code>
    </td>
    
    <td>
      Not before (seconds)
    </td>
    
    <td>
      Always — rejects tokens used before the specified time
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        iat
      </code>
    </td>
    
    <td>
      Issued at (seconds)
    </td>
    
    <td>
      Type-checked; used with <code>
        options.maxTokenAge
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        jti
      </code>
    </td>
    
    <td>
      JWT ID
    </td>
    
    <td>
      Not validated, but the H3 session adapters use it as a <code>
        session.id
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        typ
      </code>
    </td>
    
    <td>
      Type header (e.g. JWT)
    </td>
    
    <td>
      Only if <code>
        options.typ
      </code>
      
       is set
    </td>
  </tr>
</tbody>
</table>

Full definitions in [RFC 7519 §4.1](https://www.rfc-editor.org/rfc/rfc7519#section-4.1).

## What's an "algorithm"?

A JWS or JWE header always carries an `alg` field naming the algorithm the token was produced with. unjwt reads it back to choose the preferred verification path. The algorithms split into two families:

- **Signing algorithms** (`alg` in a JWS): HMAC (`HS*`), RSA (`RS*`, `PS*`), ECDSA (`ES*`), EdDSA (`Ed25519`). See [JWS algorithms](/jwt/jws/algorithms).
- **Key-management algorithms** (`alg` in a JWE): how the content key is delivered. Direct (`dir`), AES Key Wrap, RSA-OAEP, PBES2, ECDH-ES. See [JWE algorithms](/jwt/jwe/algorithms).

Plus one more for JWE only:

- **Content-encryption algorithms** (`enc` in a JWE): AES-GCM or AES-CBC+HMAC-SHA2 — the cipher that encrypts the actual payload.

When you pass a JWK that has an `alg` field set (which `generateJWK()` always does), unjwt uses it automatically. You only need to specify `alg`/`enc` explicitly when the key carries no such metadata (raw bytes, untyped JWKs, `CryptoKey` objects without algorithm hints).

Full algorithm registry: [RFC 7518](https://www.rfc-editor.org/rfc/rfc7518).

## Ready?

- If you want to start writing code: [Quickstart →](/getting-started/quickstart)
- If you want to understand the signed path: [JWS →](/jwt/jws)
- If you want the encrypted path: [JWE →](/jwt/jwe)
