Changelog
All notable changes to AuthFort are documented here. The format is based on Keep a Changelog.
v0.0.21
Section titled “v0.0.21”- Default
pool_recyclelowered from 3600s to 300s — preventsConnectionDoesNotExistErrorbehind PgBouncer
- New
pool_recycleconfig option onAuthFort()— tune connection recycling interval (default 300s)
v0.0.20
Section titled “v0.0.20”- Input validation and sanitization for all user-facing fields (VAPT fix)
- Email validation using
email-validator— rejects SQL injection, XSS, header injection, XXE payloads - Name and phone sanitization using
nh3— strips all HTML tags (prevents stored XSS) - Avatar URL validation — only
http://andhttps://URLs accepted - Minimum password length enforcement (
min_password_lengthconfig, default 8) - New dependencies:
email-validator(>=2.3.0),nh3(>=0.3.4)
v0.0.19
Section titled “v0.0.19”set_password(user_id, new_password)— passwordless users (magic link, OTP, OAuth) can set an initial passwordPOST /auth/set-passwordREST endpoint (authenticated)PasswordSetevent fired when a passwordless user sets their initial passwordcreate_password_reset_token()now works for all users — passwordless/OAuth users can use forgot-password to set a password
- Passwordless users no longer get misleading “social login” error — new
no_passworderror code guides them correctly change_password()distinguishes OAuth (oauth_account) from passwordless (no_password) users- Banned check in
login()moved after password verification (security: prevents banned-account probing)
v0.0.18
Section titled “v0.0.18”create_user(email_verified=True)— mark email as verified at creation time (admin-created accounts)update_user(user_id, email_verified=True)— admin can manually verify or unverify a user’s emailEmailVerifiedevent fires automatically on verification viacreate_user()orupdate_user()(no duplicate if already verified)
v0.0.17
Section titled “v0.0.17”trust_proxyconfig — trustX-Forwarded-For/X-Real-IPfrom any sourcetrusted_proxiesconfig — only trust proxy headers from listed IPs/CIDRs (recommended for production)- Centralized IP extraction across all auth and OAuth endpoints
- Stable
session_idacross refresh token rotation — JWTsidclaim no longer changes on refresh - Migration
002_add_session_id— addssession_idcolumn (runauthfort migrateto apply) get_sessions()deduplicates bysession_id— one entry per logical sessionrevoke_session()andrevoke_all_sessions(exclude=...)operate on stablesession_id
- client: Cookie-mode refresh deduplication — concurrent 401s share a single
/refreshcall
v0.0.16
Section titled “v0.0.16”change_password()returns 400 (not 401) for wrong old password — prevents client SDK 401 retry loop- Login on OAuth-only account returns 400 (not 401) with
oauth_accountcode — wrong auth method is a bad request, not an auth failure
v0.0.15
Section titled “v0.0.15”authfort migrateCLI command — run migrations without a bootstrap script (uvx authfort migrate --database-url "...")register_foreign_tables(metadata)— register AuthFort table stubs for FK resolution in consumer modelsalembic_filters()— returns bothinclude_nameandinclude_objectfilters forcontext.configure(**alembic_filters())
Removed
Section titled “Removed”alembic_exclude()— replaced byalembic_filters()
v0.0.14
Section titled “v0.0.14”- Boolean column defaults in migration use
falseinstead of0— fixes PostgreSQL table creation failure
v0.0.13
Section titled “v0.0.13”AuthUserandAuthUserRoleexports — SQLAlchemy models for ORM JOINs against consumer tables
v0.0.12
Section titled “v0.0.12”bannedfield onUserResponse— visible in all user responses (login, signup, get_user, list_users, current_user)
v0.0.11
Section titled “v0.0.11”RateLimitConfig— per-endpoint rate limits ("5/min"format) with in-memory sliding window- IP-based rate limiting on all 8 auth endpoints
- Email-based rate limiting on login, signup, magic-link, otp, otp/verify (catches distributed attacks)
- 429 +
Retry-Afterheader on rate limit exceeded RateLimitExceededevent with endpoint, IP, email, limit, key_typeRateLimitStoreprotocol — pluggable for Redis or other backendsauth.list_users()— paginated listing with query/banned/role filters, sort_by/sort_orderauth.get_user(user_id)— single user lookup with rolesauth.delete_user(user_id)— cascade delete (roles → tokens → accounts → verification tokens → user)auth.get_user_count()— count with same filtersListUsersResponseschema,UserDeletedeventondelete="CASCADE"on all user foreign keysRateLimitConfig,RateLimitExceeded,ListUsersResponse,UserDeletedexported from top-level
v0.0.10
Section titled “v0.0.10”- Email verification flow —
create_email_verification_token()/verify_email() - Magic link passwordless login —
create_magic_link_token()/verify_magic_link() - Email OTP passwordless login —
create_email_otp()/verify_email_otp() GenericOAuthProvider— connect any OAuth 2.0 provider with custom endpointsGenericOIDCProvider— connect any OIDC provider via discovery URLallow_passwordless_signupconfig — auto-create users for unknown emails via magic link/OTPemail_verify_ttl,magic_link_ttl,email_otp_ttlconfig params- 5 new endpoints:
/magic-link,/magic-link/verify,/otp,/otp/verify,/verify-email - 6 new events:
email_verification_requested,email_verified,magic_link_requested,magic_link_login,email_otp_requested,email_otp_login - client:
requestMagicLink(),verifyMagicLink(),requestOTP(),verifyOTP(),verifyEmail()methods - client:
OAuthProvidertype accepts any string for generic providers
v0.0.9
Section titled “v0.0.9”auth.has_role(user_id, role)— single-role convenience checkauth.get_jwks()— JWKS dict for non-FastAPI frameworksauth.cleanup_expired_sessions()— delete expired/revoked sessionsauth.update_user(user_id, *, name, avatar_url, phone)— update profile fieldsauth.get_provider_tokens(user_id, provider)— retrieve stored OAuth tokensphonefield on User model,create_user(), and/auth/signuprsa_key_sizeconfig — configurable RSA key size (default 2048)frontend_urlconfig — cross-origin OAuth redirect support- OAuth
redirect_toquery param — redirect after callback - OAuth
mode=popup— popup flow withpostMessage - OAuth
extra_scopes— request additional provider API scopes - OAuth provider token storage — saves
access_tokenandrefresh_tokenfrom providers UserUpdatedevent — fired on profile updateAuthTokensadded to public exports- All 16 event classes exported from top-level
- client:
OAuthProvidertype,OAuthSignInOptions, popup mode,avatarUrl/phoneinsignUp() - client: Auto-initialize in React, Vue, and Svelte integrations
Changed
Section titled “Changed”- OAuth providers use
extra_scopesinstead ofscopes— required scopes always included jwt_algorithmremoved — RS256 is hardcoded (usersa_key_sizefor key strength)
- OAuth provider
refresh_tokenwas never saved — now stored on callback
v0.0.8
Section titled “v0.0.8”Breaking
Section titled “Breaking”- server: Replaced
sqlmodeldependency withsqlalchemy[asyncio]>=2.0 - server: Bundled migrations reset to single
001_initial_schema.py— existing dev databases need a freshauth.migrate()(drop old DB first)
Changed
Section titled “Changed”- All models use SQLAlchemy
DeclarativeBase+mapped_column()instead of SQLModel - All repositories use
session.execute().scalars()instead ofsession.exec() models/__init__.pyexportsBasefor Alembic and test usage
Removed
Section titled “Removed”sqlmodeldependency- Bundled migration
002_composite_index.py(merged into 001)
- Eliminated 85 false SQLModel deprecation warnings in pytest
v0.0.7
Section titled “v0.0.7”- OAuth ban check — banned users can no longer login via OAuth
- OAuth email normalization — provider emails are lowercased before lookup
- OAuth concurrent signup —
IntegrityErroron duplicate email is caught gracefully - Atomic
bump_token_version(),ban_user(),revoke_all_user_refresh_tokens(), signing key deactivation - Introspection checks session validity via
sidclaim - Password change and reset now revoke all refresh tokens
- OAuth
login_failedevents fired on all error paths auth.cleanup_expired_tokens()for verification token cleanup- Migration
002_composite_indexfor query performance get_refresh_token_by_id()repository function
v0.0.6
Section titled “v0.0.6”Breaking
Section titled “Breaking”- All database tables renamed with
authfort_prefix (requires fresh DB) SQLModel.metadata.create_all()replaced byawait auth.migrate()
auth.migrate()— bundled Alembic migrationsalembic_exclude()— filter AuthFort tables from your Alembic autogenerateCookieConfig(domain=".example.com")— subdomain cookie sharingServiceAuth(cookie_name="access_token")— cookie fallback
v0.0.5
Section titled “v0.0.5”RefreshRequest.refresh_tokennow optional — fixes 422 when client sends empty body with cookie
Changed
Section titled “Changed”- client: Bearer mode sends refresh token in request body
- client: Bearer mode
fetch()no longer sendscredentials: 'include'
- client:
TokenStorageinterface — pluggable storage for bearer mode
v0.0.4
Section titled “v0.0.4”create_password_reset_token(email)— generate reset tokenreset_password(token, new_password)— one-time use resetchange_password(user_id, old_password, new_password)— authenticated changerevoke_all_sessions(user_id, *, exclude=session_id)— keep current sessionsession_idonUserResponse(fromsidJWT claim)password_reset_ttlconfig param- 3 new events:
password_reset_requested,password_reset,password_changed
v0.0.3
Section titled “v0.0.3”- ESM imports in client SDK (added
.jsextensions) - JWKS rate limiting (
_last_fetch_attemptinitialized to-inf) dependenciesplacement inpyproject.toml
- Exported
UserResponseandAuthResponsefrom top-level "files": ["dist"]in clientpackage.json- CI/CD pipeline (
ci.yml+release.yml)
v0.0.1
Section titled “v0.0.1”Initial release with core authentication, JWT RS256, refresh token rotation, OAuth 2.1 + PKCE (Google, GitHub), RBAC, session management, ban/unban, 15 event types, JWKS, token introspection, multi-database support, FastAPI integration, microservice verifier, and TypeScript client SDK with React/Vue/Svelte integrations.