Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[🐛🔥] @react-native-firebase/auth (phone auth) + Expo Router - Redirected to appScheme://firebaseuth/link after resolving recaptcha in ios simulator #7258

Closed
2 of 9 tasks
bryanprimus opened this issue Jul 23, 2023 · 47 comments · May be fixed by #8203

Comments

@bryanprimus
Copy link

bryanprimus commented Jul 23, 2023

Issue

Describe your issue here

I'm currently developing a phone authentication for my ios app using @react-native-firebase/auth specifically Phone Auth

in ios simulator we don't have access to Silent APNs notifications for app verification and we will need to resolve recaptcha instead
ref:
image

The integration went smoothly, but when I tried to resolve the reCAPTCHA on the simulator, I got redirected to appScheme://firebaseuth/link. I believe it's a deep link to that route.

Since I use expo-router, it automatically detects that link and redirected to that screen as shown in this video

Screen.Recording.2023-07-23.at.21.34.49.mov

Expected Result

It should stay with the current screen after resolving the captcha


Project Files

Javascript

Click To Expand

package.json:

{
  "name": "",
  "version": "1.0.0",
  "main": "expo-router/entry",
  "scripts": {
    "start": "expo start",
    "android": "expo run:android",
    "ios": "expo run:ios",
    "web": "expo start --web",
    "lint": "echo \"Running eslint check\" && eslint --fix .",
    "tsc": "tsc"
  },
  "dependencies": {
    "@react-native-firebase/app": "^18.3.0",
    "@react-native-firebase/auth": "^18.3.0",
    "expo": "~49.0.3",
    "expo-build-properties": "~0.8.3",
    "expo-constants": "~14.4.2",
    "expo-dev-client": "~2.4.6",
    "expo-linking": "~5.0.2",
    "expo-router": "2.0.0",
    "expo-splash-screen": "~0.20.4",
    "expo-status-bar": "~1.6.0",
    "react": "18.2.0",
    "react-dom": "18.2.0",
    "react-native": "0.72.3",
    "react-native-gesture-handler": "~2.12.0",
    "react-native-safe-area-context": "4.6.3",
    "react-native-screens": "~3.22.0",
    "react-native-web": "~0.19.6"
  },
  "devDependencies": {
    "@babel/core": "^7.20.0",
    "@types/react": "~18.2.14",
    "eslint": "^8.45.0",
    "eslint-config-universe": "^11.3.0",
    "eslint-plugin-prettier": "5.0.0",
    "eslint-plugin-react-hooks": "^4.6.0",
    "prettier": "^3.0.0",
    "typescript": "^5.1.3"
  },
  "private": true
}

firebase.json for react-native-firebase v6:

# N/A

iOS

Click To Expand

ios/Podfile:

  • I'm not using Pods
  • [] I'm using Pods and my Podfile looks like:
# N/A

AppDelegate.m:

// N/A


Android

Click To Expand

Have you converted to AndroidX?

  • my application is an AndroidX application?
  • I am using android/gradle.settings jetifier=true for Android compatibility?
  • I am using the NPM package jetifier for react-native compatibility?

android/build.gradle:

// N/A

android/app/build.gradle:

// N/A

android/settings.gradle:

// N/A

MainApplication.java:

// N/A

AndroidManifest.xml:

<!-- N/A -->


Environment

Click To Expand

react-native info output:

 OS: macOS 13.4.1
  CPU: (8) arm64 Apple M1
  Memory: 103.33 MB / 8.00 GB
  Shell:
    version: "5.9"
    path: /bin/zsh
Binaries:
  Node:
    version: 20.3.1
    path: /opt/homebrew/bin/node
  Yarn:
    version: 1.22.19
    path: /opt/homebrew/bin/yarn
  npm:
    version: 9.6.7
    path: /opt/homebrew/bin/npm
  Watchman:
    version: 2023.06.26.00
    path: /opt/homebrew/bin/watchman
Managers:
  CocoaPods:
    version: 1.12.1
    path: /Users/bryan/.gem/ruby/2.7.6/bin/pod
