Metadata-Version: 2.5
Name: fastapi-enciphers
Version: 3.0.0
Summary: Encrypted session middleware for FastAPI using enciphers
Project-URL: Homepage, https://github.com/mjlad/fastapi-enciphers
Author: Mejlad Alsubaie
License: Apache-2.0
License-File: LICENSE
Keywords: enciphers,encryption,fastapi,session,starlette
Classifier: Development Status :: 4 - Beta
Classifier: Framework :: FastAPI
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Security :: Cryptography
Requires-Python: >=3.11
Requires-Dist: enciphers<4,>=3
Requires-Dist: itsdangerous>=2.1
Requires-Dist: orjson
Requires-Dist: starlette<2,>=1.0
Provides-Extra: test
Requires-Dist: fastapi; extra == 'test'
Requires-Dist: httpx; extra == 'test'
Requires-Dist: pytest>=8; extra == 'test'
Description-Content-Type: text/markdown

# fastapi-enciphers

Encrypted session middleware for FastAPI using [enciphers](https://pypi.org/project/enciphers/).

Replaces Starlette's default signed cookie session with a fully encrypted one.

> **Version 3.0** supports `enciphers>=3,<4`. Existing 2.x session
> cookies remain readable with the same key and backend. See
> [Upgrading from 2.x](#upgrading-from-2x) and [CHANGELOG.md](CHANGELOG.md).

## Installation

```bash
python -m pip install "fastapi-enciphers>=3,<4" fastapi
```

Requires Python 3.11+ and Starlette 1.x. When upgrading an existing
FastAPI application, use a FastAPI version that supports Starlette 1.x.

## Usage

Generate a key once, store it securely, and configure `CIPHER_KEY` with
its decimal value. Reuse this value across restarts and workers:

```bash
python -c 'import secrets; print(secrets.randbits(128))'
```

```python
from fastapi import FastAPI, Request
from fastapi_enciphers import EnciphersMiddleware
from enciphers import Backend

app = FastAPI()
app.add_middleware(
    EnciphersMiddleware, backend=Backend.AES256_GCM, key_env="CIPHER_KEY"
)

@app.get("/login")
async def login(request: Request):
    request.session["user_id"] = 1
    return {"status": "logged in"}

@app.get("/profile")
async def profile(request: Request):
    return {"user_id": request.session.get("user_id")}
```

## Configuration

| Parameter | Type | Default | Description |
|---|---|---|---|
| `backend` | `Backend` | `Backend.AES256_GCM` | `Backend.AES256_GCM` or `Backend.XCHACHA20_POLY1305` |
| `key` | `int` or `None` | random if neither key source is provided | Secret key, a random 128-bit value; mutually exclusive with `key_env` |
| `key_env` | `str` or `None` | None | Name of the environment variable containing the key as a decimal integer |
| `session_cookie` | `str` | `"session"` | Cookie name |
| `max_age` | `int` or `None` | 1209600 | Cookie lifetime in seconds; `None` or `0` disables expiry |
| `path` | `str` | `"/"` | Cookie path |
| `same_site` | `str` | `"lax"` | SameSite flag |
| `https_only` | `bool` | `False` | Secure flag |
| `domain` | `str` | None | Cookie domain |

> If neither `key` nor `key_env` is provided, a random 128-bit value is generated at
> startup — fine for local development, but every process in a real
> deployment needs to share the same key, or sessions won't be
> portable between them.

> **Warning:** Do not use `EnciphersMiddleware` together with Starlette's `SessionMiddleware`.

## Session expiry

With a positive `max_age` (the default), every session token carries
an authenticated expiry timestamp in its metadata. The timestamp is
visible but cannot be changed without invalidating the token. Decryption
enforces this expiry even if a client ignores the cookie's `Max-Age`
attribute. Setting `max_age=None` removes both; `max_age=0` retains
the same behavior for compatibility. Both pass `expires_at=None` to
`enciphers`, because version 3 rejects an explicit zero timestamp.

## Upgrading from 2.x

- Upgrade to `fastapi-enciphers>=3,<4`, which requires `enciphers>=3,<4`
  and Starlette 1.x. The middleware constructor is unchanged.
- Keep the same key and backend: existing 2.x cookies, including
  non-expiring cookies, remain readable. No forced logout or key
  rotation is required by this migration.
- `max_age` is a duration in seconds; `expires_at` in `enciphers` is an
  absolute Unix timestamp. Existing middleware configurations with
  `max_age=None` or `0` continue working. Any application code calling
  `cipher.encrypt(..., expires_at=0)` directly must use `None` instead.
- Upgrade every worker to pick up the upstream nonce-generation fix
  for ciphers initialized before `fork`.

Cookies from 0.1.x remain incompatible and start a fresh, empty session.

## Development

```bash
python -m pip install -e '.[test]'
python -m pytest
```

Tests cover both encryption backends, existing 2.x cookies, expiry,
invalid tokens, key configuration, HTTP sessions, and WebSockets.

## License

Apache-2.0 — Copyright 2026 Mejlad Alsubaie
