Metadata-Version: 2.4
Name: aura-auth
Version: 0.1.1
Summary: Pluggable authentication framework for FastAPI.
Author-email: Chapi <chapimenge3@gmail.com>
Requires-Python: >=3.11
Requires-Dist: aiosqlite>=0.17
Requires-Dist: bcrypt>=4.0
Requires-Dist: email-validator>=2.0
Requires-Dist: fastapi>=0.100
Requires-Dist: pydantic>=2.0
Requires-Dist: pyjwt[crypto]>=2.0
Requires-Dist: python-multipart>=0.0.5
Requires-Dist: sqlalchemy[asyncio]>=2.0
Provides-Extra: 2fa
Requires-Dist: pyotp>=2.9; extra == '2fa'
Provides-Extra: admin
Requires-Dist: jinja2>=3.0; extra == 'admin'
Provides-Extra: all
Requires-Dist: aiomysql>=0.2; extra == 'all'
Requires-Dist: asyncpg>=0.27; extra == 'all'
Requires-Dist: httpx>=0.24; extra == 'all'
Requires-Dist: jinja2>=3.0; extra == 'all'
Requires-Dist: pyotp>=2.9; extra == 'all'
Requires-Dist: sqlmodel>=0.0.14; extra == 'all'
Requires-Dist: webauthn>=2.0; extra == 'all'
Provides-Extra: facebook
Requires-Dist: httpx>=0.24; extra == 'facebook'
Provides-Extra: github
Requires-Dist: httpx>=0.24; extra == 'github'
Provides-Extra: google
Requires-Dist: httpx>=0.24; extra == 'google'
Provides-Extra: magic-link
Requires-Dist: httpx>=0.24; extra == 'magic-link'
Provides-Extra: mysql
Requires-Dist: aiomysql>=0.2; extra == 'mysql'
Provides-Extra: oauth
Requires-Dist: httpx>=0.24; extra == 'oauth'
Provides-Extra: otp
Provides-Extra: passkey
Requires-Dist: webauthn>=2.0; extra == 'passkey'
Provides-Extra: postgres
Requires-Dist: asyncpg>=0.27; extra == 'postgres'
Provides-Extra: sqlmodel
Requires-Dist: sqlmodel>=0.0.14; extra == 'sqlmodel'
Description-Content-Type: text/markdown

<div align="center">

# Aura Auth

**Batteries-included authentication for FastAPI — install, wire three lines, ship.**

