Supabase Auth NextJS Integration Simplified

published on 06 December 2023

Most site owners would agree: properly implementing authentication is critical, yet can be an intimidating task.

By leveraging Supabase's robust authentication services and seamless NextJS integration, you can efficiently implement secure sign-in, authorization, and user management flows with minimal effort.

In this post, you'll learn step-by-step how to configure Supabase and NextJS to enable authentication on both client and server-side, securely manage user sessions, customize UI components, manage profiles, and more using easy-to-follow code examples and best practices.

Introduction to Supabase Auth and NextJS

Integrating authentication into a NextJS application provides critical security and access control. Supabase Auth makes adding authentication to NextJS apps simple and secure with features like password encryption, email verification, and API documentation.

When paired with NextJS, Supabase Auth offers realtime user session handling across pages without needing a custom server. Let's explore why integrating Supabase Auth benefits NextJS apps.

Exploring the Supabase Auth NextJS App Directory

Most NextJS projects have a pages directory containing React components that render for each route. Supabase Auth fits nicely into this structure.

Some common places Supabase Auth code appears in NextJS apps:

  • _app.js - Handles user sessions across every page
  • pages/dashboard.js - Checks if a user is logged in before allowing dashboard access
  • pages/login.js - Renders the login form and signs users in
  • pages/profile.js - Shows user data from the database

By centralizing auth logic instead of duplicating code, integrating Supabase Auth helps organize NextJS apps.

Benefits of Integrating Supabase Auth with NextJS

Supabase Auth offers many advantages over custom auth solutions:

  • Automatic session handling - Get user state across all pages without an auth server using useUser() hooks and SessionContext.
  • Secure password encryption - User passwords are securely hashed with bcrypt before storing in the database.
  • Email verification - Verify user email addresses to prevent fake accounts.
  • User management UI - Easily manage user accounts, profiles and permissions in the Supabase dashboard.

Overall, by handling tedius auth tasks, Supabase Auth lets developers focus on building great apps rather than complex infrastructure.

Let's walk through implementating Supabase Auth in a NextJS app step-by-step.

Does Supabase have authentication?

Supabase Auth provides easy-to-implement authentication for Next.js applications. It is built on top of OAuth and supports features like multi-factor authentication, magic links, and social logins.

Supabase generates typescript definitions based on your database schema. This allows for auto-complete and type safety when accessing the database from a Next.js frontend.

Here is an example of setting up Supabase client with Next.js API routes:

import { createClient } from '@supabase/supabase-js'

const supabase = createClient(process.env.NEXT_PUBLIC_SUPABASE_URL!, process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!)

export default async function handler(req, res) {
  const { data: { session } } = await supabase.auth.getSession()  

  if (!session) {
    return res.status(401).json({ error: 'Unauthorized' })
  }
  
  // ...carry on with the request
}

This checks if there is an active user session before allowing the API route to execute.

Some key benefits of using Supabase Auth with Next.js:

  • Works for both server-side rendering and static site generation
  • Secure - protects API routes and data
  • Simple setup with typescript support
  • Features like magic links and social logins

So in summary, Supabase makes adding authentication to Next.js apps easy and secure. The integration works smoothly on the frontend and with API routes.

Does Next.js have authentication?

Next.js is a popular React framework that makes it easy to build web applications with features like server-side rendering and static site generation. While Next.js itself does not include built-in authentication, it has great integration with third-party authentication providers.

One popular authentication solution that works well with Next.js is Supabase. Supabase provides open-source authentication along with a fully managed Postgres database. Adding Supabase authentication to a Next.js application takes just a few steps.

Setting up Supabase

First, you'll need to create a new Supabase project. After logging into your Supabase dashboard:

  • Create a new project
  • Go to the Auth tab and enable email/password authentication under providers.
  • Grab your API URL and public anon key from the project settings.

Next, install the Supabase JS client library:

npm install @supabase/supabase-js

