auth0/react-native-auth0

Supporting multiple domains in the SDK

chrisbruford opened this issue · 11 comments

Checklist

Description

Create a new Auth0Provider with a different domain prop still results in directing users to the initial domain when using the authorize method provided by useAuth0 hook.

Worked on 2.17.4

Reproduction

  1. Initialise the Auth0Provider with any valid domain prop
  2. Initialise a new Auth0Provider with a new valid domain prop
  3. Observe that the login function provided by useAuth0 still directs the browser to the initial domain

Additional context

We use multiple Auth0 Tenants to isolate users from different customers and in our React Native app we allow the user to select which customer they are from and thus we dynamically change the domain being sent to the Auth0 SDK.

This worked well up until we upgraded to v3 and now it fails to change the domain. I have traced it through the auth0 SDK and found the domain change is successfully being propogated all the way to the NativeModule import; but the SDK still directs the browser window to the previously set domain. Seems like the domain gets cached somewhere (perhaps the useAuth0 hook using the old object?)

I understand that Organisations is the recommended approach to this use case now; but the use of auth0 tenenants is still listed as a supported method - and thus I believe this change to be a bug.

Note: this has already been asked on the forums but no answer has been given for over a year

react-native-auth0 version

3.0.1

React Native version

0.72.5

Expo version

49.0.0

Platform

Android

Platform version(s)

13

@chrisbruford This can be achieved by using 2 instances of auth0 class instance rather than hooks which can be initialized like this

import Auth0 from 'react-native-auth0';

const auth0 = new Auth0({
  domain: 'YOUR_AUTH0_DOMAIN',
  clientId: 'YOUR_AUTH0_CLIENT_ID',
});

However using you have to be careful using the credentials manager as we can store only 1 access token and using both the instances alternatively will definitely result in an error.

So i'd suggest limiting this use case just for authentication purpose and not credentials management.

@poovamraj does the fact that this behaviour is not consistent across iOS and Android (as this only happens on Android) not constitute this being a bug? it seems odd to me that the "solution" is to not use the hooks functionality...that seems more like a workaround to achieve the same behaviour on Android as we already get for free on iOS...and as you pointed out, has undocumented pitfalls to doing so?

@poovamraj I tried your approach which ended up looking like this:

const auth0 = useMemo(() => {
  if (!workspace) return;
  return new Auth0({
    domain: workspace?.variables.AUTH0_DOMAIN,
    clientId: workspace?.variables.AUTH0_CLIENT_ID,
  });
}, [workspace]);

I then tried to login like this:

console.log({ domain: auth0?.auth.domain });
auth0?.webAuth
  .authorize({
    audience: workspace?.variables.AUTH0_AUDIENCE || 'https://inbanx/api',
  })
  .then((credentials) => {
     ...
  })

The console.log shows me the domain has changed, but the call to authorize still directs me to the domain for the first workspace, regardless.

As I mentioned in my earlier comment, this doesn't behave like this on iOS even when using hooks.

@chrisbruford You are right! As you mentioned we only have 1 instance of Auth0 object in the native module. We have a hasValidAuth0Instance check that will avoid initializeAuth0 if there is already an existing instance.

I have to check why the iOS native module is not performing this check properly as it would mean creating a new instance of the SDK each time a web authentication or credentials management call is made.

The proper way to support this would be is to have a list of auth0 instances in the native module and use the right one depending on the client id making the request. Or we should provide a method to invalidate the existing instance of Auth0 client.

On a similar note it would be good if the hasValidAuth0Instance was smart enough to realise that the domain/clientID has changed and allow the initializeAuth0 to be made. Our App only has a single Auth0Provider at a time but before logging in you can switch from a production environment to a dev environment. This used to work fine in older versions of react-native-auth0 but now we're having to restart the entire App when there's an environment change.

I am also encountering this issue after upgrading from 2.x to 3.x. I used to be able to switch domain/clientId without having to restart the app and now, with 3.x, the app fails to authorize the user because the wrong domain/clientId is being used after creating a new instance of the default exported class.

