Authentication and request identity¶
Stable within the documented backend boundary. Authentication establishes who is calling. Permissions and PolicyEngine decide what that caller may do. A header, user ID, or tenant ID supplied by a client is not authenticated identity by itself.
For a complete running application, start with the ticket-desk authentication adapter, then add tenant membership and roles. Those examples verify credentials on the server and attach both the canonical Principal and the user-shaped state used by permission classes.
Connect an identity provider to the application¶
The application owns credential validation: for example, a server-managed API token, a validated session, or a JWT verified with your identity provider's keys, issuer, audience, and expiry rules. After validation, resolve current account status, roles, and tenant membership from trusted state.
Attach request.state.principal for PolicyEngine and
request.state.user for the permission/dependency compatibility surface. Keep
them consistent. The user-shaped object supplies id, is_authenticated,
is_active, is_staff, and is_superuser as needed by your permissions.
A Principal alone does not expose every one of those user attributes.
Principal.for_user(...) constructs an authenticated identity; it does not
verify credentials or check membership. Never construct it from an unverified
user ID or client-provided roles. Reset request context in finally when your
adapter sets tenant or user context variables; the tenancy tutorial shows this
lifecycle.
The old AuthenticationMiddleware, TokenAuthentication, JWTAuthentication,
login(), and logout() recipes on this page are not a supported turnkey
application-authentication stack. Aksara provides backend primitives and optional
contrib authentication; choose and implement the application's login/session
boundary explicitly.
Use the optional built-in user model¶
aksara.contrib.auth.User stores email, hashed_password, status flags,
optional metadata, and the inherited ID/timestamps. It has no plain password
field or user.check_password() method. Use create_user() to hash a password;
ordinary objects.create(password=...) is not that contract.
Enable aksara.contrib.auth in the global installed_apps configuration and
apply its internal migrations with the migration role before using the account
helpers. The application database must be connected. See
configuration,
migrations, and
production roles.
These complete service functions can live in app/accounts.py:
from aksara.contrib.auth import User, verify_password
async def create_account(email: str, password: str):
# The application must validate signup eligibility and password policy first.
return await User.objects.create_user(email=email, password=password)
async def authenticate_account(email: str, password: str):
# Returns an active User or None. Does not issue a cookie, token, or Principal.
return await User.objects.authenticate(email=email, password=password)
def password_matches(user: User, password: str) -> bool:
return verify_password(password, user.hashed_password)
create_user() normalizes email and defaults to an active, non-staff,
non-superuser account. authenticate() rejects a wrong password, unknown
account, or inactive account. It is not a password-policy validator or a
rate limiter. A failed authentication result also does not prove database health;
check operational failures through your diagnostics and logs.
Do not return a whole user record from a public registration endpoint. Select
response fields explicitly and exclude hashed_password, status flags, and
private metadata. Do not let signup payloads set is_staff or is_superuser.
For a custom user model, subclass AbstractUser and deliberately configure its
manager and authentication integration. The built-in helpers and session
lookup target the concrete built-in User; a subclass does not automatically
replace it throughout the framework.
Understand the FastAPI dependencies¶
| Helper | What it actually does |
|---|---|
get_current_user(request) |
Reads the user already attached to request.state; does not parse or validate a bearer token |
get_current_active_user(request) |
Also rejects an inactive attached user |
require_auth() |
Creates a dependency requiring an active attached user; default denial is HTTP 401 |
require_staff() |
Requires an active user and the is_staff flag; missing user is 401, non-staff is 403 |
require_superuser() |
Requires an active user and is_superuser; missing user is 401, insufficient role is 403 |
Use these only after your authentication adapter is installed. Their presence on an endpoint does not manufacture authenticated state. Generated ViewSet permission denials use HTTP 403; do not infer a universal authentication error code from one helper. See permissions.
Sessions, registration, and recovery¶
Contrib auth also exports create_session_token(db, user, expires_in=...),
get_user_from_session_token(db, token), invalidate_session_token(db, token),
and cleanup_expired_sessions(db). These manage opaque tokens in
aksara_sessions. They require provisioned tables and do not set a response
cookie or install a general application authentication middleware.
The application owns its HTTP login/logout routes, cookie settings, CSRF protection for cookie-authenticated writes, rate limits, signup policy, email verification, password reset, session revocation policy, and secret handling. Admin has its own login flow; it is not a ready-made application identity provider. Do not copy the historical password-reset or email-verification methods from this page: they were not APIs exported by the installed package.
Humans, tools, and delayed work¶
An MCP token identifies a machine actor with provenance, scopes, and expiry; it is not interchangeable with a human login cookie. Use the official MCP client tutorial to carry that identity through generated execution.
Ordinary tasks do not persist a complete Principal. Durable Operations instead store an identity reference that your resolver uses to reconstruct current authority at execution time. See application boundaries and Durable Operations.