Then initialize the client in your app using your project details:

import { createClient } from '@supabase/supabase-js'

const supabase = createClient(
  'https://your-project-id.supabase.co', 
  'your-public-anon-key'
)

Implementing Auth Flows

With Supabase set up, you can start using the auth functions like signIn and signOut in your Next.js application.

For example, to add a sign in form:

async function signIn({ email, password }) {
  const { error } = await supabase.auth.signIn({ email, password }) 
  if (!error) {
    router.push('/dashboard') 
  }
}

That covers the basics of adding authentication using Supabase and Next.js! With these building blocks, you can create secure user flows for login, signup, password recovery and more.

How would you implement secure authentication and authorization in a Next.js application?

Implementing secure authentication and authorization in a Next.js application can be achieved in a few ways, depending on your needs.

Use Supabase for Authentication

One great option is to use Supabase for authentication. Supabase makes it easy to add user management and authentication to your Next.js application.

To get started, you'll first need to create a Supabase project. Once your project is ready, install the Supabase JS library:

npm install @supabase/supabase-js

Then initialize the Supabase client in your app:

import { createClient } from '@supabase/supabase-js'

const supabase = createClient(
  'YOUR_SUPABASE_URL',
  'YOUR_SUPABASE_KEY'
)

Supabase offers a wide range of authentication options out of the box, including email/password, phone login, OAuth providers like Google and GitHub, and more.

For example, here is how you can implement a login form with email/password using Supabase:

async function signIn({ email, password }) {
  const { data, error } = await supabase.auth.signIn({
    email,
    password,
  })
}

Supabase seamlessly handles secure password storage, refresh tokens, email confirmations, and more.

Overall, Supabase auth provides a simple yet robust authentication system for Next.js apps. The Supabase JS library handles all of the complexities around implementing secure auth.

Use NextAuth.js

Another option is NextAuth.js, an open source authentication library designed for Next.js applications.

To get started with NextAuth.js, first install the library:

npm install next-auth

Then wrap your app in the NextAuthProvider:

import { NextAuthProvider } from 'next-auth/client'

function MyApp({ Component, pageProps }) {
  return (
    <NextAuthProvider session={pageProps.session}>
      <Component {...pageProps} />
    </NextAuthProvider>
  ) 
}

export default MyApp

NextAuth.js handles setting and refreshing session tokens, user verification, OAuth integration with providers like Google and GitHub, and more.

For example, here is how you can enable username/password authentication:

Providers: [
  Credentials({
    name: 'Credentials',
    credentials: {
      username: { label: 'Username', type: 'text' },
      password: {  label: 'Password', type: 'password' }
    },
    async authorize(credentials) {
      // Implement user login here  
    }
  })
] 

Overall, NextAuth.js is a great choice for implementing secure, production-ready authentication in Next.js.

So in summary, Supabase and NextAuth.js both provide robust and easy-to-use authentication for Next.js applications. Choose the one that best fits your needs!

How do you protect routes in Next.js 13?

To protect routes that require authentication or authorization in Next.js 13, we can use the App component to check the user's status and redirect them if needed. This is done by using the useRouter and useEffect React hooks.

Here is an example using Supabase for authentication:

import { useRouter } from 'next/router';
import { useEffect } from 'react';
import { useSession } from '@supabase/auth-helpers-react';

export default function App() {
  const router = useRouter(); 
  const { session } = useSession();

  useEffect(() => {
    if (!session && router.pathname !== '/login') {
      router.push('/login'); 
    } 
  }, [session, router]);

  return <>{/* children components */}</>;
}

This will check if there is an active user session on each route change. If no session exists, it will redirect the user to the /login page.

We can build upon this to only allow logged in users to access certain pages by checking session and router.pathname in the useEffect hook.

For example, to protect an /account route:

useEffect(() => {
  if (!session && router.pathname === '/account') {
    router.push('/login');
  }
}, [session, router]); 

