Home
> Update Firebase Email with Reauthentication
Get updates on future FREE course and blog posts!
Subscribe

Update Firebase Email with Reauthentication

7 min read

Jonathan Gamble

jdgamble555 on Wednesday, May 29, 2024 (last modified on Wednesday, May 29, 2024)

Updating an email or password is handled differently in Firebase than updating a user’s profile name or photo URL. There is a separate function, you may have to reauthenticate the user, and the email must be confirmed.

TL;DR#

After 5 minutes of logging in with Firebase, you must reauthenticate your login session to change a password or update an email. This requires a different login function than authenticating for the first time. You must check for an error code and direct the user correctly.

Other Methods#

You could technically store the user’s email in Firestore, and use a Trigger Function to update the email in the Firebase Auth database like you might use when updating the profile information, but this would be less secure and could cause conflicts. You want to update the email directly in the Firebase Auth database to ensure integrity and security.

Shared State#

You need some version of shared state. This app uses Svelte because I can prototype a demo very quickly. The Svelte Shared Store allows you to use a writable context anywhere in your app.

	// writable store context
export const useWritable = <T>(name: string, value?: T) =>
    useSharedStore(name, writable, value);
    
// use reLogin
export const useRelogin = () => useWritable('relogin', false);

Relogin#

Along with the login, we need to be able to relogin when necessary.

Functions#

	export const loginWithGoogle = async () =>
    await signInWithPopup(
        auth, new GoogleAuthProvider()
    );

// requires the user
export const reLoginWithGoogle = async () => {
    if (auth.currentUser) {
        await reauthenticateWithPopup(
            auth.currentUser,
            new GoogleAuthProvider()
        );
    }
}

Login Component#

Here, we check for the re-login state. Otherwise, we display a regular login component.

	<script lang="ts">
    import { loginWithGoogle, reLoginWithGoogle, useRelogin } from '$lib/user-user';

    const relogin = useRelogin();

    const login = async () => {
        if ($relogin) {
            await reLoginWithGoogle();
            relogin.set(false);
            return;
        }
        await loginWithGoogle();
    };
</script>

<form method="POST" on:submit|preventDefault={login}>
    <button type="submit" class="bg-red-600 p-2 font-semibold text-white">
        Signin with Google
    </button>
</form>
{#if $relogin}
    <p>You must re-signin to change your email.</p>
{/if}

Update Email#

The function returns an error or nothing.

	export const updateProfileEmail = async (
    email: string
) => {

    if (!auth.currentUser) {
        return {
            error: 'Not Logged In!'
        };
    }
    try {
        await updateEmail(auth.currentUser, email);
    } catch (e) {
        if (e instanceof FirebaseError) {
            return {
                error: e.message
            };
        }
    }
    return {};
};

Reauthenticate#

In Firebase Auth, some actions require reauthentication. If it is necessary, you will be thrown an error.

	// error.code === 'auth/requires-recent-login'

or

// error.message === 'Firebase: Error (auth/requires-recent-login).'

Because I’m only dealing with messages in my app for simplicity, I check for the error message.

	const user = useUser();
const toast = useToast();
const relogin = useRelogin();

let email: HTMLInputElement;

const updateProfile = async () => {
    const { error } = await updateProfileEmail(email.value);
    if (error) {
        if (error === 'Firebase: Error (auth/requires-recent-login).') {
            relogin.set(true);
            return;
        }
        toast.error(error);
        return;
    }

    toast.open('Email Updated!');
};

This sets the relogin state dynamically, which will display the relogin button.

	{#if $user}
    <main class="mt-5 flex flex-col items-center justify-center gap-5">
        <form class="flex flex-col items-center gap-5" on:submit|preventDefault={updateProfile}>
            {#if $relogin}
                <LoginWithGoogle />
            {:else}
                <div>
                    <label for="email" class="mb-2 block text-sm font-medium text-gray-900 dark:text-white">
                        Email
                    </label>
                    <input
                        bind:this={email}
                        type="text"
                        id="email"
                        name="email"
                        value={$user.email}
                        class="block w-full rounded-lg border border-gray-300 bg-gray-50 p-2.5 text-sm text-gray-900 focus:border-blue-500 focus:ring-blue-500"
                        required
                    />
                </div>
                <button
                    type="submit"
                    class="w-fit rounded-lg border bg-blue-600 p-3 font-semibold text-white"
                >
                    Update
                </button>
            {/if}
        </form>
    </main>
{:else}
    <LoginWithGoogle />
{/if}

After a relogin, the state will return to editing an email. This could be done in a million different ways, but I tried to show you a simple one.

When Is Reauthentication Necessary?#

After 5 minutes you must reauthenticate to change your password or change your email. You will not see me showing any password authentication states on this website, as I do not believe it is good practice or secure.

Reauthenticate with Firebase

Demo: Vercel Serverless

Repo: GitHub

See more on Svelte with Firebase


Related Posts

© 2024 Code.Build