Authenticate with Firebase Using Email Link in iOS

You can use Firebase Authentication to sign in a user by sending them an email containing a link, which they can click to sign in. In the process, the user's email address is also verified.

There are numerous benefits to signing in by email:

  • Low friction sign-up and sign-in.
  • Lower risk of password reuse across applications, which can undermine security of even well-selected passwords.
  • The ability to authenticate a user while also verifying that the user is the legitimate owner of an email address.
  • A user only needs an accessible email account to sign in. No ownership of a phone number or social media account is required.
  • A user can sign in securely without the need to provide (or remember) a password, which can be cumbersome on a mobile device.
  • An existing user who previously signed in with an email identifier (password or federated) can be upgraded to sign in with just the email. For example, a user who has forgotten their password can still sign in without needing to reset their password.

Before you begin

  1. Add Firebase to your iOS project. Include the following pods in your Podfile:
    pod 'Firebase/Auth'
    
  2. If you haven't yet connected your app to your Firebase project, do so from the Firebase console.

To sign in users by email link, you must first enable the Email provider and Email link sign-in method for your Firebase project:

  1. In the Firebase console, open the Auth section.
  2. On the Sign in method tab, enable the Email/Password provider. Note that email/password sign-in must be enabled to use email link sign-in.
  3. In the same section, enable Email link (passwordless sign-in) sign-in method.
  4. Click Save.

To initiate the authentication flow, present the user with an interface that prompts the user to provide their email address and then call sendSignInLinkToEmail:actionCodeSettings:completion: to request that Firebase send the authentication link to the user's email.

  1. Construct the ActionCodeSettings object, which provides Firebase with instructions on how to construct the email link. Set the following fields:

    • url: The deep link to embed and any additional state to be passed along. The link's domain has to be whitelisted in the Firebase Console list of authorized domains, which can be found by going to the Sign-in method tab (Authentication -> Sign-in method).
    • iOSBundleID and androidPackageName : The apps to use when the sign-in link is opened on an Android or iOS device. Learn more on how to configure Firebase Dynamic Links to open email action links via mobile apps.
    • handleCodeInApp: Set to true. The sign-in operation has to always be completed in the app unlike other out of band email actions (password reset and email verifications). This is because, at the end of the flow, the user is expected to be signed in and their Auth state persisted within the app.

    Swift

    let actionCodeSettings = ActionCodeSettings()
    actionCodeSettings.url = URL(string: "https://www.example.com")
    // The sign-in operation has to always be completed in the app.
    actionCodeSettings.handleCodeInApp = true
    actionCodeSettings.setIOSBundleID(Bundle.main.bundleIdentifier!)
    actionCodeSettings.setAndroidPackageName("com.example.android",
                                             installIfNotAvailable: false, minimumVersion: "12")
    

    Objective-C

    FIRActionCodeSettings *actionCodeSettings = [[FIRActionCodeSettings alloc] init];
    [actionCodeSettings setURL:[NSURL URLWithString:@"https://www.example.com"]];
    // The sign-in operation has to always be completed in the app.
    actionCodeSettings.handleCodeInApp = YES;
    [actionCodeSettings setIOSBundleID:[[NSBundle mainBundle] bundleIdentifier]];
    [actionCodeSettings setAndroidPackageName:@"com.example.android"
                        installIfNotAvailable:NO
                               minimumVersion:@"12"];
    

    To learn more on ActionCodeSettings, refer to the Passing State in Email Actions section.

  2. Ask the user for their email.

  3. Send the authentication link to the user's email, and save the user's email in case the user completes the email sign-in on the same device.

    Swift

    Auth.auth().sendSignInLink(toEmail:email,
                               actionCodeSettings: actionCodeSettings) { error in
      // ...
        if let error = error {
          self.showMessagePrompt(error.localizedDescription)
          return
        }
        // The link was successfully sent. Inform the user.
        // Save the email locally so you don't need to ask the user for it again
        // if they open the link on the same device.
        UserDefaults.standard.set(email, forKey: "Email")
        self.showMessagePrompt("Check your email for link")
        // ...
    }
    

    Objective-C

    [[FIRAuth auth] sendSignInLinkToEmail:email
                       actionCodeSettings:actionCodeSettings
                               completion:^(NSError *_Nullable error) {
      // ...
        if (error) {
          [self showMessagePrompt:error.localizedDescription];
           return;
        }
        // The link was successfully sent. Inform the user.
        // Save the email locally so you don't need to ask the user for it again
        // if they open the link on the same device.
        [NSUserDefaults.standardUserDefaults setObject:email forKey:@"Email"];
        [self showMessagePrompt:@"Check your email for link"];
        // ...
    }];
    

