Buyer’s Guide

Best Auth Solutions for Next.js

Written by Govind Kumar Lohar. Reviewed for technical accuracy by Deepak Gupta and Bhaskar Suthar on · Review panel

  • auth
  • nextjs
  • session
  • identity

Independent buyer’s guide. No vendor paid to be included, ranked or described a particular way. Written for engineers, architects and the people who sign off on their tooling budget. Editorial policy.

Almost every Next.js auth question is really one of two questions in disguise. Where am I allowed to write a cookie, and what can I safely do inside middleware. Get those wrong and you get the classic symptoms: a session that refreshes on some routes and silently expires on others, a redirect loop that only reproduces in production, or a protected page that renders fine for a logged-out user because the only guard was a middleware matcher that did not match.

The framework is doing this to you on purpose. A React Server Component render is supposed to be a pure read, so Next.js will not let you mutate the request context from inside it. Middleware runs on a different runtime with a different set of available APIs from the rest of your app. Those two constraints, plus the split between App Router and Pages Router, explain nearly every confusing auth bug people file against these libraries.

So the useful comparison is not which library has the prettiest sign-in component. It is which one models Next.js rendering honestly: where it refreshes sessions, whether it needs a database connection in middleware, and what it does when your deployment target is the Edge runtime rather than a Node server.

Key takeaways

  • You cannot set a cookie during a server component render. Session rotation has to happen in middleware, a Route Handler or a Server Action, and every library works around this differently.
  • Middleware is the wrong place for a database lookup or a permission check. It runs on every matched request, before your cache, and it is an optimistic gate, not an authorization boundary.
  • The Edge runtime has no Node crypto, no TCP sockets and no native modules, so bcrypt and a direct Postgres driver are both out. That single fact eliminates several otherwise good libraries from middleware entirely.
  • Auth checks belong next to the data they protect. A layout or a middleware matcher is the wrong trust boundary because both are easy to bypass by requesting a route nobody added to the list.

The four places auth code can run, and what each one allows

Next.js gives you four execution contexts, and the difference between them is the whole subject.

Server components. These render on the server and can read cookies through the cookies() API. They cannot write them. Calling a cookie mutation during a render throws, because a server component render may be replayed, streamed or partially prerendered, and a Set-Cookie header from the middle of a stream has no defined meaning. This is why “my session never refreshes” is the single most common Next.js auth report: the library tried to rotate the token during a page render and got silently skipped or noisily crashed.

Route Handlers. A route.ts file is a normal request and response cycle. You can read cookies, write cookies, talk to a database, use any Node API if the route runs on the Node runtime. This is where OAuth callbacks, token exchange and sign-out live in every library here.

Server Actions. A function invoked from the client that runs on the server, with permission to mutate cookies. This makes it the correct home for credential sign-in and for session rotation triggered by user activity. The trap is that a Server Action is a public HTTP endpoint with an auto-generated identifier, so it needs its own authorization check. Nothing about being imported into a protected page protects the action.

Middleware. Runs before the request reaches your route, on the Edge runtime by default. It can read cookies, write cookies, rewrite and redirect. It is also the most abused piece of the model, which the next section covers.

Pages Router is simpler on this axis and that is genuinely why it still has fans for auth work. getServerSideProps and API routes both hand you req and res, so reading and writing cookies happens in the same function that renders, on the Node runtime, with no restrictions. Almost every App Router auth difficulty is the absence of that one convenience.

The practical rule to carry into every library evaluation: find where the library rotates the session. If the answer is “in middleware” it works but taxes every request. If the answer is “in a Route Handler you call from the client” it works but needs a client-side trigger. If the answer is vague, you are going to debug expired sessions in production.

Middleware is a gate, not an authorization boundary

Middleware feels like the right place to put auth. One file, one matcher, every route protected. It is the wrong place for two separate reasons, and they compound.

It runs on every matched request. Middleware executes ahead of routing and ahead of the cache for matched paths, so whatever you put in it becomes fixed overhead on the hot path, including for requests that would otherwise have been served from cache. Put a database lookup in there and you have made your session store the highest-QPS service you own, queried once per navigation, once per prefetch, and once per asset request the matcher failed to exclude.

It is bypassable by construction. A matcher is a list of path patterns. Add a route that the pattern does not cover and it is unprotected, with no error and no test failure. Rewrites, route groups and parallel routes all make it easier to end up with a path that the author of the matcher never imagined. The well-known Next.js middleware authorization bypass class of bug exists precisely because a single header or path shape could convince the framework to skip middleware, and every app whose only check lived there was wide open.

