v0.10.0: mixin pivot, audit hardening, manual middleware#21
Merged
Conversation
Models ship as `*Mixin` classes from `fastapi_fullauth.models.{sqlalchemy,sqlmodel}` —
bring your own `Base` / `SQLModel`. `FullAuthBase` and the concrete `*Model` /
`*Record` classes are gone, and apps no longer end up with the library's
tables on their `Base.metadata`. Adapter constructors take every concrete
model as a keyword arg (`user_model`, `refresh_token_model`, optional
`role_model`, `user_role_model`, `permission_model`, `role_permission_model`,
`oauth_account_model`, `passkey_model`). Adapter modules flattened to
`adapters/sqlalchemy.py` and `adapters/sqlmodel.py`. `fastapi_fullauth.migrations`
removed — your own `Base.metadata` is the source of truth.
Audit hardening:
- `/auth/refresh` now requires the refresh-token row to exist in DB before
issuing a new pair; a signed JWT without a backing row is rejected.
- New `PREVENT_LOGIN_TIMING_ATTACKS` (default off): when on, login runs a
dummy argon2 verify on unknown-user / no-password paths.
- `CSRFMiddleware(secret=...)` is required and validated (>= 32 chars);
the env-pulling fallback is gone.
- `UUID(payload.sub)` wrapped at every call site (`current_user`,
`/refresh`, `verify_email`, `reset_password`, `logout` hook) — bad sub
-> 401 / TokenError instead of 500.
- `verify_password` catches argon2 `InvalidHashError` and bcrypt
`ValueError` on malformed stored hashes.
- Passkey `/authenticate/begin` always returns a list `allowCredentials`
when an email is supplied, so unknown emails can't be enumerated by
response shape.
- `SECRET_KEY` rejected when shorter than 32 chars.
- `ALGORITHM` constrained to `Literal["HS256", "HS384", "HS512"]`.
- Rate limits added on `/verify-email/{request,confirm}`,
`/password-reset/confirm`, `/oauth/{provider}/callback`, and
`/passkeys/authenticate/complete`.
Config surface:
- New `BACKEND` and `ORIGINS` global settings propagate to per-feature
`*_BACKEND` and `PASSKEY_ORIGINS`. Per-feature overrides still win.
- Removed: `INCLUDE_USER_IN_LOGIN` (login/OAuth/passkey always include
`user`), `CSRF_ENABLED`, `INJECT_SECURITY_HEADERS`, `RATE_LIMIT_ENABLED`,
`ACCOUNT_LOCKED_EXCEPTION` (dead since 0.9.0 anti-enum change).
Middleware is no longer auto-wired. `init_app()` only mounts routers.
Import `SecurityHeadersMiddleware`, `CSRFMiddleware`, `RateLimitMiddleware`
from `fastapi_fullauth.middleware` and `app.add_middleware(...)` them
yourself. `init_middleware()` and the `auto_middleware` kwarg are removed.
`create_rate_limiter()` is exported from `fastapi_fullauth.protection` for
Redis-backed manual wiring.
`exclude_routers` renamed to `include_routers` on `init_app()` —
allowlist instead of denylist. `include_routers=None` (default) keeps
the previous "everything" behaviour.
Layout / naming cleanup: `router/` -> `routers/`, `_models.py` ->
`_schemas.py`, `dependencies/require_role.py` -> `dependencies/rbac.py`,
`flows/update_profile.py` -> `flows/profile.py`, `_get_fullauth` ->
`get_fullauth`, `core/redis_blacklist.py` folded into
`core/blacklist.py` with parallel `InMemoryTokenBlacklist` /
`RedisTokenBlacklist` naming. `RateLimitMiddleware` moved from
`protection/` to `middleware/`.
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (97)
📝 WalkthroughWalkthroughRefactors to mixin-based models and injected adapter models, updates routers to include_routers, removes auto-wired middleware, adds config validators and security hardening, consolidates blacklist backends, and updates docs, examples, and tests to the new v0.10.0 contracts. Changesv0.10.0 Architecture and API Refactor
Sequence Diagram(s)sequenceDiagram
participant Client
participant FullAuth
participant AuthRouters
participant SQLModelAdapter
Client->>AuthRouters: Login/OAuth/Passkey
AuthRouters->>FullAuth: Delegate flow
FullAuth->>SQLModelAdapter: Use injected models (User/Refresh/OAuth/Passkey)
SQLModelAdapter-->>FullAuth: Entities/updates
FullAuth-->>Client: Responses (user always included on success)
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120 minutes Possibly related PRs
Poem
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Breaking release.
Models now ship as
*Mixinclasses fromfastapi_fullauth.models.{sqlalchemy,sqlmodel}. Bring your ownBase/SQLModel; the library no longer attaches tables to your metadata. Adapter constructors take every concrete model as a kwarg.fastapi_fullauth.migrationsremoved.Middleware is no longer auto-wired.
init_app()only mounts routers — addSecurityHeadersMiddleware,CSRFMiddleware,RateLimitMiddlewareyourself.init_middleware()andauto_middlewaregone.create_rate_limiter()exported fromfastapi_fullauth.protection.Audit hardening:
/auth/refreshrequires the refresh-token row in DB; signed JWT without a backing row is rejected.PREVENT_LOGIN_TIMING_ATTACKSruns a dummy argon2 verify on unknown-user paths.CSRFMiddleware(secret=...)required, validated >= 32 chars.SECRET_KEYrejected under 32 chars;ALGORITHMconstrained to HS256/384/512.UUID(payload.sub)wrapped everywhere — bad sub returns 401, not 500./authenticate/beginreturns identical shape for known vs unknown email.Config:
BACKENDandORIGINSpropagate to per-feature settings.INCLUDE_USER_IN_LOGIN,CSRF_ENABLED,INJECT_SECURITY_HEADERS,RATE_LIMIT_ENABLED,ACCOUNT_LOCKED_EXCEPTION.Renames:
exclude_routers->include_routers(allowlist),router/->routers/,dependencies/require_role.py->dependencies/rbac.py,flows/update_profile.py->flows/profile.py.core/redis_blacklist.pyfolded intocore/blacklist.py.Summary by CodeRabbit
v0.10.0 Release Notes
Breaking Changes
include_routersallowlist;exclude_routersremoved.app.add_middleware().fastapi_fullauth.migrationshelper; manage Alembic metadata yourself.INCLUDE_USER_IN_LOGIN,INJECT_SECURITY_HEADERS,CSRF_ENABLEDremoved.Security Improvements
PREVENT_LOGIN_TIMING_ATTACKS.Bug Fixes