Own Auth with Express
Mount createOwnAuthHandler at /api/auth before express.json(). Own Auth reads the request stream, enforces its body limit, validates the request, and writes the session cookie.
Install Own Auth
npm install own-auth express cookie-parser
npm install --save-dev @types/express @types/cookie-parserSet the database connection, then apply the Own Auth schema.
DATABASE_URL=postgres://user:password@localhost:5432/myappnpx own-auth migrateCreate the auth instance
Add the token pepper used by sessions and one-time auth tokens.
OWN_AUTH_TOKEN_PEPPER=replace-with-a-long-random-secretimport { createOwnAuth } from "own-auth";
const tokenPepper = process.env.OWN_AUTH_TOKEN_PEPPER!;
export const auth = createOwnAuth({
tokenPepper,
});Mount the auth handler
import { Readable } from "node:stream";
import express, {
type Request as ExpressRequest,
type Response as ExpressResponse,
} from "express";
import { createOwnAuthHandler } from "own-auth/http";
import { accountRouter } from "./account";
import { auth } from "./auth";
const app = express();
const requestContexts = new WeakMap<
Request,
{ ipAddress?: string; userAgent?: string }
>();
const authHandler = createOwnAuthHandler(auth, {
getRequestContext: (request) => requestContexts.get(request) ?? {},
});
app.all("/api/auth/{*path}", async (request, response) => {
const webRequest = toWebRequest(request);
requestContexts.set(webRequest, {
ipAddress: request.ip,
userAgent: request.get("user-agent"),
});
await sendWebResponse(response, await authHandler(webRequest));
});
app.use(express.json());
app.use(accountRouter);
app.listen(3000);
function toWebRequest(request: ExpressRequest): Request {
if (!request.host) throw new Error("Host header is required");
const method = request.method.toUpperCase();
const init: RequestInit & { duplex?: "half" } = {
method,
headers: toWebHeaders(request),
};
if (method !== "GET" && method !== "HEAD") {
init.body = Readable.toWeb(request) as ReadableStream<Uint8Array>;
init.duplex = "half";
}
return new Request(
new URL(request.originalUrl, `${request.protocol}://${request.host}`),
init,
);
}
function toWebHeaders(request: ExpressRequest): Headers {
const headers = new Headers();
for (const [name, value] of Object.entries(request.headers)) {
if (Array.isArray(value)) {
for (const entry of value) headers.append(name, entry);
} else if (value !== undefined) {
headers.set(name, value);
}
}
return headers;
}
async function sendWebResponse(
expressResponse: ExpressResponse,
response: Response,
) {
expressResponse.status(response.status);
for (const [name, value] of response.headers) {
if (name !== "set-cookie") expressResponse.setHeader(name, value);
}
const cookies = response.headers.getSetCookie();
if (cookies.length) expressResponse.setHeader("set-cookie", cookies);
expressResponse.send(Buffer.from(await response.arrayBuffer()));
}The mounted handler exposes the complete Own Auth HTTP API under /api/auth. The context bridge keeps IP-based OAuth and One Tap limits active. Configure Express trust proxy only for proxies you operate.
Protect application routes
import cookieParser from "cookie-parser";
import {
type NextFunction,
type Request,
type Response,
Router,
} from "express";
import { defaultSessionCookieName } from "own-auth/http";
import { auth } from "./auth";
export const accountRouter = Router();
accountRouter.use(cookieParser());
async function requireAuth(
request: Request,
response: Response,
next: NextFunction,
) {
const token = request.cookies[defaultSessionCookieName];
const current = token ? await auth.getCurrentSession(token) : null;
if (!current) {
return response.status(401).json({ error: "Unauthorized" });
}
response.locals.currentAuth = current;
next();
}
accountRouter.get("/account", requireAuth, (_request, response) => {
const { id, email, name, imageUrl } = response.locals.currentAuth.user;
response.json({ user: { id, email, name, imageUrl } });
});The middleware authenticates the request once. The response exposes only id, email, name, and imageUrl; use response.locals.currentAuth.user.id for ownership and permission checks.
Add the browser client
import { createOwnAuthClient } from "own-auth/client";
export const authClient = createOwnAuthClient();