This way we ensure that only authenticated users have access to pages or routes that require a login. The possibilities are endless when leveraging Supabase auth with Next.js routers and effects!

sbb-itb-5683811

Setting Up Supabase Server-Side Auth in NextJS

Supabase Auth makes it easy to add user authentication and authorization capabilities to your NextJS applications. With Supabase's auto-generated Auth APIs and secure database, you can get complete user management infrastructure without having to run your own servers.

In this guide, we'll explore integrating Supabase Auth in NextJS using getServerSideProps to check a user's logged-in status on each request before rendering pages. This helps ensure protected routes stay secure.

Initial Supabase Client Configuration

Start by setting up a reusable Supabase Client instance that can be imported anywhere across your NextJS app. This client will manage interactions with your Supabase project.

Here is an example utils/supabaseClient.js file:

import { createClient } from '@supabase/supabase-js'

const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY

export const supabase = createClient(supabaseUrl, supabaseKey)

Make sure to add the Supabase URL and anon public key to your environment variables.

Now the supabase instance can be imported and used to make authenticated API calls.

Securing Pages with Supabase/SSR

Next, use the Supabase client in getServerSideProps to check if a user session exists on each request.

If no active session is found, redirect the user:

export const getServerSideProps = async ({ req, res }) => {

  // Check user is authenticated
  const { user } = await supabase.auth.api.getUserByCookie(req)

  if (!user) {
    return {
      redirect: {
        destination: '/signin',
        permanent: false  
      }
    }
  }

  // If authenticated, return props
  return {
    props: {},
  }
}

This ensures only logged-in users can access the page.

Server-Side Authentication Example

Here is a full pages/dashboard.js example leveraging Supabase Auth on the server:

import { supabase } from '../utils/supabaseClient'

export const getServerSideProps = async ({ req, res }) => {

  // Check user token
  const { user } = await supabase.auth.api.getUserByCookie(req)

  // Redirect unauthenticated
  if (!user) {
    return {
      redirect: {
        destination: '/signin',
        permanent: false
      }
    }
  }

  // Fetch user data
  const { data } = await supabase
    .from('profiles')
    .select()
    .eq('id', user.id)
    .single()

  return {
    props: {
      user: data
    }
  }
}

export default function Dashboard({ user }) {
  // Display user data
  return (
    <div>
      <h1>Dashboard</h1>
      <p>Hello {user.username}</p> 
    </div>
  )
}

This checks the user cookie on every request, handles redirects, fetches user data from Supabase's database, and displays personalized content in the NextJS page.

By handling authentication logic on the server in getServerSideProps, you can securely render pages for logged in users while keeping anonymous visitors out. Combined with Supabase's auto-generated Auth APIs, adding login functionality is a breeze.

Building Client-Side Auth Flows with Supabase/auth-helpers-react

Integrating authentication in a NextJS app can be tricky, but the Supabase auth helpers for React make it simple. The useUser hook is the key, providing the user object and session details that enable client-side auth flows.

useUser Hook Overview

The useUser hook returns the user data and active session information. It automatically refetches the data if the auth state changes.

Here's an example usage:

import { useUser } from '@supabase/auth-helpers-react'

function Profile() {
  const { user } = useUser()

  return (
    <div>
      <h2>Profile</h2>
      {!user ? (
        <p>Loading...</p> 
      ) : (
        <p>Hello {user.email}!</p>
      )}
    </div>
  )
}

This allows showing a loading state before rendering profile details only for logged in users.

Conditionally Showing UI Elements

A common use case is conditionally showing UI elements based on auth state. For example, showing a Login button for anonymous users, and a Logout button for authenticated users.

function Header() {

  const { user } = useUser()

  return (
    <header>
      <Logo /> 
       {user ? (
         <LogoutButton />
       ) : (
         <LoginButton />
       )}
    </header>
  )
}

The user object has properties like user.id and user.role that enable granular conditional logic.

