Authdog

Koa quickstart

Last updated Oct 13, 2026npmlatestCI passing
View as Markdown

@authdog/koa resolves Authdog sessions for Koa. Its middleware sets ctx.state.authdog and exposes an authentication guard.

Availability

Unreleased: @authdog/koa is not on npm at the time of writing (the registry returns 404). bun add @authdog/koa will fail until it is published. Source lives in the `packages/koa` directory of the web SDK repository (version 0.3.0, configured to publish to npm with public access). Vendor a repository checkout until a release ships.

Install

npm install @authdog/koa koa

koa is a peer dependency (^2.15.0 || ^3.0.0); @authdog/node-commons installs automatically. Examples also use @koa/router, which is not required by the SDK.

Configure

Create one instance with your environment's public key (pk_...):

import Koa from "koa"
import Router from "@koa/router"
import { createAuthdog } from "@authdog/koa"

const app = new Koa()
const router = new Router()
const authdog = createAuthdog({ publicKey: process.env.PK_AUTHDOG! })

The key is safe to expose. It is parsed once at startup; a missing key throws Public key is not defined, and malformed keys or identity hosts outside the trusted HTTPS allowlist fail immediately. createAuthdog also accepts an optional secretKey, which is reserved and currently unused.

Attach the session

Mount attachSession() before routes:

app.use(authdog.attachSession())

It prefers the authdog-session cookie, then reads Authorization: Bearer <token>. A valid token requires a successful userinfo response (meta.code === 200 with a user). Missing, invalid, or unverifiable tokens never fail the request or return a 500; they yield anonymous context on ctx.state.authdog:

{
  token: string | null,
  user: unknown | null,
  isAuthenticated: boolean,
  userInfo?: UserInfoResponse | null,
}

attachSession({ fetchUser: false }) only surfaces the unverified token. It leaves user null and isAuthenticated false, so requireAuth rejects the request with 401. Use this option only when separate server-side validation and enforcement replace Authdog's gate.

Protect a route

requireAuth is the security boundary and returns 401 {"error":"Unauthorized"} unless isAuthenticated is true:

router.get("/me", authdog.requireAuth, (ctx) => {
  ctx.body = ctx.state.authdog!.user
})

Authentication identifies a caller; it does not authorize actions. Apply your authorization checks after requireAuth. Client-side checks never protect server routes.

Sign out

logout expires the authdog-session cookie (HttpOnly, SameSite=Lax, Secure when NODE_ENV is production) and redirects (302) to a sanitized same-origin redirect_uri, falling back to /:

router.get("/logout", authdog.logout)

This does not revoke a bearer token or end an upstream identity-provider session.

Next steps

Learn more