Search code examples
authenticationnext.jssupabase

Issues Implementing OAuth with Supabase and SSR in Next.js


I am working on integrating OAuth in my Next.js project using Supabase and "@supabase/ssr". I've successfully implemented MagicLink and email authentication, but I'm facing issues with OAuth. The authentication process seems to halt unexpectedly when I try to sign in using a social provider like Google.

async function signInWithEmail(formData: FormData) {
    "use server"

    const origin = headers().get("origin")
    const email = formData.get("email") as string
    const cookieStore = cookies()
    const supabase = createClient(cookieStore)

    const { error } = await supabase.auth.signInWithOtp({
      email: email,
      options: {
        emailRedirectTo: `${origin}/auth/callback`,
      },
    })

    if (error) {
      console.error(error)
      return redirect("/login?message=Could not authenticate user")
    }

    redirect("/login?message=Check your email inbox to continue signing in")
  }

with this callback:

import { createClient } from "@/lib/supabase/server"
import { NextResponse } from "next/server"
import { cookies } from "next/headers"

export async function GET(request: Request) {
  const requestUrl = new URL(request.url)
  const code = requestUrl.searchParams.get("code")

  if (code) {
    const cookieStore = cookies()
    const supabase = createClient(cookieStore)
    await supabase.auth.exchangeCodeForSession(code)
  }

  return NextResponse.redirect(requestUrl.origin)
}

lib/supabase/server.ts:

import { createServerClient, type CookieOptions } from "@supabase/ssr"
import { cookies } from "next/headers"

export const createClient = (cookieStore: ReturnType<typeof cookies>) => {
  return 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) {
          try {
            cookieStore.set({ name, value, ...options })
          } catch (error) {
            console.error(error)
          }
        },
        remove(name: string, options: CookieOptions) {
          try {
            cookieStore.set({ name, value: "", ...options })
          } catch (error) {
            console.error(error)
          }
        },
      },
    }
  )
}

This is the function I'm trying to use to get Oauth to work:

  async function signInWithSocialProvider(provider: any) {
    "use server"

    const origin = headers().get("origin")
    const cookieStore = cookies()
    const supabase = createClient(cookieStore)

    const { error } = await supabase.auth.signInWithOAuth({
      provider: provider,
      options: {
        redirectTo: `${origin}/auth/callback`,
      },
    })

    if (error) {
      console.error(error)
      return redirect("/login?message=Could not authenticate user")
    }
  }

Questions:

  1. Is there something I'm missing in the OAuth setup with Supabase and SSR?

  2. Could there be a configuration issue within Supabase or in my Next.js setup causing this behavior?

Any insights or suggestions to resolve this issue would be greatly appreciated.

Expected Behavior: After choosing to sign in with a social provider (e.g., Google), I expect to be redirected to the provider's sign-in page and then back to my application with the user authenticated.

Actual Behavior: The authentication process does not proceed beyond the initial OAuth request. When I log the name and value in the set function within createServerClient, I receive a token, but there's no redirection to the Google sign-in page, and the authentication does not complete.

Logs:

  • Name: sb-mlirbfytwbmtpcchmvlr-auth-token-code-verifier

  • Value: "d8d738ef35947d057b013bb19adaee03e44d74003c47ea27b8426b59df264ece2a9f98f78fbdfc5d3e562f021bdf0e0460b633246e889b6c"

Solution:

Adding redirect(data.url) as the last line within the signInWithSocialProvider function solved the issue. Here is the full function:

async function signInWithSocialProvider(provider: any) {
    "use server"

    const origin = headers().get("origin")
    const cookieStore = cookies()
    const supabase = createClient(cookieStore)

    const { data, error } = await supabase.auth.signInWithOAuth({
      provider: provider,
      options: {
        redirectTo: `${origin}/auth/callback`,
      },
    })

    if (error) {
      console.error(error)
      return redirect("/login?message=Could not authenticate user")
    }

    redirect(data.url)
  }

Solution

  • There is no way for the redirect to happen automatically on the server as each server-side framework has a different way of handling redirects. In the browser there is only one API to redirect so this can be hardcoded into the supabase-js library. You will need to get the data property back from the function and redirect to the data.url with the method Next.js provides for redirecting. You can read more here https://github.com/orgs/supabase/discussions/15862