Client-Side Auth Flow Example

Here is a full example leveraging useUser to enable client-side auth in a NextJS app, using Supabase for the backend.

// pages/_app.js
import { SessionContextProvider } from '@supabase/auth-helpers-react'
import { createClient } from '@supabase/supabase-js'

const supabase = createClient(
  process.env.NEXT_PUBLIC_SUPABASE_URL, 
  process.env.NEXT_PUBLIC_SUPABASE_KEY
)

function MyApp({ Component, pageProps }) {
  return (
    <SessionContextProvider supabaseClient={supabase}>
      <Component {...pageProps} />
    </SessionContextProvider>
  ) 
}

export default MyApp

This wraps the app with the SessionContextProvider from @supabase/auth-helpers-react, passing the Supabase client.

Then we can use the useUser hook in our pages:

// pages/dashboard.js

import { useUser } from '@supabase/auth-helpers-react'

function Dashboard() {

  const { user } = useUser()

  return (
    <div>
      <h2>Dashboard</h2>
      {!user ? (
        <p>You must be logged in to view this page</p> 
      ) : (
        <PrivateData />
      )}
    </div>
  )
}

function PrivateData() {
  return <p>Private user data</p>
}

export default Dashboard

Now the dashboard conditionally shows private data only for logged in users.

The useUser hook manages the auth state automatically across pages, providing user details anywhere they are needed to gate access.

This is just one approach to client-side auth with Supabase and NextJS. There are many possibilities to fit any use case leveraging these tools. Conditional rendering based on auth state is simplified using the useUser hook from @supabase/auth-helpers-react.

Managing User Profiles with Supabase NextJS Pages

Supabase makes it easy to manage user profiles in NextJS applications. With Supabase's authentication and database services, you can securely fetch profile data, update user metadata, and implement password recovery flows.

Fetching and Displaying User Details

To display a user's profile data in NextJS pages, you can use Supabase's useUser hook along with server-side calls. Here is an example pages/profile.js route that fetches and shows a user's name and email:

import { useUser } from '@supabase/auth-helpers-react'
import { createClient } from '@supabase/supabase-js'

export async function getServerSideProps() {

  // Create authenticated Supabase Client
  const supabase = createClient()

  // Get the user from the server with the auth cookie
  const { user } = await supabase.auth.api.getUserByCookie()

  // Fetch user data
  let { data: profile, error } = await supabase
    .from('profiles')
    .select(`name, email`)
    .eq('id', user.id)
    .single()

  return {
    props: {
      user: profile,        
    },
  }
}

export default function Profile({ user }) {

  const supabaseUser = useUser()

  if (!supabaseUser) {
    return <p>Loading...</p> 
  }

  return (
    <div>
      <h1>{user.name}</h1>
      <p>{user.email}</p>
    </div>
  )
}

This allows you to securely access the user's data on the server and pass it as props to the page component.

Update User Data

To allow users to update their names, emails, or other custom metadata, you can create forms that update the Supabase user profiles table.

For example, to add an "update profile" form:

// Update profile info
async function updateProfile(data) {

  // Get user
  const user = supabase.auth.user()

  const updates = {
    id: user.id,
    ...data,
    updated_at: new Date(),    
  }  

  let { error } = await supabase.from('profiles').upsert(updates)
  if (error) throw error
  alert('Profile updated!')
}

// In UpdateForm component
function UpdateForm() {

  async function handleSubmit(e) {
    e.preventDefault()
    
    // Call updateProfile method
  }

  return (
    <form onSubmit={handleSubmit}>
      {/* name, email, etc. inputs */}
      <button>Update profile</button> 
    </form>
  )
}

This allows users to securely modify their stored profile data.

Implementing Password Recovery and Updates

Supabase Auth comes with built-in support for password recovery and updates.

To initiate recovery, call supabase.auth.api.resetPasswordForEmail():

