Why

When doing some greenfield work with a native application and Supabase there was not an easy way to set users’ passwords without having a way to handle the redirect back from Supabase.

What

The script found in the snippet below allows us to set a password for a live email that we have access to that has been added to our supabase instance.

How

The supabase-js package provides the passwordResetForEmail function.

Normally, your server calls this when a password reset is attempted. The user then receives an email with a URL and a password reset token.

The link in the email takes us to a supabase URL which then redirects us back to our server.

/**
 * This script is the easiest way to set a password.
 * Make sure TEST_USER_EMAIL and TEST_USER_PASSWORD are set in your dotenv file
 * Run node set-pw.js
 * This will email you a link to supabase which will redirect you to localhost
 * with a token in the query param
 * the script will wait for you to input the full URL
 * Example URL https://ktpqdrvkcmewncabhiyt.supabase.co/auth/v1/verify?token=FAKE_TOKEN&type=recovery&redirect_to=http://localhost:3000
 */
//
const { createClient } = require('@supabase/supabase-js');
require('dotenv').config();

const { SUPABASE_URL, SUPABASE_KEY, TEST_USER_EMAIL, TEST_USER_PASSWORD } =
  process.env;

if (!SUPABASE_URL || !SUPABASE_KEY)
  throw new Error('Supabase credentials where not set (check .env.example?)');

const supabase = createClient(SUPABASE_URL, SUPABASE_KEY);

if (!TEST_USER_EMAIL || !TEST_USER_PASSWORD)
  throw new Error(
    'Make sure TEST_USER_EMAIL is set to your email and TEST_USER_PASSWORD is defined'
  );

supabase.auth.api
  .resetPasswordForEmail(TEST_USER_EMAIL)
  .then(async () => {
    console.log('Password reset email sent.');
    console.log(
      'Click the link in the email which will redirect you to localhost'
    );
    const readline = require('readline').createInterface({
      input: process.stdin,
      output: process.stdout
    });

    readline.question(
      'Copy the localhost URL and paste here: ',
      async (passwordResetURL) => {
        // Supabase query params use a hashtag for some reason...
        const token = new URLSearchParams(passwordResetURL.split('#')[1]).get(
          'access_token'
        );
        console.log(`Token was ${token}`);
        const { error, data } = await supabase.auth.api.updateUser(token, {
          password: TEST_USER_PASSWORD
        });
        if (error) {
          console.log(error);
          throw error;
        } else {
          console.log('Data returned', data);
        }
        readline.close();
      }
    );
  })
  .catch((e) => console.log(`ERROR ${e}`));