---
title: jwt_issuer
sidebarTitle: jwt_issuer
---

# `fastmcp.server.auth.jwt_issuer`


JWT token issuance and verification for FastMCP OAuth Proxy.

This module implements the token factory pattern for OAuth proxies, where the proxy
issues its own JWT tokens to clients instead of forwarding upstream provider tokens.
This maintains proper OAuth 2.0 token audience boundaries.


## Functions

### `derive_jwt_key` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/auth/jwt_issuer.py#L25" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>

```python
derive_jwt_key(upstream_secret: str, server_salt: str) -> bytes
```


Derive JWT signing key from upstream client secret and server salt.

Uses HKDF (RFC 5869) to derive a cryptographically secure signing key from
the upstream OAuth client secret combined with a server-specific salt.

**Args:**
- `upstream_secret`: The OAuth client secret from upstream provider
- `server_salt`: Random salt unique to this server instance

**Returns:**
- 32-byte key suitable for HS256 JWT signing


### `derive_encryption_key` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/auth/jwt_issuer.py#L46" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>

```python
derive_encryption_key(upstream_secret: str) -> bytes
```


Derive Fernet encryption key from upstream client secret.

Uses HKDF to derive a cryptographically secure encryption key for
encrypting upstream tokens at rest.

**Args:**
- `upstream_secret`: The OAuth client secret from upstream provider

**Returns:**
- 32-byte Fernet key (base64url-encoded)


### `derive_key_from_secret` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/auth/jwt_issuer.py#L67" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>

```python
derive_key_from_secret(secret: str | bytes, salt: str, info: bytes) -> bytes
```


Derive 32-byte key from user-provided secret (string or bytes).

Accepts any length input and derives a proper cryptographic key.
Uses HKDF to stretch weak inputs into strong keys.

**Args:**
- `secret`: User-provided secret (any string or bytes)
- `salt`: Application-specific salt string
- `info`: Key purpose identifier

**Returns:**
- 32-byte key suitable for HS256 JWT signing or Fernet encryption


## Classes

### `JWTIssuer` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/auth/jwt_issuer.py#L90" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>


Issues and validates FastMCP-signed JWT tokens using HS256.

This issuer creates JWT tokens for MCP clients with proper audience claims,
maintaining OAuth 2.0 token boundaries. Tokens are signed with HS256 using
a key derived from the upstream client secret.


**Methods:**

#### `issue_access_token` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/auth/jwt_issuer.py#L116" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>

```python
issue_access_token(self, client_id: str, scopes: list[str], jti: str, expires_in: int = 3600) -> str
```

Issue a minimal FastMCP access token.

FastMCP tokens are reference tokens containing only the minimal claims
needed for validation and lookup. The JTI maps to the upstream token
which contains actual user identity and authorization data.

**Args:**
- `client_id`: MCP client ID
- `scopes`: Token scopes
- `jti`: Unique token identifier (maps to upstream token)
- `expires_in`: Token lifetime in seconds

**Returns:**
- Signed JWT token


#### `issue_refresh_token` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/auth/jwt_issuer.py#L163" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>

```python
issue_refresh_token(self, client_id: str, scopes: list[str], jti: str, expires_in: int) -> str
```

Issue a minimal FastMCP refresh token.

FastMCP refresh tokens are reference tokens containing only the minimal
claims needed for validation and lookup. The JTI maps to the upstream
token which contains actual user identity and authorization data.

**Args:**
- `client_id`: MCP client ID
- `scopes`: Token scopes
- `jti`: Unique token identifier (maps to upstream token)
- `expires_in`: Token lifetime in seconds (should match upstream refresh expiry)

**Returns:**
- Signed JWT token


#### `verify_token` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/auth/jwt_issuer.py#L211" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>

```python
verify_token(self, token: str) -> dict[str, Any]
```

Verify and decode a FastMCP token.

Validates JWT signature, expiration, issuer, and audience.

**Args:**
- `token`: JWT token to verify

**Returns:**
- Decoded token payload

**Raises:**
- `JoseError`: If token is invalid, expired, or has wrong claims


### `TokenEncryption` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/auth/jwt_issuer.py#L255" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>


Handles encryption/decryption of upstream OAuth tokens at rest.


**Methods:**

#### `encrypt` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/auth/jwt_issuer.py#L266" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>

```python
encrypt(self, token: str) -> bytes
```

Encrypt a token for storage.

**Args:**
- `token`: Plain text token

**Returns:**
- Encrypted token bytes


#### `decrypt` <sup><a href="https://github.com/jlowin/fastmcp/blob/main/src/fastmcp/server/auth/jwt_issuer.py#L277" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>

```python
decrypt(self, encrypted_token: bytes) -> str
```

Decrypt a token from storage.

**Args:**
- `encrypted_token`: Encrypted token bytes

**Returns:**
- Plain text token

**Raises:**
- `cryptography.fernet.InvalidToken`: If token is corrupted or key is wrong

