Passwordless Firebase authentication without ejecting

I wanted to use Firebase as an authentication provider for my expo app, using their passwordless email magic link. It’s possible to do this without ejecting your Expo app, but it requires that you set up a small “proxy” server that redirects to an Expo generated deep link in the Firebase completion step.

  1. Create a deep link in Expo.
const expoLink = Linking.makeUrl('your/expo/link');
  1. Create a link to your redirect server that redirects to your Expo link. I’ve provided a Webtask function that you can test this with.
const proxyUrl = `${FIREBASE_LINK_PROXY}?redirectUrl=${encodeURIComponent(expoLink)}`;
  1. Send an email using the Firebase JS SDK, passing along your proxy url.
// import firebase from 'firebase/app';
// import 'firebase/auth';

firebase.auth().sendSignInLinkToEmail(email, {
    handleCodeInApp: true,
    url: proxyUrl
  .then(/* ... */)
  .catch(/* ... */)
// You probably also want to save the email for the completion step
// Here's using Expo's AsyncStorage
AsyncStorage.setItem('@YourApp:unverifiedEmail', email);
  1. Add the proxy to your authorized domains in Firebase.
If you use the Webtask function from above, add the following domain

to this place (replace <YOUR_FIREBASE_PROJECT>)<YOUR_FIREBASE_PROJECT>/authentication/providers
  1. Open the link in the email Firebase sends you—you’ll be sent to your Expo app (or asked if on iOS).
  2. Handle the link in Expo and complete sign-in with the Firebase JS SDK using the link
// The Expo link will be available after successfully
// being redirected into the app.
// Feel free to set this up however you want.
// Here I've put it in componentDidMount()
async componentDidMount() {
  const url = await Linking.getInitialURL();
  if (url) {

  Linking.addEventListener('url', ({ url }) => {
// Some function that handles the url using the Firebase JS SDK
async function handleUrl(url) {
  const isSignInWithEmailLink = firebase.auth().isSignInWithEmailLink(url);
  if (isSignInWithEmailLink) {
    try {
      const email = await AsyncStorage.getItem('@YourApp:unverifiedEmail');
      const result = await firebase.auth().signInWithEmailLink(email, url);

      this.setState({ user: result.user });
    } catch (error) {
  1. You should now have the user object from Firebase. Use it! :fire:

Redirect server code

This is just Node.js. You can adapt it easily to any serverless function provider (Zeit Now, Webtask, AWS Lambda, Azure Functions, Netlify Functions, Firebase Functions, etx…) or you own Node.js server.

const url = require('url');

module.exports = (req, res) => {
  const { query } = url.parse(req.url, true);
  const { redirectUrl, } = query;
  if (redirectUrl) {
    const Location = url.format({ pathname: redirectUrl, query: rest });
    res.writeHead(302, { Location });
    res.end(`Redirecting to ${Location}`);
  } else {
    res.end('You must provide a `redirectUrl` in the query');
1 Like

Thanks for this! It was just what I was looking for.