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

Support for multiple Auth0 tenants #421

Closed
naajaw opened this issue Oct 14, 2021 · 13 comments
Closed

Support for multiple Auth0 tenants #421

naajaw opened this issue Oct 14, 2021 · 13 comments
Labels
question waiting for customer This issue is waiting for a response from the issue or PR author

Comments

@naajaw
Copy link

naajaw commented Oct 14, 2021

Before version 2.9, we were able to add support for multiple tenants (for example, we have a dev tenant and a prod tenant) by simply adding multiple <data/> tags to the intent filter, one for each auth0 domain we use.
With the updates in version 2.9, we can only specify one Auth0 domain. We can swap between Auth0 domains between doing production builds and dev builds, but in our use case, our team needs to be able to swap between dev & prod servers at runtime (which worked with multiple data tags).

Don't know enough about Android/Gradle to know the limitations of manifestPlaceholders, but it doesn't seem like having list-type placeholders that generate tags would be possible.
Another solution would be to have option to specify the intent filter in the user's AndroidManifest, like how it was done pre-v2.9 (not sure if there's already a way to override the react-native-auth0's AndroidManifest)

Was able to get around this by adding multiple <data> tags to the intent filter inside of node_modules/react-native-auth0, and add corresponding manifestPlaceholders (e.g. auth0Domain_prod and auth0Domain_dev) but obviously this is not a real solution.

@naajaw naajaw added the feature request A feature has been asked for or suggested by the community label Oct 14, 2021
@adammcarth
Copy link

adammcarth commented Oct 19, 2021

Yeah +1 mate, this caveat of the v2.9.0 release has been a real challenge for us as well.

Our situation is similar, except instead of using multiple tenants to isolate staging and production - we use them to region specific logins so that we comply with GDPR for customers in the EU. For instance, if a project is in the UK, we sign in users with our EU tenant - whereas a project in Australia would sign in using a global tenant. At one point we contemplated having region specific builds to handle this, except users can sometimes switch between global regions and alas the dynamic approach made much more sense for us.

03113DED-B456-4646-8280-233778FBE12A

As @ptrowan indicates above, the approach prior to v2.9.0 was something like this:

<!-- android/app/src/main/AndroidManifest.xml -->

<activity
  android:name=".MainActivity"
  android:launchMode="singleTask">
  <intent-filter>
    <action android:name="android.intent.action.VIEW" />
    <category android:name="android.intent.category.DEFAULT" />
    <category android:name="android.intent.category.BROWSABLE" />
    <!-- You could list all necessary Auth0 URL's below, eg -->

    <!-- Australia Region URL -->
    <data
      android:host="example.au.auth0.com"
      android:pathPrefix="/android/${applicationId}/callback"
      android:scheme="${applicationId}" />
    <!-- EU Region URL -->
    <data
      android:host="example.eu.auth0.com"
      android:pathPrefix="/android/${applicationId}/callback"
      android:scheme="${applicationId}" />

  </intent-filter>
</activity>

We were then able to manage tenant switching ourselves on the JavaScript side by simply reinitializing the Auth0 class with a new Base URL as required.

But since v2.9.0 now requires us to declare Base URL's in the android/app/build.gradle like this:

android {
    defaultConfig {
        // Add the next line
        manifestPlaceholders = [auth0Domain: "YOUR_AUTH0_DOMAIN", auth0Scheme: "${applicationId}"]
    }
    ...
}

...there unfortunately seems to be no option for us to "safelist" multiple URL's anymore? 😟

@xanderdeseyn
Copy link

This is completely blocking our team from upgrading as well.

@ghostneneji
Copy link

This new flow is very inconvenient at dev env, nobody want to mess up in the production env, so we have to change continuesly the config.
In this moment, we are facing that with a dedicated android dev env branch... but its not smart enough

@alessioemireni
Copy link

alessioemireni commented Nov 9, 2021

Same problem here. We have in production an App with open Domain and now we are blocked with the v2.9.0. Before we could set the <intent-filter> like below:

<intent-filter>
                <action android:name="android.intent.action.VIEW" />

                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE" />
                <data
                <!-- host is not necessary for multi domain -->
                    android:pathPrefix="/android/com.example/callback"
                    android:scheme="com.example" />
            </intent-filter>

In the actual version of the library I try to remove the auth0Domain in the build.gradle but the app not compile.

Could be possible to set an empty auth0Domain in the next release, so that we will accept a multi domain?

@Widcket
Copy link
Contributor

Widcket commented Nov 10, 2021

Hi @ptrowan, thanks for raising this.
I believe auth0/Auth0.Android#333 (comment) will answer your question (the Android SDK also uses manifest placeholders and this question has come up in the past).

