All answers

Supabase

How do I build a Supabase password reset flow in an Expo app?

Send the reset email with a redirect URL that points back into your app, add that URL to Supabase's allowed redirect URLs, then turn the link your app receives into a session and call updateUser with the new password.

Updated October 31, 2026

A password reset is one of the few flows where a user leaves your app for their email and has to come back, which is why it’s an easy one to get stuck on. Each piece of it is small, though. The email needs a link that opens your app, and your app needs to know what to do with that link.

How the flow works

The user taps Forgot password, your app asks Supabase to send an email, and the link in that email opens your app. Once it’s open, your app turns the link into a signed-in session, shows a new password screen, and saves the new password.

How to build it

  1. Give your app a link scheme in app.json, such as "scheme": "myapp". We’d build the redirect URL with expo-linking rather than typing it out, because it gives the right URL in Expo Go and in your real builds:

    import * as Linking from "expo-linking";
    
    const redirectTo = Linking.createURL("reset-password");
  2. Allow that URL in Supabase. In your project, open Authentication, then URL Configuration, and add it under Redirect URLs (for example myapp://**). If the URL isn’t on the list, the link goes to your Site URL instead, so when a reset link opens a web page rather than the app, check this list first.

  3. Send the email:

    await supabase.auth.resetPasswordForEmail(email, { redirectTo });
  4. Turn the incoming link into a session. Which values arrive depends on the auth flow your Supabase client uses. With the PKCE flow you get a code, and with the implicit flow you get tokens after the #:

    async function handleResetLink(url: string): Promise<boolean> {
      const { queryParams } = Linking.parse(url);
      if (typeof queryParams?.code === "string") {
        const { error } = await supabase.auth.exchangeCodeForSession(queryParams.code);
        return !error;
      }
      const hash = new URLSearchParams(url.split("#")[1] ?? "");
      const access_token = hash.get("access_token");
      const refresh_token = hash.get("refresh_token");
      if (!access_token || !refresh_token) return false;
      const { error } = await supabase.auth.setSession({ access_token, refresh_token });
      return !error;
    }

    Only show the new password screen when it returns true. A false means the link expired or was already used, so show a message asking for a new one. Skipping this check would let the next step change the password of whoever is already signed in on that phone.

    Call it from Linking.getInitialURL() when the app opens from the link, and from a Linking.addEventListener("url", ...) listener when it’s already running. With PKCE, the link has to be opened on the same device that asked for the reset, because the app saved part of the exchange when it sent the request.

  5. Show a new password screen and save it:

    await supabase.auth.updateUser({ password: newPassword });

How you know it works

To test it, request a reset on a real phone and open the email on that same phone before you tap the link. The app opens on your new password screen, the new password saves, and you can sign out and back in with it.

Start with the allowed redirect URLs, and check that the scheme in app.json matches the link. Reset links also expire, so request a fresh one when you test. And if the email opens on a computer, there’s no app there to open, so test with the email on the phone.

Ask the agent

If your app is in Draftbit, paste this into the agent, and it can read your code and make the change for you.

Add a forgot password flow to my app with Supabase. Send the reset email with a redirect link that opens the app, add that link to Supabase's redirect URLs, sign the user in from the link, then show a screen where they set a new password. Test it end to end on a phone.

Open Draftbit

More Supabase answers