SDKs:
  iOS SDK:
    Platforms:
      - DriverKit 22.4
      - iOS 16.4
      - macOS 13.3
      - tvOS 16.4
      - watchOS 9.4
  Android SDK:
    API Levels:
      - "29"
      - "31"
      - "32"
      - "33"
      - "34"
    Build Tools:
      - 30.0.2
      - 30.0.3
      - 31.0.0
      - 33.0.0
      - 34.0.0
    System Images:
      - android-33 | Google APIs ARM 64 v8a
      - android-34 | Google APIs ARM 64 v8a
    Android NDK: Not Found
IDEs:
  Android Studio: 2022.2 AI-222.4459.24.2221.10121639
  Xcode:
    version: 14.3.1/14E300c
    path: /usr/bin/xcodebuild
Languages:
  Java:
    version: 11.0.19
    path: /usr/bin/javac
  Ruby:
    version: 2.7.6
    path: /Users/bryan/.rubies/ruby-2.7.6/bin/ruby
npmPackages:
  "@react-native-community/cli": Not Found
  react:
    installed: 18.2.0
    wanted: 18.2.0
  react-native:
    installed: 0.72.3
    wanted: 0.72.3
  react-native-macos: Not Found
npmGlobalPackages:
  "*react-native*": Not Found
Android:
  hermesEnabled: true
  newArchEnabled: false
iOS:
  hermesEnabled: true
  newArchEnabled: false
  • Platform that you're experiencing the issue on:
    • iOS
    • Android
    • iOS but have not tested behavior on Android
    • Android but have not tested behavior on iOS
    • Both
  • react-native-firebase version you're using that has this issue:
    • "@react-native-firebase/app": "^18.3.0",
      "@react-native-firebase/auth": "^18.3.0",
  • Firebase module(s) you're using that has the issue:
    • firebase phone auth
  • Are you using TypeScript?
    • Yes


@bryanprimus bryanprimus added help: needs-triage Issue needs additional investigation/triaging. type: bug New bug report labels Jul 23, 2023
@noickare
Copy link

noickare commented Aug 3, 2023

Try expo-router unmatched route as described https://docs.expo.dev/routing/error-handling/ and check the path name and if it matches firebaseauthlink redirect back see code sample below.

function UnmatchedScreen(props: UnmatchedProps) {
    const param = useGlobalSearchParams();

    if (param.unmatched === 'firebaseauth,link') {
        return <Redirect href="../" />;
    }

    return (
        <View>
            <Text>404</Text>
       </View>
    );
}

@chen-rn
Copy link

chen-rn commented Aug 26, 2023

Running into the same issue!

Anyone know why it's redirecting to a deep link? Is it possible to change it?

@lsps9150414
Copy link

Same, can't find any document to customize this behavior

@github-actions
Copy link

Hello 👋, to help manage issues we automatically close stale issues.

This issue has been automatically marked as stale because it has not had activity for quite some time.Has this issue been fixed, or does it still require attention?

This issue will be closed in 15 days if no further activity occurs.

Thank you for your contributions.

@github-actions github-actions bot added the Type: Stale Issue has become stale - automatically added by Stale bot label Oct 13, 2023
@lsps9150414
Copy link

Still waiting for reply 🙏🏻

@github-actions github-actions bot removed the Type: Stale Issue has become stale - automatically added by Stale bot label Oct 20, 2023
@riccardogiorato
Copy link

Having this exact same issue, don't close the issue

@balgamat
Copy link

Same here.

Copy link

Hello 👋, to help manage issues we automatically close stale issues.

This issue has been automatically marked as stale because it has not had activity for quite some time.Has this issue been fixed, or does it still require attention?

This issue will be closed in 15 days if no further activity occurs.

Thank you for your contributions.

@github-actions github-actions bot added the Type: Stale Issue has become stale - automatically added by Stale bot label Dec 10, 2023
@lsps9150414
Copy link

Still waiting for reply

@github-actions github-actions bot removed the Type: Stale Issue has become stale - automatically added by Stale bot label Dec 13, 2023
@Kledal
Copy link

Kledal commented Dec 13, 2023

I'm running into the same issue. Seems to originate from the recaptcha triggered by firebase authentication that tries to redirect to app-schema://firebaseauth/link, which the expo-router catches

@mikehardy
Copy link
Collaborator

This is an open source repository, all updates are visible in the form of comments here. The only updates are people asking for updates, no one appears to have the time to investigate and provide more information. If this is impacting you, allocating resources to troubleshoot it in depth is the way to move it forward

Copy link

Hello 👋, to help manage issues we automatically close stale issues.