If it's of interest to others, we've been able to workaround this behavior by calling the underlying initializeAuth0 function when the clientID and/or domain changes...

  useEffect(() => {
    // There is a "feature" in the react-native-auth0 library where it will only initialize the native
    // layer once with the first clientID and domain it is given. Any changes to these values will have
    // no impact until the app is restarted.
    // See https://github.com/auth0/react-native-auth0/issues/747#issuecomment-1791128894
    //
    // This code works around that by calling the initializeAuth0 native function ourselves when the
    // clientID or domain changes
    NativeModules.A0Auth0.initializeAuth0(clientID, domain);
  }, [clientID, domain]);

thanks for sharing a workaround, @mrbrentkelly!

We are planning to address this issue along with other improvements in the next major release coming up this quarter. I'll share exact timelines by the mid of June.

Hi everyone,

We've fixed this issue as part of version 4.0.0-beta.0 and more details about it can be found here

Closing this issue for now, feel free to re-open it, if you run into any issues

Hi @desusai7 et al,

I have installed 4.0.0-beta.0 to test if the issue has been resolved, and whilst iOS is working perfectly, I am still facing some issues on Android.

As per the V4 documentation, here is the value of my android/app/build.gradle:

android {
  defaultConfig {
    // Is this holding back multiple domain support on android? Can I add multiple here somehow?
    manifestPlaceholders = [auth0Domain: "tenant1.auth0.com", auth0Scheme: "${applicationId}.auth0"]
  }
}

Then, similar to @chrisbruford, in our JavaScript code we dynamically switch the Domain & Client ID based on a user's selection during the login process:

import Auth0 from 'react-native-auth0';

const doAuthorization = async (domain: string, clientId: string): Promise<void> => {
  const auth0 = new Auth0({ domain, clientId });
  await auth0.webAuth.authorize({});
};

Whenever we call doAuthorization with "tenant1.auth0.com", we see that the login works. However, whenever it is called with any other valid domain (not present in android/app/build.gradle), the CORRECT web URL opens in a browser, but upon authenticating successfully it just hangs in the in-app-browser and never redirects back into our app.

So it seems like we're getting closer now that that correct domain opens up for authentication, but on Android it still doesn't appear to allow you to sign in. Any ideas or suggestions @desusai7?

  • React Native Version: 0.73.9
  • Android: 14

Oh okay, I have found a solution to the above issue. It is documented in the pull request referenced above (#931).

In addition to implementing version 4.0.0-beta.0 of this library, I also had to modify my android/app/src/main/AndroidManifest.xml file to add the following:

  <manifest
    xmlns:android="http://schemas.android.com/apk/res/android"
+   xmlns:tools="http://schemas.android.com/tools">
    <application ...>
      <activity
        android:name=".MainActivity" ...>
        ...
      </activity>
+     <activity
+       android:name="com.auth0.android.provider.RedirectActivity"
+       tools:node="replace"
+       android:exported="true">
+       <intent-filter>
+         <action android:name="android.intent.action.VIEW" />
+         <category android:name="android.intent.category.DEFAULT" />
+         <category android:name="android.intent.category.BROWSABLE" />
+         <data
+           android:host="tenant1.auth0.com"
+           android:pathPrefix="/android/${applicationId}/callback"
+           android:scheme="${applicationId}.auth0" />
+         <data
+           android:host="tenant2.auth0.com"
+           android:pathPrefix="/android/${applicationId}/callback"
+           android:scheme="${applicationId}.auth0" />
+       </intent-filter>
+     </activity>
    </application>
  </manifest>

I also removed the manifestPlaceholders option from my android/app/build.gradle file:

  android {
    defaultConfig {
-     manifestPlaceholders = [auth0Domain: "tenant1.auth0.com", auth0Scheme: "${applicationId}.auth0"]
    }
  }

Following the above steps, I was able to get our dynamic login across multiple tenants working again.

May I suggest putting this inside the installation documentation for your stable V4 release @desusai7, as it was quite confusing to implement given the differences to the latest installation instructions for Android.