cannot get user after Google Auth sign in
shawnesquivel opened this issue · comments
Bug report
- I confirm this is a bug with Supabase, not with my own application.
- I confirm I have searched the Docs, GitHub Discussions, and Discord.
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
- User DOES get added to the Authentication Dashboard, with a Provider = Google, which suggests they were successfully authorized
- Sign in with google redirects to localhost:3000 with a
code_challenge
parameter, which suggests that Google is authenticating the login - Configured all redirects on Supabase + Google Console as per the documentation
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
- User would be signed in after signing in with Google.
- Actual:
supabase.auth.getUser()
returns null
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
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);
}
}
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
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`
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
After setting this env to auth container, get things working well now.
GOTRUE_EXTERNAL_KEYCLOAK_REDIRECT_URI: https://MY-SUPABASE-HOSTNAME/auth/v1/callback
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.`