The model that survives contact with production is layered:

  • Middleware does a cheap, stateless check. Is a session cookie present, and does its signature verify. If not, redirect to sign-in. This is an optimistic redirect for user experience, nothing more.
  • The real check happens in the data access layer. Every function that reads or writes user-scoped data starts by resolving the current session and asserting the caller is allowed. Not in the page, not in the layout, in the function that touches the row.

Layouts deserve a specific warning. A layout does not re-render on every navigation within its segment, and it does not wrap Route Handlers or Server Actions at all. Putting your only if (!session) redirect() in a layout gives you a check that runs sometimes. That is worse than no check, because it looks like protection in code review.

Needs first-hand data: Measure p50 and p99 added latency for your own middleware with each candidate library, by deploying the same app with the middleware matcher empty and then with your real matcher, against identical synthetic traffic. Record the delta separately for cached and uncached routes, because the cached case is where a middleware database call hurts most.

The Edge runtime is the constraint that disqualifies libraries

Middleware runs on the Edge runtime unless you explicitly opt a route into Node. The Edge runtime is a Web-APIs-only sandbox. It has fetch, crypto.subtle, TextEncoder and the standard web primitives. It does not have node:crypto in its full form, node:fs, native addons, or raw TCP sockets.

Three consequences decide your shortlist.

No bcrypt, scrypt or argon2 in middleware. These are native modules or Node crypto. Password verification cannot happen at the edge. That is fine, because password verification should happen once in a Route Handler at sign-in, not on every request. It stops being fine when a library’s session validation is entangled with its password code path and the whole module fails to build for the Edge target.

No direct database driver. A Postgres or MySQL client opens a TCP connection. You cannot do that in Edge middleware. Any session strategy that requires reading a session row therefore needs either an HTTP-based database proxy, a separate fetch to your own API, or a move of middleware to the Node runtime. All three are real options and all three cost you something. This is the actual reason so many Next.js libraries default to a stateless JWT session: it is the only session model that validates at the edge with no I/O.

JWT verification must use Web Crypto. Libraries built on jsonwebtoken use Node crypto and will not run in middleware. Libraries built on jose use Web Crypto and will. When you evaluate a library, check which one it depends on. It tells you immediately whether its session check is edge-capable.

The tradeoff underneath all of this is the one from session management: a stateless JWT validates anywhere with no lookup, and cannot be revoked before it expires. A database-backed session revokes instantly and needs a lookup you cannot perform at the edge. Most Next.js apps land on short-lived JWT access tokens with a refresh token that hits a Route Handler, which gives you edge-fast checks and a bounded revocation window. Decide what that window is deliberately, because it is your answer when someone asks how fast a compromised session can be killed.

Needs first-hand data: For each candidate, build a minimal app that runs the library session check inside middleware with runtime: "edge" and record whether it builds at all, and if it does, what the cold-start and per-request cost is compared with the same check on the Node runtime.

OAuth 2.0 and OIDC: the standards underneath, not products

Every product below is, at bottom, an implementation of the same two specs, and knowing what belongs to the spec rather than the vendor tells you what you can move.

What they give you

  • The Authorization Code flow with PKCE, which is the only browser-appropriate flow and the one every library here uses for social login
  • A standard ID token shape in OIDC, so the claims your app reads about a user look the same regardless of who issued it
  • Discovery documents and JWKS endpoints, which mean key rotation happens without a deploy on your side
  • A common vocabulary for scopes, audiences and refresh, so migrating between providers is a re-integration rather than a redesign

What they do not do

  • Neither spec says anything about how you store a session in your app. Cookies, rotation, the JWT-versus-database choice and the Edge constraints above are all yours.
  • OIDC standardises the ID token, not the access token. Access token format is provider-specific, so any code that parses one is provider-coupled.
  • Neither covers user management, organizations, invitations, roles, or the admin UI. That gap is exactly what the commercial products in this article sell.
  • Conformance varies. Two providers can both be OIDC compliant and still differ on refresh behaviour, logout, and what lands in the ID token by default.

Auth.js (NextAuth)

Auth.js homepage

Auth.js, still widely known by its NextAuth name, is the default answer for Next.js and has been for years. It is a library, not a service: it runs inside your app, handles the OAuth dance with a long list of providers, and gives you a session through either a signed JWT cookie or an adapter-backed database session. The v5 rewrite reshaped the API specifically around App Router, exporting auth, signIn and signOut from one config so the same primitive works in a server component, a Route Handler and middleware.