async function recoverPassword(email) {

  let { error } = await supabase.auth.api.resetPasswordForEmail(email)
  if (error) throw error
  alert('Check your email for the recovery link!')
}

Then to allow password updates, use the updateUser() method:

function UpdatePasswordForm() {

  async function handleSubmit(password) {
    
    const { error } = await supabase.auth.updateUser({ password })  
    
    if (!error) {
      alert('Password updated!')
    }
  }

  return (
    <form onSubmit={handleSubmit}>
      <input name="password" type="password" />
      <button>Update Password</button>
    </form>
  )
}

By integrating these Supabase methods into your auth flows, you can securely manage passwords.

Overall, Supabase's APIs make managing user profiles, custom data, and passwords simple within NextJS apps. The React hooks and server-side methods provide the building blocks for full-featured user dashboards and account management.

Customizing Supabase Auth UI in Your NextJS Project

Override and enhance Supabase's hosted login, register, and password recovery UI screens in your NextJS application.

Implementing Supabase Auth UI Components

Supabase provides a set of pre-built React UI components for quickly adding authentication flows to your app. These include:

  • Auth.UserContext - Tracks user state and returns information about the signed-in user
  • Auth.SessionContext - Lower level context to access the user session object
  • Auth.AuthContext - Handles common auth actions like signIn(), signOut()
  • Auth.useUser() - React hook returning user object

To implement, first install the NPM package:

npm install @supabase/auth-ui-react

Then import and use the components:

import { Auth, ThemeSupa } from '@supabase/auth-ui-react'

export default function Home() {
  return (
    <Auth.UserContextProvider supabaseClient={supabase}> 
      <ThemeSupa>
        {/* Display login or user UI based on auth state */}
        <Auth checkSession>
          <Account />
          <Login />
        </Auth>
      </ThemeSupa>
    </Auth.UserContextProvider>
  )
}

This will handle the auth state and display the appropriate screens. Customize the layout or design by passing props and JSX to components like ThemeSupa.

Creating Custom Authentication Pages

To build completely custom auth pages while integrating with Supabase for the backend/APIs, use the contexts and hooks:

import { Auth } from '@supabase/auth-ui-react'

function Login() {

  const { signIn } = useAuth()

  const handleLogin = async (email, password) => {
    signIn({ email, password }) 
  }

  return (
    <form onSubmit={handleLogin}>
      {/* Custom login form */}
    </form>
  )
}

function App() {
  return (
   <Auth.SessionContextProvider>
     <Login /> 
   </Auth.SessionContextProvider>
  )
}

This keeps the auth session handling separate while you design fully customized screens.

Tailoring Localization and Theming

To translate text and customize styles/branding:

<ThemeSupa
  theme={tailwindTheme}
  language={language}
  components={{
    Button: CustomButton  
  }}
>
  {/* Auth Components */} 
</ThemeSupa>

Pass translations, override default CSS with theme, swap components like Button, and more.

In summary, Supabase's auth-ui library provides building blocks for fast and secure authentication flows in NextJS. Customize the pre-built screens or create your own using the hooks and contexts!

Leveraging a NextJS Supabase Template for Rapid Development

Jumpstarting development with a pre-built NextJS and Supabase template offers many advantages for accelerating your projects. Templates provide working code examples, configured workflows, and integrated features that allow skipping repetitive setup tasks. However, templates can also introduce unnecessary complexity or force restrictions on your application architecture. Understanding the tradeoffs helps determine if a template aligns to your use case.

Benefits of Using a NextJS Supabase Template

Integrating Supabase's authentication into a NextJS app from scratch requires configuring services across the client and server. Pre-built templates simplify setup by handling this wiring for you with working code for common flows like:

  • User signup, login, logout
  • Session handling
  • Account management
  • Password recovery
  • OAuth via social platforms

