Auth.js (NextAuth v5) Integration
Auth.js (NextAuth v5) Integration
Section titled “Auth.js (NextAuth v5) Integration”Auth.js (NextAuth v5) is a flexible authentication library for Next.js and Node.js. This guide shows how to protect your Tabula Lens endpoints using Auth.js sessions and pass credentials to the DatabaseViewer component.
Prerequisites
Section titled “Prerequisites”Install the appropriate Auth.js package for your framework:
# # Next.jsnpm i next-auth@beta# # Next.jspnpm add next-auth@beta# # Next.jsyarn add next-auth@beta# # Expressnpm i @auth/express# # Expresspnpm add @auth/express# # Expressyarn add @auth/expressGenerate an AUTH_SECRET for production:
npx auth secretpnpm auth secretyarn auth secretAdd the generated value as AUTH_SECRET in your environment variables.
Backend Setup
Section titled “Backend Setup”Auth.js Configuration
Section titled “Auth.js Configuration”Create an auth.ts file at your project root to configure Auth.js:
import NextAuth from 'next-auth';import GitHub from 'next-auth/providers/github';
export const { handlers, signIn, signOut, auth } = NextAuth({ providers: [GitHub],});Add the Auth.js route handler to handle sign-in and sign-out:
import { handlers } from '@/auth';export const { GET, POST } = handlers;Next.js App Router
Section titled “Next.js App Router”Protect the Tabula Lens route by calling auth() and checking for an active session:
import { auth } from '@/auth';import { TabulaLens, createNextRouteHandler } from '@tabula-lens/node';
const tabulaLens = new TabulaLens(process.env.DATABASE_URL!);const handler = createNextRouteHandler(tabulaLens);
export async function GET(request: Request) { const session = await auth(); if (!session?.user) return new Response('Unauthorized', { status: 401 }); return handler(request);}Express
Section titled “Express”Use ExpressAuth to mount the Auth.js endpoints and getSession to validate requests:
import express from 'express';import { ExpressAuth, getSession } from '@auth/express';import GitHub from 'next-auth/providers/github';import { TabulaLens, expressAdapter } from '@tabula-lens/node';
const app = express();const tabulaLens = new TabulaLens(process.env.DATABASE_URL!);const authConfig = { providers: [GitHub] };
// Required for Auth.js to read cookies behind a proxyapp.set('trust proxy', true);
// Mount Auth.js sign-in/sign-out routesapp.use('/auth/*', ExpressAuth(authConfig));
// Session validation middlewareasync function requireAuth(req, res, next) { const session = await getSession(req, authConfig); if (!session?.user) return res.status(401).json({ error: 'Unauthorized' }); res.locals.session = session; next();}
app.use('/api/tabula-lens', requireAuth, expressAdapter(tabulaLens));Frontend Integration
Section titled “Frontend Integration”Auth.js sessions are cookie-based. For same-origin Next.js API routes, the browser sends the session cookie automatically — no Authorization header is needed:
'use client';import { useSession } from 'next-auth/react';import { DatabaseViewer } from '@tabula-lens/react';
export function TabulaLensViewer() { const { data: session, status } = useSession();
if (status === 'loading') return <div>Loading...</div>; if (!session) return <div>Sign in to view data</div>;
return ( <DatabaseViewer path="/api/tabula-lens" getAuthHeaders={async () => ({})} /> );}Cross-Origin Requests
Section titled “Cross-Origin Requests”If your frontend and backend are on different origins, cookies won’t be forwarded automatically. Expose the OAuth access token through the session and pass it as a Bearer token:
// auth.ts — store the OAuth access token in the sessionexport const { handlers, signIn, signOut, auth } = NextAuth({ providers: [GitHub], callbacks: { async jwt({ token, account }) { if (account) token.accessToken = account.access_token; return token; }, async session({ session, token }) { session.accessToken = token.accessToken as string; return session; }, },});// Use the stored token in getAuthHeaders<DatabaseViewer path="/api/tabula-lens" getAuthHeaders={async () => ({ Authorization: `Bearer ${session.accessToken}`, })}/>Important Notes
Section titled “Important Notes”- Session cookies are HttpOnly — client-side JavaScript cannot read the raw JWT. Use the
sessioncallback to expose only what you need. - Same-origin routes need no headers. Pass
getAuthHeaders={async () => ({})}and the browser handles cookies automatically. AUTH_SECRETis required in production. Runnpx auth secretto generate one; the app will throw at startup if it’s missing.- v5 breaking change: Configuration now lives in a root
auth.tsfile. The v4 pattern of exportingauthOptionsfrom the API route is no longer used — update your imports to@/authaccordingly. auth()must import from your local@/auth, not directly fromnext-auth. The local export carries your providers and callbacks configuration.
Troubleshooting
Section titled “Troubleshooting”401 returned even when signed in
Confirm the auth() call in your route handler imports from @/auth (your local config), not from next-auth directly. Importing from next-auth bypasses your configuration and the session won’t be found.
session.accessToken is undefined
Add the jwt and session callbacks to your auth.ts as shown in the cross-origin section above. Without them, account.access_token is never persisted to the session.
Express: getSession returns null
Ensure app.set('trust proxy', true) is set before any middleware. Without it, Auth.js may not be able to read cookies when the app is behind a load balancer or reverse proxy.