Updated March 8, 2025

Simplifying Multi-Tenancy with Authentication

Wyatt Miller

Wyatt Miller

Author

Get started with Update

See why developers are using Update to supercharge their apps.

Join Update
Multi-tenancy is a powerful architecture for SaaS applications, allowing a single instance to serve multiple customers while keeping their data isolated. When paired with authentication, it ensures each tenant’s users access only their designated resources, balancing efficiency with security. For developers using Next.js and Supabase, implementing multi-tenancy can feel complex—database design, access control, and scalability all come into play. This article breaks it down, offering a clear path to simplify multi-tenant authentication. While tools like Update can streamline this further, the focus here is on mastering the core implementation.

What Is Multi-Tenancy?

Multi-tenancy refers to a single application serving multiple customers, or tenants, each with their own isolated data and settings. Imagine a SaaS platform hosting different companies: Company A’s users see only their data, not Company B’s. Authentication ties users to their tenant, enforcing access boundaries through mechanisms like Row Level Security or role-based access control. This setup reduces infrastructure costs by sharing resources, but it demands robust user management to prevent data leaks—a critical concern for any multi-tenant system.

Why It Matters

Multi-tenancy is foundational for SaaS applications, offering cost efficiency and scalability. Authentication ensures security by verifying user identity and restricting them to their tenant’s domain, a requirement that grows more complex as tenants multiply. Developers face challenges like managing user sessions across tenants, ensuring data isolation, and integrating billing for per-tenant pricing. Community discussions highlight these hurdles, with a clear demand for solutions that simplify the process without compromising performance or safety.

How to Simplify Multi-Tenancy with Next.js and Supabase

Simplifying multi-tenancy with authentication in Next.js and Supabase involves a structured approach: database setup, access control, and session management. Below is a step-by-step guide, addressing common questions and leveraging community insights for a scalable, secure implementation.

Step 1: Set Up the Database

Start with a Next.js project (npx create-next-app@latest my-app --typescript) and a Supabase project (supabase.com). In Supabase, create two tables: tenants for tenant metadata and users to link users to tenants. Run this SQL in the Supabase dashboard:

CREATE TABLE public.tenants (
id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE TABLE public.users (
id UUID REFERENCES auth.users(id) ON DELETE CASCADE,
email VARCHAR(255) UNIQUE NOT NULL,
tenant_id INTEGER NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
FOREIGN KEY (tenant_id) REFERENCES public.tenants(id)
);

The users table uses id from auth.users to sync with Supabase authentication, ensuring each user is tied to a tenant_id.

Step 2: Enable Row Level Security (RLS)

Developers often ask, “How do I set up RLS for tenant isolation?” Supabase’s RLS ensures users access only their tenant’s data. Enable RLS on both tables in the Supabase dashboard, then add policies:

CREATE POLICY "Users see their tenant only" ON public.tenants
FOR SELECT TO authenticated
USING (auth.uid() IN (SELECT id FROM public.users WHERE tenant_id = tenants.id));
CREATE POLICY "Users see their own data" ON public.users
FOR SELECT TO authenticated
USING (auth.uid() = id);

These policies restrict access based on the authenticated user’s tenant_id, a best practice from community examples.

Step 3: Configure Authentication

Install Supabase’s auth helpers:

npm install @supabase/supabase-js @supabase/auth-helpers-nextjs

Set up environment variables in .env.local:

.env.local
NEXT_PUBLIC_SUPABASE_URL=your-supabase-url
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key

Create a Supabase client in lib/supabase.js:

lib/supabase.js
import { createClient } from '@supabase/supabase-js';
export const supabase = createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY
);

Wrap your app with a session provider in app/layout.js:

app/layout.js
import { createServerComponentClient } from '@supabase/auth-helpers-nextjs';
import { cookies } from 'next/headers';
export default async function RootLayout({ children }) {
const supabase = createServerComponentClient({ cookies });
const { data: { session } } = await supabase.auth.getSession();
return (
<html lang="en">
<body>{children}</body>
</html>
);
}

