supabase / auth-helpers

A collection of framework specific Auth utilities for working with Supabase.
https://supabase.github.io/auth-helpers/
MIT License
893 stars 240 forks source link

cannot get user after Google Auth sign in #718

Closed shawnesquivel closed 5 months ago

shawnesquivel commented 5 months ago

Bug report

Describe the bug

I'm using Supabase Auth helpers. I configured and tested that signing up a user with email/password is good.

However, when trying to set up Google auth, I cannot login my user. This is somewhat related to this unresolved discussion: https://github.com/orgs/supabase/discussions/16743

Tutorial I Followed https://supabase.com/docs/guides/auth/social-login/auth-google#using-the-oauth-flow-for-web

Troubleshooting

To Reproduce

I can get the URL from my google auth

      const googleRes = await supabase.auth.signInWithOAuth({
        provider: "google",
        options: {
          redirectTo: "http://localhost:3000",
        },
      });

which returns a URL with the code_challenge

{\"data\":{\"provider\":\"google\",\"url\":\"https://qctnbkfmquxoifgzwmml.supabase.co/auth/v1/authorize?provider=google&code_challenge=<CODE_CHALLENGE>&code_challenge_method=s256\"}

now back in my main page, if I execute getUser, it returns null.

supabase.auth.getUser() 

Expected behavior

Screenshots

If applicable, add screenshots to help explain your problem.

System information

"@supabase/auth-helpers-nextjs": "^0.8.7",
"@supabase/supabase-js": "^2.39.2",
"next": "14.0.4",

Full Code

See my full code snippet ```javascript const supabase = createClientComponentClient( NEXT_PUBLIC_SUPABASE_URL, NEXT_PUBLIC_SUPABASE_ANON_KEY ); const handleGoogleSignIn = async () => { try { const googleRes = await supabase.auth.signInWithOAuth({ provider: "google", options: { redirectTo: "http://localhost:3000", }, }); const tokenRes = await passGoogleTokenToSupabase(googleRes); // router.refresh(); } catch (err) { console.error(err); } }; async function passGoogleTokenToSupabase(request) { try { const requestUrl = new URL(request.data.url); const code = requestUrl.searchParams.get("code_challenge"); if (code) { console.log("got the code"); localStorage.setItem("codePassedToExchangeCodeForSesssion", code); const { data, error } = await supabase.auth.exchangeCodeForSession( code ); return data; } else { return null; } } catch (err) { console.error(err); } } ```
dijonmusters commented 5 months ago

Hey! Thanks for reporting this. By default, the auth-helpers and ssr packages use the PKCE auth flow, which requires you to redirect to a code exchange route after the authentication process has completed. You will need a route to handle this exchange, like this:

// /auth/callback.ts

import { cookies } from 'next/headers'
import { NextResponse } from 'next/server'
import { type CookieOptions, createServerClient } from '@supabase/ssr'

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

  if (code) {
    const cookieStore = cookies()
    const supabase = createServerClient(...)

    const { error } = await supabase.auth.exchangeCodeForSession(code)

    if (!error) {
      // change this to wherever you want to redirect the user after authentication completes
      return NextResponse.redirect('http://localhost:3000/dashboard')
    }
  }

  // return the user to an error page with instructions
  return NextResponse.redirect('http://localhost:3000/auth/error')
}

Then your call to signInWithOAuth needs to redirect to the above Route Handler.

const googleRes = await supabase.auth.signInWithOAuth({
  provider: "google",
  options: {
    redirectTo: "http://localhost:3000/auth/callback",
  },
});

The best place to start with a Next.js and Supabase app is the with-supabase template. This already has server-side authentication configured, so you can just focus on building an awesome app!

You can use this with the create-next-app command like this:

npx create-next-app@latest -e with-supabase
FingerLiu commented 2 months ago

similiar error here, with latest supabase(self hosted) when trying keycloak oauth: `

⨯ AuthApiError: invalid flow state, no valid flow state found at handleError (webpack-internal:///(rsc)/./node_modules/.pnpm/@supabase+auth-js@2.63.0/node_modules/@supabase/auth-js/dist/module/lib/fetch.js:74:11) at process.processTicksAndRejections (node:internal/process/task_queues:95:5) at async _handleRequest (webpack-internal:///(rsc)/./node_modules/.pnpm/@supabase+auth-js@2.63.0/node_modules/@supabase/auth-js/dist/module/lib/fetch.js:120:9) at async _request (webpack-internal:///(rsc)/./node_modules/.pnpm/@supabase+auth-js@2.63.0/node_modules/@supabase/auth-js/dist/module/lib/fetch.js:99:18) at async SupabaseAuthClient._exchangeCodeForSession (webpack-internal:///(rsc)/./node_modules/.pnpm/@supabase+auth-js@2.63.0/node_modules/@supabase/auth-js/dist/module/GoTrueClient.js:417:33) at async eval (webpack-internal:///(rsc)/./node_modules/.pnpm/@supabase+auth-js@2.63.0/node_modules/@supabase/auth-js/dist/module/GoTrueClient.js:744:28) { __isAuthError: true, status: 404, code: 'flow_state_not_found' } GET /auth/callback?state=xxx&session_state=xxx&code=xxx 500 in 426070ms`

