Authentication
FAuth provides a built-in authenticate() method on AuthProvider that handles credential verification using the IdentityLoader protocol and Argon2 password hashing.
Basic setup
from pydantic import BaseModel
from fauth import AuthConfig, AuthProvider, hash_password
class User(BaseModel):
id: str
username: str
hashed_password: str
is_active: bool = True
# Identity loader retrieves user by username/email/etc.
async def load_identity(identifier: str) -> User | None:
return await db.get_user_by_username(identifier)
# Token-based user loader (used by require_user)
async def load_user(payload) -> User | None:
return await db.get_user_by_id(payload.sub)
auth = AuthProvider(
config=AuthConfig(secret_key="my-secret"),
user_loader=load_user,
identity_loader=load_identity,
)
Using authenticate() in a login endpoint
from fastapi import FastAPI
app = FastAPI()
@app.post("/login")
async def login(username: str, password: str):
# Verifies password and checks is_active
user = await auth.authenticate(username, password)
# Issue tokens
return await auth.login(sub=user.id)
authenticate() performs three checks in order:
- User exists — looks up the user via
IdentityLoader. Raises401if not found. - Password is valid — verifies the plain password against the hashed password stored on the user. Raises
401if invalid. - User is active — checks the user's active status field (defaults to
is_active, configurable viaFieldNames(active_status=...)). Raises401if inactive.
Custom field names
When your user model uses non-default attribute names, pass a FieldNames instance to AuthProvider:
from fauth import AuthProvider, FieldNames
class User(BaseModel):
id: str
pw_hash: str # instead of hashed_password
active: bool # instead of is_active
auth = AuthProvider(
config=config,
user_loader=load_user,
identity_loader=load_identity,
field_names=FieldNames(password="pw_hash", active_status="active"),
)
| Attribute | Default | Used by |
|---|---|---|
password |
"hashed_password" |
authenticate() |
active_status |
"is_active" |
authenticate(), require_active_user(), refresh() |
roles |
"roles" |
require_roles() |
permissions |
"permissions" |
require_permissions() |
Note: If
AuthProvideris created without anidentity_loader, callingauthenticate()will raise aRuntimeError.
Custom Token Payload
If you need custom claims in your tokens (e.g., tenant_id, organization_id), subclass TokenPayload and pass it to AuthProvider:
from fauth import AuthConfig, AuthProvider, TokenPayload
class MyTokenPayload(TokenPayload):
tenant_id: str
plan: str = "free"
auth = AuthProvider(
config=AuthConfig(secret_key="my-secret"),
user_loader=load_user,
token_payload_schema=MyTokenPayload, # JWTs will be decoded into MyTokenPayload
)
When issuing tokens, pass custom claims via the extra parameter:
Your user_loader will then receive a MyTokenPayload instance with typed access to payload.tenant_id and payload.plan.