Outcome: a Next.js App Router app that signs a user in and shows their email on one protected page. About five minutes.

0. Prerequisites: Node 20+, a browser, and an Authdog account. You will leave with `NEXT_PUBLIC_PK_AUTHDOG` / `PK_AUTHDOG` set and `/dashboard` gated.

## 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** (for example `dev`).
2. Open **Dashboard** (`/dashboard/home`) for that environment.
3. Copy **Public key** (`pk_...`). You can also copy it from the environment picker → **Authdog Public Key**.

The public key is publishable. It selects the environment. Do not put an `adenv_` API secret in the Next.js client. Details: [Environments](/docs/console/environments).

## 2. Install the SDK

```package-install
@authdog/nextjs-app
```

Supports Next.js 15/16 and React 18 or 19.

## 3. Set the env vars

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

Both values are the same public key. The `NEXT_PUBLIC_` copy is for the browser provider; `PK_AUTHDOG` is for server middleware.

## 4. Wrap the app and exchange the callback

```tsx
// app/layout.tsx
import { AuthdogProvider } from "@authdog/nextjs-app/client"

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html>
      <body>
        <AuthdogProvider>{children}</AuthdogProvider>
      </body>
    </html>
  )
}
```

```ts
// middleware.ts
import { useAuthMiddleware } from "@authdog/nextjs-app/server"
export default useAuthMiddleware(process.env.PK_AUTHDOG!)

export const config = {
  matcher: ["/((?!_next/static|_next/image|favicon.ico).*)"],
}
```

Return from hosted sign-in to a URL this middleware matches. The helper writes HttpOnly cookies. It does not authorize later requests on its own.

## 5. Protect one route

```tsx
// app/dashboard/page.tsx
"use client"
import { useUser } from "@authdog/nextjs-app"

export default function Dashboard() {
  const { user, isLoading } = useUser()
  if (isLoading) return null
  if (!user) return <p>Not signed in</p>
  return <p>Signed in as {user.emails?.[0]?.value}</p>
}
```

Send users to hosted sign-in from your [Account portal](/docs/account-portal), then back to `/dashboard`.

## 6. Verify

You should see the signed-in email on `/dashboard` after completing hosted sign-in.

If it fails:

- **Blank user after redirect** — confirm the callback URL is matched by `middleware.ts` and both env vars are the same `pk_...`.
- **Wrong environment** — the public key is from `dev` but you signed in on `prod`, or the reverse.
- **401 on a Route Handler** — `useAuthMiddleware` does not protect API routes. Validate the session with a [backend SDK](/docs/backend).

## Next

The [Next.js guide](/docs/frameworks/nextjs) covers `useAuth`, logout, and what the middleware does not do.
