Expo - React Native - Auth session missing!
thiagoadsix 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 trying to reset a password for a user, but every time I attempt to update the password, Supabase returns an error message saying 'Auth session missing!', even though I have already set a new session
To Reproduce
- I requested to reset my password.
- I received the email in my inbox.
- I clicked on the link.
- I was redirected to the app.
- I set the new password.
- I received the error message.
Expected behavior
Once the user is inside the app, he should be able to reset the password.
Screenshots
Here is the print showing the email:
Here is the moment after I clicked on the link:
Here is the moment after I clicked on open:
More informations
Here is my Supabase Context:
import { GoogleSignin } from "@react-native-google-signin/google-signin";
import { Session, User } from "@supabase/supabase-js";
import * as Linking from "expo-linking";
import { usePathname, useRouter, useSegments } from "expo-router";
import React, { createContext, useEffect, useState } from "react";
import { Alert } from "react-native";
import { supabase } from "@/config/supabase";
type SignUpParams = {
email: string;
password: string;
name: string;
redirectTo: string;
};
type ResetPasswordParams = {
email: string;
redirectTo: string;
};
type SupabaseContextProps = {
user: User | null;
session: Session | null;
initialized?: boolean;
updatePassword: (password: string) => Promise<void>;
requestResetPassword: ({
email,
redirectTo,
}: ResetPasswordParams) => Promise<void>;
};
type SupabaseProviderProps = {
children: React.ReactNode;
};
GoogleSignin.configure({
scopes: ["https://www.googleapis.com/auth/drive.readonly"],
webClientId: "...",
iosClientId: "...",
});
const parseSupabaseUrl = (url: string) => {
let parsedUrl = url;
if (url.includes("#")) {
parsedUrl = url.replace("#", "?");
}
return parsedUrl;
};
export const SupabaseContext = createContext<SupabaseContextProps>({
user: null,
session: null,
initialized: false,
updatePassword: async () => {},
requestResetPassword: async () => {},
});
export const SupabaseProvider = ({ children }: SupabaseProviderProps) => {
const [user, setUser] = useState<User | null>(null);
const [session, setSession] = useState<Session | null>(null);
const [initialized, setInitialized] = useState<boolean>(false);
const pathname = usePathname();
const segments = useSegments()[0];
const router = useRouter();
const updatePassword = async (password: string) => {
console.log({ password });
const { data, error } = await supabase.auth.updateUser({
password,
});
if (error) {
console.error(error);
throw error;
}
console.log({ data });
};
const requestResetPassword = async ({
email,
redirectTo,
}: ResetPasswordParams) => {
const { error } = await supabase.auth.resetPasswordForEmail(email, {
redirectTo,
});
if (error) {
throw error;
}
};
useEffect(() => {
supabase
.channel("schema-db-changes")
.on(
"postgres_changes",
{
event: "UPDATE",
schema: "public",
table: "roles",
},
(payload) => supabase.auth.refreshSession()
)
.subscribe();
supabase
.channel("schema-db-changes")
.on(
"postgres_changes",
{
event: "INSERT",
schema: "public",
table: "roles",
},
(payload) => supabase.auth.refreshSession()
)
.subscribe();
}, []);
useEffect(() => {
const { data } = supabase.auth.onAuthStateChange(async (event, session) => {
setSession(session);
setUser(session ? session.user : null);
setInitialized(true);
});
return () => {
data.subscription.unsubscribe();
};
}, []);
useEffect(() => {
(async () => {
const url = await Linking.getInitialURL();
if (url?.includes("reset-password")) {
const transformedUrl = parseSupabaseUrl(url);
const parsedUrl = Linking.parse(transformedUrl);
const accessToken = parsedUrl.queryParams?.access_token;
const refreshToken = parsedUrl.queryParams?.refresh_token;
if (
typeof accessToken === "string" &&
typeof refreshToken === "string"
) {
try {
const { data, error } = await supabase.auth.setSession({
access_token: accessToken,
refresh_token: refreshToken,
});
await supabase.auth.refreshSession();
if (error) throw error;
setSession(data.session);
setUser(session ? session.user : null);
if (data.session) {
router.replace("/(app)/settings/reset-password/");
}
} catch (error) {
console.error("Failed to set session:", error);
}
}
}
})();
}, []);
useEffect(() => {
if (!initialized) return;
if (!session && pathname !== "/(auth)/welcome") {
router.replace("/(auth)/welcome");
} else if (session && !pathname.includes("/settings/reset-password")) {
router.replace("/(app)/home/");
}
}, [initialized, session, segments, router]);
return (
<SupabaseContext.Provider
value={{
user,
session,
initialized,
updatePassword,
requestResetPassword,
}}
>
{children}
</SupabaseContext.Provider>
);
};
System information
- OS: [e.g. macOS]
- Version of @supabase/supabase-js: [e.g. 2.38.0]
- Version of Node.js: [e.g. 20.10.0]
Maybe I'm missing something about states, react component mounting, or something else that I don't have any idea.
I'm trying to update the session from user on this useEffect
, but for some reason, this is not updating the session, because Supabase keeps saying: Auth session missing!
:
useEffect(() => {
(async () => {
const url = await Linking.getInitialURL();
if (url?.includes("reset-password")) {
const transformedUrl = parseSupabaseUrl(url);
const parsedUrl = Linking.parse(transformedUrl);
const accessToken = parsedUrl.queryParams?.access_token;
const refreshToken = parsedUrl.queryParams?.refresh_token;
if (
typeof accessToken === "string" &&
typeof refreshToken === "string"
) {
try {
const { data, error } = await supabase.auth.setSession({
access_token: accessToken,
refresh_token: refreshToken,
});
await supabase.auth.refreshSession();
if (error) throw error;
setSession(data.session);
setUser(session ? session.user : null);
if (data.session) {
router.replace("/(app)/settings/reset-password/");
}
} catch (error) {
console.error("Failed to set session:", error);
}
}
}
})();
}, []);