Please let me know if that helps.

@Widcket Widcket added question waiting for customer This issue is waiting for a response from the issue or PR author and removed feature request A feature has been asked for or suggested by the community labels Nov 10, 2021
@naajaw
Copy link
Author

naajaw commented Nov 15, 2021

@Widcket adding an activity with tools:node="replace" seemed to work! thanks!!
for the sake of writing out a solution on this thread, I added this to my AndroidManifest:

<activity android:name="com.auth0.react.RedirectActivity"
          tools:node="replace">
    <intent-filter android:autoVerify="true"
                   tools:targetApi="m">
        <action android:name="android.intent.action.VIEW" />

        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />

        <data android:host="domain1.auth0.com"
              android:pathPrefix="/android/${applicationId}/callback"
              android:scheme="${auth0Scheme}" />
        <data android:host="domain2.auth0.com"
              android:pathPrefix="/android/${applicationId}/callback"
              android:scheme="${auth0Scheme}" />
    </intent-filter>
</activity>

(which is identical to the <activity/> tag in the package manifest, with the exception of explicitly defining multiple domains and adding the tools:node="replace")
and remember to add xmlns:tools="http://schemas.android.com/tools" to the topmost <manifest/> tag

@naajaw naajaw closed this as completed Nov 15, 2021
@fragilehm
Copy link

fragilehm commented Nov 23, 2021

@ptrowan thanks for sharing the solution, so android:launchMode="singleTask" from the main activity should be removed right? And no need to add manifestPlaceholders?
I am having a weird issue, after I signing the web view disappears (expected) but then the application restarts.

@Sumit2202
Copy link

@ptrowan thanks for sharing the solution, so android:launchMode="singleTask" from the main activity should be removed right? And no need to add manifestPlaceholders? I am having a weird issue, after I signing the web view disappears (expected) but then the application restarts.

I am also facing this exact issue in Android where after signing in, the application is restarting, how did you end up resolving it?

@kimball-j
Copy link

@ptrowan thanks for sharing the solution, so android:launchMode="singleTask" from the main activity should be removed right? And no need to add manifestPlaceholders? I am having a weird issue, after I signing the web view disappears (expected) but then the application restarts.

I am also facing this exact issue in Android where after signing in, the application is restarting, how did you end up resolving it?

Deleting tools:targetApi="m" worked for me

@jnjdev42
Copy link

Now, auth0 documentation says you have to write this in your build.gradle :

manifestPlaceholders = [auth0Domain: "domain", auth0Scheme: "${applicationId}.auth0"]

But, this is totally dumb as you can't pass multiple domains??? Any solution?

@Inlustra
Copy link

Inlustra commented Dec 16, 2024

For anyone else coming across this.
I've created an expo plugin so you don't need to go full bare-workflow. You can just use expo prebuild

Usage:
If you have an app.json, change that over to an app.config.js

const withAuth0MultiTenancy = require("./withAuth0MultiTenancy");

module.exports = {
  expo: {
    name: "YourAppName",
    slug: "your-app-slug",
    android: {
      package: "com.example.app", // Replace with your app's package name
    },
    plugins: [
      [
        withAuth0MultiTenancy,
        {
          flters: [
            {
              host: "host1.com",
              pathPrefix: "/android/${applicationId}/callback",
              scheme: "mysheme",
            },
            {
              host: "host2.com",
              pathPrefix: "/android/${applicationId}/callback",
              scheme: "myscheme",
            },
          ]
        },
      ],
    ],
  },
};

withAuth0MultiTenancy.js

const { withAndroidManifest } = require("@expo/config-plugins");

/**
 * Adds a custom activity with intent filters to the AndroidManifest.xml.
 * @param {object} androidManifest - The parsed AndroidManifest.xml object.
 * @param {Array} filters - Array of objects containing host, pathPrefix, and scheme.
 * @returns {object} - The modified AndroidManifest.xml object.
 */
const addCustomActivity = (androidManifest, filters) => {
  const appNode = androidManifest.manifest.application[0];

  // Define the new activity with the provided filters
  const newActivity = {
    $: {
      "android:name": "com.auth0.react.RedirectActivity",
      "android:exported": "true",
      "tools:node": "replace",
    },
    "intent-filter": [
      {
        action: [
          {
            $: {
              "android:name": "android.intent.action.VIEW",
            },
          },
        ],
        category: [
          {
            $: {
              "android:name": "android.intent.category.DEFAULT",
            },
          },
          {
            $: {
              "android:name": "android.intent.category.BROWSABLE",
            },
          },
        ],
        data: filters.map((filter) => ({
          $: {
            "android:host": filter.host,
            "android:pathPrefix": filter.pathPrefix,
            "android:scheme": filter.scheme,
          },
        })),
      },
    ],
  };

  // Initialize activities array if it doesn't exist
  if (!appNode["activity"]) {
    appNode["activity"] = [];
  }

  // Find index of existing Auth0 redirect activity
  const existingActivityIndex = appNode["activity"].findIndex(
    (activity) => activity.$?.["android:name"] === "com.auth0.react.RedirectActivity"
  );

  // Replace if exists, otherwise add
  if (existingActivityIndex !== -1) {
    appNode["activity"][existingActivityIndex] = newActivity;
  } else {
    appNode["activity"].push(newActivity);
  }

  return androidManifest;
};

