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.
import { createAuth } from "npm:@aura-stack/auth"
export const auth = createAuth({
oauth: ["github"],
basePath: "/api/auth",
})
export const { handlers, api, jose } = authThe 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.
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.
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.
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.
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
basePathaligned with your auth route. If your auth endpoint is/api/auth/*, the auth config should usebasePath: "/api/auth". - Import the middleware from the correct folder. The shared middleware lives in
src/middlewares/with-auth.ts. - Use
session.authenticatedas 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.