Skip to content

Fix: Ensure Supabase token refresh updates client cookies #2

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

aburio
Copy link

@aburio aburio commented Feb 26, 2025

Fix: Ensure Supabase token refresh updates client cookies

The getUser function in Supabase will refresh the token if needed. However, since this operation is performed server-side, the response must include the updated headers to properly update the cookie stored by the client.

This branch includes all the necessary changes to ensure that the cookie is either refreshed or deleted on the client side when required by the server.

Key Change

Previously, the function getServerClient only returned the Supabase client, but it did not expose the updated headers needed for the client to update its cookies.

Before:

export const getServerClient = (request: Request) => {
    const headers = new Headers();
    const supabase = createServerClient(
        process.env.SUPABASE_URL!,
        process.env.SUPABASE_ANON_KEY!,
        {
            cookies: {
                getAll() {
                    return parseCookieHeader(request.headers.get("Cookie") ?? "") ?? {};
                },
                setAll(cookiesToSet) {
                    cookiesToSet.forEach(({ name, value, options }) =>
                        headers.append(
                            "Set-Cookie",
                            serializeCookieHeader(name, value, options),
                        ),
                    );
                },
            },
        },
    );

    return supabase;
};

After (Fix):

The updated function now returns both the Supabase client and the headers, ensuring that any changes to the cookies are properly passed back in the response.

export const getServerClient = (request: Request) => {
    const headers = new Headers();
    const supabase = createServerClient(
        process.env.SUPABASE_URL!,
        process.env.SUPABASE_ANON_KEY!,
        {
            cookies: {
                getAll() {
                    return parseCookieHeader(request.headers.get("Cookie") ?? "") ?? {};
                },
                setAll(cookiesToSet) {
                    cookiesToSet.forEach(({ name, value, options }) =>
                        headers.append(
                            "Set-Cookie",
                            serializeCookieHeader(name, value, options),
                        ),
                    );
                },
            },
        },
    );

    return { client: supabase, headers };
};

Example Usage in a React Router v7 Loader/Action

To ensure that supabase updates the client-side cookies correctly, you should include the returned headers in your loader or action response.

export async function loader({ request }: Route.LoaderArgs) {
    const sbServerClient = getServerClient(request);
    const userResponse = await sbServerClient.client.auth.getUser();
    if (userResponse.error || !userResponse.data.user) {
        throw redirect("/login", { headers: sbServerClient.headers });
    }

    return data({ user: userResponse?.data?.user || null}, { headers: sbServerClient.headers });
}

export const loader = async ({ request }: Route.LoaderArgs) => {
	const sbServerClient = getServerClient(request);
	const { data: items, error } = await sbServerClient.client
		.from("items")
		.select("*");
	return data({ items, error }, { headers: sbServerClient.headers });
};

This ensures that if the user's token is refreshed or invalid, the appropriate response headers are sent back to update or remove the cookies on the client.

How to Verify Token Refresh

To verify that the token is correctly refreshed, you can open the developer console in your browser, retrieve the token from your cookies or local storage, and copy it into jwt.io. Check the exp or expires_at field in the payload. When the expiration time is reached, refresh the page on your site and verify that a new token is generated.

This fix ensures that when the server refreshes the token, the client-side cookies stay in sync.

Let me know if any further improvements are needed! 🚀

Link,
redirect,
useNavigate,
type MetaFunction,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs data imported as well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants