hawkapi-auth
JWT auth for HawkAPI. Access + refresh tokens, argon2id password hashing, DI guards, scope-based access control.
Install
pip install hawkapi-auth
Quickstart
from hawkapi import Depends, HawkAPI, HTTPException
from hawkapi_auth import (
JWTConfig,
hash_password,
init_auth,
random_secret,
requires_user,
verify_password,
)
app = HawkAPI()
init_auth(app, config=JWTConfig(secret=random_secret()))
@app.post("/register")
async def register(email: str, password: str):
await db.create_user(email=email, password_hash=hash_password(password))
return {"ok": True}
@app.post("/login")
async def login(email: str, password: str):
user = await db.find_user(email)
if not user or not verify_password(password, user.password_hash):
raise HTTPException(401, detail="Invalid credentials")
issuer = app.state.auth
return {
"access_token": issuer.issue_access(user.id),
"refresh_token": issuer.issue_refresh(user.id),
}
@app.get("/me")
async def me(user_id: str = Depends(requires_user)):
return await db.fetch_user(user_id)
Token issue / verify
issuer = app.state.auth # TokenIssuer
access = issuer.issue_access("user-1", role="admin", scope="read write")
refresh = issuer.issue_refresh("user-1")
claims = issuer.verify_access(access) # raises TokenError on bad token
claims = issuer.verify_refresh(refresh) # ditto, plus checks the token type
issue_access / issue_refresh accept arbitrary keyword claims (role, scope, anything JSON-serialisable).
JWTConfig
JWTConfig(
secret="…", # HMAC secret for HS256/384/512
algorithm="HS256",
access_ttl_seconds=15 * 60,
refresh_ttl_seconds=30 * 24 * 60 * 60,
issuer="my-service", # optional iss claim
audience="my-api", # optional aud claim
private_key="", # RS*/ES* — PEM
public_key="",
)
Use random_secret() to mint one. Store it outside of git.
DI guards
from hawkapi_auth import requires_user, requires_claims, requires_scopes
@app.get("/me")
async def me(user_id: str = Depends(requires_user)):
...
@app.get("/dump")
async def dump(claims: dict = Depends(requires_claims)):
...
@app.get("/admin", dependencies=[Depends(requires_scopes("admin"))])
async def admin():
...
requires_scopes(*scopes) expects either a space-separated scope claim or a list under scope / scopes. Missing scopes → 403.
Refresh + revocation
from hawkapi_auth import RevocationList
rev = RevocationList()
init_auth(app, config=JWTConfig(secret=...), revocation=rev)
@app.post("/refresh")
async def refresh(refresh_token: str):
issuer = app.state.auth
claims = issuer.verify_refresh(refresh_token)
return {"access_token": issuer.issue_access(claims["sub"])}
@app.post("/logout")
async def logout(refresh_token: str):
app.state.auth.revoke_refresh(refresh_token)
return {"ok": True}
RevocationList is in-memory only. For multi-process deployments, swap in a Redis-backed implementation (planned in v0.2.0).
Password hashing
from hawkapi_auth import hash_password, verify_password, needs_rehash
h = hash_password("hunter2") # argon2id
ok = verify_password("hunter2", h) # constant-time, returns bool
if needs_rehash(h):
h = hash_password("hunter2") # re-hash after a successful login
verify_password never raises — safe to use directly in handler bodies.
What's not included (v0.2.0 roadmap)
- Social OAuth providers (Google / GitHub / Discord / Microsoft).
- Email-based password reset + verification flows.
- Pre-built user model and storage.
- Redis-backed
RevocationList.
Development
git clone https://github.com/Hawk-API/hawkapi-auth.git
cd hawkapi-auth
uv sync --extra dev
uv run pytest -q
uv run ruff check . && uv run ruff format --check .
uv run pyright src/
License
MIT.
Metadata
Release files for hawkapi-auth 0.2.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| hawkapi_auth-0.2.1.tar.gz | 30.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| hawkapi_auth-0.2.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 41.1 kB
Release files / hawkapi_auth-0.2.1.tar.gz
| Download URL | hawkapi_auth-0.2.1.tar.gz |
|---|---|
| Size | 30.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
a9735640f394cd6db7248fda3640f5694df0c1fef7f72ded39a9de6b49c2a7d2
|
|
BLAKE2b-256 checksum How to use checksums |
8d64fb93557fe7dd65c970da52b4a0a9bf6e79b62f467f322ce6b1dee82c427e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jun 10, 2026.
Transparency logRelease files / hawkapi_auth-0.2.1-py3-none-any.whl
| Download URL | hawkapi_auth-0.2.1-py3-none-any.whl |
|---|---|
| Size | 11.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
92e6153f623e48d0c46cc900fce0632c3e467a618de412ea446666354d50a29f
|
|
BLAKE2b-256 checksum How to use checksums |
8c5e48ea00a93c68cde55f067444860fc7eaf3ac250d43c9cec767eb8a9437a4
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jun 10, 2026.
Transparency log