Additional template benefits include:

  • Quickstarts Development: Get started faster by cloning a project instead of an empty folder
  • Production-Ready Code: Templates often follow best practices for performance, security, and scalability
  • Customization Launchpoint: Tweak and extend template features to match your requirements
  • Time Savings: Spend more time building app functionality vs routine AuthN/AuthZ tasks
  • SEO Optimizations: Templates may integrate popular SEO enhancers like next-seo out-of-the-box

Drawbacks of Locking into Templates

While templates accelerate initial progress, they can sometimes create blockers later in development:

  • Overengineering: Bulky templates include many unused features that create unnecessary bloat
  • Vendor Lock-in: Tied to specific tools that are hard to replace if needs change
  • Upgrade Headaches: Breaking changes in dependent packages require fixing template inconsistencies
  • Constraint Creativity: Restrict project structure changes due to template coupling

So while tempting to skip straight to coding with a beefy starter kit, consider if it aligns with your application plans or restricts future ideas. Vetting template tradeoffs early helps prevent painful refactors down the road.

Tips for Evaluating NextJS Supabase Templates

When assessing a NextJS Supabase template, check that it:

  • Uses the latest stable Supabase and NextJS versions
  • Provides clear, idiomatic code examples
  • Separates reusable logic into hooks and utils
  • Implements React best practices
  • Follows compartmentalized folder structure conventions
  • Documents key dependencies and configuration
  • Highlights customization points for extending
  • Offers lightweight and slimmed down installation options
  • Stays active with recent releases and updates

Integrating Supabase Auth into a NextJS Application

If skipping the template route, Supabase offers NextJS specific helpers that enable wiring up user management flows directly:

import { createClient } from '@supabase/supabase-js'
import { SessionContextProvider } from '@supabase/auth-helpers-react'

const supabase = createClient(/* supabase url */) 

export default function MyApp({ Component, pageProps }) {
  return (
    <SessionContextProvider supabaseClient={supabase}>
      <Component {...pageProps} />
    </SessionContextProvider>
  )
}

This handles session state, user context, login/logout triggers, and server-side rendering flows. Customizing the integration further supports role-based views, policies, and advanced use cases.

Overall, while templates help fast track basic authentication, take time to evaluate if an integrated example aligns with your plans or causes unnecessary restrictions. Starting minimal then wiring up only required features also keeps your project lean. This lets you iterate without fighting template assumptions down the road.

Key Takeaways and Moving Forward with Supabase Auth in NextJS

Supabase Auth integrates smoothly with NextJS applications using getServerSideProps for server-side authentication and useUser hook for client-side auth flows. User management, custom data, and UI customization expand the possibilities further.

Essential Integration Techniques Recap

Here is a quick example using getServerSideProps for server-side authentication in NextJS with Supabase:

export const getServerSideProps = async ({ req, res }) => {

  // Create authenticated Supabase Client
  const supabase = createClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL, 
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY
  )

  const {
    data: { session },
  } = await supabase.auth.getSession()

  if (!session) 
    return { redirect: { destination: '/login' } } 
  
  // Pass the user info to the page
  return { props: { user: session.user } }  
}

And using the useUser hook for client-side auth with Supabase:

import { useUser } from '@supabase/auth-helpers-react'

function Home() {
  const { user } = useUser()

  if (!user) 
    return <Auth />
  
  return <PageContent /> 
}

These provide the core authentication building blocks when using Supabase Auth in NextJS.

Advanced Authentication Strategies

Some ideas to level up authentication in your Supabase + NextJS application:

  • Store custom user data with Supabase profiles
  • Enable OAuth via Google, GitHub etc
  • Implement granular user permissions
  • Set up Single Sign On (SSO) through SAML
  • Build custom auth UI with Supabase components

Leveraging these advanced strategies unlocks additional capabilities like access control, social logins, centralized auth, and more.

Supabase Community and Further Resources

Use the Supabase docs, example repos, courses, and Discord community for further learning!

Related posts

Read more

Make your website with
Unicorn Platform Badge icon