This issue has been automatically marked as stale because it has not had activity for quite some time.Has this issue been fixed, or does it still require attention?

This issue will be closed in 15 days if no further activity occurs.

Thank you for your contributions.

@github-actions github-actions bot added the Type: Stale Issue has become stale - automatically added by Stale bot label Jan 10, 2024
@github-actions github-actions bot closed this as not planned Won't fix, can't repro, duplicate, stale Jan 25, 2024
@micaeldias
Copy link

micaeldias commented Jan 31, 2024

For Expo v50 with expo-router I had to use the following:

import { Unmatched, Redirect, useGlobalSearchParams } from "expo-router";

export default () => {
    const param = useGlobalSearchParams<{ unmatched: string[] }>();

    if (isFirebaseCallback(param.unmatched)) {
        return <Redirect href="/auth/sign-in" />;
    }

    return <Unmatched />;
};

const isFirebaseCallback = (unmatched: string[] | undefined) => {
    return (
        unmatched?.length === 2 &&
        unmatched[0] === "firebaseauth" &&
        unmatched[1] === "link"
    );
};

@joaqo
Copy link
Contributor

joaqo commented Feb 8, 2024

I have the same issue.

@iarmankhan
Copy link

Can we please reopen this issue? I was able to use @micaeldias 's code, but it doesn't keep the state of that screen. How can we keep the local state?

@mikehardy
Copy link
Collaborator

Sure, can reopen. Please note this is an open source repository. If this is important to you, devoting resources to finding the root cause and proposing a PR is the way to get it fixed

@mikehardy mikehardy reopened this Feb 14, 2024
@github-actions github-actions bot removed the Type: Stale Issue has become stale - automatically added by Stale bot label Feb 14, 2024
@2n2n
Copy link

2n2n commented Feb 20, 2024

for those who have the same problem you just need to create a route for /firebaseauth/link.js that route should be found. that's where we put the OTP screen afterwards

Copy link

Hello 👋, to help manage issues we automatically close stale issues.

This issue has been automatically marked as stale because it has not had activity for quite some time.Has this issue been fixed, or does it still require attention?

This issue will be closed in 15 days if no further activity occurs.

Thank you for your contributions.

@github-actions github-actions bot added the Type: Stale Issue has become stale - automatically added by Stale bot label Mar 19, 2024
@iarmankhan
Copy link

Hey @2n2n How do you maintain the state of your app? The confirmation is in state and it got lost

@Misterr-H
Copy link

@al3xstathis while I could not replicate @2n2n's Solution, I found another that seemed to solve the problem for me. Instead of handling the OTP on /firebaseauth/link.js I just redirect backwards from that page and keep my otp on the login screen like such:

import { router } from "expo-router";
import React, { useEffect } from "react";
import { View } from "react-native";

export default function FirebaseauthLinkPage() {
  
  useEffect(() => {
    router.back();
  }, []);

  return (
    <View>
    </View>
  );
}

For me when I return to the login screen of my app the state is still lost has anyone found a solution that works since?

State must not be lost since screen is still exiting in route stack

Copy link

Hello 👋, to help manage issues we automatically close stale issues.

This issue has been automatically marked as stale because it has not had activity for quite some time.Has this issue been fixed, or does it still require attention?

This issue will be closed in 15 days if no further activity occurs.

Thank you for your contributions.

@github-actions github-actions bot added Type: Stale Issue has become stale - automatically added by Stale bot and removed Type: Stale Issue has become stale - automatically added by Stale bot labels Aug 13, 2024
@russellwheatley
Copy link
Member

This isn't a bug, it's a way for Firebase Auth to send the user back to the app post recaptcha completion. You can customise the URL if you prefer it to have a different value:

It's something like this:

<key>CFBundleURLTypes</key>
<array>
    <dict>
        <key>CFBundleURLName</key>
        <string>com.yourapp.app</string>
        <key>CFBundleURLSchemes</key>
        <array>
            <string>yourcustomscheme</string>
        </array>
    </dict>
</array>

either way, you'll have to handle the URL in your expo navigation once the deeplink redirects to your app after recaptcha. There is nothing to fix from RNFB side. There are a number of suggestions already in this thread that covers ways to handle this. Labelling as a documentation issue.

@dicompathakofficial
Copy link

