Reset Password Functionality With Supabase and React

Reset Password Functionality With Supabase and React

Hello smart Devs!

Late July I started coding a daily sales recording app using React, Redux, Tailwindcss for styling and Supabase. Setting up my supabase backend was fun and easy, but I find implementing some supabase auth features a bit challenging. One of this auth feature is the reset password. I really find implement this feature challenging because supabase documentation on this feature is not detailed as expected, and I couldn’t find any blog post on this subject. So in this article I will share my idea on how to implement the reset password functionality using Supabase. But before we dive in, let’s have a brief introduction to supabase.

INTRODUCTION TO SUPABSE

Supabase is an open source Firebase alternative that lets you create a real-time backend in less than two minutes.

Supabase has continued to gain hype and adoption with developers in my network over the past few months. And a lot of the people I've talked to about it prefer the fact that it leverages a SQL-style database, and they like that it's open source, too.

When you create a project Supabase automatically gives you a Postgres SQL database, user authentication, and API. From there you can easily implement additional features like realtime subscriptions and file storage.

Note: In this article we would focus on the idea of implementing a reset password functionality using Supabase . And I will assume you have a knowledge of supabase; and you have setup the login page with a forget password link. You can read this article Supabase Auth

Let’s dive in!

BASIC PASSWORD RESET CONCEPT

The first step to implement the password reset functionality is to understand the core concept or how the reset password functionality works. When a user clicks on the reset password link in our app, we should be able to:

  1. Show a page or a modal that the user would input an email associated with their account in our app.
  2. Send a password reset link to the email provided
  3. Redirect the user to a page where the user would input a new password and maybe confirm the password, when they click on the reset password link sent to their email.
  4. Update our database with the new password and redirect the user to the login page, so they could log in with the new password.

Supabase Auth

Supabase provides auth instances that are part of the Supabse.js library. Supabase auth is easy to use. Just call any auth instance and pass the needed params and you're good to go. To set up a reset password functionality we would use the api.resetPasswordForEmail('user email') instance.

 const resetPassword = async (email) => {
  const res = await supabase.auth.api.resetPasswordForEmail(email, {

  });

};

Normally, we would provide a placeholder as params and add the email dynamically.

The Supabase api.resetPasswordForEmail('user email') accepts two params:

  1. Email (required) - this is the user email associated with their account in our app.

  2. redirectTo (optional) - a URL or mobile address to send the user to after they confirmed i.e when the user clicks on the password reset link sent to their email.

When a user provides their email in the page or modal we show to them, a password reset request would be sent and Supabase would send a link to the email provided . on click on the link, supabase would forward the user to this URL

SITE_URL>#access_token=x&refresh_token=y&expires_in=z&token_type=bearer&type=recovery

localhost:3000#access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJhdXRoZW50aWNhdGVkIiwiZXhwIjoxNjI5MTE1ODkyLCJzdWIiOiJhMjljNGM2My1hNDE2LTRjODgtOWMzMi04NzA4NDBmYWUzMjUiLCJlbWFpbCI6ImVqZW1iaXRob21hczYxQGdtYWlsLmNvbSIsInBob25lIjoiIiwiYXBwX21ldGFkYXRhIjp7InByb3ZpZGVyIjoiZW1haWwifSwidXNlcl9tZXRhZGF0YSI6e30sInJvbGUiOiJhdXRoZW50aWNhdGVkIn0.G1gap3rMbhFMteeyCSx-9t0_mhpMlKTbLgwHuKW1uik&expires_in=3600&refresh_token=yh92kdTY4h74fYTKfZx09Q&token_type=bearer&type=recovery

The above URL contains the URL to redirect the user to in our app, which by default would be the homepage (localhost:3000 in our case), and the access token.

You may wonder: What if we had setup a page where the user would input their new password, how do we redirect the user to that page?

Answer: Remember the api.resetPasswordForEmail('user email') accepts a second params which is a URL. We can provide the link to the 'new password' page, like so

  const res = await supabase.auth.api.resetPasswordForEmail(email, {
    redirectTo: "http://localhost:3000/password/new"
  });

So, when the user click on the link in their email they would be redirected to that link.

In order to update the new password, we would need to send the new password created by the user and the access token to Supabase. Now, how do we pull out the access token from the URL?

Since we are using React on the frontend, we must be using react-router to link all pages in our app. React-router comes with params that we can use to read the URL and pull out the access token from the URL. The location object provided by React-router saves the path of the URL. so if we should run this code

console.log(prop.location)

we would have access to the hash variable containing some string which contains the URL.

console.log(prop.location.hash)

