Metadata-Version: 2.4
Name: angel-verified-identity
Version: 0.1.0
Summary: Angel Verified Identity (AVI) — WSGI/ASGI middleware that verifies the Angel-Identity assertion and rewrites the identity headers from verified claims
Keywords: angel,identity,ed25519,wsgi,asgi,middleware
Classifier: License :: Other/Proprietary License
Classifier: Programming Language :: Python :: 3
Requires-Python: >=3.9
Description-Content-Type: text/markdown
Requires-Dist: cryptography>=41
Provides-Extra: test
Requires-Dist: pytest>=7; extra == "test"

# angel-verified-identity

WSGI and ASGI middleware for Angel apps. It verifies the signed
`Angel-Identity` assertion on each request, then sets the identity headers
from the verified claims. Flask, Django, FastAPI and Starlette share one
implementation.

## Install

```
pip install angel-verified-identity
```

Python 3.9 or later. The only dependency is `cryptography`.

## Use

Wrap the whole application, not single routes, so that static files are
covered too.

Flask (WSGI):

```python
from flask import Flask, request
from angel_verified_identity import AngelIdentityWSGIMiddleware

app = Flask(__name__)
app.wsgi_app = AngelIdentityWSGIMiddleware(app.wsgi_app, probe_path="/admin")

@app.get("/admin")
def admin():
    return {"who": request.headers["X-Forwarded-Email"]}
```

FastAPI (ASGI):

```python
from fastapi import FastAPI, Request
from angel_verified_identity import AngelIdentityASGIMiddleware

app = FastAPI()
app.add_middleware(AngelIdentityASGIMiddleware, probe_path="/admin")

@app.get("/admin")
def admin(request: Request):
    return {"who": request.headers["x-forwarded-email"]}
```

The verified claims are also in `environ["angel.identity"]` (WSGI) and
`scope["state"]["angel_identity"]` (ASGI).

## What it does

1. Refuses a request that another app replayed to this app, with 403. It does
   this in both modes.
2. Verifies the `Angel-Identity` header: a compact JWS, EdDSA over Ed25519.
3. If the header is valid, deletes `x-forwarded-email`, `x-forwarded-user`,
   `x-email` and `x-user`, then sets them from the verified claims. It also
   sets `x-angel-principal-type`.
4. If the header is not valid:
   - in `enforce` mode, deletes those headers and answers 401;
   - in `observe` mode, logs the failure and passes the request on unchanged.
     The headers are then not verified. Use `observe` only during a rollout.

## Configuration

The middleware reads these environment variables.

| Variable                  | Meaning                                                                            |
| ------------------------- | ---------------------------------------------------------------------------------- |
| `FLY_APP_NAME`            | The app's name, and the expected audience. Verification fails without it.          |
| `AVI_PUBLIC_KEYS`         | The JWKS document with the public keys. Read first.                                |
| `AVI_JWKS_URL`            | JWKS URL, fetched only for an unknown key id. Cached for 5 minutes.                |
| `ANGEL_CONTROL_PLANE_URL` | Alternative to `AVI_JWKS_URL`. The library adds `/.well-known/avi-jwks.json`.      |
| `ANGEL_IDENTITY_MODE`     | `observe` or `enforce`. The default is `enforce`.                                  |
| `ANGEL_WORKLOAD_ALLOW`    | Comma-separated `sub` values that can call as `typ: "workload"`.                   |
| `ANGEL_ALLOW_REPLAY`      | `1` turns off the replay check. Use it only if the app replays requests to itself. |

## Attestation endpoint

`GET /_angel/avi-attestation` returns the declared probe path and the current
mode. It answers 503 if no probe path is declared. Declare it with the
`probe_path` argument, or call `register_probe_path("/admin")` before the app
serves a request.
