SupaLaunch logo
SUPALAUNCH
DemoShowcaseDocs
SupaLaunch
HomeDemoDashboardDocumentationBlogShowcaseServices and FrameworksTerms of ServicePrivacy policy
Tools
DB Schema GeneratorStartup Idea Generator
Other Projects
Syntha AICreateChatsCron Expression Generator
Contacts
Created with ❤️ by Denisdenis@supalaunch.com
  • Demo
  • Showcase
  • Docs
Blog

Google Authentication with NextJS 14 Server Actions and Supabase Guide

29 Apr 2025
By Denis
nextjssupabaseauthentication

Launch your startup with our Supabase + NextJS Starter Kit

SupaLaunch is a SaaS boilerplate built using Supabase and Next.js. It includes authentication, Stripe payments, Postgres database, 20+ TailwindCSS themes, emails, OpenAI API streaming, file storage and more.

  • Save weeks of your time: No need to setup authentication, payments, emails, file storage, etc.
  • Focus on what matters: Spend your time building your product, not boilerplate code.
Get started with SupaLaunch

Google Authentication with NextJS and Supabase

Authentication is a crucial part of any SaaS. If a few years ago it was a nightmare to implement, now it's much easier with the help of services like Supabase. In this guide, we will learn how to configure Google Authentication with NextJS and Supabase.

If you are struggling to implement Google Authentication yourself, check out SupaLaunch — Next.JS & Supabase starter kit. It has everything you need to start building your SaaS: authentication, database, payments, file storage, emails, SEO, landing page, and more.

Configuring Authentication in Google Cloud and Supabase

Let us start with configuring Google Authentication in Google Cloud and in Supabase. When everything is configured, we can start implementing the authentication flow in our NextJS app.

Authentication in Google Cloud consists of two parts: setting up the OAuth consent screen and creating OAuth credentials. We will use these credentials to authenticate users in our NextJS app.

  1. Go to the Google Cloud and create a new project if you don't have one already.

  2. Go to OAuth consent screen in Google Cloud to create a new consent screen. This is the screen that users will see when they log in to your app using Google.

    • [OAuth consent screen] Set up the consent screen settings

      • User Type: External
      • Application name/logo: anything you want
      • Add your website URL, privacy policy and terms of service URLs. It is important that you have both of these pages on your website.
      • Add your domain as an authorized domain. You can add your domain to Google Search Console to verify it.
      • Add your email address
    • [Scopes] Add the following scopes to Your non-sensitive scopes:

      • .../auth/userinfo.email
      • .../auth/userinfo.profile
    • [Test users] section

      • Add your email address as a test user
    • [Summary] section

      • Check if everything is correct. If yes, click Back to Dashboard button.
  3. Now let's configure Credentials. Go to Credentials and click Create Credentials to create a new OAuth Client ID.

    • Application type: Web application
    • Name: anything you want
    • Authorized JavaScript origins: leave empty
    • Authorized redirect URIs: add http://localhost:54321/auth/v1/callback for local development and https://<project-id>.supabase.co/auth/v1/callback for production (replace <project-id> with your Supabase project ID).
    • Click Create button
    • Save Client ID and Client Secret for later

Now let's turn on Google Authentication in Supabase. Go to Supabase -> Authentication -> Providers.

Supabase Authentication Providers

Turn on Google Authentication and add your Client ID and Client Secret from Google Cloud.

Make sure that Callback URL (for OAuth) from Supabase matches the Authorized redirect URIs from Google Cloud.

Click Save button. Now Google Authentication is enabled in your Supabase project in test mode. We will need to get our app approved by Google to use it in production. But we will do it later when our app is ready. For now, we can use it in test mode.

Implementing Google Authentication in NextJS

Now let's implement Google Authentication in our NextJS app. We will be using next.js server actions to handle the authentication flow. If you are not familiar with server actions, check our guide on how to create forms with Next.JS 14 Server Actions.

Authentication Page and React Component

First, we start with the authentication page and React component. We will use tailwindcss and daisyUI for styling. Create a new file @/app/auth/auth-form.tsx:

'use client'

import {login} from "@/app/auth/actions";

export default function AuthForm() {
    return (
            <div className="card-body">
                <h2 className="card-title">Login</h2>
                    <div>
                        <div className="p-5 col-6">
                            <form className='flex justify-center p-5'>
                                <button
                                    className='btn btn-primary w-full px-10'
                                    formAction={login}
                                >
                                    <img src="/images/google_logo.png" alt="google" className="w-6 h-6"/>
                                    Login with Google
                                </button>
                            </form>
                        </div>
                    </div>
            </div>
    )
}

Now let's create a server action (note 'use server' on the top of the file) to handle authentication. Create a new file @/app/auth/actions.ts:

'use server'

import { revalidatePath } from 'next/cache'
import { redirect } from 'next/navigation'
import { createClient } from "@/lib/utils/supabase/server"; // createClient is a function to create a Supabase client

