Skip to content

Repository Structure

.
├── app/
│   ├── main.py                 # Canonical FastAPI entrypoint
│   ├── openapi.py              # Global OpenAPI policy composition
│   ├── browser_sessions/       # Browser sessions, CSRF, cookies, and services
│   ├── oauth2/                 # OAuth2/OIDC flows and scoped session administration
│   ├── workflow_tokens/        # Single-use verification/reset token lifecycle
│   ├── bootstrap/              # First-run operator creation and process lock
│   ├── notifications/          # Durable authentication notifications and worker
│   ├── web/                    # Optional built-in Jinja browser presentation
│   ├── identity/               # Identity DTOs, registration, services, and persistence mapping
│   ├── password/               # Password hashing protocol and implementation
│   ├── api/                    # Versioned self-service and administration APIs
│   ├── security/               # AuthN/AuthZ composition and session_revocation
│   ├── core/                   # Shared errors, request helpers, logging, and primitives
│   ├── db/                     # SQLite engine, ORM models, and session setup
│   ├── mail/                   # Email rendering and SMTP delivery
│   └── settings/               # Server settings
├── tests/
│   ├── routes/                 # Black-box HTTP tests using the canonical app lifespan
│   ├── app/                    # Feature-level service, router, and contract tests
│   ├── fixtures/               # Shared canonical-app and protocol test setup
│   └── docs/                   # Executable documentation and snippet tests
├── docs/
│   ├── getting-started/       # Installation, initial configuration, first client
│   ├── guides/                # OAuth2, OIDC, sessions, users, and organizations
│   ├── operations/            # Deployment, persistence, workers, and security
│   ├── reference/             # Public settings, routes, and error contracts
│   └── development/           # Internals, architecture, tests, and contribution
├── alembic/                    # Relational schema migrations for the canonical server
├── alembic.ini                 # Alembic entrypoint configuration
├── config/
├── compose.yaml
└── pyproject.toml

Canonical Server Boundary

Zero Auth Lite is organized as one configurable authentication and authorization server. Feature folders own their domain behavior, HTTP dependencies, routes, and persistence. app/main.py is the server entrypoint: it attaches stable app state, always mounts the identity-management baseline, and composes the configured authentication mechanisms and protocol surfaces.

Identity workflows, workflow-token processing, identity and organization administration, and outbox dispatch are permanent server capabilities. Browser sessions, OAuth2, OIDC, and JWKS are configurable mechanisms. The built-in workflow HTML pages are controlled by ui.identity_workflow_mode. Session-backed organization and operator presentation is independently controlled by ui.organization_admin_enabled and ui.operator_enabled; authorization roles, permissions, and access checks remain part of the permanent baseline.

app/security/authentication.py composes browser-session and OAuth2 authentication into the canonical contexts defined by app/security/principals.py; app/security/roles.py owns application role identifiers. app/security/authorization.py owns the FastAPI dependencies that enforce roles, permissions, and OAuth2 scopes. The package also owns the transactional session_revocation boundary that invalidates browser and OAuth2 sessions together. Feature-neutral constant-time comparison helpers remain under app/core/compare.py; core does not compose feature implementations.

app/openapi.py is the application-level OpenAPI orchestrator. Generic schema transformations remain in app/core/openapi.py, OAuth2/OIDC protocol transformations remain in app/oauth2/openapi.py, and application API security policy remains in app/security/openapi.py. This keeps app/core/ independent of feature domains while making the global ordering visible at the root.

Authorization has two deliberate layers. FastAPI dependencies enforce the complete HTTP contract: required roles, application permissions, OAuth2 scopes, and CSRF proof where applicable. Actor-bound services repeat the role check so their domain entry points cannot be called with the wrong kind of actor, then enforce resource ownership, organization boundaries, and lifecycle invariants. Service role checks do not replace route permission or scope checks.

Black-box HTTP behavior is tested under tests/routes/ with HTTPX, the real FastAPI lifespan, and isolated migrated databases. Tests under tests/app/ exercise feature-level services, routers, validation, and generated OpenAPI contracts without claiming ownership of the black-box route surface. Reusable setup and test-data builders live under tests/fixtures/. Shared fake service implementations used by isolated feature tests live under tests/mocks/.

app/api/v1/ is not generic application CRUD. It is the minimal identity and organization administration surface needed to operate the authentication server: current profile, organization identities, current organization metadata, and, when at least one grant is enabled, OAuth2 client administration.

Server-operator OAuth2 client administration is composed from separate route modules for registry operations, credential rotation, user-organization access, and machine-organization access. Their URLs remain under /api/v1/server/oauth2/clients.

