Skip to main content
RapidDev - Software Development Agency
flutterflow-tutorials

How to Implement Passwordless Login in FlutterFlow

FlutterFlow supports two passwordless login methods: magic links via Firebase sendSignInLinkToEmail paired with Firebase Dynamic Links (email-based, no password), and SMS OTP via Firebase Phone Authentication (phone number + 6-digit code). Magic links require configuring Dynamic Links with your app's domain. Phone Auth works out of the box in FlutterFlow's built-in auth actions. Both methods must be enabled in the Firebase Authentication console first.

What you'll learn

  • How to enable and configure magic link sign-in using Firebase Dynamic Links
  • How to set up SMS OTP login with Firebase Phone Authentication in FlutterFlow
  • Why Firebase Dynamic Links must be configured before magic links will work
  • How to handle the sign-in link redirect when users return to the app
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Beginner10 min read30-45 minFlutterFlow Free+March 2026RapidDev Engineering Team
TL;DR

FlutterFlow supports two passwordless login methods: magic links via Firebase sendSignInLinkToEmail paired with Firebase Dynamic Links (email-based, no password), and SMS OTP via Firebase Phone Authentication (phone number + 6-digit code). Magic links require configuring Dynamic Links with your app's domain. Phone Auth works out of the box in FlutterFlow's built-in auth actions. Both methods must be enabled in the Firebase Authentication console first.

Two Paths to Passwordless Login in FlutterFlow

Removing passwords reduces sign-up friction and eliminates the most common support request: 'I forgot my password'. FlutterFlow supports two passwordless approaches. Magic links are ideal for web-focused apps — the user enters their email, clicks a link in their inbox, and is signed in. SMS OTP is better for mobile-first apps where users are on their phone and cannot easily switch to email. This tutorial implements both, including the Firebase Dynamic Links configuration that most magic link tutorials skip and that is the most common point of failure.

Prerequisites

  • FlutterFlow project with Firebase connected
  • Firebase Authentication console access
  • For magic links: a custom domain or Firebase Hosting subdomain for Dynamic Links
  • For SMS OTP: Firebase Blaze plan (phone auth requires billing enabled)

Step-by-step guide

1

Enable Email Link and Phone Authentication in Firebase

Open the Firebase console and navigate to Authentication → Sign-in method. Click Email/Password and toggle the 'Email link (passwordless sign-in)' option on — this is separate from the standard Email/Password toggle. Also enable Phone as a sign-in provider if you want SMS OTP. Click Save after each change. Both of these must be enabled in the Firebase console before any code in FlutterFlow will work. Back in FlutterFlow, go to your project Settings → Firebase and confirm your Firebase config is up to date by clicking Sync Firebase.

Expected result: Email Link and Phone sign-in methods show as enabled (green) in the Firebase Authentication console's Sign-in method tab.

2

Configure Firebase Dynamic Links for magic link sign-in

Magic links use Firebase Dynamic Links to redirect the user back to your app after they click the email link. In the Firebase console, go to Dynamic Links (under the Grow section). Click 'Get started' if you have not set it up before. Create a new URL prefix — this is the domain your links will use, like 'yourapp.page.link'. Under Link behavior, set both iOS and Android to 'Open the link in your app' and specify your app's bundle ID (iOS) and package name (Android). For web apps, set the fallback URL to your app's web URL. Note the full dynamic link domain — you will need it in your FlutterFlow action settings.

Expected result: A Dynamic Links URL prefix is created and appears in the Dynamic Links dashboard with your configured iOS and Android behavior.

3

Build the magic link sign-in flow in FlutterFlow

Create a page named MagicLinkPage. Add an EmailTextField widget, a 'Send Magic Link' button, and a text confirmation message. Create a Custom Action named sendMagicLink. Inside the action, call FirebaseAuth.instance.sendSignInLinkToEmail(email: emailAddress, actionCodeSettings: settings) where settings specifies your Dynamic Link URL, the iOS and Android bundle IDs, and handleCodeInApp: true. Store the email in Secure Storage after sending so you can read it when the user returns via the link. Show a confirmation message ('Check your inbox') and disable the send button to prevent duplicate sends.

send_magic_link.dart
1// Custom Action: sendMagicLink
2import 'package:firebase_auth/firebase_auth.dart';
3import 'package:flutter_secure_storage/flutter_secure_storage.dart';
4
5Future<bool> sendMagicLink(String email) async {
6 const storage = FlutterSecureStorage();
7
8 final actionCodeSettings = ActionCodeSettings(
9 // Replace with your Dynamic Links domain or custom domain
10 url: 'https://yourapp.page.link/signin?email=${Uri.encodeComponent(email)}',
11 handleCodeInApp: true,
12 iOSBundleId: 'com.yourcompany.yourapp',
13 androidPackageName: 'com.yourcompany.yourapp',
14 androidInstallApp: true,
15 androidMinimumVersion: '21',
16 );
17
18 try {
19 await FirebaseAuth.instance.sendSignInLinkToEmail(
20 email: email,
21 actionCodeSettings: actionCodeSettings,
22 );
23 // Store email to retrieve when user returns via the link
24 await storage.write(key: 'magic_link_email', value: email);
25 return true;
26 } catch (e) {
27 return false;
28 }
29}