SOLUTION FOUND !! AT [https://docs.expo.dev/router/error-handling/] just create a new file called +not-found.tsx at inside app. app/+not-found.tsx and inside it only have import { Unmatched } from "expo-router";
export default Unmatched;
So by default no 404 pages will be shown. Do lemme know if it helps. Cheers

@emrahaydemir
Copy link

emrahaydemir commented Aug 27, 2024

It's frustrating not being able to set the redirect route after solving the captcha. I didn't expect that I might have to redirect it through a hack. I've been working on this issue for a few hours now. To be honest, I thought this would be easily resolved.

@emrahaydemir
Copy link

For now, I'll implement a similar solution. Here's what I did:

I made a small change to my not-found page so that it redirects back directly without actually accessing the missing page.

import { View, Text } from "react-native";
import React, { useEffect } from "react";
import { usePathname, useRouter } from "expo-router";

export default function NotFound() {
  const pathname = usePathname();
  const router = useRouter();
  useEffect(() => {
    if (pathname == "/firebaseauth/link") router.back();
  }, [pathname]);

  return (
    <View>
      <Text>not_found</Text>
    </View>
  );
}

@andepants
Copy link

For now, I'll implement a similar solution. Here's what I did:

I made a small change to my not-found page so that it redirects back directly without actually accessing the missing page.

import { View, Text } from "react-native";
import React, { useEffect } from "react";
import { usePathname, useRouter } from "expo-router";

export default function NotFound() {
  const pathname = usePathname();
  const router = useRouter();
  useEffect(() => {
    if (pathname == "/firebaseauth/link") router.back();
  }, [pathname]);

  return (
    <View>
      <Text>not_found</Text>
    </View>
  );
}

Worked like a charm thank you!

Copy link

github-actions bot commented Oct 8, 2024

Hello 👋, to help manage issues we automatically close stale issues.

This issue has been automatically marked as stale because it has not had activity for quite some time.Has this issue been fixed, or does it still require attention?

This issue will be closed in 15 days if no further activity occurs.

Thank you for your contributions.

@bytemtek
Copy link

bytemtek commented Dec 4, 2024

Is there any update about this issue ?

@patrickkeenan
Copy link

Same issue, any proper resolution. I cannot pass the confirmation back to the original view.

@abhishrek
Copy link

abhishrek commented Dec 21, 2024

This isnt working for me as the param.unmatched comes as undefined.
I am not sure if moving every undefined path to login screen is a good idea.

function UnmatchedScreen(props: UnmatchedProps) {
    const param = useGlobalSearchParams();

    if (param.unmatched === 'firebaseauth,link') {
        return <Redirect href="../" />;
    }

    return (
        <View>
            <Text>404</Text>
       </View>
    );
}

@abhishrek
Copy link

for those who have the same problem you just need to create a route for /firebaseauth/link.js that route should be found. that's where we put the OTP screen afterwards

This worked on iOS emulator, but I am not sure how to work with routing if recaptcha is not asked.
via the official docs

When silent push notifications are properly configured, only a very small percentage of users will experience the reCAPTCHA flow.

PS: this hack is not required with React Navigation (i.e. not using expo router)

@jey
Copy link

jey commented Dec 26, 2024

I spent a few hours looking into this and believe I've identified the root cause, along with a fix that's principled and robust. (I've tried the other solutions in this thread, like adding a route /firebaseauth/link that invokes router.back(), but while that did go back to my phone auth screen, the component was unmounted and remounted, so the component's state was reset.)

The root cause of this is a bad interaction between a feature in the Firebase iOS SDK and one in Expo Router:

  • Firebase uses a technique called "swizzling" to automatically intercept the openURL call and do its business, but then it also passes along the same openURL call to our application so we can handle it too.
  • Our iOS app's AppDelegate is supposed to return TRUE if it can handle the provided URL, and FALSE if it can't.
  • When using Expo Router, our AppDelegate is configured to just pass the URL along onto the router.
  • However, Expo Router always answers "yes I can handle this URL" -- even if it's going to "handle" it by displaying a "Not Found" error.

This interacts badly because it's effectively like Expo Router is hijacking the call that's intended for Firebase Auth. Yet, this situation only occurs because Firebase Auth is forwarding the openURL call to our app even after it has already handled it.

Proposed solution: update your AppDelegate's openURL method to not handle URLs for the hostname "firebaseauth", under the assumption that it's already being handled by Firebase. Put the following into a file called fire-auth-url-fix.patch then cd ios/MyApp and run patch -p1 < fire-auth-url-fix.patch

diff --git a/AppDelegate.mm b/AppDelegate.mm
index 67adadf..9577f68 100644
--- a/AppDelegate.mm
+++ b/AppDelegate.mm
@@ -36,6 +36,10 @@ - (NSURL *)bundleURL

 // Linking API
 - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
+  if ([url.host caseInsensitiveCompare:@"firebaseauth"] == NSOrderedSame) {
+    // invocations for Firebase Auth are handled elsewhere and should not be forwarded to Expo Router
+    return NO;
+  }
   return [super application:application openURL:url options:options] || [RCTLinkingManager application:application openURL:url options:options];
 }

