Skip to content

Cheat Sheet

This page has the essentials of the library / embed-in-your-app path. If you're looking for the docker compose up self-hosted IAM path, see the Self-Hosted Quickstart instead.

Install

uv add neozen
uv add "neozen[password,totp,webauthn,oauth]"  # all extras

Database Setup

from sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker, AsyncSession
from neozen.storage.sqlalchemy.base import Base

engine = create_async_engine("sqlite+aiosqlite:///data/app.db")
async with engine.begin() as conn:
    await conn.run_sync(Base.metadata.create_all)
session_factory = async_sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)

Register a User

import secrets
from pwdlib import PasswordHash
from neozen.storage.sqlalchemy.repositories import SubjectRepository

hasher = PasswordHash.recommended()

async with session_factory() as db:
    repo = SubjectRepository(session=db)
    subject_id = f"user_{secrets.token_hex(8)}"
    await repo.create(
        id=subject_id,
        email="alice@example.com",
        hashed_password=hasher.hash("password123"),
        is_active=True,
        is_verified=False,
    )
    await db.commit()

Password Authentication

from neozen.core.factors.password import create_password_factor
from neozen.core.identifiers.email import create_email_identifier

async with session_factory() as db:
    repo = SubjectRepository(session=db)
    identifier = create_email_identifier(repo)
    factor = create_password_factor(identifier, repo, hasher)

    user = await factor.authenticate({
        "identifier": "alice@example.com",
        "password": "password123",
    })

Sessions

from neozen.storage.sqlalchemy import SessionServiceFactory

session_service = SessionServiceFactory(session_factory, secret_key="change-me")

# Create
token = await session_service.create_session(user.id, ip_address="1.2.3.4")

# Validate
session = await session_service.validate_session(token)

# Revoke
await session_service.revoke_session_by_token(token)

# List
sessions = await session_service.list_sessions(user.id)
from neozen.core.factors.magic_link import MagicLinkFactor, MagicLinkConfig
from neozen.core.email.console import ConsoleEmailSender

factor = MagicLinkFactor(
    config=MagicLinkConfig(link_base_url="https://app.com/magic-login/verify"),
    token_repository=token_repo,
    subject_email_lookup=email_lookup,
    email_sender=ConsoleEmailSender(),
    token_hash_key="secret",
)

# Send link (always returns success — anti-enumeration)
await factor.begin_authentication(user.id, ip_address="1.2.3.4")

# Verify (in callback handler)
valid = await factor.complete_authentication(user.id, response=token)

TOTP / 2FA

from neozen.core.factors.totp import TOTPFactor, TOTPConfig

totp = TOTPFactor(
    config=TOTPConfig(issuer="My App"),
    subject_repository=user_repo,
    backup_code_repository=backup_repo,
    hasher=hasher,
)

# Enroll
enrollment = await totp.begin_enrollment(user, account_name=user.email)
# Show enrollment.qr_code_data_uri to user
user, backup_codes = await totp.confirm_enrollment(user, code="123456")

# Authenticate
valid = await totp.authenticate(user, code="123456")
# Backup codes also work
valid = await totp.authenticate(user, code="ABCD1234")

Authorization (RBAC)

from neozen.storage.sqlalchemy import RoleServiceFactory, get_subject_roles
from neozen.core.authz.models import Permission

role_service = RoleServiceFactory(session_factory)

# Assign / revoke
await role_service.assign_role(user.id, "admin")
await role_service.revoke_role(user.id, "admin")

# Check
roles = await get_subject_roles(session_factory, user.id)
if "admin" in roles:
    ...

# Permission format: resource:action[:scope]
Permission.parse("user:read")
Permission.parse("document:write:own")
Permission.parse("*:*")  # superuser

Built-in roles: superuser (*:*), admin (user:*, role:*, client:*), user (*:*:own)

Flask Integration

from flask import Flask
from neozen.ext.flask import NeoZen, current_user, login_required, login_user, logout_user
from neozen.storage.sqlalchemy import create_subject_loader

app = Flask(__name__)
app.secret_key = "change-me"

neozen = NeoZen(
    app,
    session_service=session_service,
    subject_loader=create_subject_loader(session_factory),
)

@app.route("/login", methods=["POST"])
def login():
    user = User.get_by_email(request.form["email"])
    if user and user.check_password(request.form["password"]):
        login_user(user, remember=True)
        return redirect("/dashboard")

@app.route("/dashboard")
@login_required
def dashboard():
    return f"Hello {current_user.email}"

@app.route("/logout", methods=["POST"])
@login_required
def logout():
    logout_user()
    return redirect("/")

Litestar Integration