Its strength is also its shape: everything is in your process and your database, so there is no vendor, no user directory you do not control, and no per-user meter. Its weakness is the amount of correctness you own. Session rotation, account linking rules, email verification and the security posture of your adapter are your responsibility, and the documentation assumes more OAuth fluency than most teams have on day one.

Pros

  • Huge provider catalogue covering essentially every social and enterprise OAuth source you are likely to need
  • v5 exports a single auth helper usable from server components, Route Handlers, Server Actions and middleware, which removes most of the context confusion
  • JWT session strategy verifies on the Edge runtime with no I/O, so middleware checks stay cheap
  • Adapters exist for most databases and ORMs, so the user table lives in your schema rather than someone else’s

Cons

  • The database session strategy does not work in Edge middleware, because the adapter needs a driver you cannot open a socket for, so you are pushed to JWT and the revocation delay that comes with it
  • Account linking defaults are a security decision the library makes for you, and the consequences of linking by verified email are not obvious until someone abuses it
  • Documentation and community answers are split across NextAuth v4 and Auth.js v5, and the two have materially different APIs
  • Nothing for organizations, roles, invitations, SAML or SCIM, so the first enterprise customer starts a separate project

Best for: Teams who want the user table in their own database, have someone comfortable reasoning about OAuth, and do not need enterprise connections in the near term.

Pricing: No vendor and no bill. It is an open-source library, so the cost is your database, the engineering time to configure it correctly, and the maintenance of that configuration across major versions.

Better Auth

Better Auth homepage

Better Auth is the newer TypeScript-first library that took the Auth.js problem statement and answered it with more batteries included. It ships email and password, social providers, sessions, two-factor, and a plugin system that covers organizations, multi-tenancy and passkeys, all running in your app against your database. The design difference that matters in practice is type inference: your plugin configuration shapes the types on both the server client and the browser client, so a misconfigured field is a compile error rather than a runtime undefined.

It is framework-agnostic but the Next.js integration is first-class, with an explicit account of which helpers are safe in which context. For a team that would otherwise assemble Auth.js plus three custom tables for organizations, it collapses a lot of that into configuration.

Pros

  • Organizations, invitations, roles and multi-tenancy are built-in plugins rather than tables you design yourself
  • End-to-end type inference from server config to client calls catches configuration mistakes at build time
  • Email and password with sensible hashing is included, which Auth.js deliberately pushes you to build yourself
  • Self-contained: user data, sessions and organization structure all live in your database with no external directory

Cons

  • Younger project with a smaller body of production experience behind it, which matters more for auth than for most dependencies
  • Its database session model means middleware checks need either a Node runtime or an extra hop, the same Edge constraint everyone faces
  • The plugin surface is broad, and breadth in a security-critical library means more configuration you can get wrong
  • No SAML or SCIM story comparable to the commercial platforms, so enterprise buyers still route you elsewhere

Best for: TypeScript teams building a multi-tenant product who want organizations and sessions in their own database without adopting a vendor.

Pricing: No vendor and no bill. Open-source library, so you pay in database capacity, the time to configure the plugin set correctly, and ongoing upgrades.

Lucia

Lucia deserves a place here for an unusual reason: it stopped being a library and became a teaching resource. The maintainer deprecated the package and redirected the project toward documentation that shows you how to implement sessions yourself, on the argument that session management is small enough to own and that a dependency in this position creates more coupling than it removes. If you are choosing a dependency today, Lucia is not the answer.

Read it anyway. The material is the clearest available explanation of session ID generation, hashing session IDs at rest, cookie attributes, and idle-versus-absolute expiry, and those are the details every other library on this list is making decisions about on your behalf. Teams that write their own session layer following it typically end up with something smaller and more inspectable than a general-purpose library, at the cost of owning every future edge case themselves.

Pros

  • The reference material explains session mechanics precisely enough that you can implement and audit your own layer
  • Rolling your own session table removes a dependency from the most security-sensitive path in your app
  • No abstraction between you and your database, so the Edge-versus-Node decision is yours to make explicitly
  • Nothing to upgrade, and no library-level breaking change can ever force a migration on you

Cons

  • Deprecated as a package, so adopting it as a dependency today means adopting something with no maintenance path
  • Owning session code means owning every future requirement: rotation, revocation, device listing, concurrent session limits
  • OAuth provider integration is entirely on you, and provider quirks are where the real work lives
  • Hand-rolled auth is the thing every security reviewer flags first, whether or not yours is correct

