Skip to main content

User lifecycle for HawkAPI — register, login, password reset, email verification, on top of hawkapi-auth + hawkapi-sqlalchemy + hawkapi-mail

Project description

hawkapi-users

Full user lifecycle for HawkAPI. Register, login, email verification, password reset — all on top of three existing plugins:

No fastapi-users dependency; pure stdlib + the three plugins above.

Install

pip install hawkapi-users

Quickstart

from hawkapi import HawkAPI
from hawkapi_sqlalchemy import Base, init_database
from hawkapi_mail import init_mail, SMTPBackend, SMTPConfig
from hawkapi_users import (
    SQLAlchemyBaseUserTable,
    UserManager, UserTokens, UserTokenConfig,
    init_users, send_verification_email, send_password_reset_email,
)


class User(SQLAlchemyBaseUserTable):
    __tablename__ = "users"


app = HawkAPI()
init_database(app, url="postgresql+asyncpg://...")
mail = init_mail(
    app,
    backend=SMTPBackend(SMTPConfig(host="smtp.example.com", port=587, start_tls=True)),
    default_sender="noreply@example.com",
)

tokens = UserTokens(UserTokenConfig(secret="…stable secret, ≥32 chars…"))
manager = UserManager(model=User, tokens=tokens)


async def on_request_verify(user, token):
    await send_verification_email(
        mail, to=user.email, token=token,
        verify_url_template="https://app.example.com/verify/{token}",
    )


async def on_forgot_password(user, token):
    await send_password_reset_email(
        mail, to=user.email, token=token,
        reset_url_template="https://app.example.com/password-reset/{token}",
    )


manager.on_after_request_verify = on_request_verify
manager.on_after_forgot_password = on_forgot_password

init_users(app, manager=manager)

The plugin mounts under /users (configurable):

Route Description
POST /users/register Create account, returns public user record
POST /users/login Verify credentials, returns public user
POST /users/verify/request Send a verification email (always 202)
POST /users/verify/{token} Apply a verification token
POST /users/password-reset/request Send a reset email (always 202)
POST /users/password-reset/{token} Apply a new password

Security defaults

  • Argon2id password hashing (via hawkapi-auth's pinned parameters).
  • Token type binding — verify tokens cannot be replayed as reset tokens (the type claim is enforced).
  • password_version binding — each token carries the user's current password_version. A successful reset bumps the counter, invalidating every outstanding verify/reset token in one DB write.
  • Account enumeration prevention/verify/request and /password-reset/request always return 202 regardless of whether the email exists.
  • Timing-safe lookupauthenticate() runs argon2id against a dummy hash when the user does not exist, equalizing wall-clock time across the hit/miss paths.
  • Email normalizationemail is lower-cased and stripped server-side; uniqueness is enforced after normalization.

Known accepted risks

These are real tradeoffs the plugin does NOT mitigate. Operators must layer additional protection where it matters.

  • Email enumeration via /register (409 response) — registering with an already-used email returns 409 Conflict. An attacker can confirm whether any email has an account. Mitigation: rate-limit /register per source IP and require CAPTCHA for unauthenticated traffic. The anti-enumeration guarantee applies only to /verify/request and /password-reset/request (always 202).
  • Inactive-account 403 confirms credentials/login returns 401 for bad credentials and 403 for a disabled account. A 403 confirms the email+password combination is correct. Treat 403 as a credential-leak event in audit pipelines.
  • No rate limiting/login, /register, /verify/request, /password-reset/request have no built-in throttling. Pair with hawkapi-ratelimit plus per-account lockout.
  • No session issued by /login — the route validates credentials and returns the public user record. Issuing a JWT, setting a cookie, or starting a server-side session is the operator's job (manager.on_after_login).
  • Token-in-URL leakage — verify/reset tokens travel in URL paths and may appear in Referer headers, browser history, and email-server access logs. Use HTTPS + short TTLs (default 1 hour) and prefer SPA flows that strip the token from the URL after consumption.

Hooks

manager.on_after_register          # async (user) -> None
manager.on_after_login             # async (user) -> None
manager.on_after_request_verify    # async (user, token) -> None
manager.on_after_verify            # async (user) -> None
manager.on_after_forgot_password   # async (user, token) -> None
manager.on_after_reset_password    # async (user) -> None

Use these to mint a session cookie, write an audit log, send the email, etc.

Custom email templates

from hawkapi_mail import TemplateRenderer
from hawkapi_users import send_verification_email

renderer = TemplateRenderer(directory="my/email/templates")

await send_verification_email(
    mail,
    to=user.email,
    token=token,
    template="my_verify.html",
    text_template="my_verify.txt",
    renderer=renderer,
)

The built-in templates live in hawkapi_users/templates/ (verify.html/.txt, password_reset.html/.txt) and are used by default.

Development

git clone https://github.com/Hawk-API/hawkapi-users.git
cd hawkapi-users
uv sync --extra dev
uv run pytest -q
uv run ruff check . && uv run ruff format --check .
uv run pyright src/

License

MIT.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

hawkapi_users-0.2.0.tar.gz (49.7 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

hawkapi_users-0.2.0-py3-none-any.whl (16.4 kB view details)

Uploaded Python 3

File details

Details for the file hawkapi_users-0.2.0.tar.gz.

File metadata

  • Download URL: hawkapi_users-0.2.0.tar.gz
  • Upload date:
  • Size: 49.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for hawkapi_users-0.2.0.tar.gz
Algorithm Hash digest
SHA256 e1993e9f4068d27f16628e7e528770f3ef4c091247e3e1597c27355e0e3cefcc
MD5 ae9849d368eb47a83ea64a2d74597aed
BLAKE2b-256 823569156ad7c5661e1172305c47e04bb578f18df0891ff1ce15b781d1e0beaa

See more details on using hashes here.

Provenance

The following attestation bundles were made for hawkapi_users-0.2.0.tar.gz:

Publisher: release.yml on Hawk-API/hawkapi-users

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file hawkapi_users-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: hawkapi_users-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 16.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for hawkapi_users-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 e2fc60914e65b1c014464ae2a68b8dbb82f1343fb306dca03e7f7daced27ef76
MD5 f19d8533cc0564e19c2bd27b26ff5b82
BLAKE2b-256 d476a324ca06d649805724105e86c2dc71cd3c603524c4bfd06b6bbb7a38afac

See more details on using hashes here.

Provenance

The following attestation bundles were made for hawkapi_users-0.2.0-py3-none-any.whl:

Publisher: release.yml on Hawk-API/hawkapi-users

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page