Aura Auth
Integrations

Oak

Integrate Aura Auth with Oak (Deno)

This guide walks you through to implement Aura Auth in a Oak application to a complete support. If you haven't configured Aura Auth yet, start with the Installation Guide and Quick Start Guide to set up your Auth instance and environment variables. Then follow the steps in this guide to integrate Aura Auth with your Oak application.

Setup Aura Auth

Create an Auth Instance

Create an auth.ts file in src/lib directory to configure your Aura Auth instance.

src/lib/auth.ts
import { createAuth } from "npm:@aura-stack/auth"

export const auth = createAuth({
  oauth: ["github"],
  basePath: "/api/auth",
})

export const { handlers, api, jose } = auth

The basePath should match the path where your auth route handlers are mounted and baseURL should point to your local development server or deployed application URL.

Mount HTTP Handlers

Oak does not call Aura Auth handlers directly, so create a small adapter that forwards Oak requests to the web-standard handlers Aura Auth expects. The adapter below converts Oak's request context into a standard Web Request and forwards the result back to Oak's response object.

src/lib/handler.ts
import { handlers } from "@/lib/auth.ts"
import type { RouterContext } from "@oak/oak"

export const toSetHeaders = <Route extends string>(ctx: RouterContext<Route>, headers: Headers) => {
  for (const [key, value] of headers.entries()) {
    ctx.response.headers.set(key, value)
  }
}

const isRedirect = (response: Response) => {
  const location = response.headers.get("Location")
  return location !== null && response.status >= 300 && response.status < 400
}

export const toOakHandler = async (ctx: RouterContext<"/api/auth/(.*)">) => {
  const handler = handlers[ctx.request.method as keyof typeof handlers]
  if (!handler) {
    ctx.response.status = 405
    ctx.response.body = { error: "Method Not Allowed" }
    return
  }
  const toWebRequest = ctx.request.source
  if (!toWebRequest) {
    ctx.response.status = 400
    ctx.response.body = { error: "Bad Request" }
    return
  }
  const response = await handler(toWebRequest)
  if (isRedirect(response)) {
    const location = response.headers.get("Location")!
    ctx.response.status = 302
    toSetHeaders(ctx, response.headers)
    ctx.response.redirect(location)
    return
  }
  const body = await response.json()
  toSetHeaders(ctx, response.headers)
  ctx.response.body = body
}

Set up a catch-all route in Oak that forwards authentication requests to the adapter. The adapter takes care of converting Oak's request context into the web-standard request Aura Auth needs.

src/index.ts
import { Application, Router } from "@oak/oak"
import { toOakHandler } from "@/lib/handler.ts"
import type { GlobalState } from "@/middlewares/with-auth.ts"

const router = new Router<GlobalState>()
const app = new Application()

router.all("/api/auth/(.*)", toOakHandler)

app.use(router.routes())
await app.listen({ port: 3000 })

This keeps all auth endpoints in one place and leaves the rest of your app free to use the same auth instance.

Usage

Middleware

Create a middleware that loads the active session into Oak state so protected routes can reuse it.

src/middlewares/with-auth.ts
import { api } from "@/lib/auth.ts"
import type { Session } from "@aura-stack/auth"
import type { Next, RouteParams, RouterContext } from "@oak/oak"

const unauthorizedBody = {
  error: "Unauthorized",
  message: "Active session required.",
}

export interface GlobalState {
  session: Session | null
}

export type RouterContextWithState<Route extends string, Params extends RouteParams<Route> = RouteParams<Route>> = RouterContext<
  Route,
  Params,
  GlobalState
>

export const withAuth = async <Route extends string>(ctx: RouterContextWithState<Route>, next: Next) => {
  try {
    const session = await api.getSession({
      headers: ctx.request.headers,
    })
    if (!session.authenticated) {
      ctx.response.status = 401
      ctx.response.body = unauthorizedBody
      return
    }
    ctx.state.session = session.session
    return await next()
  } catch {
    ctx.response.status = 401
    ctx.response.body = unauthorizedBody
  }
}

The middleware returns session: null for anonymous requests, which keeps downstream route handlers simple and predictable.

Get Session

Use the middleware in your app and guard protected routes with the session it provides.

src/index.ts
import { Application, Router } from "@oak/oak"
import { toOakHandler } from "@/lib/handler.ts"
import { type GlobalState, withAuth } from "@/middlewares/with-auth.ts"

const router = new Router<GlobalState>()
const app = new Application()

router.get("/", (ctx) => {
  ctx.response.body = "Welcome to Aura Auth Oak App!"
})

router.all("/api/auth/(.*)", toOakHandler)

router.get("/api/protected", withAuth, (ctx) => {
  ctx.response.body = {
    message: "You have access to this protected resource.",
    session: ctx.state.session,
  }
})

app.use(router.routes())
await app.listen({ port: 3000 })

This pattern works well for dashboards, account endpoints, and any route that should not return private data unless the request is authenticated.


Common Pitfalls

  • Keep basePath aligned with your auth route. If your auth endpoint is /api/auth/*, the auth config should use basePath: "/api/auth".
  • Import the middleware from the correct folder. The shared middleware lives in src/middlewares/with-auth.ts.
  • Use session.authenticated as the guard. Check that flag before exposing private data.
  • Keep the auth handler and route logic separate. The auth route should only forward requests to Aura Auth.

Resources

On this page