from litestar import Litestar, Request, get, post
from litestar.response import Redirect
from neozen.ext.litestar import (
    NeoZenConfig, NeoZenPlugin,
    get_current_user, login_required, login_user, logout_user,
)

neozen_config = NeoZenConfig(
    session_service=session_service,
    subject_loader=create_subject_loader(session_factory),
    cookie_secure=False,
)

@post("/login")
async def login_submit(request: Request) -> Response:
    form = await request.form()
    # ... verify credentials ...
    response = Redirect(path="/dashboard")
    await login_user(request, response, subject)
    return response

@get("/dashboard", guards=[login_required()])
async def dashboard(request: Request) -> Response:
    user = await get_current_user(request)
    return Response(content=f"Hello {user.email}")

app = Litestar(route_handlers=[...], plugins=[NeoZenPlugin(neozen_config)])

OAuth Social Login

from neozen.core.oauth_client.service import OAuthClientService
from neozen.core.oauth_client.providers.google import GoogleProvider

service = OAuthClientService(social_account_repo, state_repo, http_client=httpx.AsyncClient())
service.register_provider(GoogleProvider(config=google_config, http_client=http))

# Step 1: redirect user to provider
result = service.begin_authorization("google", redirect_uri="https://app.com/callback")
return redirect(result.authorization_url)

# Step 2: handle callback
callback = await service.handle_callback("google", code=code, state=state, redirect_uri=...)
subject_id, is_new = await service.get_or_create_subject_id(callback)

# Refresh expired tokens
new_tokens = await service.refresh_access_token("google", refresh_token)

Bring Your Own Model

from neozen.core.identifiers.email import create_email_identifier
from neozen.core.factors.password import create_password_factor

# No type: ignore needed — factories accept Any-typed storage
identifier = create_email_identifier(my_user_repo_adapter)
factor = create_password_factor(identifier, my_user_repo_adapter, hasher)

Adapter only needs get_one_by(**filters) and update_obj(obj, **data) for PasswordFactor.

Email

from neozen.core.email.base import EmailMessage
from neozen.core.email.console import ConsoleEmailSender   # dev
from neozen.core.email.smtp import SMTPEmailSender         # prod

sender = SMTPEmailSender(host="smtp.example.com", port=587, username="u", password="p", use_tls=True)
await sender.send(EmailMessage(to="user@example.com", subject="Hello", body_text="Hi!"))

Configuration Defaults

Setting Default
Session lifetime 7 days
Sliding window 1 hour
Magic link lifetime 15 minutes
Magic link rate limit 3 pending / hour
Password reset lifetime 1 hour
TOTP digits 6
TOTP interval 30s
TOTP backup codes 8

Token Prefixes

Prefix Purpose
nz_session_ Session token
nz_ev_ Email verification
nz_ml_ Magic link
nz_pr_ Password reset
nz_at_ / nz_rt_ / nz_ac_ OAuth tokens

Email OTP

from neozen.core.factors.email_otp import EmailOTPFactor, EmailOTPConfig

factor = EmailOTPFactor(
    config=EmailOTPConfig(),
    code_repository=otp_repo,
    subject_email_lookup=email_lookup,
    email_sender=sender,
    code_hash_key="secret",
)

# Send a code
await factor.begin_authentication(user_id)

# Verify
ok = await factor.complete_authentication(user_id, response="123456")

Groups

from neozen.core.groups import GroupService, GroupRole

service = GroupService(group_repository=g_repo, membership_repository=m_repo)

group = await service.create_group("engineering", owner_id="user_1")
await service.add_member(group.id, "user_2", actor_id="user_1")
await service.change_role(group.id, "user_2", GroupRole.ADMIN, actor_id="user_1")
members = await service.list_members(group.id)

Invitations

from neozen.core.groups import InvitationService, InvitationConfig

service = InvitationService(
    config=InvitationConfig(
        accept_url_template="https://app.com/invite?token={token}",
    ),
    repository=inv_repo,
    token_hash_key="secret",
)

# Create
invitation, token = await service.create_invitation(
    email="new@example.com",
    invited_by="admin_1",
    group_ids=["grp_1"],
    role_names=["user"],
)

# Accept
details = await service.accept_invitation(token, accepted_by=new_user_id)

OIDC Provider

from neozen.core.oidc import InMemoryKeyManager, IDTokenGenerator
from neozen.web.oidc import (
    DiscoveryEndpoint, JWKSEndpoint, UserInfoEndpoint,
    EndSessionEndpoint, RegistrationEndpoint,
)
from neozen.core.oidc.discovery import build_metadata_from_base_url

key_manager = InMemoryKeyManager()
id_token_gen = IDTokenGenerator(
    key_manager=key_manager, issuer="https://auth.example.com",
)