/**
 * Custom Expo Config Plugin to add an Auth0 Multi-Tenant Redirect Activity.
 * @param {object} config - The Expo configuration object.
 * @param {Array} filters - Array of objects containing host, pathPrefix, and scheme.
 * @returns {object} - The modified Expo configuration object.
 */
const withAuth0MultiTenancy = (config, { filters = [] }) => {
  return withAndroidManifest(config, (config) => {
    config.modResults = addCustomActivity(config.modResults, filters);
    return config;
  });
};

module.exports = withAuth0MultiTenancy;

@caolong0204
Copy link

For anyone else coming across this. I've created an expo plugin so you don't need to go full bare-workflow. You can just use expo prebuild

Usage: If you have an app.json, change that over to an app.config.js

const withAuth0MultiTenancy = require("./withAuth0MultiTenancy");

module.exports = {
expo: {
name: "YourAppName",
slug: "your-app-slug",
android: {
package: "com.example.app", // Replace with your app's package name
},
plugins: [
[
withAuth0MultiTenancy,
{
flters: [
{
host: "host1.com",
pathPrefix: "/android/${applicationId}/callback",
scheme: "mysheme",
},
{
host: "host2.com",
pathPrefix: "/android/${applicationId}/callback",
scheme: "myscheme",
},
]
},
],
],
},
};
withAuth0MultiTenancy.js

const { withAndroidManifest } = require("@expo/config-plugins");

/**

  • Adds a custom activity with intent filters to the AndroidManifest.xml.
  • @param {object} androidManifest - The parsed AndroidManifest.xml object.
  • @param {Array} filters - Array of objects containing host, pathPrefix, and scheme.
  • @returns {object} - The modified AndroidManifest.xml object.
    */
    const addCustomActivity = (androidManifest, filters) => {
    const appNode = androidManifest.manifest.application[0];

// Define the new activity with the provided filters
const newActivity = {
$: {
"android:name": "com.auth0.react.RedirectActivity",
"android:exported": "true",
"tools:node": "replace",
},
"intent-filter": [
{
action: [
{
$: {
"android:name": "android.intent.action.VIEW",
},
},
],
category: [
{
$: {
"android:name": "android.intent.category.DEFAULT",
},
},
{
$: {
"android:name": "android.intent.category.BROWSABLE",
},
},
],
data: filters.map((filter) => ({
$: {
"android:host": filter.host,
"android:pathPrefix": filter.pathPrefix,
"android:scheme": filter.scheme,
},
})),
},
],
};

// Initialize activities array if it doesn't exist
if (!appNode["activity"]) {
appNode["activity"] = [];
}

// Find index of existing Auth0 redirect activity
const existingActivityIndex = appNode["activity"].findIndex(
(activity) => activity.$?.["android:name"] === "com.auth0.react.RedirectActivity"
);

// Replace if exists, otherwise add
if (existingActivityIndex !== -1) {
appNode["activity"][existingActivityIndex] = newActivity;
} else {
appNode["activity"].push(newActivity);
}

return androidManifest;
};

/**

  • Custom Expo Config Plugin to add an Auth0 Multi-Tenant Redirect Activity.
  • @param {object} config - The Expo configuration object.
  • @param {Array} filters - Array of objects containing host, pathPrefix, and scheme.
  • @returns {object} - The modified Expo configuration object.
    */
    const withAuth0MultiTenancy = (config, { filters = [] }) => {
    return withAndroidManifest(config, (config) => {
    config.modResults = addCustomActivity(config.modResults, filters);
    return config;
    });
    };

module.exports = withAuth0MultiTenancy;

How can I use your approach in bare RN project

@adammcarth
Copy link

@caolong0204 @jnjdev42 Supporting multiple tenants has been made possible again (in a barebones RN project) if you are able to upgrade your Auth0 SDK to Version 4. iOS requires no changes after following the setup instructions (since you no longer need to add your domain config in XCode), and you can refer to my answer here to get Android working: #747 (comment)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question waiting for customer This issue is waiting for a response from the issue or PR author
Projects
None yet
Development

No branches or pull requests