The above code would return anything after the URL of the page we are at, which contain the access token, like what we have below

#access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJhdXRoZW50aWNhdGVkIiwiZXhwIjoxNjI5MTE1ODkyLCJzdWIiOiJhMjljNGM2My1hNDE2LTRjODgtOWMzMi04NzA4NDBmYWUzMjUiLCJlbWFpbCI6ImVqZW1iaXRob21hczYxQGdtYWlsLmNvbSIsInBob25lIjoiIiwiYXBwX21ldGFkYXRhIjp7InByb3ZpZGVyIjoiZW1haWwifSwidXNlcl9tZXRhZGF0YSI6e30sInJvbGUiOiJhdXRoZW50aWNhdGVkIn0.G1gap3rMbhFMteeyCSx-9t0_mhpMlKTbLgwHuKW1uik&expires_in=3600&refresh_token=yh92kdTY4h74fYTKfZx09Q&token_type=bearer&type=recovery

Now that we have the URL, we are faced with two serious problems:

  1. we only have access to the above URL when our page first renders to the screen. The props.location.hash would be set to an empty string when the page re-renders. so we need to save the URL before the page re-render.
  2. What we need is the access token but we have the access token, expires_in, refresh_token, token_type and type variables(I don't know what to call it) all together in the URL. so we would have to find a way to read only the access_token out of the URL

Let solve the first problem.

import React, { useState, useEffect } from "react";
const NewPassword = props => {
  const [token, setToken] = useState("");

 useEffect(() => {
    const res = props.location.hash;

    setToken(res);
  }, []);

}

The above code is pretty straight forward. When the page first renders to the screen we want to save the URL to the res variable from the props.location.hash and then update our token state, and when the page re-renders we would still have the saved token because the useEffect((),[]) runs once. we can use componentDidMount() lifecycle method if we are using class bases component.

To the second problem:

To read the access_token from the URL we would the help of a Js string method. We would need to split the string to an array seperating them with the &, then we would be able to get the access_token. like so:

import React, { useState, useEffect } from "react";
const NewPassword = props => {
  const [token, setToken] = useState("");

 useEffect(() => {
    const res = props.location.hash.split('&');

    setToken(res);
  }, []);

We split the content of the URL by the &. so we would expect an array of the seperated string. Next we would to have access to the access_token. Add the blow code

import React, { useState, useEffect } from "react";
const NewPassword = props => {
  const [token, setToken] = useState("");

 useEffect(() => {
    const res = props.location.hash.split('&')[0];

    setToken(res);
  }, []);

Now we have access to the access_token. but, if you print the res variable in the console you should see this printed.

#access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJhdXRoZW50aWNhdGVkIiwiZXhwIjoxNjI5MTE1ODkyLCJzdWIiOiJhMjljNGM2My1hNDE2LTRjODgtOWMzMi04NzA4NDBmYWUzMjUiLCJlbWFpbCI6ImVqZW1iaXRob21hczYxQGdtYWlsLmNvbSIsInBob25lIjoiIiwiYXBwX21ldGFkYXRhIjp7InByb3ZpZGVyIjoiZW1haWwifSwidXNlcl9tZXRhZGF0YSI6e30sInJvbGUiOiJhdXRoZW50aWNhdGVkIn0.G1gap3rMbhFMteeyCSx-9t0_mhpMlKTbLgwHuKW1uik

We have the #access_token= in the access token itself. we do not need that. To remove it we use another string method, slice().

import React, { useState, useEffect } from "react";
const NewPassword = props => {
  const [token, setToken] = useState("");

 useEffect(() => {
    const res = props.location.hash.split('&')[0].slice('14);

    setToken(res);
  }, []);

The above line of code would give us the access token needed to update the user new password. The next step is to send the new password and the retrieved access token to Supabase so it would be updated.

Supabase Password Update

We have successfully saved the access token that would enable us update the user password.

Supabase provides another auth instance: auth.api.updateUser(access_token, { password : new_password }).

This auth instance expects two params- access token and new password.

The access token is that long string we just retrieved from our URL. so we would need to pass it, with the new password created by the user, to the auth.api.updateUser(access_token, { password : new_password }) instance to update the user password.

 const updatePassword = async(
  access_token,
  new_password
) => {
  const { error, data } = await supabase.auth.api.updateUser(access_token, {
    password: new_password
  });
};

When the request is made, Supabase would update the user password. We can then navigate the user to a page or modal programmatically telling them the request is successful, and they can log in with their new password.

When the above is properly implemented, the user can now log in with the new password.

Pretty simple right? I think so too!

That is all for now, Thank You for reading.

You can connect with me on LinkedIn