To reset a password in Firebase Auth, call sendPasswordResetEmail() with the user's email address. Firebase sends a password reset link to the email. You can customize the email template in the Firebase Console under Authentication > Templates, and configure actionCodeSettings to redirect users back to your app after resetting. Always handle errors like user-not-found and too-many-requests gracefully.
Implementing Password Reset in Firebase Auth
Firebase Auth provides a built-in password reset flow for email/password accounts. This tutorial walks you through sending the reset email, customizing the email template and redirect behavior, handling the reset action link in your app, and confirming the password change. You will also learn how to handle edge cases like invalid emails, expired links, and rate limiting.
Prerequisites
- A Firebase project with Authentication enabled
- Email/Password sign-in method enabled in Firebase Console > Authentication > Sign-in method
- Firebase JS SDK v9+ installed in your project
- A working sign-in flow so users have existing accounts to reset
Step-by-step guide
Send a password reset email
Send a password reset email
Use the sendPasswordResetEmail() function from the Firebase Auth SDK. Pass the auth instance and the user's email address. Firebase sends a pre-built email with a password reset link. The function is fire-and-forget from the user's perspective, but you should handle errors to provide good UX. For security, do not reveal whether the email exists in your system to prevent email enumeration.
1import { getAuth, sendPasswordResetEmail } from 'firebase/auth';23const auth = getAuth();45async function handlePasswordReset(email: string) {6 try {7 await sendPasswordResetEmail(auth, email);8 // Always show success message even if email doesn't exist9 // This prevents email enumeration attacks10 console.log('Password reset email sent');11 } catch (error: any) {12 switch (error.code) {13 case 'auth/invalid-email':14 console.error('Invalid email address format');15 break;16 case 'auth/too-many-requests':17 console.error('Too many attempts. Please try again later.');18 break;19 default:20 console.error('Error sending reset email:', error.message);21 }22 }23}Expected result: Firebase sends a password reset email to the specified address if the account exists.
Configure actionCodeSettings for redirect
Configure actionCodeSettings for redirect
Pass an actionCodeSettings object to sendPasswordResetEmail() to control where users are redirected after clicking the reset link. The url property specifies your app's URL where the password reset will be completed. This is especially important for single-page applications and mobile apps. Add the redirect URL to your authorized domains in Firebase Console > Authentication > Settings > Authorized domains.
1import { getAuth, sendPasswordResetEmail, ActionCodeSettings } from 'firebase/auth';23const auth = getAuth();45const actionCodeSettings: ActionCodeSettings = {6 url: 'https://your-app.com/auth/reset-complete',7 handleCodeInApp: true8};910async function sendReset(email: string) {11 try {12 await sendPasswordResetEmail(auth, email, actionCodeSettings);13 console.log('Reset email sent with custom redirect');14 } catch (error: any) {15 console.error('Error:', error.message);16 }17}Expected result: The password reset email links back to your specified URL with an oobCode query parameter.
Customize the email template
Customize the email template
Firebase provides a default password reset email template that you can customize. In the Firebase Console, go to Authentication > Templates > Password reset. You can edit the subject line, sender name, message body, and action URL. The template supports placeholders like %APP_NAME%, %LINK%, and %EMAIL%. For fully custom emails, use Firebase Admin SDK server-side to generate the reset link and send it through your own email provider.
1// Server-side: Generate a custom reset link with Admin SDK2import { getAuth } from 'firebase-admin/auth';34async function generateCustomResetLink(email: string) {5 const auth = getAuth();6 const actionCodeSettings = {7 url: 'https://your-app.com/auth/reset-complete'8 };910 const link = await auth.generatePasswordResetLink(11 email,12 actionCodeSettings13 );1415 // Send the link via your own email service16 // await sendCustomEmail(email, link);17 return link;18}Expected result: Users receive a branded password reset email with your custom subject, message, and redirect URL.
Handle the reset action link in your app
Handle the reset action link in your app
When users click the password reset link, they are redirected to your app with an oobCode (out-of-band code) in the URL query parameters. Extract this code, verify it with verifyPasswordResetCode() to get the associated email, then prompt the user for a new password. Finally, call confirmPasswordReset() with the code and the new password to complete the reset.
1import {2 getAuth,3 verifyPasswordResetCode,4 confirmPasswordReset5} from 'firebase/auth';67const auth = getAuth();89async function handleResetAction(oobCode: string, newPassword: string) {10 try {11 // Verify the code is valid and get the associated email12 const email = await verifyPasswordResetCode(auth, oobCode);13 console.log('Reset code valid for:', email);1415 // Confirm the password reset16 await confirmPasswordReset(auth, oobCode, newPassword);17 console.log('Password has been reset successfully');1819 // Optionally sign the user in automatically20 // await signInWithEmailAndPassword(auth, email, newPassword);21 } catch (error: any) {22 switch (error.code) {23 case 'auth/expired-action-code':24 console.error('Reset link has expired. Please request a new one.');25 break;26 case 'auth/invalid-action-code':27 console.error('Reset link is invalid or has already been used.');28 break;29 case 'auth/weak-password':30 console.error('Password is too weak. Use at least 6 characters.');31 break;32 default:33 console.error('Error resetting password:', error.message);34 }35 }36}Expected result: The user's password is changed to the new password and they can sign in with it immediately.
Build a password reset form component
Build a password reset form component
Create a complete React component that handles both the reset request and the reset confirmation. The component checks the URL for an oobCode parameter to determine which view to show: the email input form for requesting a reset, or the new password form for completing the reset.
1import { useState, useEffect } from 'react';2import {3 getAuth,4 sendPasswordResetEmail,5 verifyPasswordResetCode,6 confirmPasswordReset7} from 'firebase/auth';89function PasswordReset() {10 const auth = getAuth();11 const [email, setEmail] = useState('');12 const [newPassword, setNewPassword] = useState('');13 const [message, setMessage] = useState('');14 const [oobCode, setOobCode] = useState<string | null>(null);1516 useEffect(() => {17 const params = new URLSearchParams(window.location.search);18 const code = params.get('oobCode');19 if (code) setOobCode(code);20 }, []);2122 const handleSendReset = async () => {23 try {24 await sendPasswordResetEmail(auth, email);25 setMessage('Check your email for a reset link.');26 } catch {27 setMessage('Something went wrong. Try again.');28 }29 };3031 const handleConfirmReset = async () => {32 if (!oobCode) return;33 try {34 await confirmPasswordReset(auth, oobCode, newPassword);35 setMessage('Password reset! You can now sign in.');36 } catch {37 setMessage('Link expired or invalid. Request a new one.');38 }39 };4041 // Render either the request form or the confirm form42}Expected result: Users can request a password reset and complete it within your application without leaving to an external page.
Complete working example
1import {2 getAuth,3 sendPasswordResetEmail,4 verifyPasswordResetCode,5 confirmPasswordReset,6 signInWithEmailAndPassword,7 ActionCodeSettings8} from 'firebase/auth';9import { initializeApp } from 'firebase/app';1011const app = initializeApp({12 apiKey: 'YOUR_API_KEY',13 authDomain: 'YOUR_PROJECT.firebaseapp.com',14 projectId: 'YOUR_PROJECT_ID'15});1617const auth = getAuth(app);1819const actionCodeSettings: ActionCodeSettings = {20 url: 'https://your-app.com/auth/reset-complete',21 handleCodeInApp: true22};2324export async function requestPasswordReset(email: string): Promise<string> {25 try {26 await sendPasswordResetEmail(auth, email, actionCodeSettings);27 return 'If an account exists with this email, a reset link has been sent.';28 } catch (error: any) {29 if (error.code === 'auth/too-many-requests') {30 return 'Too many attempts. Please wait before trying again.';31 }32 if (error.code === 'auth/invalid-email') {33 return 'Please enter a valid email address.';34 }35 return 'An error occurred. Please try again.';36 }37}3839export async function completePasswordReset(40 oobCode: string,41 newPassword: string42): Promise<{ success: boolean; message: string; email?: string }> {43 try {44 const email = await verifyPasswordResetCode(auth, oobCode);45 await confirmPasswordReset(auth, oobCode, newPassword);4647 // Auto sign-in after reset48 await signInWithEmailAndPassword(auth, email, newPassword);4950 return {51 success: true,52 message: 'Password reset successfully. You are now signed in.',53 email54 };55 } catch (error: any) {56 const messages: Record<string, string> = {57 'auth/expired-action-code': 'This reset link has expired. Request a new one.',58 'auth/invalid-action-code': 'This reset link is invalid or already used.',59 'auth/weak-password': 'Password must be at least 6 characters.'60 };61 return {62 success: false,63 message: messages[error.code] || 'Failed to reset password.'64 };65 }66}Common mistakes when resetting Password in Firebase Auth
Why it's a problem: Revealing whether an email exists by showing different messages for existing vs non-existing accounts
How to avoid: Always show a generic success message like 'If an account exists, a reset link has been sent.' This prevents email enumeration attacks where attackers probe for valid email addresses.
Why it's a problem: Not adding the redirect URL to the authorized domains list in Firebase Console
How to avoid: Go to Firebase Console > Authentication > Settings > Authorized domains and add the domain used in your actionCodeSettings URL. Without this, users see a 'domain not authorized' error.
Why it's a problem: Assuming the oobCode in the URL never expires
How to avoid: Password reset codes expire after 1 hour and can only be used once. Always handle the auth/expired-action-code and auth/invalid-action-code errors and prompt users to request a new reset email.
Best practices
- Show a generic success message regardless of whether the email exists to prevent enumeration attacks
- Use actionCodeSettings to redirect users back to your app after clicking the reset link
- Customize the email template in Firebase Console to match your brand
- Validate the new password on the client side before calling confirmPasswordReset()
- Handle all error codes including expired-action-code, invalid-action-code, and weak-password
- Consider auto-signing the user in after a successful password reset for better UX
- Add rate limiting UI feedback to inform users about the 5 emails per hour limit
- Log password reset events for security auditing purposes
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
Walk me through implementing a complete password reset flow in Firebase Auth including sending the reset email with custom actionCodeSettings, handling the reset link in my app, and error handling for expired and invalid codes.
Write a complete Firebase Auth password reset module in TypeScript using modular SDK v9 with sendPasswordResetEmail, verifyPasswordResetCode, and confirmPasswordReset. Include custom actionCodeSettings and comprehensive error handling for all auth error codes.
Frequently asked questions
How long does a Firebase password reset link last?
Password reset links expire after 1 hour. If a user clicks an expired link, they will see an error. Your app should handle the auth/expired-action-code error and prompt the user to request a new reset email.
Can I customize the password reset email content?
Yes. Go to Firebase Console > Authentication > Templates > Password reset to edit the subject, sender name, and body text. For fully custom emails with your own design, use the Admin SDK's generatePasswordResetLink() to get the link and send it via your own email service.
What happens if the user's email doesn't exist in Firebase Auth?
By default, sendPasswordResetEmail() throws an auth/user-not-found error. However, if you enable Email enumeration protection in Firebase Console > Authentication > Settings, the function succeeds silently. Either way, show a generic success message to prevent email enumeration.
Can I set minimum password requirements for the new password?
Firebase enforces a minimum password length of 6 characters. For stricter requirements, validate the password on the client side before calling confirmPasswordReset(). Firebase does not have built-in complexity rules, so implement those checks in your app.
Does password reset work with OAuth accounts (Google, GitHub)?
No. Password reset only applies to email/password accounts. If a user signed up with Google or GitHub OAuth, they do not have a Firebase password to reset. Their authentication is managed by the OAuth provider.
Can I send password reset emails programmatically from the server?
Yes. Use the Firebase Admin SDK's getAuth().generatePasswordResetLink(email, actionCodeSettings) to generate the reset link server-side, then send it via your own email service like SendGrid or Mailgun for full control over the email design.
Can RapidDev help build a custom password reset flow?
Yes. RapidDev can implement a fully customized password reset experience including branded email templates, custom redirect flows, server-side link generation, and integration with your existing email service provider.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation