Add sign-in to a Python backend with Authdog. This guide uses **FastAPI**, but
the same three primitives — a session resolver, a `require_auth` gate, and a
logout handler — exist for Flask, Django, Starlette, and aiohttp over the same
shared core.

## Prerequisites

- Python 3.10+
- A clone of the
  [`authdog/web-sdk`](https://github.com/authdog/web-sdk) repository pinned to
  the commit you have reviewed. The Python package is currently source-only and
  is not published on PyPI.
- An Authdog environment and its **public key** (`pk_…`), available in the
  [Authdog console](https://console.authdog.com).
- A frontend that signs users in against the same Authdog environment. The
  Python SDK **validates** the session that your frontend establishes — it reads
  the `authdog-session` cookie (or an `Authorization: Bearer <token>` header) and
  verifies it against the identity provider's `userinfo` endpoint.

## Install from source

```bash
git clone https://github.com/authdog/web-sdk.git
cd web-sdk
python -m pip install "./packages/python[fastapi]"
```

Choose the extra for your framework:

```bash
python -m pip install "./packages/python[flask]"
python -m pip install "./packages/python[django]"
```

Pin the repository commit in deployment automation. Published compatibility is
not yet guaranteed, so evaluate the source and run your integration tests before
production use.

## Configure the public key

Set your Authdog public key as an environment variable rather than hard-coding
it:

```bash
export PK_AUTHDOG="pk_..."
```

The key is validated and parsed **once at startup** — a malformed key, or one
whose identity host is not on the trusted allowlist, raises immediately instead
of failing on the first request.

## Resolve the session

Create an `Authdog` instance and use `authdog.session` as a FastAPI dependency.
It reads the token, calls `userinfo`, and returns a typed `AuthdogContext`. It
**never raises** — a missing or invalid token simply yields
`is_authenticated == False`.

```python
import os
from fastapi import Depends, FastAPI, Request
from authdog.fastapi import Authdog

app = FastAPI()
authdog = Authdog(public_key=os.environ["PK_AUTHDOG"])

@app.get("/")
async def index(ctx=Depends(authdog.session)):
    return {"authenticated": ctx.is_authenticated}
```

The context is:

```python
@dataclass
class AuthdogContext:
    token: str | None = None
    user: Any | None = None
    is_authenticated: bool = False
    user_info: dict | None = None
```

## Protect a route

`authdog.require_auth` is the **real server-side enforcement point**. It raises
`HTTPException(401)` for unauthenticated requests and otherwise returns the user
object, so a handler can depend on it directly:

```python
@app.get("/me")
async def me(user=Depends(authdog.require_auth)):
    return user
```

Every protected route must depend on `require_auth`. Reading
`ctx.is_authenticated` from `session` is fine for shaping a response, but it is
not a security boundary on its own.

Because the resolved context is cached on `request.state`, combining `session`
and `require_auth` on the same request makes **at most one** outbound `userinfo`
call.

## Add a logout handler

`authdog.logout(request)` returns a `RedirectResponse` that expires the
`authdog-session` cookie and redirects to the `redirect_uri` query parameter,
sanitized against open redirects:

```python
@app.get("/logout")
async def logout(request: Request):
    return authdog.logout(request)
```

The cookie is cleared with `HttpOnly`, `SameSite=Lax`, and `Secure` when
`ENV=production`.

## Skip the userinfo round-trip

For high-throughput services that validate the token elsewhere, disable the
lookup:

```python
authdog = Authdog(public_key=os.environ["PK_AUTHDOG"], fetch_user=False)
```

`ctx.token` is populated but `is_authenticated` stays `False` — you own
validation.

## Next steps

- [Basic access control](/guides/python/access-control) — authorize routes by
  role and permission on top of the authenticated user.
- [Python SDK overview](/product/python) — the framework matrix and feature
  tour.
