From d23d788bb4b186dd8098bf7672e4e8e297044487 Mon Sep 17 00:00:00 2001 From: Sourav Basu Date: Thu, 29 Oct 2026 10:57:15 +0530 Subject: [PATCH 1/2] docs: add CLAUDE.md, AGENTS.md and agent reference docs Co-Authored-By: Claude --- AGENTS.md | 3 ++ CLAUDE.md | 108 +++++++++++++++++++++++++++++++++++++ references/code-style.md | 72 +++++++++++++++++++++++++ references/commands.md | 40 ++++++++++++++ references/docs-update.md | 32 +++++++++++ references/git-workflow.md | 24 +++++++++ references/pitfalls.md | 13 +++++ references/testing.md | 26 +++++++++ 8 files changed, 318 insertions(+) create mode 100644 AGENTS.md create mode 100644 CLAUDE.md create mode 100644 references/code-style.md create mode 100644 references/commands.md create mode 100644 references/docs-update.md create mode 100644 references/git-workflow.md create mode 100644 references/pitfalls.md create mode 100644 references/testing.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..2b934eb --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,3 @@ +# AI Agent Guidelines for auth0-fastapi + +@./CLAUDE.md for all coding guidelines, commands, project structure, code style, testing conventions, and boundaries. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..9b38718 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,108 @@ +# AI Agent Guidelines for auth0-fastapi + +## Your Role + +You are a Python SDK engineer maintaining auth0-fastapi, the FastAPI adapter over auth0-server-python for server-side web apps. You write thin async wrappers plus the routes, cookie stores, and tests around them. + +--- + +## Project Structure + +``` +auth0-fastapi/ +├── src/auth0_fastapi/ # The package (no top-level __init__.py, import from submodules) +│ ├── config.py # Auth0Config pydantic model (all integrator settings) +│ ├── auth/auth_client.py # AuthClient: public API, each method delegates to ServerClient +│ ├── server/routes.py # router + register_auth_routes (/auth/login, /callback, /logout, ...) +│ ├── errors/__init__.py # Re-exported auth0-server-python errors, ConfigurationError, exception handler +│ ├── stores/ # CookieTransactionStore, StatelessStateStore, StatefulStateStore +│ ├── util/__init__.py # URL helpers incl. to_safe_redirect and build_request_base_url +│ └── test/ # pytest suite (tests live inside the package) +├── examples/ # Feature guides: Passkeys, CustomTokenExchange, MultipleCustomDomains, Sessions +├── references/ # Agent reference docs offloaded from this file +├── .github/workflows/ # tests.yml (lint + tests), sca_scan.yml (Snyk), codeql.yml, publish.yml +├── pyproject.toml # Poetry manifest, pytest addopts (coverage) +├── requirements.txt # Pip copy of runtime and test deps, installed by the SCA workflow +├── .ruff.toml # Lint config (line length 120, py39 target, bandit rules) +├── .version # Release version read by publish.yml (release commits also bump pyproject.toml) +└── CHANGELOG.md # Release notes, written during the release flow +``` + +--- + +## Boundaries + +### Always Do + +- Run `poetry run ruff check .` and `poetry run pytest` before committing. CI runs both on Python 3.9 to 3.12. +- Make surgical changes. Touch only what the request requires, and don't refactor or reformat adjacent code that isn't broken. +- Keep `AuthClient` methods as thin pass-throughs to `self.client` (`ServerClient`). Protocol, token, and crypto logic belongs in auth0-server-python. +- Add a new feature's public methods at the end of `AuthClient`, and keep private helpers grouped together, never between public methods. +- Write docstrings as a one-line summary, then `Args`, `Returns`, `Raises`, matching the passkey and custom token exchange methods. +- Add unit tests in `src/auth0_fastapi/test/` for every change, and mark async tests with `@pytest.mark.asyncio`. +- Raise or re-export typed errors from `auth0_fastapi.errors`. Map new `Auth0Error` subclasses in `auth0_exception_handler` when they need a non-400 status. +- Pass every user-supplied redirect target (`returnTo` and similar) through `to_safe_redirect` before redirecting. +- Update `README.md` and the matching `examples/*.md` guide in the same PR when changing the public API, `Auth0Config` options, mounted routes, or supported integration patterns. +- When a runtime or test dependency changes, update both `pyproject.toml` and `requirements.txt` (the SCA workflow installs `requirements.txt` before the Snyk scan). +- Use Python 3.9 compatible syntax: `Optional[X]` and `Union[X, Y]`, not `X | Y`, in annotations that FastAPI or pydantic evaluate. + +### Ask First + +- Any breaking change. Never make one on your own initiative. Stop and ask the maintainer before writing it. +- Adding, removing, or bumping a dependency, including the `auth0-server-python` floor. +- Changing a public signature in `AuthClient`, `Auth0Config`, `register_auth_routes`, the stores, or `auth0_fastapi.errors`. +- Changing a mounted route path, its query or body contract, or its status codes. +- Changing cookie names, cookie attributes, chunking, or session expiry. A name or chunking change makes existing sessions unreadable. +- Changing the status-code mapping in `auth0_exception_handler`. +- Changing CI workflows, `.github/actions/`, `.ruff.toml`, or `.snyk`. +- Running `ruff format` on any file. CI doesn't enforce it, and it can rewrite lines the task didn't touch. + +### Never Do + +- Commit secrets, API keys, tokens, or a real `.env`. Test fixtures use synthetic values only. +- Log or echo tokens, session data, `client_secret`, or `secret`, including in error messages and exception `detail`. +- Weaken cookie flags (`secure`, `httponly`, `samesite`) or the issuer check in `StatefulStateStore.delete_by_logout_token`. +- Redirect to a URL built from request input without `to_safe_redirect`. +- Hand-edit `poetry.lock`, or bump the version in `.version` or `pyproject.toml` outside the release flow. +- Modify `.venv/`, `build/`, `dist/`, `htmlcov/`, `.pytest_cache/`, or `__pycache__/`. +- Remove or skip failing tests without fixing them. + +--- + +## Security Considerations + +- **App type:** confidential client (server-side web app). `client_secret` and `secret` stay server-side and come from the environment, never source. +- **Delegated protocol logic:** authorize, token exchange, logout token, and ID token handling all run in `ServerClient` from auth0-server-python. The two cookie stores encrypt with that package's store `encrypt`/`decrypt`, keyed by `Auth0Config.secret`. Don't add crypto here. +- **Cookies:** the transaction cookie `_a0_tx` is `secure`, `httponly`, `samesite=Lax`, `max_age=60` (`stores/cookie_transaction_store.py`). The stateless session is chunked into `_a0_session_` cookies that are `secure`, `httponly`, `samesite=Lax` (`stores/stateless_state_store.py`). +- **Stateful store:** `StatefulStateStore` sets only `httponly` on its session-ID cookie (`stores/stateful_state_store.py`). +- **Open redirect:** `to_safe_redirect` (`util/__init__.py`) only allows the same origin as `app_base_url` (or the request base URL under MCD). The callback and connect routes depend on it. `/auth/logout` passes `returnTo` straight to Auth0's logout URL without it. +- **Multiple Custom Domains:** `build_request_base_url` trusts `x-forwarded-host` and `x-forwarded-proto`. Domain resolvers must map hosts through an allowlist (see `examples/MultipleCustomDomains.md`, Security Best Practices). +- **Backchannel logout:** `StatefulStateStore.delete_by_logout_token` skips sessions whose stored domain doesn't match the token `iss` when both are present, which prevents cross-domain deletion under MCD. +- **Error responses:** `auth0_exception_handler` returns only `error` (code) and `message`. Some routes wrap failures in `HTTPException(detail=str(e))`. Don't copy that pattern into new code. +- **Lint:** ruff's bandit rules (`S`) run in CI. Of those, tests ignore only `S101`, `S105`, `S106`. + +--- + +## Commands + +Install with `poetry install`, lint with `poetry run ruff check .`, and test with `poetry run pytest`. See [references/commands.md](references/commands.md) for the full command list. Read it only when you need to build, lint, or test. + +## Testing + +The default `poetry run pytest` suite needs no credentials. HTTP is mocked with `unittest.mock` or `httpx.MockTransport`. See [references/testing.md](references/testing.md) for layout, fixtures, mocking patterns, and coverage. Read it when you write or debug tests. + +## Code Style + +`ruff check` enforces 120-character lines and isort import order. Naming isn't linted, but the code uses snake_case functions and fields and PascalCase classes. See [references/code-style.md](references/code-style.md) for examples and patterns. Read it when you add or change public API. + +## Git Workflow + +Conventional commit subjects (`feat:`, `fix:`, `docs:`, `test:`, `refactor:`, `build:`, `chore:`, `revert:`) on a typed branch such as `feat/`. See [references/git-workflow.md](references/git-workflow.md) for branch and PR conventions. Read it before you commit or open a PR. + +## Common Pitfalls + +Known gotchas include a broken local `.venv`, the ignored `cookie_name` option, and import paths. See [references/pitfalls.md](references/pitfalls.md). Read it when something fails unexpectedly or before you touch config, stores, or imports. + +## Docs Update Rules + +Docs ship in the same PR as the code they describe. See [references/docs-update.md](references/docs-update.md) for the tracked docs and the code-to-docs mapping. Read it before you finish any change to public API, config, or routes. diff --git a/references/code-style.md b/references/code-style.md new file mode 100644 index 0000000..1c5aeb0 --- /dev/null +++ b/references/code-style.md @@ -0,0 +1,72 @@ +# Code Style + +## Enforced by `ruff check` (`.ruff.toml`) + +- Line length 120, target `py39`. +- Rule sets: `E`, `W`, `F`, `I` (isort), `C4`, `UP`, `S` (bandit), `DTZ`, `G`, `A`, `C`. +- Per-file ignores exist for `__init__.py` re-exports, `config.py` long lines, `routes.py` complexity, and tests. Don't add new suppressions without a reason. + +## Naming + +- snake_case for functions, methods, variables, and `Auth0Config` fields. camelCase appears only where an external contract needs it: pydantic aliases (`clientId`, `appBaseUrl`) and query parameters or `app_state` keys (`returnTo`, `connectionScope`). +- PascalCase for classes. Errors end in `Error` and set a `code` attribute. + +## Wrapper pattern + +Public `AuthClient` methods forward their arguments to `ServerClient` with minimal glue, such as building an options object (`StartInteractiveLoginOptions`, `LogoutOptions`). Methods that accept `store_options` take it last and document it. + +Good (`auth/auth_client.py`): + +```python + async def custom_token_exchange( + self, + options: CustomTokenExchangeOptions, + store_options: dict = None, + ) -> TokenExchangeResponse: + """ + Performs an RFC 8693 token exchange for the given subject token. + Does not create or modify the current session. + + Args: + options: Subject token details and exchange parameters + (subject_token, subject_token_type, audience, scope, + organization, authorization_params). + store_options: Optional options passed to the Transaction and State + Store. Only required when using Multiple Custom Domains, where + the domain resolver needs the incoming request. + + Returns: + The raw TokenExchangeResponse (access_token, expires_in). + + Raises: + CustomTokenExchangeError: If the exchange fails or the subject + token parameters are invalid (see CustomTokenExchangeErrorCode). + """ + return await self.client.custom_token_exchange(options, store_options=store_options) +``` + +Bad: + +```python + async def custom_token_exchange(self, options, store_options: dict | None = None): + # Call the token endpoint directly + async with httpx.AsyncClient() as http: + resp = await http.post(f"https://{self.config.domain}/oauth/token", data=options) + logger.info("token exchange result %s", resp.json()) + return resp.json() +``` + +The bad version reimplements protocol logic owned by auth0-server-python, logs tokens, uses `X | None` (breaks on Python 3.9 where evaluated), drops the return type, and has a narrating comment instead of a docstring. + +## Patterns + +- **Adapter:** `AuthClient` wraps `ServerClient`. The stores subclass `TransactionStore`/`StateStore` from `auth0_server_python.store.abstract` and read `request`/`response` from `store_options`. +- **Config-driven routes:** `register_auth_routes(router, config)` mounts route groups by `mount_routes`, `mount_connect_routes`, and `mount_connected_account_routes`, and raises `ConfigurationError` on invalid combinations. +- **Dependency injection:** routes get the client through `Depends(get_auth_client)`, which reads `request.app.state.auth_client`. Apps protect routes with `Depends(auth_client.require_session)`. +- **Error mapping:** `register_exception_handlers(app)` installs `auth0_exception_handler`, which maps `Auth0Error` subclasses to HTTP statuses with a `{"error", "message"}` body. + +## Comments and docs + +- No inline comments that narrate a self-explanatory line. +- Plain hyphens only in code, docstrings, and docs. No em or en dashes. +- Public docs and docstrings state only behavior that Auth0's public docs or the auth0-server-python public API confirm. Don't describe Auth0 backend internals. diff --git a/references/commands.md b/references/commands.md new file mode 100644 index 0000000..d2def43 --- /dev/null +++ b/references/commands.md @@ -0,0 +1,40 @@ +# Commands + +Run everything through Poetry. CI pins Poetry 2.2.1 and creates the virtualenv in-project (`.github/workflows/tests.yml`). + +```bash +# Install runtime and dev dependencies +poetry install + +# Lint (the exact CI step) +poetry run ruff check . + +# Auto-fix lint findings in the files you changed +poetry run ruff check --fix + +# Run all unit tests (pyproject.toml addopts add coverage and an htmlcov/ report) +poetry run pytest + +# Run tests the way CI does +poetry run pytest --cov=auth0_fastapi --cov-report=xml --cov-report=term-missing + +# Run one test file, one class, or one test +poetry run pytest src/auth0_fastapi/test/test_routes.py +poetry run pytest src/auth0_fastapi/test/test_auth_client.py::TestPasskey +poetry run pytest -k "passkey" + +# Build the sdist and wheel into dist/ +poetry build +``` + +## Not available + +- **Formatter:** CI does not run `ruff format`, and running it reformats files the task didn't touch. Format by hand to match the surrounding code. +- **Type checker:** no mypy or pyright configuration exists. +- **Clean target:** none. Generated output (`dist/`, `htmlcov/`, `.coverage`, `.pytest_cache/`) is not tracked in git. + +## CI-only jobs + +- `sca_scan.yml` installs `requirements.txt` into a venv and runs the Snyk scan from `auth0/devsecops-tooling`. +- `codeql.yml` runs CodeQL analysis. +- `publish.yml` is a manual `workflow_dispatch` release that reads the version from `.version`, runs the RL scanner, creates the GitHub release, and publishes to PyPI. Agents don't run it. diff --git a/references/docs-update.md b/references/docs-update.md new file mode 100644 index 0000000..6a47137 --- /dev/null +++ b/references/docs-update.md @@ -0,0 +1,32 @@ +# Docs Update Rules + +A PR that adds or changes public API, configuration, routes, or integration patterns is not complete until the matching docs are updated in the same PR. + +## Tracked docs + +| Doc | Covers | Exists | +|-----|--------|--------| +| `README.md` | Features, installation, minimal and advanced setup, mounted routes, protecting routes, audience, MCD, session expiry, custom token exchange and passkey summaries | present | +| `EXAMPLES.md` | Not used. This repo keeps examples as per-feature guides under `examples/` | missing | +| `examples/Passkeys.md` | Passkey signup, login, identifier verification, security considerations, errors, MFA, organizations, DPoP | present | +| `examples/CustomTokenExchange.md` | Token exchange with and without a session, session transfer (STT), organization scoping, error codes | present | +| `examples/MultipleCustomDomains.md` | Domain resolvers, redirect URIs, proxy headers, session behavior, security best practices | present | +| `examples/Sessions.md` | Upstream IdP `session_expiry` handling | present | + +Ask before creating `EXAMPLES.md`. New guides go in `examples/.md` and get a link from `README.md`. + +## When you change code, update these docs + +| When this changes | Update | +|-------------------|--------| +| A public `AuthClient` method added, removed, renamed, or its arguments changed | `README.md` (Usage) and the feature guide in `examples/` that calls it | +| `Auth0Config` fields, defaults, or aliases | `README.md` (Setup, Routes, Usage snippets that construct `Auth0Config`) | +| Mounted routes or `mount_*` flags in `server/routes.py` | `README.md` (4. Routes) | +| Store classes or their constructor options | `README.md` (Customizing the Cookie Stores) | +| Login, callback, logout, or backchannel logout behavior | `README.md` (Features, Routes) | +| Passkey wrappers or `PasskeyError` handling | `examples/Passkeys.md` and the README Passkeys section | +| Custom token exchange or session transfer wrappers | `examples/CustomTokenExchange.md` | +| Callable `domain`, `build_request_base_url`, or MCD redirect logic | `examples/MultipleCustomDomains.md` and the README MCD section | +| Session expiry or session-read behavior | `examples/Sessions.md` | +| Errors re-exported from `auth0_fastapi.errors` or their HTTP status | The error handling section of the affected guide | +| Install requirements or minimum Python version | `README.md` (2. Installation) | diff --git a/references/git-workflow.md b/references/git-workflow.md new file mode 100644 index 0000000..7966263 --- /dev/null +++ b/references/git-workflow.md @@ -0,0 +1,24 @@ +# Git Workflow + +## Branches + +- Branch from `main`. Use a typed prefix that matches the change: `feat/`, `fix/`, `docs/`, `chore/`. Some branches use a Jira key instead (`SDK-8920-cte`). +- `release/` branches (e.g. `release/1.0.0b12`) carry the release PR that bumps `.version`, `pyproject.toml`, and `CHANGELOG.md`. Don't create them as part of a feature change. +- Dependabot opens `dependabot/pip/*` and `dependabot/github_actions/*` branches daily. It ignores semver-major bumps (`.github/dependabot.yml`). + +## Commits + +- Conventional commit subject: `feat:`, `fix:`, `docs:`, `test:`, `refactor:`, `build:` (dependency floors), `chore:`, `revert:`. One line, lowercase after the prefix, no trailing period, and say what changed (e.g. `fix: relay the status Auth0 returned for passkey errors`). +- One logical change per commit. Docs, tests, and code for the same change can land as separate commits in one PR. +- The repo has no commitlint or pre-commit config. Reviewers enforce the format. + +## Pull requests + +Follow `.github/PULL_REQUEST_TEMPLATE.md`: + +- **Changes:** what and why, including endpoints and classes or methods added, changed, or deprecated, plus a usage summary for public API changes. +- **References:** supporting links (ticket, community post). +- **Testing:** how reviewers test it, plus the unit coverage, integration coverage, and latest-platform checkboxes. +- **Checklist:** contribution guidelines, code of conduct, all tests pass. + +CODEOWNERS assigns every path to `@auth0/project-dx-sdks-engineer-codeowner`. A Claude review workflow (`claude-code-review.yml`) responds to PR comments and reviews. diff --git a/references/pitfalls.md b/references/pitfalls.md new file mode 100644 index 0000000..cc2d56f --- /dev/null +++ b/references/pitfalls.md @@ -0,0 +1,13 @@ +# Common Pitfalls + +1. **`ModuleNotFoundError: auth0_server_python` during test collection.** If auth0-server-python was linked into `.venv` as an editable install from a local checkout that has since moved or been deleted, every test module fails to import. Run `poetry install` to restore the locked version, after confirming with the developer that they don't need the local link. + +2. **No top-level package import.** `src/auth0_fastapi/` has no `__init__.py`, so `from auth0_fastapi import AuthClient` raises `ImportError`. Import from submodules: `auth0_fastapi.auth.auth_client`, `auth0_fastapi.config`, `auth0_fastapi.server.routes`, `auth0_fastapi.errors`, `auth0_fastapi.stores`. + +3. **`Auth0Config.cookie_name` is not applied.** `AuthClient.__init__` builds the default `StatelessStateStore` with a hardcoded `cookie_name="_a0_session"`. To change the session cookie name today, pass a store explicitly. Fixing the default changes cookie names, which is an Ask First change. + +4. **Unmarked async tests are skipped.** pytest-asyncio runs in strict mode (no `asyncio_mode` in `pyproject.toml`), so pytest skips an `async def test_...` without `@pytest.mark.asyncio` and only emits a warning. Copy the marker from neighbouring tests. + +5. **Route registration mutates the router you pass.** `register_auth_routes(router, config)` adds route handlers to the given `APIRouter`. Calling it twice on the shared module-level `router` registers the routes twice. In tests, build a fresh `APIRouter()` per case. Enabling both `mount_connect_routes` and `mount_connected_account_routes` raises `ConfigurationError`. + +6. **Dependency lists live in two files.** `pyproject.toml` drives Poetry and `poetry.lock`. `requirements.txt` drives the Snyk SCA job. A bump made in only one leaves the scan checking a different version than users install. diff --git a/references/testing.md b/references/testing.md new file mode 100644 index 0000000..9f09da3 --- /dev/null +++ b/references/testing.md @@ -0,0 +1,26 @@ +# Testing + +- **Framework:** pytest with pytest-asyncio, pytest-mock, and pytest-cov (versions in `pyproject.toml` `[tool.poetry.group.dev.dependencies]`). +- **Location:** `src/auth0_fastapi/test/test_*.py`, one file per module or feature area. +- **Coverage:** `pyproject.toml` addopts always run `--cov=auth0_fastapi --cov-report=term-missing --cov-report=html`. No coverage threshold is configured. +- **Credentials:** the default `poetry run pytest` suite is unit-only and needs no tenant or secrets. + +## Conventions + +- Group tests in `Test` classes (`TestLoginFlow`, `TestSessionSecurity`, `TestPasskey`) and give each test a one-line docstring. +- Put fixtures at the top of the file (`auth_config`, `mock_request`, `mock_response`, `auth_client`), not between test classes. +- pytest-asyncio runs in strict mode (no `asyncio_mode` set), so every async test needs `@pytest.mark.asyncio`. +- Use synthetic values only: `test.auth0.com`, `test_client_id`, `test_secret_key_minimum_32_characters`. +- Plain `assert` is allowed in tests (`S101` is ignored for `src/auth0_fastapi/test/**`). + +## Mocking + +| Layer under test | Pattern | Example | +|------------------|---------|---------| +| Routes | `Mock(spec=AuthClient)` with `AsyncMock` methods, call `register_auth_routes` on a fresh router | `test_routes.py` `mock_auth_client` fixture | +| `AuthClient` wrappers | `patch.object(auth_client.client, "", new_callable=AsyncMock)` and assert the forwarded arguments | `test_auth_client.py` `TestLoginFlow` | +| `AuthClient` construction | `patch("auth0_fastapi.auth.auth_client.ServerClient")` and inspect the kwargs | `test_auth_client.py` `TestAuthClientInitialization` | +| End to end through the real `ServerClient` | Monkeypatch `httpx.AsyncClient` to use `httpx.MockTransport` with a fake Auth0 that signs ID tokens with a generated JWK | `test_passkey_http.py` `auth0` fixture | +| Stores | Mock `Request.cookies` and `Response.set_cookie`/`delete_cookie`, or a fake async backend store for `StatefulStateStore` | `test_stateless_store.py`, `test_stateful_store.py` | + +Use the `httpx.MockTransport` pattern when the behavior depends on how auth0-server-python parses Auth0's HTTP response (status codes, error bodies, ID token validation). Wrapper-only changes are covered by `patch.object`. From d27780fd6ec26ff086e4cb980384560fdd86f702 Mon Sep 17 00:00:00 2001 From: Sourav Basu Date: Thu, 29 Oct 2026 12:53:07 +0530 Subject: [PATCH 2/2] docs: correct inaccurate claims in agent guidelines Co-Authored-By: Claude --- CLAUDE.md | 6 +++--- references/code-style.md | 2 +- references/git-workflow.md | 4 ++-- references/pitfalls.md | 2 +- references/testing.md | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index 9b38718..e9610be 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -2,7 +2,7 @@ ## Your Role -You are a Python SDK engineer maintaining auth0-fastapi, the FastAPI adapter over auth0-server-python for server-side web apps. You write thin async wrappers plus the routes, cookie stores, and tests around them. +You are a Python SDK engineer maintaining auth0-fastapi, the FastAPI adapter over auth0-server-python for server-side web apps. You write thin wrappers plus the routes, cookie stores, and tests around them. --- @@ -11,7 +11,7 @@ You are a Python SDK engineer maintaining auth0-fastapi, the FastAPI adapter ove ``` auth0-fastapi/ ├── src/auth0_fastapi/ # The package (no top-level __init__.py, import from submodules) -│ ├── config.py # Auth0Config pydantic model (all integrator settings) +│ ├── config.py # Auth0Config pydantic model (integrator settings) │ ├── auth/auth_client.py # AuthClient: public API, each method delegates to ServerClient │ ├── server/routes.py # router + register_auth_routes (/auth/login, /callback, /logout, ...) │ ├── errors/__init__.py # Re-exported auth0-server-python errors, ConfigurationError, exception handler @@ -52,7 +52,7 @@ auth0-fastapi/ - Adding, removing, or bumping a dependency, including the `auth0-server-python` floor. - Changing a public signature in `AuthClient`, `Auth0Config`, `register_auth_routes`, the stores, or `auth0_fastapi.errors`. - Changing a mounted route path, its query or body contract, or its status codes. -- Changing cookie names, cookie attributes, chunking, or session expiry. A name or chunking change makes existing sessions unreadable. +- Changing cookie names, cookie attributes, chunking, or session expiry. Renaming a cookie makes existing sessions unreadable. - Changing the status-code mapping in `auth0_exception_handler`. - Changing CI workflows, `.github/actions/`, `.ruff.toml`, or `.snyk`. - Running `ruff format` on any file. CI doesn't enforce it, and it can rewrite lines the task didn't touch. diff --git a/references/code-style.md b/references/code-style.md index 1c5aeb0..1fc5a0b 100644 --- a/references/code-style.md +++ b/references/code-style.md @@ -9,7 +9,7 @@ ## Naming - snake_case for functions, methods, variables, and `Auth0Config` fields. camelCase appears only where an external contract needs it: pydantic aliases (`clientId`, `appBaseUrl`) and query parameters or `app_state` keys (`returnTo`, `connectionScope`). -- PascalCase for classes. Errors end in `Error` and set a `code` attribute. +- PascalCase for classes. Errors end in `Error`. `ConfigurationError`, the only error defined in this package, sets a `code` attribute. ## Wrapper pattern diff --git a/references/git-workflow.md b/references/git-workflow.md index 7966263..830678a 100644 --- a/references/git-workflow.md +++ b/references/git-workflow.md @@ -2,9 +2,9 @@ ## Branches -- Branch from `main`. Use a typed prefix that matches the change: `feat/`, `fix/`, `docs/`, `chore/`. Some branches use a Jira key instead (`SDK-8920-cte`). +- Branch from `main`. Use a typed prefix that matches the change: `feat/`, `docs/`, `chore/`. Some branches use a Jira key instead (`SDK-8920-cte`). - `release/` branches (e.g. `release/1.0.0b12`) carry the release PR that bumps `.version`, `pyproject.toml`, and `CHANGELOG.md`. Don't create them as part of a feature change. -- Dependabot opens `dependabot/pip/*` and `dependabot/github_actions/*` branches daily. It ignores semver-major bumps (`.github/dependabot.yml`). +- Dependabot opens `dependabot/pip/*` and `dependabot/github_actions/*` branches daily. For pip it ignores semver-major bumps (`.github/dependabot.yml`). ## Commits diff --git a/references/pitfalls.md b/references/pitfalls.md index cc2d56f..67e872c 100644 --- a/references/pitfalls.md +++ b/references/pitfalls.md @@ -10,4 +10,4 @@ 5. **Route registration mutates the router you pass.** `register_auth_routes(router, config)` adds route handlers to the given `APIRouter`. Calling it twice on the shared module-level `router` registers the routes twice. In tests, build a fresh `APIRouter()` per case. Enabling both `mount_connect_routes` and `mount_connected_account_routes` raises `ConfigurationError`. -6. **Dependency lists live in two files.** `pyproject.toml` drives Poetry and `poetry.lock`. `requirements.txt` drives the Snyk SCA job. A bump made in only one leaves the scan checking a different version than users install. +6. **Dependency lists live in two files.** `pyproject.toml` drives Poetry and `poetry.lock`. `requirements.txt` is what the SCA workflow installs before its Snyk scan. Update both so the scanned dependencies match what Poetry resolves. diff --git a/references/testing.md b/references/testing.md index 9f09da3..676a386 100644 --- a/references/testing.md +++ b/references/testing.md @@ -3,7 +3,7 @@ - **Framework:** pytest with pytest-asyncio, pytest-mock, and pytest-cov (versions in `pyproject.toml` `[tool.poetry.group.dev.dependencies]`). - **Location:** `src/auth0_fastapi/test/test_*.py`, one file per module or feature area. - **Coverage:** `pyproject.toml` addopts always run `--cov=auth0_fastapi --cov-report=term-missing --cov-report=html`. No coverage threshold is configured. -- **Credentials:** the default `poetry run pytest` suite is unit-only and needs no tenant or secrets. +- **Credentials:** the default `poetry run pytest` suite runs offline and needs no tenant or secrets. ## Conventions