![Python 3.11+](https://img.shields.io/badge/python-3.11+-3776AB?style=flat&logo=python&logoColor=white)
![FastAPI](https://img.shields.io/badge/FastAPI-0.100+-009688?style=flat&logo=fastapi&logoColor=white)
![License](https://img.shields.io/badge/license-TBD-lightgrey?style=flat)

</div>

> **Early beta** — APIs, module layout, and defaults may change without a major-version bump while we learn from real apps. Pin versions in production and read the release notes when upgrading.

---

## Why this exists

If you have built auth in Python, you have probably felt the same friction:

- **“Batteries included” rarely means “works on Monday.”** You still glue together hashing, sessions or JWTs, a user model, migrations, login routes, dependency injection, and error shapes — often from blog posts that disagree with each other.
- **Frameworks tend to stop at the tutorial.** The happy path is documented; edge cases, transport choices (cookie vs bearer), and clean extension points are left as an exercise.
- **Teams get discouraged** and either ship something fragile, over-buy a SaaS, or copy-paste security-sensitive code they do not fully own.

**Aura Auth** is an attempt to fix that for the **FastAPI + async SQLAlchemy** lane: a **small, explicit library** where the default install gives you **email/password auth that actually runs** — identity and credential **accounts**, **server-side sessions**, models you can extend, and a ready-made router — while **optional extras** stay pluggable (OAuth, magic links, OTP, 2FA, passkeys, etc.) as the project grows.

We are not trying to be every auth product at once on day one. We *are* trying to be the library you reach for when you want **clarity, defaults that work, and a path to more** without rewriting your app.

---

## What you get today (default install)

| Capability | Notes |
|------------|--------|
| **Account-based data model** | `User` (identity) + `Account` (how you sign in, e.g. `provider_id="credential"` with bcrypt hash) + `Session` + `Verification` |
| **Mixins + defaults** | `UserMixin`, `AccountMixin`, `SessionMixin`, `VerificationMixin`; `DefaultUser` / `DefaultAccount` / `DefaultSession` / `DefaultVerification` for zero-config |
| **Async SQLAlchemy backend** | CRUD for all four entities with sensible errors (e.g. duplicate email) |
| **Password strategy** | Register creates user + credential account; login verifies password on that account |
| **Opaque session tokens** | Stored in `sessions` (revocable, listable); transported via **Bearer** or **cookie** — not a stateless login JWT |
| **Security helpers** | `PasswordHelper`, `TokenHelper` (JWT for future or custom flows), `generate_random_token` / `generate_session_token` |
| **`UserManager`** | Register, login, session resolution, logout, list/revoke sessions, email verification rows |
| **FastAPI integration** | `AuraAuth`, `init_app`, configurable `route_prefix`, `raw_router`, dependencies |

Optional installs (see `pyproject.toml` → `[project.optional-dependencies]`) add **OAuth** (`google`, `github`, `facebook`, `oauth`), **magic-link**, **2fa**, **passkey**, **sqlmodel**, **admin**, **postgres**, **mysql**, and an **`all`** extra.

---

## Installation

Requires **Python 3.11+**. `email-validator` is included so Pydantic `EmailStr` works out of the box.

**From PyPI** (once the package is published):

```bash
uv add aura-auth
# or
pip install aura-auth
```

**From GitHub** (typical during early beta):

```bash
uv add "aura-auth @ git+https://github.com/YOUR_ORG/aura-auth.git"
# or
pip install "git+https://github.com/YOUR_ORG/aura-auth.git"
```

Replace `YOUR_ORG/aura-auth` with your real repository path. For local development, use `uv pip install -e .` from a clone of this repo.

---

## Quickstart (copy-paste)

Minimal FastAPI app: create tables on startup, mount auth, protect a route.

```python
from fastapi import Depends, FastAPI

from aura_auth import AuraAuth

app = FastAPI()

# `secret` is required on the config surface today (signing / future JWT flows).
auth = AuraAuth(
    database_url="sqlite+aiosqlite:///./app.db",
    secret="change-me-to-a-long-random-secret-at-least-32-chars",
)


@app.on_event("startup")
async def startup() -> None:
    await auth.create_tables()


auth.init_app(app)  # registers /auth routes + exception handlers


@app.get("/protected")
async def protected(user=Depends(auth.current_user())):
    return {"message": f"Hello, {user.name}"}
```

**Default response shape for register and login** (`AuthResponse`): JSON includes **`user`** (profile) and **`session`** (`id`, `token`, `expires_at`). Send **`Authorization: Bearer <session.token>`** (or rely on the cookie when `cookie_transport=True`).

**Endpoints under `route_prefix` (default `/auth`):**

| Method | Path | Purpose |
|--------|------|---------|
| `POST` | `/auth/register` | Body: `name`, `email`, `password` → `201` + `AuthResponse`; sets session cookie if configured |
| `POST` | `/auth/login` | Body: `email`, `password` → `AuthResponse`; sets session cookie if configured |
| `POST` | `/auth/logout` | Revokes the current session; clears cookie when using cookie transport |
| `GET` | `/auth/me` | Current user (`UserRead`); requires session token |
| `GET` | `/auth/sessions` | Lists your non-expired sessions |
| `DELETE` | `/auth/sessions/{session_id}` | Revokes one of your sessions (`204` on success) |

**Custom URL prefix** (for example `/api/v1/auth`):

```python
auth = AuraAuth(
    database_url="sqlite+aiosqlite:///./app.db",
    secret="...",
    route_prefix="/api/v1/auth",
)
```

**Mount routes yourself** (full control over path):

```python
app.include_router(auth.raw_router, prefix="/identity")
```

**Dependencies you can attach to routes:**

- `Depends(auth.current_user())` — must be authenticated (401 otherwise)
- `Depends(auth.current_verified_user())` — must have `email_verified` (403 otherwise)

---

## Mental model (for humans and AI coding agents)

Think in **four layers** plus **four tables**. Everything else hangs off these names in the repo.

```mermaid
flowchart LR
  subgraph transport [Transport]
    T[Bearer / Cookie]
  end
  subgraph strategy [Strategy]
    S[Password — more later]
  end
  subgraph backend [Backend]
    B[SQLAlchemy async CRUD]
  end
  subgraph data [Data]
    U[User]
    A[Account]
    SE[Session]
    V[Verification]
  end
  T <--> SE
  S <--> A
  B <--> U
  B <--> A
  B <--> SE
  B <--> V
```

| Layer | Role | Main entry points in code |
|-------|------|---------------------------|
| **Transport** | How the **session token** is read/written on HTTP | `aura_auth.transport.*` |
| **Strategy** | How credentials map to a **user** + hooks | `aura_auth.strategies.password` |
| **Backend** | Persistence implementing **`BackendProtocol`** | `aura_auth.backend.sqlalchemy` |
| **Orchestration** | Wires pieces together for apps | `aura_auth.app.AuraAuth`, `aura_auth.manager.UserManager` |

**Contracts** (types, protocols, schemas, exceptions, security helpers) live under **`aura_auth._core`**. Routers and FastAPI dependencies live under **`aura_auth.router`** and **`aura_auth.dependencies`**.

If you are an **AI agent** implementing or extending this library:

1. Read **`AGENTS.md`** in this repo — phased plan, folder target, and conventions (imports, docstrings, testing layout). Some phase text may predate the account/session architecture; trust the source tree and this README for the current shape.
2. Prefer **protocols** in `_core` over importing concrete backends from “higher” layers — keeps cycles and coupling down.
3. Run **`make check`** (or `uv run ruff check .`, `uv run ty check`, `uv run pytest`) before proposing a PR-style change.

---

## Configuration highlights

`AuraAuth(database_url=..., secret=..., **kwargs)` forwards supported keys to **`AuraAuthConfig`** (`aura_auth._core.config`), including:

- `route_prefix` — URL prefix for the bundled router (default `"/auth"`)
- `session_lifetime_seconds` — session row lifetime and cookie `max-age` when using cookies (default one week)
- `verify_token_lifetime_seconds` — lifetime for **verification** rows (email verify, etc.)
- `cookie_transport` — register/login set `Set-Cookie`; `current_user` reads the session from the cookie (no `Authorization` header needed)
- `cookie_name`, `cookie_secure`, `cookie_httponly`, `cookie_samesite` — tune the session cookie (`cookie_secure=False` for plain HTTP local dev)
- `password_min_length` — minimum password length (default `8`)
- `user_model`, `account_model`, `session_model`, `verification_model` — optional custom SQLAlchemy models (defaults match the mixins)
- `engine` — *advanced / testing*: inject a pre-built async engine (see tests)

---

## Extending models

Subclass the mixins from `aura_auth.models.base` (or start from `DefaultUser` in `aura_auth.models.sqlalchemy`) and pass your types into `AuraAuth(..., user_model=MyUser, ...)`. Fields beyond **`UserProtocol`** are yours; the auth core only relies on the protocol surface.

---

## Development

Clone the repo, then:

```bash
make sync    # install with dev dependencies (uv)
make check   # lint + format check + types + tests
```

Individual targets: `make lint`, `make format`, `make type`, `make test`, `make test-cov`.

**Documentation site** (Fumadocs, Next.js) lives in **`docs/`**. From that directory: `pnpm install`, `pnpm dev`, and optional **`pnpm build`**. Endpoints **`/llms.txt`** and **`/llms-full.txt`** are generated for LLM-friendly exports.

---

## Project layout (source of truth)

```
src/aura_auth/
├── _core/           # types, protocols, schemas, exceptions, config, security
├── app.py           # AuraAuth + FastAPI wiring
├── manager.py       # UserManager (orchestration)
├── models/          # SQLAlchemy mixins + default models (user, account, session, verification)
├── backend/         # BaseBackend + SQLAlchemy implementation
├── strategies/      # Password (extensible)
├── transport/       # Bearer + cookie
├── router/          # FastAPI auth routes
├── dependencies.py  # current_user, verified
└── __init__.py      # lazy export of AuraAuth (avoids heavy imports on submodule import)
```

Tests mirror this under `tests/` (see `AGENTS.md` for the full testing map).

---

## Roadmap & stability

- **Now:** Email/password, server-side sessions, four-table SQLAlchemy backend, FastAPI router and dependencies, bearer/cookie transports, configurable route prefix.
- **Next:** Optional extras (OAuth, magic links, OTP, 2FA, passkeys) as documented in `AGENTS.md` and `pyproject.toml` extras.
- **Stability:** Until **1.0**, treat semver as *best effort*; breaking changes may land in **0.x** while the public surface stabilizes. Issues and design feedback are welcome.

---

## License

License TBD — add a `LICENSE` file when you publish to GitHub.

---

<div align="center">

**Built so you spend less time wiring auth and more time shipping your product.**

If Aura Auth saves you a day, a star on GitHub helps others find it.

</div>