Expected result: Entering an email and tapping 'Send Magic Link' sends a Firebase magic link email. The user's email is stored in Secure Storage for later retrieval.

4

Handle the magic link redirect when the user returns

When the user taps the magic link in their email, your app must detect the incoming link and complete the sign-in. Add a Custom Action named handleMagicLinkReturn to your app's root page (the first page loaded on startup). The action checks if the app was opened with a sign-in link using FirebaseAuth.instance.isSignInWithEmailLink(currentUrl). If it was, reads the stored email from Secure Storage, calls FirebaseAuth.instance.signInWithEmailLink(email: storedEmail, emailLink: currentUrl), and navigates the user to the home page. Handle the case where the stored email is missing — show a prompt asking the user to re-enter their email address.

handle_magic_link_return.dart
1// Custom Action: handleMagicLinkReturn
2import 'package:firebase_auth/firebase_auth.dart';
3import 'package:flutter_secure_storage/flutter_secure_storage.dart';
4import 'dart:html' as html show window;
5
6Future<bool> handleMagicLinkReturn() async {
7 final currentUrl = html.window.location.href;
8
9 if (!FirebaseAuth.instance.isSignInWithEmailLink(currentUrl)) {
10 return false; // Not a magic link redirect
11 }
12
13 const storage = FlutterSecureStorage();
14 String? email = await storage.read(key: 'magic_link_email');
15
16 if (email == null) {
17 // User opened the link on a different device — email is unknown
18 // In this case, show a UI prompt to re-enter their email
19 return false;
20 }
21
22 try {
23 await FirebaseAuth.instance.signInWithEmailLink(
24 email: email,
25 emailLink: currentUrl,
26 );
27 await storage.delete(key: 'magic_link_email');
28 return true; // Sign-in succeeded
29 } catch (e) {
30 return false;
31 }
32}

Expected result: When a user opens the app via a magic link, they are automatically signed in and redirected to the home page without entering a password.

5

Implement SMS OTP login as an alternative to magic links

Firebase Phone Authentication provides a simpler passwordless option. In FlutterFlow, go to Authentication → Actions and look for the Phone Sign-In action. Add a page with a phone number TextField (use the Phone type with country code picker) and a 'Send OTP' button. Use FlutterFlow's built-in Phone Auth action — no custom code needed for the basic flow. After the user submits their number, Firebase automatically sends an SMS and shows the OTP verification screen. On iOS, App Attest verification is required for production; on Android, SafetyNet handles verification automatically. For the web, a reCAPTCHA appears before the SMS is sent.

Expected result: Users can enter their phone number, receive an SMS with a 6-digit code, enter the code, and be signed in — all within your FlutterFlow app without a password.

Complete working example

magic_link_signin_page.dart
1// Standalone Custom Widget: MagicLinkSignInForm
2// Handles both sending the magic link and the return URL check
3import 'package:flutter/material.dart';
4import 'package:firebase_auth/firebase_auth.dart';
5import 'package:flutter_secure_storage/flutter_secure_storage.dart';
6
7class MagicLinkSignInForm extends StatefulWidget {
8 final VoidCallback onSignedIn;
9 const MagicLinkSignInForm({Key? key, required this.onSignedIn}) : super(key: key);
10
11 @override
12 State<MagicLinkSignInForm> createState() => _MagicLinkSignInFormState();
13}
14
15class _MagicLinkSignInFormState extends State<MagicLinkSignInForm> {
16 final _emailController = TextEditingController();
17 bool _sent = false;
18 bool _loading = false;
19 String? _error;
20 final _storage = const FlutterSecureStorage();
21
22 Future<void> _sendLink() async {
23 final email = _emailController.text.trim();
24 if (email.isEmpty || !email.contains('@')) {
25 setState(() => _error = 'Please enter a valid email address');
26 return;
27 }
28
29 setState(() { _loading = true; _error = null; });
30
31 final settings = ActionCodeSettings(
32 url: 'https://yourapp.page.link/signin?email=${Uri.encodeComponent(email)}',
33 handleCodeInApp: true,
34 iOSBundleId: 'com.yourcompany.yourapp',
35 androidPackageName: 'com.yourcompany.yourapp',
36 androidInstallApp: true,
37 androidMinimumVersion: '21',
38 );
39
40 try {
41 await FirebaseAuth.instance.sendSignInLinkToEmail(
42 email: email,
43 actionCodeSettings: settings,
44 );
45 await _storage.write(key: 'magic_link_email', value: email);
46 setState(() { _sent = true; _loading = false; });
47 } catch (e) {
48 setState(() {
49 _error = 'Failed to send link. Please try again.';
50 _loading = false;
51 });
52 }
53 }
54
55 @override
56 Widget build(BuildContext context) {
57 if (_sent) {
58 return Column(
59 mainAxisSize: MainAxisSize.min,
60 children: [
61 const Icon(Icons.mark_email_read_outlined, size: 64, color: Colors.green),
62 const SizedBox(height: 16),
63 const Text('Check your inbox', style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold)),
64 const SizedBox(height: 8),
65 Text('We sent a sign-in link to ${_emailController.text}'),
66 ],
67 );
68 }
69
70 return Column(
71 mainAxisSize: MainAxisSize.min,
72 children: [
73 TextFormField(
74 controller: _emailController,
75 keyboardType: TextInputType.emailAddress,
76 decoration: const InputDecoration(labelText: 'Email address'),
77 ),
78 if (_error != null)
79 Padding(
80 padding: const EdgeInsets.only(top: 8),
81 child: Text(_error!, style: const TextStyle(color: Colors.red)),
82 ),
83 const SizedBox(height: 16),
84 SizedBox(
85 width: double.infinity,
86 child: ElevatedButton(
87 onPressed: _loading ? null : _sendLink,
88 child: _loading
89 ? const SizedBox(height: 20, width: 20,
90 child: CircularProgressIndicator(strokeWidth: 2))
91 : const Text('Send Magic Link'),
92 ),
93 ),
94 ],
95 );
96 }
97}