Security concerns

To prevent a sign-in link from being used to sign in as an unintended user or on an unintended device, Firebase Auth requires the user's email address to be provided when completing the sign-in flow. For sign-in to succeed, this email address must match the address to which the sign-in link was originally sent.

You can streamline this flow for users who open the sign-in link on the same device they request the link, by storing their email address locally when you send the sign-in email. Then, use this address to complete the flow.

After sign-in completion, any previous unverified mechanism of sign-in will be removed from the user and any existing sessions will be invalidated. For example, if someone previously created an unverified account with the same email and password, the user's password will be removed to prevent the impersonator who claimed ownership and created that unverified account from signing in again with the same account.

Completing sign-in in an iOS mobile app

Firebase Authentication uses Firebase Dynamic Links to send the email link to a mobile device. For sign-in completion via mobile application, the application has to be configured to detect the incoming application link, parse the underlying deep link and then complete the sign-in.

Firebase Auth uses Firebase Dynamic Links when sending a link that is meant to be opened in a mobile application. In order to use this feature, Dynamic Links need to be configured in the Firebase Console.

  1. Enable Firebase Dynamic Links:

    1. In the Firebase console, open the Dynamic Links section.
    2. If you have not yet accepted the Dynamic Links terms and created a Dynamic Links domain, do so now.

      If you already created a Dynamic Links domain, take note of it. A Dynamic Links domain typically looks like the following example:

      example.page.link

      You will need this value when you configure your iOS or Android app to intercept the incoming link.

  2. Configuring iOS applications:

    1. If you plan on handling these links from your iOS appliction, the iOS bundle ID needs to be specified in the Firebase Console project settings. In addition, the App Store ID and the Apple Developer Team ID also need to be specified.
    2. You will also need to configure the FDL universal link domain as an Associated Domain in your application capabilities.
    3. If you plan to distribute your application to iOS versions 8 and under, you will need to set your iOS bundle ID as a custom scheme for incoming URLs.
    4. For more on this, refer to Receiving iOS Dynamic Links instructions.

After you receive the link as described above, verify that it is meant for email link authentication and complete the sign in.

Swift

if Auth.auth().isSignIn(withEmailLink: link) {
        Auth.auth().signIn(withEmail: email, link: self.link) { (user, error) in
          // ...
        }
}

Objective-C

if ([[FIRAuth auth] isSignInWithEmailLink:link]) {
    [[FIRAuth auth] signInWithEmail:email
                               link:link
                         completion:^(FIRAuthDataResult * _Nullable authResult, NSError * _Nullable error) {
      // ...
    }];
}

To learn about how to handle sign-in with email link in an Android application, refer to the Android guide.

To learn about how to handle sign-in with email link in a web application, refer to the Web guide.

You can also link this method of authentication to an existing user. For example a user previously authenticated with another provider, such as a phone number, can add this method of sign-in to their existing account.

The difference would be in the second half of the operation:

Swift

  let credential = EmailAuthCredential.credential(withEmail:email
                                                       link:link)
  Auth.auth().currentUser?.linkAndRetrieveData(with: credential) { authData, error in
    if (error) {
      // And error occurred during linking.
      return
    }
    // The provider was successfully linked.
    // The phone user can now sign in with their phone number or email.
  }

Objective-C

  FIRAuthCredential *credential =
      [FIREmailAuthCredential credentialWithEmail:email link:link];
  [FIRAuth auth].currentUser
      linkAndRetrieveDataWithCredential:credential
                             completion:^(FIRAuthDataResult *_Nullable result,
                                          NSError *_Nullable error) {
    if (error) {
      // And error occurred during linking.
      return;
    }
    // The provider was successfully linked.
    // The phone user can now sign in with their phone number or email.
  }];