Route ownership follows a simple mount rule:

  • app/main.py mounts only top-level server surfaces such as /oauth2, /api, /health/*, and the settings-driven built-in web UI.
  • app/web/ owns HTML adapters and assets, while authentication and protocol services continue to own validation and business behavior.
  • app/api/v1/ contains only versioned API modules.
  • App-owned auth workflows that may evolve belong under /api/v1/auth.
  • Current-organization administration routes are grouped in app/api/v1/organization/; its router composes metadata, user, and optional OAuth2 session administration from explicitly named subpackages.
  • Browser-session behavior stays in app/browser_sessions/; its JSON transport adapter lives in app/api/v1/browser_sessions/, and the versioned route composition belongs to app/api/v1/router.py.
  • External OAuth2 interaction adapters live in app/api/v1/oauth2_interactions/; OAuth2 protocol validation and persisted interaction state remain in app/oauth2/.
  • Current-user OAuth2 session inspection and revocation lives in app/oauth2/user_oauth2_sessions/; its HTTP route is /api/v1/me/oauth2/sessions.
  • Current-user profile and account operations live in app/api/v1/me/profile.py. Its ordinary profile routes accept the configured user authentication mechanisms, while password replacement and account deletion are mounted only with browser sessions. This authentication policy does not make those account operations part of session administration; app/api/v1/me/sessions.py owns only browser-session listing and revocation.

The management presentation is grouped under app/web/management/, including the dashboard and account self-service route. Actor-specific routes live under operator/ and organization/; their small composers include user, session, and OAuth2-client routers only when the corresponding capability is enabled. Operator and organization user-session adapters declare their FastAPI routes explicitly. They share only actor-independent confirmation, mutation, cookie, and redirect behavior through app/web/management/user_session_actions.py.

Persistence

SQLite is the only supported relational target for the canonical server. Relational services receive the request-scoped SQLAlchemy session directly. Browser sessions and OAuth2 state always use SQLAlchemy so related lifecycle changes can share one transaction. Canonical ORM rows live in app/db/models/, while identity DTOs, criteria, services, and domain types remain under app/identity/. Engine and session setup also lives in app/db/. Alembic migrations live at the repository root under alembic/, and app/db/alembic.py exists only to expose the canonical relational metadata to that migration tool.

app/api/v1/auth/ owns the versioned authentication request schemas, domain-specific endpoint adapters, route paths, and startup-time composition. Its router mounts registration and new self-registration verification requests under identity_workflow.registration_enabled. Within an enabled interactive JSON transport, verification confirmation remains mounted for issued tokens, while email changes, password recovery, and invitation acceptance remain independently available. Built-in HTML confirmation is selected separately by the identity-workflow presentation mode. app/identity/services/registration.py owns the organization and initial-user transaction, while app/workflow_tokens/ owns single-use token issuance and confirmation. app/notifications/ persists notification intentions in the request transaction and builds them after commit, while app/mail/ owns rendering and SMTP integration. This keeps HTTP transport, identity lifecycle, token lifecycle, and delivery infrastructure as explicit boundaries. MailService accepts a narrow provider protocol. SMTP is the canonical shipped implementation; an embedding application can supply another asynchronous provider without adding vendor-specific integrations to the server itself.

Feature-local dtos.py modules contain reusable service-boundary and persistence shapes, named with a DTO suffix. Use Pydantic models when data crosses a validation or ORM boundary. Small immutable values that are already validated may remain dataclasses beside the behavior that owns them, with a domain name such as AccessTokenPayload, IssuedTokens, or ClientAuth. Actor-specific DTOs may be normalized into an immutable, actor-neutral *Command in a feature-local commands.py module when one lifecycle mutation needs a complete instruction independent of its caller. Services own the SQLAlchemy queries supporting their domain decisions and do not import app/api/. Application-owned HTTP request and response models live beside their versioned routers under app/api/v1/ and use Request or Response suffixes. Standardized OAuth2 and OIDC protocol routes retain their protocol-local transport models and typed FastAPI extraction.

OAuth2 grant handlers keep grant-specific authentication, authorization, and validation visible. Once those decisions are complete, app/oauth2/tokens/issuance.py owns shared token creation, token hashing, new OAuth2-session persistence, and construction of the standardized token response. Refresh rotation reuses token creation while retaining its separate conditional update and family-reuse protections.

Deliberate Non-Goals

The repository avoids SAML, SCIM, LDAP, social login, provider federation, enterprise IAM administration, bot or fraud detection, built-in request limiting, WAF or edge-security systems, and Kubernetes deployment scaffolding. Those topics would make the auth concepts harder to inspect in a small reference server.