Best for: Teams who want to understand sessions properly before picking a library, and small apps whose session needs genuinely fit in one file they are willing to own.

Pricing: No vendor and no bill. The cost is entirely the engineering time to write and maintain the session layer yourself, and it is not a small number over three years.

SuperTokens

SuperTokens homepage

SuperTokens splits the difference between library and service. You run a core service, either self-hosted or on their managed cloud, and your Next.js app talks to it through a backend SDK. The architecture is genuinely different from the JWT-cookie libraries: SuperTokens implements rotating refresh tokens with reuse detection, so a stolen refresh token gets the whole token family revoked the moment the legitimate client presents the old one. That is a real security property most JWT setups do not have.

The consequence is an extra moving part. You are deploying and upgrading a service, and its database, alongside your app. In exchange you get session revocation that actually works, a self-hosted option with no per-user pricing, and a defensible answer when someone asks how you kill a compromised session.

Pros

  • Rotating refresh tokens with reuse detection gives real session theft detection, not just a short expiry
  • Self-hosting is a first-class deployment mode rather than a community afterthought, so user data can stay in your infrastructure
  • Sessions are revocable server-side, which closes the gap every stateless JWT setup has
  • Recipes for passwordless, social login and multi-tenancy are separate modules you enable rather than one monolithic configuration

Cons

  • You are running a stateful service and its database, which is a real operational commitment compared with a library
  • The frontend and backend SDK pair must stay version-aligned, and upgrades are more coordinated than bumping a library
  • Session verification in Edge middleware needs care because the core is reachable over HTTP but the SDK surface is Node-shaped
  • Enterprise SSO breadth is thinner than the dedicated platforms, so check your specific requirements before committing

Best for: Teams who need genuine server-side session revocation and are willing to run one more service to get it, especially where data residency rules out a hosted directory.

Pricing: Open-source and free to self-host at infrastructure cost, with a managed cloud priced per monthly active user and paid add-on features above the core.

Supabase Auth

Supabase Auth is the right choice for one specific reason and the wrong one outside it. If your data already lives in Supabase Postgres and you are using Row Level Security, then the auth system issuing the JWT and the database enforcing the policy are the same system, and auth.uid() in a policy is the same identity your Next.js server component resolved. That closes the gap where application-layer auth and database-layer authorization disagree, which is a genuinely hard problem elsewhere.

It is a full auth service: social providers, email and password, magic links, phone OTP, MFA. The Next.js integration uses cookie-based sessions with a server client that reads and refreshes tokens, and the documented pattern deliberately refreshes in middleware precisely because a server component cannot write the cookie.

Pros

  • Row Level Security policies read the same identity the application does, so authorization can live in the database instead of being reimplemented per route
  • The Next.js server and client helpers handle the cookie-write restriction explicitly rather than leaving you to discover it
  • Open-source and self-hostable, so the exit path exists even if most teams use the hosted version
  • Auth, database, storage and realtime under one project removes a whole class of integration work for a small team

Cons

  • The value collapses if your primary database is not Supabase Postgres, and at that point it is a mid-tier auth service competing with specialists
  • RLS is powerful and easy to get subtly wrong, and a policy mistake is a data breach rather than a 500
  • Enterprise SSO and directory features sit in higher plan tiers and are not the product focus
  • Coupling your identity layer to your database vendor is a real lock-in decision, not a small one

Best for: Teams already on Supabase Postgres who want Row Level Security enforcing authorization against the same identity their Next.js app sees.

Pricing: Included with the Supabase platform on a per-project plan, with monthly active user allowances and separate charges for enterprise SSO and higher tiers.

Clerk

Clerk homepage

Clerk is the fastest route from nothing to a working, good-looking Next.js login, and it is not close. Prebuilt components, a middleware helper, organization support, and an integration that understands App Router contexts specifically rather than treating Next.js as one framework among twenty. For a team whose competitive advantage is not their sign-in page, it removes weeks.

What you are buying is a hosted user directory. Your users live in Clerk, their profile data lives in Clerk, and your database holds a foreign key. That is the correct tradeoff for a lot of products and an unacceptable one for others, and the decision should be made explicitly at the start rather than discovered during a migration. Comparing it against Auth0 and WorkOS is the clearest way to see what each bet costs.