While a more pedantic and precise solution would be to reject all URLs with a scheme that's intended for Firebase, those values are different for each Firebase project, so harder to create a universally applicable patch. The above patch will work as long as you aren't needing to handle URLs for a host called "firebaseauth".

Ideally this would be integrated into the @react-native-firebase/auth plugin.

@mikehardy
Copy link
Collaborator

@jey that's fantastic investigation! I think you may have nailed it

Ideally this would be integrated into the @react-native-firebase/auth plugin.

I agree, and would happily merge any related PR that did that. Unfortunately I think it would be a plugin addition of the "unsafe edit" variety, in Expo plugin terms since it needs to directly modify AppDelegate logic and perhaps I'm wrong but I'm not aware of any expo plugin APIs that allow that cleanly. Still, pretty important. Maybe a separate "auth-plus-expo-router" plugin instead of the main one? Or a config entry or something that only turns on that edit as needed

Alternatively, could just be a documentation edit on whatever page makes sense on rnfirebase.io? (there's an edit button at top right of every page)

Either way I've edited the title of this issue to better reflect the cause / symptom for people searching

@mikehardy mikehardy changed the title [🐛🔥] @react-native-firebase/auth (phone auth) - Redirected to appScheme://firebaseuth/link after resolving recaptcha in ios simulator [🐛🔥] @react-native-firebase/auth (phone auth) + Expo Router - Redirected to appScheme://firebaseuth/link after resolving recaptcha in ios simulator Dec 26, 2024
@jey
Copy link

jey commented Dec 28, 2024

@mikehardy I've submitted PR #8203 with an Expo plugin implementing this fix and believe it's ready for review. BTW, thanks for the encouragement and feedback! I "should" have been focusing on an deadline, but along the way this bug got stuck in my craw and really irritated me to look for a fix and create an easy onramp to save others from the same pain and trouble.

@mcewball13
Copy link

Proposed solution: update your AppDelegate's openURL method to not handle URLs for the hostname "firebaseauth", under the assumption that it's already being handled by Firebase. Put the following into a file called fire-auth-url-fix.patch then cd ios/MyApp and run patch -p1 < fire-auth-url-fix.patch

diff --git a/AppDelegate.mm b/AppDelegate.mm
index 67adadf..9577f68 100644
--- a/AppDelegate.mm
+++ b/AppDelegate.mm
@@ -36,6 +36,10 @@ - (NSURL *)bundleURL

 // Linking API
 - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
+  if ([url.host caseInsensitiveCompare:@"firebaseauth"] == NSOrderedSame) {
+    // invocations for Firebase Auth are handled elsewhere and should not be forwarded to Expo Router
+    return NO;
+  }
   return [super application:application openURL:url options:options] || [RCTLinkingManager application:application openURL:url options:options];
 }

Fantastic explanation. That you for all your hard work on that. I did try this fix, but it's still redirecting after stopping and restarting expo after applying the patch. Any recommendations?

@jey
Copy link

jey commented Dec 30, 2024

@mcewball13,

I did try this fix, but it's still redirecting after stopping and restarting expo after applying the patch. Any recommendations?

Bummer to hear that. You might have better luck with the branch in #8203 which automates this process with error checking. Note that you'll have to run yarn && yarn lerna:prepare if you want to install directly from that branch.

But, aside from that here are some specific debugging questions:

  • Are you sure you're running the iOS build of your app, and not using Expo Go? The iOS build is started with expo run:ios instead of the usual expo start --ios command.
  • Can you provide a minimal reproducible example with the patch applied which demonstrates this behavior?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.