App Store Rejection Due to Web Login – Issue with Phone Passwordless + react-native-auth0 and SFSafariViewController

Hello everyone,

I’m currently trying to submit a React Native app to the App Store and got rejected by Apple with the following message:

Guideline 4.0 - Design
We noticed that the user is taken to the default web browser to sign in or register for an account, which provides a poor user experience.

Next Steps
To resolve this issue, please revise your app to enable users to sign in or register for an account in the app.
You may also choose to implement the Safari View Controller API to display web content within your app…


To comply with this guideline, I’ve updated my implementation using react-native-auth0 and enabled useSFSafariViewController: true, like so:

const credentials = await authorize(
  {
    audience: process.env.EXPO_PUBLIC_AUTH0_AUDIENCE,
  },
  {
    useSFSafariViewController: true,
  },
);

This does open the login page within SFSafariViewController, which is accepted by Apple.
However, I’m using passwordless authentication via SMS (phone number), and after entering the code and completing login, the app opens but lands on an “unmatched route” screen in Expo.

Here’s my setup:

  • I’m using react-native-auth0.
  • In app.json, I have:
{
  "scheme": "myapp"
}

In the Auth0 dashboard, my callback URL uses auth0 suggested scheme:
myapp.auth0://YOUR_AUTH0_DOMAIN/ios/YOUR_BUNDLE_ID/callback

Despite that, after the redirect from Safari, the app doesn’t handle it properly and shows an “unmatched route” screen.

Question:
Why does the redirect from Auth0 land on an unmatched route despite the correct scheme?

Any guidance would be greatly appreciated!

Thanks in advance!

Hi @yurii.sytnyk,

Welcome to the Auth0 Community!

By any chance, did you rename the entry file to a custom name? If yes, could you try renaming it to index.js/index.ts and clearing the cache?

Also, I would recommend opening an issue on the SDKs GitHub page. You can also take a look at these related GitHub issues:

Otherwise, you can look into our documentation regarding implementing Embedded Login in our native application.

If you have any other questions, let me know!

Have a good one,
Vlad

Hi @vlad.murarasu,

Thanks a lot for sharing all this info — really appreciate it.
After spending quite some time trying to get things working, I ended up with a workaround that seems to do the trick. I’m not entirely sure it’s the correct way to handle authentication with SFSafariViewController, but I wanted to share it here in case it helps others facing the same issue.

Let’s break it down step by step:

1. Update app.json (or ideally rename to app.config.ts):

Make sure your config includes the following:

export default {
  expo: {
    scheme: "com.myapp", // you can skip the `auth0` suffix here
    plugins: [
      [
        'react-native-auth0',
        {
          domain: process.env.EXPO_PUBLIC_AUTH0_DOMAIN,
        },
      ],
    ],
  },
};

2. Handle native deep links in +native-intent.tsx:

Create a file at the root of your app directory. This will intercept all deep links — including the Auth0 callback URL — and prevent Expo Router from throwing an “Unmatched route” error.

import { ERoutes } from '@/types/routes.enum';
import { AUTH0_SCHEME_SUFFIX } from '@/constants/auth'; // e.g. 'auth0://'

export function redirectSystemPath({ path }: { path: string }) {
  if (path.includes(AUTH0_SCHEME_SUFFIX)) {
    return ERoutes.SIGN_IN;
  }

  return path;
}

3. Implement login logic:

Here’s how to use authorize with SFSafariViewController:

import { Redirect } from 'expo-router';
import { useAuth0 } from 'react-native-auth0';
import { Button } from '@/components/Button';
import { ERoutes } from '@/types/routes.enum';
import { AUTH0_CUSTOM_SCHEME } from '@/constants/auth'; // e.g. 'com.myapp.auth0'

export const LoginButton = () => {
  const { user, authorize, isLoading } = useAuth0();

  const onLoginPress = async () => {
    await authorize(
      {
        audience: process.env.EXPO_PUBLIC_AUTH0_AUDIENCE,
        additionalParameters: { prompt: 'login' }, // optional but useful (explained below)
      },
      {
        useSFSafariViewController: true, // important!
        ephemeralSession: true, // optional, see docs
        customScheme: AUTH0_CUSTOM_SCHEME,
      },
    );
  };

  if (!isLoading && user) {
    return <Redirect href={ERoutes.EVENTS} />;
  }

  return <Button fullWidth disabled={isLoading} title="Continue" onPress={onLoginPress} />;
};

Why use prompt: 'login'?

SFSafariViewController may not clear session data fully after logout. Without prompt: 'login', trying to log in again immediately after logout might not present the login screen. Adding prompt: 'login' ensures the login prompt is always shown.


:warning: Why does “Unmatched route” happen?

Not 100% sure, but based on what I’ve seen in the source code of react-native-auth0, here’s what’s happening:

  • In node_modules/react-native-auth0/webauth/agent.ts, the login() method uses:
Linking.addEventListener('url', handler);
  • This allows the Auth0 SDK to intercept the deep link (e.g., com.myapp.auth0://...) and exchange the code for an access token.
  • Even though we intercept this route in +native-intent.tsx, it still reaches the SDK — and that’s what we want.

So technically, Expo Router gets a chance to intercept the link (to avoid unmatched routes), but the SDK still receives it and proceeds with the token exchange.


In conclusion, there’s no bug in Auth0 here — but clearer documentation around useSFSafariViewController and recommended setup for deep link handling in React Native would be super helpful.

Until then, I hope this helps others struggling with similar issues. And if you’re ever unsure, digging into the source code and asking AI to explain it line by line is honestly a great approach.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.