Outcome: an Express app that rejects anonymous callers on `GET /me` and returns the Authdog user after a valid session. About five minutes.

This is a **backend** gate. Hosted sign-in still happens in a browser (Account portal or a frontend SDK). This page only validates the session Express receives.

0. Prerequisites: Node 20+ and an Authdog environment. You will set `PK_AUTHDOG` to that environment's public key.

## 1. Create an environment and copy the public key

In the [console](https://console.authdog.com):

1. Create a **tenant**, a **project**, and an **environment**.
2. Open **Dashboard** (`/dashboard/home`) and copy **Public key** (`pk_...`).

The public key is publishable. Do not put an `adenv_` API secret in this process unless you are calling Vault or another privileged API. Details: [Environments](/docs/console/environments).

## 2. Install the SDK

```package-install
@authdog/express express
```

`@authdog/express` is on npm. Express `^4.18` or `^5` is a peer.

## 3. Set the env var

```bash
PK_AUTHDOG=pk_...
```

## 4. Create the client and attach the session

```ts
import express from "express"
import { createAuthdog } from "@authdog/express"

const app = express()
const authdog = createAuthdog({ publicKey: process.env.PK_AUTHDOG! })

app.use(authdog.attachSession())
```

`attachSession` records context. It does not return 401 on its own.

## 5. Protect one route

```ts
app.get("/me", authdog.requireAuth, (req, res) => {
  res.json(req.authdog!.user)
})

app.listen(3000)
```

`requireAuth` is the security boundary. Missing or invalid sessions get `401 {"error":"Unauthorized"}`.

## 6. Verify

Send a request **without** a session. You should get 401.

Send the same request with a valid `authdog-session` cookie or `Authorization: Bearer <token>` from a completed hosted sign-in. You should get the user JSON.

If it fails:

- **401 with a cookie you just set** — the public key is from a different environment than the one that minted the session.
- **User is always null** — you used `attachSession` without `requireAuth`, or `fetchUser: false`.
- **Startup throw** — malformed `pk_...` or an identity host outside the trusted HTTPS allowlist.

## Next

The [Express guide](/docs/backend/express) covers logout, `fetchUser: false`, and what authentication does not authorize. Apply [authorization](/docs/concepts/authorization) after `requireAuth`.
