Created
August 28, 2024 19:36
-
-
Save Gbillington1/97135ec16aab7863ff71aa535d7970d9 to your computer and use it in GitHub Desktop.
Supabase JS 2.42.2 Error with exchangeCodeForSession in PKCE Flow
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ./src/middleware.js | |
import { updateSession } from "@/src/libs/supabase/middleware"; | |
export async function middleware(request) { | |
return await updateSession(request); | |
} | |
export const config = { | |
matcher: [ | |
/* | |
* Match all request paths except for the ones starting with: | |
* - _next/static (static files) | |
* - _next/image (image optimization files) | |
* - favicon.ico (favicon file) | |
* Feel free to modify this pattern to include more paths. | |
*/ | |
'/((?!_next/static|_next/image|favicon.ico|icon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)', | |
], | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ./src/signin/page.js | |
// This is where the OAuth flow is started. I want to use PKCE so I can pass the user type through to supabase, so I can use it in a trigger | |
"use client"; | |
import Link from "next/link"; | |
import { useState } from "react"; | |
import config from "@/src/config"; | |
import { createClient } from "@/src/libs/supabase/client"; | |
import { USER_ROLE } from "@/src/libs/constants"; | |
import toast from "react-hot-toast"; | |
import { signUpWithOAuth } from "../actions"; | |
import { set } from "zod"; | |
// This a login/singup page for Supabase Auth. | |
// Successfull login redirects to /api/auth/callback where the Code Exchange is processed (see app/api/auth/callback/route.js). | |
export default function Login() { | |
const [isLoading, setIsLoading] = useState(false); | |
console.log(process.env.NODE_ENV) | |
const handleSignup = async (e, options) => { | |
e?.preventDefault(); | |
setIsLoading(true); | |
// ignore, this is the server actions implementations (yields same error) | |
// const redirectURL = window.location.origin + "/api/auth/callback"; | |
// try { | |
// await signUpWithOAuth("google", redirectURL); | |
// } catch (error) { | |
// toast.error("Failed to sign-up with Google"); | |
// } finally { | |
// setIsLoading(false); | |
// } | |
try { | |
const { type, provider } = options; | |
const redirectURL = window.location.origin + "/api/auth/callback"; | |
if (type === "oauth") { | |
const supabase = createClient(); | |
await supabase.auth.signInWithOAuth({ | |
provider, | |
options: { | |
redirectTo: redirectURL, | |
data: { | |
role: USER_ROLE.USER.id, | |
} | |
}, | |
}); | |
} | |
} catch (error) { | |
console.log(error); | |
} finally { | |
setIsLoading(false); | |
} | |
}; | |
return ( | |
<main className="p-8 md:p-24" data-theme={config.colors.theme}> | |
// handle sign up event happens here | |
<button | |
className="btn btn-block" | |
onClick={(e) => | |
handleSignup(e, { type: "oauth", provider: "google" }) | |
} | |
disabled={isLoading} | |
> | |
Sign-up with Google | |
</button> | |
</main> | |
); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ./src/app/api/auth/callback/route.js | |
import { NextResponse } from "next/server"; | |
import config from "@/src/config"; | |
import { createClient } from "@/src/libs/supabase/server"; | |
export const dynamic = "force-dynamic"; | |
// This route is called after a successful login. It exchanges the code for a session and redirects to the callback URL (see config.js). | |
export async function GET(req) { | |
const requestUrl = new URL(req.url); | |
const code = requestUrl.searchParams.get("code"); | |
if (code) { | |
const supabase = createClient(); | |
const { error } = await supabase.auth.exchangeCodeForSession(code); // THIS IS WHERE THE ERROR HAPPENS | |
if (error) { | |
console.error("Error exchanging code for session", error); | |
return NextResponse.error(new Error("Error exchanging code for session")); | |
} | |
} | |
// URL to redirect to after sign in process completes | |
return NextResponse.redirect(requestUrl.origin + config.auth.callbackUrl); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ./src/libs/supabase/middleware.js | |
import { createServerClient } from "@supabase/ssr"; | |
import { NextResponse } from "next/server"; | |
export async function updateSession(request) { | |
let supabaseResponse = NextResponse.next({ | |
request, | |
}); | |
const supabase = createServerClient( | |
process.env.NEXT_PUBLIC_SUPABASE_URL, | |
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY, | |
{ | |
cookies: { | |
getAll() { | |
return request.cookies.getAll() | |
}, | |
setAll(cookiesToSet) { | |
cookiesToSet.forEach(({ name, value, options }) => request.cookies.set(name, value)) | |
supabaseResponse = NextResponse.next({ | |
request, | |
}) | |
cookiesToSet.forEach(({ name, value, options }) => | |
supabaseResponse.cookies.set(name, value, options) | |
) | |
}, | |
}, | |
} | |
) | |
// IMPORTANT: Avoid writing any logic between createServerClient and | |
// supabase.auth.getUser(). A simple mistake could make it very hard to debug | |
// issues with users being randomly logged out. | |
const { data: { user } } = await supabase.auth.getUser() | |
console.log("MIDDLEWARE USER", user) // prints NULL always | |
// IMPORTANT: You *must* return the supabaseResponse object as it is. If you're | |
// creating a new response object with NextResponse.next() make sure to: | |
// 1. Pass the request in it, like so: | |
// const myNewResponse = NextResponse.next({ request }) | |
// 2. Copy over the cookies, like so: | |
// myNewResponse.cookies.setAll(supabaseResponse.cookies.getAll()) | |
// 3. Change the myNewResponse object to fit your needs, but avoid changing | |
// the cookies! | |
// 4. Finally: | |
// return myNewResponse | |
// If this is not done, you may be causing the browser and server to go out | |
// of sync and terminate the user's session prematurely! | |
return supabaseResponse | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment