Metadata-Version: 2.4
Name: goassay-provider-verify
Version: 0.1.0
Summary: Verify Assay Ed25519 request signatures (RFC 9421 style) with ASGI and WSGI middleware.
License-Expression: MIT
Project-URL: Homepage, https://github.com/assay-ai/assay-provider-verify
Project-URL: Source, https://github.com/assay-ai/assay-provider-verify/tree/main/python
Project-URL: Issues, https://github.com/assay-ai/assay-provider-verify/issues
Classifier: Programming Language :: Python :: 3
Classifier: Typing :: Typed
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: cryptography>=42
Provides-Extra: redis
Requires-Dist: redis>=5; extra == "redis"
Provides-Extra: fastapi
Requires-Dist: fastapi>=0.110; extra == "fastapi"
Provides-Extra: flask
Requires-Dist: flask>=3; extra == "flask"
Provides-Extra: test
Requires-Dist: pytest==9.1.1; extra == "test"
Requires-Dist: httpx==0.28.1; extra == "test"
Requires-Dist: fastapi==0.142.2; extra == "test"
Requires-Dist: starlette==1.7.0; extra == "test"
Requires-Dist: flask==3.1.3; extra == "test"
Requires-Dist: werkzeug==3.1.9; extra == "test"
Dynamic: license-file

# goassay-provider-verify

Verify that a request to your API really comes from [Assay](https://goassay.ai). Assay signs every
request it forwards with an Ed25519 signature (HTTP Message Signatures, RFC 9421 style); this package
checks it with ASGI and WSGI middleware. The only runtime dependency is `cryptography`. Python 3.9 or
newer (the test suite runs on 3.10+).

```sh
pip install goassay-provider-verify
```

Rejected requests get `401`, `content-type: application/json` and
`{"error":"invalid_assay_signature","reason":"<CODE>"}`. Accepted requests reach your app unchanged,
with the full body still readable.

## One line per framework

```python
from goassay_provider_verify import AssayVerifyMiddleware, AssayVerifyWSGI

app.add_middleware(AssayVerifyMiddleware)                # FastAPI / Starlette (ASGI)
app.wsgi_app = AssayVerifyWSGI(app.wsgi_app)             # Flask (WSGI)
application = AssayVerifyWSGI(get_wsgi_application())    # Django, in wsgi.py
```

Without a framework, call the verifier yourself:

```python
from goassay_provider_verify import Verifier

result = Verifier().verify(method, url, headers, body)  # url = path + query; body = raw bytes or None
if not result.ok:
    print(result.reason)
```

Testnet keys: pass `environment="testnet"`. Behind a proxy that rewrites `Host`, pass
`authority="api.example.com"`. With more than one instance, use `redis_replay_store`
(`pip install "goassay-provider-verify[redis]"`).

## Documentation

- [Python guide](https://github.com/assay-ai/assay-provider-verify/blob/main/docs/level-1-python.md): middleware, options, Redis, proxies, local testing.
- [Troubleshooting](https://github.com/assay-ai/assay-provider-verify/blob/main/docs/troubleshooting.md): what each reject reason means.
- [How the signature works](https://github.com/assay-ai/assay-provider-verify/blob/main/docs/overview.md).

## License

MIT, see [LICENSE](LICENSE).