Common mistakes

Why it's a problem: Not configuring Firebase Dynamic Links before implementing magic link sign-in

How to avoid: Configure a Dynamic Links URL prefix in the Firebase console under Dynamic Links before writing any sign-in code. Pass this domain as the url parameter in ActionCodeSettings.

Why it's a problem: Not storing the user's email before sending the magic link

How to avoid: Store the email in Secure Storage immediately after calling sendSignInLinkToEmail. When handling the return link, read from Secure Storage. If the email is missing, prompt the user to re-enter it.

Why it's a problem: Calling handleMagicLinkReturn on every page load instead of only on app startup

How to avoid: Call handleMagicLinkReturn only from your app's first page (root route) On Page Load action, or use a Universal Links handler that fires only when the app is opened from a specific URL scheme.

Best practices

  • Store the user's email in Secure Storage immediately before calling sendSignInLinkToEmail so sign-in can complete even on a different device.
  • Check handleCodeInApp: true in ActionCodeSettings — without this, Firebase opens the link in a browser instead of your app.
  • For SMS OTP, add test phone numbers in the Firebase console during development to avoid using real SMS quota.
  • Show a 60-second cooldown on the magic link send button to prevent users from requesting multiple links in rapid succession.
  • Always check FirebaseAuth.instance.isSignInWithEmailLink(url) before attempting to sign in with a URL to avoid errors on normal page loads.
  • Handle the 'opened on different device' case by showing a one-field form asking users to re-enter their email when the stored email is not found.
  • Monitor your Firebase Authentication usage dashboard after launch — phone authentication has SMS costs and unexpectedly high volume can affect your bill.

Still stuck?

Copy one of these prompts to get a personalized, step-by-step explanation.

ChatGPT Prompt

I am building a FlutterFlow app and want to add passwordless magic link sign-in using Firebase. Explain the full implementation including Dynamic Links configuration, sending the email link, storing the email locally, and handling the return redirect to complete sign-in.

FlutterFlow Prompt

Add passwordless email magic link authentication to my FlutterFlow app. Create a sign-in page where users enter their email, then handle the return link on app startup to complete sign-in using Firebase Dynamic Links and flutter_secure_storage to persist the email.

Frequently asked questions

What is the difference between magic links and SMS OTP for passwordless login?

Magic links send a clickable URL to the user's email — they click it and are signed in. SMS OTP sends a 6-digit code to their phone — they enter it in the app. Magic links are better for web and desktop-first users. SMS OTP is better for mobile-first apps where switching to email is inconvenient.

Do I need Firebase Dynamic Links for magic link sign-in?

Yes, for mobile apps. Dynamic Links ensure the magic link URL opens your app instead of a browser. Without them, tapping the link opens a generic Firebase page. For web-only apps, Dynamic Links are not required — the link opens the web app directly.

Are Firebase Dynamic Links being deprecated?

Yes. Firebase announced Dynamic Links deprecation with shutdown planned for August 2025. For new projects, use a custom domain with Firebase Hosting and handle the redirect with a custom URL scheme in your app. Check the Firebase console for updated recommendations.

Does Phone Authentication cost money in Firebase?

Phone Auth requires the Firebase Blaze (pay-as-you-go) plan. SMS messages cost approximately $0.01-0.06 per verification depending on the destination country. India and some other regions have higher SMS costs. Monitor usage in the Firebase console.

What happens if the user opens the magic link on a different device than where they requested it?

The email stored in Secure Storage is not available on the other device. Firebase will fail to complete sign-in without the email. Handle this by detecting the missing email and showing a prompt asking the user to re-enter their email address before completing the sign-in.

Can I use both passwordless and password-based login in the same app?

Yes. Enable both Email/Password and Email Link in Firebase Authentication settings. Some users may prefer to create a password account while others use magic links. Firebase handles both methods under the same user account when the same email is used.

How long is a magic link valid before it expires?

Firebase magic links expire after 1 hour by default. You cannot change this expiry time — it is set by Firebase's security infrastructure. If the user does not click the link within an hour, they need to request a new one.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.