How to overwrite auth0 provider method found in `getInitialProviders()` - @strapi/plugin-users-permissions

System Information
  • Strapi Version: 4.3.0-beta.2
  • Operating System: OSX, Linux
  • Database: PostgreSQL
  • Node Version: v16.13.1
  • NPM Version: v8.1.2
  • Yarn Version: —

I want to replace the auth0 provider method found in getInitialProviders() in node_modules/@strapi/plugin-users-permissions/server/services/providers-registry.js. Basically, I want to access all the properties returned by Auth0, not the tiny subset currently returned. Here is an example of my ‘alternative method’ that simply returns a _raw property with all of these data provided by Auth0.

  async auth0({ access_token, providers }) {
    const auth0 = purest({ provider: 'auth0' });

    return auth0
      .get('userinfo')
      .subdomain(providers.auth0.subdomain)
      .auth(access_token)
      .request()
      .then(({ body }) => {
        const username = body.username || body.nickname || body.name || body.email.split('@')[0];
        const email = body.email || `${username.replace(/\s+/g, '.')}@strapi.io`;

        return {
          _raw: { ...body },
          username,
          email,
        };
      });
  },

Is this something I need to put in my src/extensions/users-permissions/strapi-server.ts?

Solved: :white_check_mark:

After revisiting the docs and the code I sorted out that I can just overwrite the auth0 provider in the project register() function.

I sorted out the service I needed to request via the CLI:

npm run strapi -- services:list 

Then I register a replacement for the auth0 provider with my own version.

import type { Strapi } from '@strapi/strapi';

export default {
  /**
   * An asynchronous register function that runs before
   * your application is initialized.
   *
   * This gives you an opportunity to extend code.
   */
  register( { strapi }: { strapi: Strapi }) {
    strapi
      .service('plugin::users-permissions.providers-registry')
      .register(`auth0`, ({ purest }) => ({ access_token, providers }) => {
        console.log(`My Auth0`)
        const auth0 = purest({ provider: 'auth0' });

        return auth0
          .get('userinfo')
          .subdomain(providers.auth0.subdomain)
          .auth(access_token)
          .request()
          .then(({ body }) => {
            const username = body.username || body.nickname || body.name || body.email.split('@')[0];
            const email = body.email || `${username.replace(/\s+/g, '.')}@strapi.io`;

            return {
              _raw: {...body},
              username,
              email,
            };
          });
      })
  },
};

… now I just have to try to improve the types and write a Unit Test… :wink:

3 Likes

That’s a really useful approach to know, thanks for taking the time to post it. I’ve been digging into the docs/source because I needed to customize the providers service to prevent duplicated usernames (when a local account exists say with username “john” and someone comes in from another provider, you can end up with duplicated usernames [known issue]). The username is also case insensitive so username Jon would still be allowed if a username jon was present via the admin panel.

The providers-registry generates/obtains the username for the various providers. The providers service then deals with creating the strapi user if required. I ended up doing it in strapi-server.js in the /extensions/users-permissions/strapi-server.js

module.exports = (plugin) => {
  plugin.services.providers = require('../server/services/providers')
  return plugin;
};

Where providers is the strapi implementation from the node folder copied across and customized as required.

Interestingly I’ve just also noticed strapi will duplicate the email address (create two accounts with the same) if you have a local account already existing say jon@some_provider.com and you then log in with same provider for example, strapi will then still provision another account with the same email address. (but obviously with a differing provider stored - despite advanced settings set to force one account per email address), so that’s something else I’ll need to address for my use case. It’s a problem where people create an account locally (using a provider type address gmail for example) then later log in directly with a provider.

1 Like

Thanks @haysclark

I do the same for Twitter by modifying your code as it uses OAuth 2.0 only for new app registrations and Strapi’s registry for Twitter does not support it.

Thanks for this method, I had to use this approach with providers-registry.js too because the one described by @haysclark didn’t work for me (it logged the console.log('My Auth0') meaning it was reaching this part, but then it gave me an error). It’s a pity since I liked this aproach more.

If using this approach, the strapi logs showed:

/api/connect/auth0/callback?code=XXX (863 ms) 302

And if using the overwriting the providers-registry.js service approach, the logs look like this:

/api/auth/auth0/callback?id_token=ID_TOKEN&access_token=ACCESS_TOKEN&raw%5Baccess_token%5D=ACCESS_TOKEN&raw%5Bid_token%5D=ID_TOKEN&raw%5Bscope%5D=openid%20profile%20email&raw%5Bexpires_in%5D=86400&raw%5Btoken_type%5D=Bearer (824 ms) 200

I am using latest strapi version v4.11.1.