FingerLiu commented 2 months ago

similiar error here, with latest supabase(self hosted) when trying keycloak oauth: `

⨯ AuthApiError: invalid flow state, no valid flow state found at handleError (webpack-internal:///(rsc)/./node_modules/.pnpm/@supabase+auth-js@2.63.0/node_modules/@supabase/auth-js/dist/module/lib/fetch.js:74:11) at process.processTicksAndRejections (node:internal/process/task_queues:95:5) at async _handleRequest (webpack-internal:///(rsc)/./node_modules/.pnpm/@supabase+auth-js@2.63.0/node_modules/@supabase/auth-js/dist/module/lib/fetch.js:120:9) at async _request (webpack-internal:///(rsc)/./node_modules/.pnpm/@supabase+auth-js@2.63.0/node_modules/@supabase/auth-js/dist/module/lib/fetch.js:99:18) at async SupabaseAuthClient._exchangeCodeForSession (webpack-internal:///(rsc)/./node_modules/.pnpm/@supabase+auth-js@2.63.0/node_modules/@supabase/auth-js/dist/module/GoTrueClient.js:417:33) at async eval (webpack-internal:///(rsc)/./node_modules/.pnpm/@supabase+auth-js@2.63.0/node_modules/@supabase/auth-js/dist/module/GoTrueClient.js:744:28) { __isAuthError: true, status: 404, code: 'flow_state_not_found' } GET /auth/callback?state=xxx&session_state=xxx&code=xxx 500 in 426070ms`

Found something weird: My nextjs app works fine with supabase instance deploy on supabase.com, but when I switch to use a self-hosted instance, it gives me this error. I debugged step by step and found the only different is the code parameter called with /auth/callback, code Self-hosted version is something like '3ccfedb0-142a-4f95-a50c-d496f02b577c.704c8b6e-0761-4a26-a365-26689fb80fcb.d650aec2-a988-49ac-a63f-670ba6959540', which is not 'uuid.NewV4()'.

Notice the code param

 GET /auth/callback?state=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE3MTMyNzk5MjAsInNpdGVfdXJsIjoiaHR0cHM6Ly9leHBsb3JhdGlvbi5sb2NhbC5kcC50ZWNoIiwiaWQiOiIwMDAwMDAwMC0wMDAwLTAwMDAtMDAwMC0wMDAwMDAwMDAwMDAiLCJmdW5jdGlvbl9ob29rcyI6bnVsbCwicHJvdmlkZXIiOiJrZXljbG9hayIsInJlZmVycmVyIjoiaHR0cDovL2xvY2FsaG9zdDozMDAxL2F1dGgvY2FsbGJhY2s_bmV4dD11bmRlZmluZWQiLCJmbG93X3N0YXRlX2lkIjoiYjlkYTA5ZDMtNWIwMy00M2E2LWE2ODktNTQ0ZThmZGNiNTFhIn0.x5lpS-RBfenlwIBFkj76Rn0YxeYp2n5mlBNhs0lrz4c&session_state=704c8b6e-0761-4a26-a365-26689fb80fcb&code=3ccfedb0-142a-4f95-a50c-d496f02b577c.704c8b6e-0761-4a26-a365-26689fb80fcb.d650aec2-a988-49ac-a63f-670ba6959540
FingerLiu commented 2 months ago

After setting this env to auth container, get things working well now.

  GOTRUE_EXTERNAL_KEYCLOAK_REDIRECT_URI: https://MY-SUPABASE-HOSTNAME/auth/v1/callback
yaberkane05 commented 2 weeks ago

After setting this env to auth container, get things working well now.

  GOTRUE_EXTERNAL_KEYCLOAK_REDIRECT_URI: https://MY-SUPABASE-HOSTNAME/auth/v1/callback

can you elaborate more please ? I'm using a self hosted supabase instance on coolify and integrated azure auth. I'm using nextjs for my app. On the server, this is my code:

`'use server'

import { createClient } from "@/utils/supabase/server"; import { redirect } from "next/navigation";

export async function azureAuth(): Promise { const supabase = createClient(); const { data, error } = await supabase.auth.signInWithOAuth({ provider: 'azure', options: { scopes: 'email', redirectTo: process.env.AZURE_AUTH_CALLBACK_URL, }, })

if (data.url) { redirect(data.url) // use the redirect API for your server framework } }`

the flow is working fine and I get to login into my microsoft account, but when the callback url is called with a valid code and state, I get a 500. I have this redirect uri for the gotrue auth container: - 'GOTRUE_EXTERNAL_AZURE_REDIRECT_URI=https://myappurl.com/api/auth/azure/callback' which leads to a nextjs api route with this:

`import { cookies } from 'next/headers' import { NextResponse } from 'next/server' import { type CookieOptions, createServerClient } from '@supabase/ssr'

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') ?? '/'

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) { return NextResponse.redirect(${origin}${next}) } }

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

I don't understand the problem here.`