electric satellite
Flutter

Creating a Password Reset Flow with AppWrite and Flutter

Arguably the most important feature of any mobile application is its authentication workflow. If users can’t log into your application consistently across devices, the chances of them sticking around are slim to none. In this article, I’ll explain how you can create a password reset sequence in your Flutter app using the AppWrite BaaS. The workflow requires 4 screens:

  1. A Sign In Screen
  2. A Sign Up Screen
  3. A Forgot Password Screen
  4. A Reset Password Screen

Sign In and Sign Up

Before users need to reset their password, they typically need to create one. AppWrite’s Account API supports users creating accounts with an email and password so that’s where we’ll start.

If you haven’t already created an AppWrite project, you can do so by clicking “Create Project” in your AppWrite console.

Once you have setup your AppWrite project, navigate to the Settings tab of the Auth activity and verify that the “Email/Password” switch is enabled. This is turned on by default so you shouldn’t need to do anything exra to get started.

Email Authentication in AppWrite In your app, you can now create /sign-in and /sign-up screens that include email and password TextFields. You can find the full code for this blog in the COTR public repo under the name “cotr_appwrite”. Both SignIn and SignUp screens in the example contain two TextFields and a button to submit the request.

To create an account in AppWrite, you must use the create method on the Account API:

Client client = Client()
.setEndpoint('https://cloud.appwrite.io/v1')
.setProject(const String.fromEnvironment('APPWRITE_PROJECT_ID'));
await Account(client).create(
userId: ID.unique(),
email: emailController.text,
password: passwordController.text,
);

Once a user has an account, you can sign them into that account using the createEmailSession method:

await Account(client).createEmailSession(
email: emailController.text,
password: passwordController.text,
);

Forgot Password

Next we need to send the reset password email using the createRecovery method.

await Account(client).createRecovery(
email: emailController.text,
url: 'https://cotr-appwrite.firebaseapp.com/reset-password', // Use the full URL
);

Create a /forgot-password screen where this method will be called. You can see my forgot password route here (it contains a single TextField for the email and a button to send the reset link).

This step is tough to get right since the configuration and environment need to be exact.

  1. The email needs to be sent from the exact domain that you listed in the AppWrite console (see image above). If you use Firebase Hosting to host your webpage and listed my-app.firebaseapp.com in the AppWrite console, sending the reset email from my-app.web.app will throw an error
  2. The full reset password URL, including the “https://”, is required by the createRecovery method
  3. If you’re using —dart-define to send environment variables to your app, make sure you also include them when you run flutter build web. In the demo app, a red banner will be displayed on top of the application if you forget this

Reset Password

We’re almost finished.

When users receive the email to reset their password. it will contain a link back to your app at a path you define. Create a /reset-password page where users will actually type in their new password. In the example repo, this page simply contains two TextFields: a password field and a confirm password field.

Next, you’ll need to add the host of your reset-password URL to your AppWrite project (Console -> Overview -> Add platform -> Web App). The AppWrite console doesn’t let you add a path or port to the hostname so ignore that for now.

Register AppWrite web app

When a user requests a reset password link, they will be redirected to this URL with their user ID and secret as query parameters. You can retrieve these URL parameters in your dart code like this:

String get userId => Uri.base.queryParameters['userId'] ?? '';
String get secret => Uri.base.queryParameters['secret'] ?? '';

These parameters will be passed along with the user’s new password to the updateRecovery endpoint to complete the reset workflow:

await Account(client)
.updateRecovery(
userId: userId,
secret: secret,
password: passwordController.text,
passwordAgain: confirmPasswordController.text,
)
.then((value) {
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
content: Text('Password reset successfully'),
));
context.go('/sign-in');
}).onError((error, stackTrace) {
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text(error.toString()),
));
});

Moment of Truth

If all of these things have been considered and setup correctly, you should be ready to test out the full workflow. Create an account on the /sign-up page, sign out, and then navigate to the /sign-in page. Here you can click the “Forgot Password?” button and then enter the email you just used to sign up. You should receive an email with the link back to your app:

Reset password email from AppWrite

Tap on the link to open your web app and enter your new password twice. The new password must be at least 8 characters.

Finally, navigate to the /sign-in page and try signing in with your new password. If it works, congrats! You’ve successfully set up a reset password flow using the AppWrite SDK for Flutter!

If the sign in fails, take note of the error message and double check that you’ve set everything up correctly (Paying special attention to the callouts in the Password Reset Email section). There is a reported bug with generating the password recovery email when your web app does NOT use the path URL strategy. If you are not already using that add the following line to main.dart:

import 'package:flutter_web_plugins/url_strategy.dart';
void main() {
usePathUrlStrategy(); // Add this
runApp(ExampleApp());
}

Conclusion

AppWrite is an open source backend for Flutter that lets you “Build Fast” and “Scale Big”. As I’ve shown here, using only a few lines of code you can add a reliable password reset email to your Flutter app and keep those user’s logged in. Compared to the password reset flow offered by Firebase, this one is a little bit more cumbersome, requiring that you deploy your own password reset URL. The benefit to this however is that you can completely control the UI and UX whereas the minimalistic password reset page from Firebase is just that - minimalistic.

If your a fan of OSS, give AppWrite a try (and follow them on Threads and Twitter to stay up to date on the latest feature releases). 🥂


The AppWrite team also published an article on resetting user passwords. Note the code snippets are written in JavaScript.

Copyright © 2024 Code On The Rocks. All rights reserved.