Pros

  • Prebuilt sign-in, sign-up and user profile components that look finished, with a middleware helper that matches the App Router model
  • Organizations, memberships, roles and invitations are built in, which is the B2B feature set most libraries leave you to build
  • Session tokens are short-lived JWTs verified at the edge, so middleware checks need no round trip to Clerk
  • The Next.js integration tracks framework releases closely, which is not true of every provider here

Cons

  • Your user directory lives with the vendor, so migrating out means exporting users and dealing with password hashes you may not be able to take
  • Per-monthly-active-user pricing scales with success, and the step from the free tier to a paid plan arrives early for consumer products
  • Heavy reliance on prebuilt components means custom flows fight the abstraction rather than compose with it
  • SAML and directory sync sit in higher tiers, so the enterprise story has a price attached

Best for: Product teams shipping a B2B SaaS on Next.js who need organizations and a polished login working this sprint rather than this quarter.

Pricing: Free tier with a monthly active user allowance, then per-MAU pricing with organizations, SAML connections and advanced features gated to higher plans or billed separately.

Kinde

Kinde homepage

Kinde positions itself between the developer-first tools and the enterprise platforms, bundling auth with feature flags, billing hooks and roles in one product. The Next.js SDK covers App Router properly, and the organization model is designed for B2B multi-tenancy from the start rather than added later. It is a smaller vendor than Auth0 or Okta, which cuts both ways: simpler to adopt, less proven at the scale and compliance depth an enterprise buyer will probe.

Pros

  • Organizations and roles are core to the data model, not a higher-tier add-on
  • Bundles feature flags and entitlement concepts alongside auth, which removes a second vendor for early-stage products
  • App Router support in the official SDK is current rather than a community port
  • Simpler pricing and configuration surface than the incumbent enterprise platforms

Cons

  • Smaller vendor with a shorter track record, which matters for a dependency that gates every request
  • Ecosystem and community answers are thinner, so unusual problems mean a support ticket rather than a search result
  • Enterprise connection breadth and compliance depth trail the established platforms
  • Bundling flags and billing with auth is convenient until you want to change one of them independently

Best for: Small B2B teams who want organizations, roles and feature flags from one vendor and do not yet have enterprise compliance requirements.

Pricing: Free tier with a monthly active user allowance, then per-MAU plans with enterprise connections and advanced features on higher tiers.

Stytch

Stytch homepage

Stytch is API-first in a way that matters for Next.js: it gives you headless primitives for passwordless, passkeys, OAuth, and session management, plus optional UI, and expects you to compose the flow. If you want a magic link flow whose emails, timing and fallback you fully control, this is a much better fit than a component library. It also treats passkeys and device fingerprinting as core rather than as a premium feature, which is where consumer auth is heading.

The cost of headless is that you write more code. There is no five-line drop-in that produces a finished sign-in page, and the flows you compose are yours to keep correct.

Pros

  • Headless APIs for passwordless, passkeys and OAuth let you own the entire user-visible flow without fighting a component
  • Passkey and WebAuthn support is a core capability rather than a higher-tier addition, which matters as passkeys become the default
  • Separate B2B and consumer product lines, so the organization model is not bolted onto a single-user design
  • Device intelligence and fraud signals sit in the same platform as the auth primitives

Cons

  • More integration code than a components-first provider, and every flow you compose is a flow you maintain
  • Two distinct product lines means checking which features exist on the one you picked rather than the marketing page
  • Hosted user directory with the same migration considerations as any vendor-held identity store
  • Per-user pricing with feature gating means the bill depends on which primitives you compose in

Best for: Teams who want full control of the passwordless or passkey user experience and are happy to write the flow code to get it.

Pricing: Per monthly active user with separate consumer and B2B plans, and advanced fraud and device features metered or tiered separately.

WorkOS

WorkOS homepage

WorkOS started as enterprise-features-as-an-API, SAML and SCIM and directory sync behind a clean interface, and added AuthKit as a full auth layer on top. For a Next.js B2B product that is the right shape: you use AuthKit for normal login, and when a customer demands SAML you configure a connection instead of implementing a SAML service provider. Their admin portal lets the customer’s IT admin do the configuration themselves, which is the difference between an integration that takes an afternoon and one that eats two engineer-weeks per customer.

It is unambiguously aimed at B2B. For a consumer app with millions of users and no enterprise buyers, it is the wrong end of the market.

Pros

  • SAML, OIDC and SCIM across many identity providers behind one normalised API, which is the hardest part of enterprise SSO to build yourself
  • Admin portal lets customer IT teams self-configure their connection, removing the per-customer engineering cost
  • AuthKit gives you ordinary social and password login from the same vendor, so there is no second system for non-enterprise users
  • Pricing model is oriented around enterprise connections rather than raw user count, which suits a B2B shape

