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/ashimov/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.1.0.tar.gz (48.6 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.1.0-py3-none-any.whl (15.4 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: hawkapi_users-0.1.0.tar.gz
  • Upload date:
  • Size: 48.6 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.1.0.tar.gz
Algorithm Hash digest
SHA256 a9ad482531bcc3bab34ecdbafb000bab9cd4625080307f21cad5c016904a83a2
MD5 f03052bd9cc7a5bb0f7166dbd7429e75
BLAKE2b-256 8e43094d671c85c59cb323f5a523d36ade86547dbdb39423561e973ca9747cbb

See more details on using hashes here.

Provenance

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

Publisher: release.yml on ashimov/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.1.0-py3-none-any.whl.

File metadata

  • Download URL: hawkapi_users-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 15.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.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 50c8513f54e736311b8a81ed9f8b6876ad179bf6c0886194a5d86e9958e62605
MD5 e166996c1408d30d21b7aadd14089117
BLAKE2b-256 6e7590259cf7787bc1ce3d9f9cdf5bdf4a3462ad9f3f16a596b1a82ac1ace485

See more details on using hashes here.

Provenance

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

Publisher: release.yml on ashimov/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