# Wire OIDC into the OAuth token endpoint
endpoint = TokenEndpoint(
    oauth_token_service,
    oauth_client_service,  # authenticates the client on every request
    id_token_generator=id_token_gen,
    claims_provider=claims_provider,
)

# HTTP endpoints
metadata = build_metadata_from_base_url(
    issuer="https://auth.example.com",
    base_url="https://auth.example.com",
)
discovery = DiscoveryEndpoint(metadata)
jwks = JWKSEndpoint(key_manager)
userinfo = UserInfoEndpoint(oauth_token_service, claims_provider)

SCIM 2.0

from neozen.core.scim import SCIMUserService, SCIMGroupService, User, Email
from neozen.web.scim import UsersEndpoint, GroupsEndpoint, DiscoveryEndpoints

user_service = SCIMUserService(
    adapter=my_adapter,
    base_url="https://api.example.com/scim/v2",
)
users = UsersEndpoint(user_service)

# Create
result = await users.create({"userName": "alice", "emails": [...]})

# List with filter
result = await users.list({"filter": 'userName sw "ali"', "count": 10})

# Discovery
discovery = DiscoveryEndpoints()

LDAP Backend

from neozen.storage.ldap import LDAPConfig, LDAPSubjectRepository, authenticate_user

config = LDAPConfig(
    url="ldap://ldap.example.com",
    bind_dn="cn=admin,dc=example,dc=com",
    bind_password="secret",
    base_dn="ou=people,dc=example,dc=com",
)

repo = LDAPSubjectRepository(config)
user = await repo.get_one_by(email="alice@example.com")

# Bind authentication
user = await authenticate_user(config, "alice@example.com", "password")

i18n

from neozen.core.i18n import builtin_translator, negotiate_locale, Catalog

# Use the built-in translator (EN/FR for emails and errors)
msg = builtin_translator.gettext(
    "email.password_reset.subject",
    locale="fr",
    app_name="My App",
)

# Add a custom locale
builtin_translator.add_catalog("de", Catalog({...}))

# Negotiate from request
locale = negotiate_locale(
    ui_locales=request.params.get("ui_locales"),
    user_preference=user.locale,
    accept_language=request.headers.get("Accept-Language"),
    available=["en", "fr", "de"],
    default="en",
)

Common Imports

# Factors
from neozen.core.factors.password import PasswordFactor, create_password_factor
from neozen.core.factors.totp import TOTPFactor, TOTPConfig
from neozen.core.factors.magic_link import MagicLinkFactor, MagicLinkConfig
from neozen.core.factors.email_otp import EmailOTPFactor, EmailOTPConfig

# Identifiers
from neozen.core.identifiers.email import EmailIdentifier, create_email_identifier

# Sessions
from neozen.storage.sqlalchemy import SessionServiceFactory

# Authorization
from neozen.storage.sqlalchemy import RoleServiceFactory, get_subject_roles
from neozen.core.authz.models import Permission, Role, RoleAssignment, ScopeType

# Groups & Invitations
from neozen.core.groups import GroupService, GroupRole, InvitationService

# Email
from neozen.core.email.base import EmailMessage, EmailSenderProtocol
from neozen.core.email.console import ConsoleEmailSender
from neozen.core.email.smtp import SMTPEmailSender
from neozen.core.email.notifications import NotificationService

# OAuth client
from neozen.core.oauth_client.service import OAuthClientService
from neozen.core.oauth_client.providers.google import GoogleProvider
from neozen.core.oauth_client.providers.github import GitHubProvider

# OIDC Provider
from neozen.core.oidc import InMemoryKeyManager, IDTokenGenerator, DefaultClaimsProvider
from neozen.core.oidc.discovery import build_metadata_from_base_url
from neozen.web.oidc import (
    DiscoveryEndpoint, JWKSEndpoint, UserInfoEndpoint,
    EndSessionEndpoint, RegistrationEndpoint,
)

# SCIM
from neozen.core.scim import SCIMUserService, SCIMGroupService, User, Group, Email
from neozen.web.scim import UsersEndpoint, GroupsEndpoint, DiscoveryEndpoints

# LDAP
from neozen.storage.ldap import LDAPConfig, LDAPSubjectRepository, authenticate_user

# i18n
from neozen.core.i18n import Translator, Catalog, builtin_translator, negotiate_locale

# Storage
from neozen.storage.sqlalchemy.repositories import SubjectRepository
from neozen.storage.sqlalchemy.base import Base
from neozen.core.repository import DoesNotExist, ReadableRepositoryProtocol, WritableRepositoryProtocol

# Framework extensions
from neozen.ext.flask import NeoZen, current_user, login_required, login_user, logout_user
from neozen.ext.litestar import NeoZenConfig, NeoZenPlugin, login_required, login_user, logout_user