export async function login(formData: FormData) {
    const supabase = createClient()
    let redirectURL = `${process?.env?.NEXT_PUBLIC_SITE_URL}api/auth/callback`

    const {data, error} = await supabase.auth.signInWithOAuth({
        provider: 'google',
        options: {
            redirectTo: redirectURL,
            scopes: 'https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile',
        },
    })

    if (error) {
        console.log('error', error)
        redirect('/auth/error')
    } else {
        revalidatePath('/', 'layout')
        redirect(data.url)
    }
}

Note two things in the code above:

  1. We are using createClient function to create a Supabase client. This function should be implemented in @/lib/utils/supabase/server.ts file. Check the Supabase documentation on how to create a Supabase client for server component.
  2. We create a redirect URL to /api/auth/callback route. We will implement this route later. But you need to add NEXT_PUBLIC_SITE_URL=your-site-url environment variable to your .env.local file. Use http://localhost:3000 for local development. For production, use your production URL.

Now let's create a login page. Create a new file @/pages/auth/login/page.tsx:

import AuthForm from "@/app/auth/auth-form";


export default function Login() {
    return (
        <div className="flex items-center justify-center h-screen">
            <div className="card bg-base-200 shadow-xl max-w-md">
                <AuthForm/>
            </div>
        </div>
    )
}

Now you can navigate to /auth/login in your browser and see the login form.

Login with Google

Cool! The login form is ready. Now let's implement the authentication callback route.

Authentication Callback Route

Create a new file @/app/api/auth/callback/route.ts:

import {cookies} from 'next/headers'
import {NextResponse} from 'next/server'
import {type CookieOptions, createServerClient} from '@supabase/ssr'
import {getSession} from "@/lib/db/server-side/user";
import {supabaseAdmin} from "@/lib/db/server-side/supabase-admin";
import {createContact, sendEmail} from "@/lib/emails/send-email";
import {getWelcomeEmail} from "@/lib/emails/email-templates";

export async function GET(request: Request) {
    const {searchParams, origin} = new URL(request.url)
    const code = searchParams.get('code')
    // if "next" is in param, use it as the redirect URL
    const next = searchParams.get('next') ?? '/sites'

    if (code) {
        const cookieStore = cookies()
        const supabase = createServerClient(
            process.env.NEXT_PUBLIC_SUPABASE_URL!,
            process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
            {
                cookies: {
                    get(name: string) {
                        return cookieStore.get(name)?.value
                    },
                    set(name: string, value: string, options: CookieOptions) {
                        cookieStore.set({name, value, ...options})
                    },
                    remove(name: string, options: CookieOptions) {
                        cookieStore.delete({name, ...options})
                    },
                },
            }
        )
        const {error} = await supabase.auth.exchangeCodeForSession(code)
        if (!error) {
            const session = await getSession()

            const {error: tokenError, statusText, status} = await supabaseAdmin
                .from('google')
                .upsert({
                    user_id: session.user.id,
                    provider_token: session.provider_token,
                    provider_refresh_token: session.provider_refresh_token,
                })

            if (tokenError) {
                console.error('tokenError', tokenError)
            }

            // check if this is a signup or a login
            if (statusText === 'Created') {
                await createContact({
                    email: session.user.email,
                    firstName: session.user.user_metadata['name'],
                })
                await sendEmail(session.user.email, getWelcomeEmail(session.user.user_metadata['name']))
            }
            return NextResponse.redirect(`${origin}${next}`)
        }
    }

    // return the user to an error page with instructions
    return NextResponse.redirect(`${origin}/auth/auth-code-error`)
}


Whenever you are ready with your app, return to the OAuth consent screen and click Publish App button. This will start the process of getting your app approved by Google. If it shows Prepare for verification button, go through the steps required.

Google will email you asking about the status of your app. Reply to this email.

:::info Usually, it took me a couple of days to get my app approved by Google. But it can take longer if you use more sensitive scopes. :::

import {createClientComponentClient} from '@supabase/auth-helpers-nextjs'

const supabase = createClientComponentClient()

Add the following environment variables to your .env.local file:

NEXT_PUBLIC_SUPABASE_URL=
NEXT_PUBLIC_SUPABASE_ANON_KEY=
SUPABASE_SERVICE_ROLE_KEY=

Y

const {data, error} = await supabase.auth.signInWithOAuth({
    provider: 'google',
    options: {
        redirectTo: redirectURL,
    },
})
if (error) {
    setError(error.message)
} else {
    setError(null)
}

API Route to handle the authentication callback

/api/auth/callback/route.ts

import { createRouteHandlerClient } from '@supabase/auth-helpers-nextjs'
import { cookies } from 'next/headers'
import { NextRequest, NextResponse } from 'next/server'

export const dynamic = 'force-dynamic'

export async function GET(req: NextRequest) {
    const supabase = createRouteHandlerClient({ cookies })
    const { searchParams } = new URL(req.url)
    const code = searchParams.get('code')

    if (code) {
        await supabase.auth.exchangeCodeForSession(code)
    }

    return NextResponse.redirect(new URL('/', req.url))
}