Authentication
AuthFort provides authentication through both REST endpoints (via the FastAPI router) and a programmatic API (for use in backend code).
Signup
Section titled “Signup”Creates a new user with email and password.
POST /auth/signup
Content-Type: application/json
{
"email": "user@example.com",
"password": "strongpassword",
"name": "Jane Doe",
"avatar_url": "https://example.com/avatar.jpg",
"phone": "+1234567890"
} POST /auth/signup
Content-Type: application/json
{
"email": "user@example.com",
"password": "strongpassword",
"name": "Jane Doe",
"avatar_url": "https://example.com/avatar.jpg",
"phone": "+1234567890"
} Returns AuthResponse with user info and tokens. Returns 400 if the email is already taken, or 403 if allow_signup=False.
result = await auth.create_user(
"user@example.com",
"strongpassword",
name="Jane Doe",
avatar_url="https://example.com/avatar.jpg",
phone="+1234567890",
)
# result.user.id, result.user.email, result.tokens.access_token
# Admin-created accounts can skip email verification
result = await auth.create_user(
"admin@example.com",
"strongpassword",
name="Admin User",
email_verified=True, # fires email_verified event
) result = await auth.create_user(
"user@example.com",
"strongpassword",
name="Jane Doe",
avatar_url="https://example.com/avatar.jpg",
phone="+1234567890",
)
# result.user.id, result.user.email, result.tokens.access_token
# Admin-created accounts can skip email verification
result = await auth.create_user(
"admin@example.com",
"strongpassword",
name="Admin User",
email_verified=True, # fires email_verified event
) create_user() always works regardless of the allow_signup setting. Use it for admin-created accounts or seeding.
Passwords are hashed with argon2 before storage. Emails are normalized (stripped and lowercased).
All fields except email and password are optional: name, avatar_url, and phone.
Update User
Section titled “Update User”Update a user’s profile fields. Uses a sentinel pattern so that omitted fields are left unchanged, while passing None explicitly clears a field:
# Update specific fields — only provided fields are changed
updated = await auth.update_user(
user_id,
name="New Name",
phone="+1987654321",
)
# Pass None to clear a field
updated = await auth.update_user(user_id, phone=None)
# Omitted fields are not touched — this only changes the name
updated = await auth.update_user(user_id, name="Jane")
# Admin can manually verify a user's email
updated = await auth.update_user(user_id, email_verified=True) # Update specific fields — only provided fields are changed
updated = await auth.update_user(
user_id,
name="New Name",
phone="+1987654321",
)
# Pass None to clear a field
updated = await auth.update_user(user_id, phone=None)
# Omitted fields are not touched — this only changes the name
updated = await auth.update_user(user_id, name="Jane")
# Admin can manually verify a user's email
updated = await auth.update_user(user_id, email_verified=True) Returns: UserResponse with the updated user data. Emits a user_updated event with the list of changed fields.
Authenticates a user and issues tokens.
POST /auth/login
Content-Type: application/json
{
"email": "user@example.com",
"password": "strongpassword"
} POST /auth/login
Content-Type: application/json
{
"email": "user@example.com",
"password": "strongpassword"
} Returns AuthResponse with tokens. Returns 401 if credentials are invalid. If the user is banned, returns 403.
result = await auth.login("user@example.com", "strongpassword")
# result.tokens.access_token, result.tokens.refresh_token result = await auth.login("user@example.com", "strongpassword")
# result.tokens.access_token, result.tokens.refresh_token On successful login, a login event is emitted. On failure, a login_failed event is emitted with the reason.
Refresh
Section titled “Refresh”Exchanges a valid refresh token for a new access token and refresh token pair.
POST /auth/refresh
Content-Type: application/json
{
"refresh_token": "the-refresh-token"
} POST /auth/refresh
Content-Type: application/json
{
"refresh_token": "the-refresh-token"
} In cookie mode, the refresh token is read from the cookie automatically — the body can be empty.
result = await auth.refresh("the-refresh-token") result = await auth.refresh("the-refresh-token") Refresh tokens are single-use. The old token is revoked and a new pair is issued. If a revoked token is reused (indicating theft), the entire token family is revoked.
Logout
Section titled “Logout”Revokes a refresh token, ending the session.
POST /auth/logout
Content-Type: application/json
{
"refresh_token": "the-refresh-token"
} POST /auth/logout
Content-Type: application/json
{
"refresh_token": "the-refresh-token"
} In cookie mode, the refresh token is read from the cookie automatically — the body can be empty.
await auth.logout("the-refresh-token") await auth.logout("the-refresh-token") Get Current User
Section titled “Get Current User”The /auth/me endpoint returns the authenticated user’s info:
GET /auth/me
Authorization: Bearer <access-token> GET /auth/me
Authorization: Bearer <access-token> In cookie mode, the access token is read from the cookie automatically — no Authorization header needed.
Returns a UserResponse with: id, email, name, email_verified, avatar_url, phone, banned, roles, created_at, and session_id.
Passwordless Login
Section titled “Passwordless Login”AuthFort also supports passwordless authentication via magic links and email OTP. These methods don’t require a password — users authenticate by clicking a link or entering a code sent to their email.
- Magic Links — login via emailed link
- Email OTP — login via 6-digit code
Both methods automatically verify the user’s email address.