Cons

  • Built for B2B, so consumer-scale features like progressive profiling and broad social login breadth are not the focus
  • Per-connection pricing means a long tail of small enterprise customers each carries a cost
  • Less prebuilt UI than Clerk, so you write more of the interface
  • Directory sync reliability depends on the customer IdP, and debugging a stalled deprovisioning webhook is still your support burden

Best for: B2B SaaS teams on Next.js whose first enterprise deal just asked for SAML and SCIM and who need it shipped without building a SAML service provider.

Pricing: Free tier for core auth with enterprise connections priced per connection per month, and directory sync billed separately.

How to choose

Answer three questions in order and the field collapses fast.

Who holds the user records? If the answer must be “my database”, you are choosing between Auth.js, Better Auth, self-hosted SuperTokens, or writing it yourself with Lucia as a guide. Everything else is a hosted directory, and no amount of export tooling makes that a small decision later. Decide it before you build, not during a migration.

Do you sell to businesses? If yes, organizations, invitations, roles and eventually SAML and SCIM are requirements, not features. Better Auth covers the organization half in your own database. Clerk, Kinde and WorkOS cover it as a service, with WorkOS strongest on the enterprise end. Auth.js covers none of it, and that is a project you will start the week your first enterprise deal closes.

Where does your middleware run? If you deploy to the Edge runtime, verify the candidate’s session check builds and runs there before writing any product code. A JWT session verified with Web Crypto works everywhere. A database session does not work in Edge middleware at all, and the workaround always costs either a network hop or a move to the Node runtime.

Then write the check in the right place regardless of what you picked. Middleware redirects, the data layer decides. If you cannot point at the function that asserts authorization on your most sensitive query, you do not have auth, you have a redirect.

OptionUser dataSession modelEdge middlewareOrganizations
Auth.js (NextAuth)Your databaseJWT or DB adapterYes with JWT strategyNot included
Better AuthYour databaseDatabase sessionsNeeds a hop or Node runtimeBuilt-in plugin
LuciaYour databaseYou write itYour design decisionYou write it
SuperTokensYour core serviceRotating refresh tokensVia the core over HTTPMulti-tenancy recipe
Supabase AuthSupabase projectJWT refreshed in middlewareYesLimited
ClerkVendor directoryShort-lived JWTYesBuilt-in
KindeVendor directoryHosted sessionYesBuilt-in
StytchVendor directoryAPI-managed sessionYesB2B product line
WorkOSVendor directoryAuthKit sessionYesBuilt-in
OAuth 2.0 / OIDC (standards, not products)Not applicableNot specifiedNot applicableNot specified

Needs first-hand data: Instrument sign-in completion rate and session-expiry-induced re-login rate for two weeks after launch on whichever option you pick. Silent session expiry caused by a rotation that never fires is invisible in error logs and shows up only as users signing in more often than they should.

Frequently asked questions

Why does my session never refresh in the App Router?

Because the refresh is being attempted during a server component render, and Next.js does not allow a cookie write there. Move rotation into middleware, a Route Handler or a Server Action. Every library on this list has a documented pattern for this, and picking the wrong one is the most common cause of sessions that expire earlier than configured.

Can I do a database session lookup in middleware?

Not on the Edge runtime, because there are no TCP sockets and therefore no database driver. Your options are an HTTP-based database proxy, a fetch to your own Route Handler, or configuring middleware to run on the Node runtime. Most teams instead use a short-lived JWT for the middleware check and accept a bounded revocation delay.

Is middleware enough to protect a route?

No. Middleware is an optimistic gate that improves the user experience by redirecting early. The authorization check has to live in the function that reads the data, because Server Actions, Route Handlers and any path your matcher does not cover bypass middleware entirely. Treat the matcher as a convenience, never as a boundary.

Should I pick a library or a service?

Decide on who holds the user records first, and price the enterprise features second. A library keeps identity in your database and costs engineering time forever. A service removes that work and creates a migration problem you will eventually have to solve. Both are defensible. What is not defensible is choosing by demo quality and discovering the tradeoff eighteen months later, which is the situation the Auth0 alternatives discussion exists to handle.

What about authorization, not authentication?

None of these tools answer “can this user edit this document” beyond simple roles. Once permissions get relational, you want a dedicated policy engine rather than boolean checks scattered through route handlers. That is a separate category covered in authorization and permissions services.