Building a Zero-Trust Authentication Architecture for Legal Tech
When building a platform that handles sensitive legal communications, security isn't a feature—it's the foundation. For Indicia AI, our analysis platform for legal professionals, we've designed an authentication architecture that implements defence in depth while keeping the codebase simple and maintainable.
The Problem with Traditional Auth
Most web applications implement authentication directly in their frontend framework. With Next.js, this typically means using NextAuth, Clerk, or a similar library. The pattern looks familiar: check the session on each page, redirect if invalid, pass tokens to API calls.
This approach has problems. Auth logic ends up duplicated—once in the frontend to protect pages, again in the API to protect data. Two places to maintain, two places where bugs can creep in, two attack surfaces to secure.
More critically, your frontend framework becomes security-critical infrastructure. Every developer working on UI components needs to understand auth. Every new page needs session checks. One forgotten middleware, one misconfigured route, and you've exposed sensitive data.
The Edge Auth Gateway Pattern
We've taken a different approach. Our Next.js application contains zero authentication logic. None. It renders UI and nothing else.
Instead, authentication lives in a Hono application deployed to Deno Deploy. This service acts as both our API layer and an authentication gateway. Every request to our application domain hits Hono first. No exceptions.
Here's how the flow works:
A user navigates to app.indicia.ai. This domain points to our Hono service on Deno Deploy, not to our Next.js application on Vercel. Hono checks for a valid session. While we use Gel Auth to validate tokens against our database at the edge, this architectural pattern works equally well with Redis, Supabase, or stateless JWTs. If the session is invalid or missing, the user sees a login page. They never reach Next.js.
If the session is valid, Hono proxies the request to our Next.js application. It forwards the request headers, adds a shared secret to prove the request came from Hono, and includes the authenticated user's ID. Next.js renders the page and returns HTML. Hono passes that response back to the browser.
The browser never knows Vercel exists. It only sees our domain returning pages.
Performance: The Streaming Proxy
A common concern with proxy architectures is latency. With Next.js specifically, there's a worry that putting a gateway in front will break React's streaming server-side rendering (SSR) or Time to First Byte (TTFB).
However, modern edge runtimes like Deno Deploy support standard Web APIs, including ReadableStream.
When Hono receives a request, it doesn't wait for Next.js to generate the full page. It establishes a connection to Vercel and immediately pipes the response body stream back to the browser. As Next.js flushes chunks of HTML—first the document shell, then the suspense boundaries—they flow through the gateway instantly.
The browser receives the stream as if it were connected directly to the application server. We get all the benefits of edge authentication without sacrificing the "snappy" feel of progressive rendering. The minimal latency of the proxy hop is partially offset by eliminating auth middleware from Next.js entirely—no token parsing, no session database calls, and no auth library overhead on the application server. This streaming model extends naturally to other HTTP-based real-time patterns.
Real-time Updates Without WebSockets
Streaming HTML works seamlessly through the proxy, but bidirectional WebSockets present a challenge. Serverless platforms like Vercel enforce execution timeouts that kill long-lived connections, making them poor targets for proxied WebSockets.
We lean on Server-Sent Events (SSE) instead. SSE is a standard HTTP stream, so it pipes through our proxy logic exactly like the HTML streaming described above. This lets us push real-time updates—analysis completion notifications, streaming AI responses, progress indicators—from our backend to the client with no additional complexity.
For the rare cases requiring true bidirectional communication (collaborative editing, for instance), we terminate those WebSocket connections directly at the Hono edge layer on Deno Deploy rather than proxying them through to Next.js.
Defence in Depth
This architecture provides three distinct security layers.
The first layer is the Hono auth gate at the edge. Unauthenticated users cannot reach Next.js. They see a login page and nothing else—no application structure, no routes, no UI components to probe or fingerprint.
This acts as a form of virtual patching for our frontend framework. When critical vulnerabilities are discovered in Next.js or React—whether deserialization attacks in server components, header injection exploits, or cache poisoning—unauthenticated attackers cannot reach the vulnerable code. The attack surface shrinks to authenticated users only, and we can deploy mitigation rules to the gateway in seconds without rebuilding or redeploying the frontend.
The second layer protects our data. Even if an attacker somehow bypassed the gateway and reached Next.js directly, the application still needs data to display. All data requests go through oRPC to our Hono API, which performs the same session validation. No valid session means no data.
The third layer lives in the database itself. Gel enforces access policies at the query level—each request automatically filters data based on the authenticated user's identity and permissions. Even if both previous layers failed catastrophically, an attacker would need to bypass database-level access controls to see other users' data.
Why This Matters for Legal Tech
Indicia AI analyses communication patterns for legal professionals, HR departments, and therapists. We handle emails, chat logs, and documents that could be subject to legal privilege, contain sensitive personal information, or serve as evidence in disputes.
A data breach wouldn't just be embarrassing—it could compromise legal cases, violate privilege, or expose vulnerable people. We need security that doesn't depend on developers remembering to add auth checks to every new component.
By centralising authentication in a single service, we reduce our attack surface. The Next.js application becomes a pure rendering engine. It assumes every request is authenticated because, architecturally, that's the only way requests can reach it. New pages don't need auth checks because auth happened before the request arrived.
The Invisible App
We separate our public marketing site from the authenticated application entirely. The brochureware at indicia.ai is a static site with no server runtime—just HTML, CSS, and JavaScript served from a CDN. No session logic, no API calls, no server-side code to exploit. This makes it immune to the class of remote execution vulnerabilities that affect modern application frameworks. The application at app.indicia.ai is a different deployment behind the auth gateway. They share nothing but branding.
We've taken this further by making our Next.js deployment effectively invisible. The Vercel URL is a random string, not indexed by search engines, not linked from anywhere. The app.indicia.ai domain points to Hono on Deno Deploy.
An attacker who tries to access our Vercel deployment directly receives a 404. The shared secret header is missing, so Next.js refuses to respond. From their perspective, nothing exists at that URL.
The application only manifests for authenticated users, accessed through our gateway, with valid sessions. Everyone else sees a login page or nothing at all.
Conclusion: The Security Pendulum
The counterintuitive result is that this rigorous architecture is actually simpler to maintain. No auth libraries in our frontend. No session management in React components, and no duplication between client and server auth logic.
For years, the industry trend has been toward the "monolith"—frameworks bundling frontend, backend, and middleware to maximize velocity. It was a necessary compromise when infrastructure was hard, but it coupled our security posture to our UI libraries, limiting our defense in depth.
We believe the pendulum is swinging back. Modern edge runtimes make independent auth gateways a ten-line implementation detail rather than an infrastructure project. This decoupled pattern restores the separation of concerns that serious engineering demands.
For Indicia AI, this isn't just an architectural preference. It's zero-trust in practice: never trust the request, always verify at the gate, and assume every layer behind might fail. For a platform handling sensitive legal communications, that is the only default that makes sense.