This ensures session data is available across the app, a common requirement for multi-tenant authentication.

Step 4: Implement Tenant-Specific Routing

Next.js’s dynamic routing simplifies tenant-specific pages. Create a file in app/[tenant]/dashboard/page.js:

app/[tenant]/dashboard/page.js
import { createServerComponentClient } from '@supabase/auth-helpers-nextjs';
import { cookies } from 'next/headers';
export default async function Dashboard({ params }) {
const supabase = createServerComponentClient({ cookies });
const { data: { session } } = await supabase.auth.getSession();
if (!session) {
return <p>Please sign in.</p>;
}
const { data: user } = await supabase
.from('users')
.select('tenant_id')
.eq('id', session.user.id)
.single();
const { data: tenant } = await supabase
.from('tenants')
.select('name')
.eq('id', user.tenant_id)
.single();
if (params.tenant !== tenant.name.toLowerCase()) {
return <p>Access denied.</p>;
}
return <p>Welcome to {tenant.name}’s dashboard.</p>;
}

This checks the user’s tenant_id against the URL parameter, ensuring they access only their tenant’s page.

Step 5: Optimize for Scalability and Security

“How do I ensure scalability in multi-tenant apps?” is a frequent concern. Community threads suggest avoiding HTTP calls in middleware for performance. Use this middleware to validate sessions efficiently:

import { createMiddlewareClient } from '@supabase/auth-helpers-nextjs';
import { NextResponse } from 'next/server';
export async function middleware(req) {
const supabase = createMiddlewareClient({ req, res: NextResponse.next() });
const { data: { session } } = await supabase.auth.getSession();
if (!session && req.nextUrl.pathname.startsWith('/[tenant]/dashboard')) {
return NextResponse.redirect(new URL('/login', req.url));
}
return NextResponse.next();
}
export const config = { matcher: '/[tenant]/dashboard/:path*' };

For added security, sync new users to the users table with a trigger:

CREATE FUNCTION public.sync_user_to_tenant()
RETURNS TRIGGER AS $$
BEGIN
INSERT INTO public.users (id, email, tenant_id)
VALUES (NEW.id, NEW.email, 1); -- Default tenant_id, adjust as needed
RETURN NEW;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
CREATE TRIGGER on_auth_user_created
AFTER INSERT ON auth.users
FOR EACH ROW EXECUTE FUNCTION public.sync_user_to_tenant();

This automates user-tenant association, a valuable tip from prior discussions.

Addressing Common Developer Questions

Community insights reveal practical concerns:

  • What is multi-tenancy, and how does authentication fit in? It’s serving multiple customers with isolated data; authentication ties users to their tenant, enforcing boundaries.

  • How do I set up RLS for tenant isolation? Use Supabase policies to restrict data by tenant_id, as shown above.

  • Can I handle billing per tenant? Yes, integrate billing providers like Stripe, a process Update simplifies with usage-based pricing support.

Enhancing with Update

Multi-tenancy with authentication works well with Supabase and Next.js, but billing and provider flexibility can complicate scaling. Update addresses this by coordinating authentication with billing systems like Stripe or Lemon Squeezy, enabling per-tenant pricing models and seamless provider switching. A developer on X paired Supabase with Stripe for multi-tenancy, noting conversion boosts—Update builds on this with a unified solution.

Conclusion

Simplifying multi-tenancy with authentication in Next.js and Supabase is achievable with the right approach: structured database design, RLS for isolation, and optimized session management. Key takeaways include leveraging Supabase’s security features, ensuring middleware scalability, and automating user-tenant syncing. Start building a multi-tenant app today, or explore Update for a pre-built solution that extends this with billing and beyond. Your SaaS deserves efficiency and security.

Get started with Update

See why developers are using Update to supercharge their apps.

Join Update