This can also be used to re-authenticate an email link user before running a sensitive operation.

Swift

  let credential = EmailAuthCredential.credential(withEmail:email
                                                       link:link)
  Auth.auth().currentUser?.reauthenticateAndRetrieveData(with: credential) { authData, error in
    if (error) {
      // And error occurred during re-authentication.
      return
    }
    // The user was successfully re-authenticated.
  }

Objective-C

  FIRAuthCredential *credential =
      [FIREmailAuthCredential credentialWithEmail:email link:link];
  [FIRAuth auth].currentUser
      reauthenticateAndRetrieveDataWithCredential:credential
                                       completion:^(FIRAuthDataResult *_Nullable result,
                                                    NSError *_Nullable error) {
    if (error) {
      // And error occurred during re-authentication
      return;
    }
    // The user was successfully re-authenticated.
  }];

However, as the flow could end up on a different device where the original user was not logged in, this flow might not be completed. In that case, an error can be shown to the user to force them to open the link on the same device. Some state can be passed in the link to provide information on the type of operation and the user uid.

In case you support both password and link-based sign-in with email, to differentiate the method of sign-in for a password/link user, use fetchSignInMethodsForEmail. This is useful for identifier-first flows where the user is first asked to provide their email and then presented with the method of sign-in:

Swift

 // After asking the user for their email.
 Auth.auth().fetchSignInMethods(forEmail: email) { signInMethods, error in
   // This returns the same array as fetchProviders(forEmail:completion:) but for email
   // provider identified by 'password' string, signInMethods would contain 2
   // different strings:
   // 'emailLink' if the user previously signed in with an email/link
   // 'password' if the user has a password.
   // A user could have both.
   if (error) {
     // Handle error case.
   }
   if (!signInMethods.contains(EmailPasswordAuthSignInMethod)) {
     // User can sign in with email/password.
   }
   if (!signInMethods.contains(EmailLinkAuthSignInMethod)) {
     // User can sign in with email/link.
   }
 }

Objective-C

 // After asking the user for their email.
 [FIRAuth auth] fetchSignInMethodsForEmail:email
                                completion:^(NSArray *_Nullable signInMethods,
                                             NSError *_Nullable error) {
   // This returns the same array as fetchProvidersForEmail but for email
   // provider identified by 'password' string, signInMethods would contain 2
   // different strings:
   // 'emailLink' if the user previously signed in with an email/link
   // 'password' if the user has a password.
   // A user could have both.
   if (error) {
     // Handle error case.
   }
   if (![signInMethods containsObject:FIREmailPasswordAuthSignInMethod]) {
     // User can sign in with email/password.
   }
   if (![signInMethods containsObject:FIREmailLinkAuthSignInMethod]) {
     // User can sign in with email/link.
   }
 }];

As described above email/password and email/link are considered the same FIREmailAuthProvider (same PROVIDER_ID) with different methods of sign-in.

Next steps

After a user signs in for the first time, a new user account is created and linked to the credentials—that is, the user name and password, phone number, or auth provider information—the user signed in with. This new account is stored as part of your Firebase project, and can be used to identify a user across every app in your project, regardless of how the user signs in.

  • In your apps, you can get the user's basic profile information from the FIRUser object. See Manage Users.

  • In your Firebase Realtime Database and Cloud Storage Security Rules, you can get the signed-in user's unique user ID from the auth variable, and use it to control what data a user can access.

You can allow users to sign in to your app using multiple authentication providers by linking auth provider credentials to an existing user account.

To sign out a user, call signOut:.

Swift

    let firebaseAuth = Auth.auth()
do {
  try firebaseAuth.signOut()
} catch let signOutError as NSError {
  print ("Error signing out: %@", signOutError)
}
  

Objective-C

    NSError *signOutError;
BOOL status = [[FIRAuth auth] signOut:&signOutError];
if (!status) {
  NSLog(@"Error signing out: %@", signOutError);
  return;
}

You may also want to add error handling code for the full range of authentication errors. See Handle Errors.

Send feedback about...

Need help? Visit our support page.