Rules Security Best Practices

Always use HTTPS

Always use HTTPS, not HTTP, when making calls to external services or when executing redirect as part of your rule implementation.

Store security sensitive values in rule Settings

Security-sensitive information, such as credentials or API keys, should be stored in your rule settings where they'll be obfuscated, encrypted, and available via the configuration object. Do not store these values as literals in your rules code. For example, do not write code like this:

const myApiKey = 'abc123';

Instead, prefer to store (secret) information so that it's accessible via the configuration object:

const myApiKey = configuration.myApiKey;

Do not send entire context object to external services

For rules that send information to an external service, make sure you are not sending the entire context object, since this object may contain tokens or other sensitive data. For rules that send information to external services, you should only send a subset of the less sensitive attributes from the context object when and where necessary.

In a similar fashion, avoid passing any aspect of the auth0 object outside of a rule.

Check if an email is verified

You can check to see if a user's email address is verified in a rule:

    function (user, context, callback) {
      // Access should only be granted to verified users.
      if (!user.email || !user.email_verified) {
    return callback(new UnauthorizedError('Access denied.'));
      }
    	  .
    	  .
    }

However, if you want to execute different code depending on the company the user belongs to, you should not rely on the email domain, but instead, on data that can link the user to the identity provider that they authenticated with (the connection or identity-provider-specific fields such as Azure’s tenant id).

Check for exact string matches

For rules that determine access control based on a particular string, such as an email domain, check for an exact string match instead of checking for a substring match. If you check only for a substring, your rule may not function as you intend. For example, in:

if( _.findIndex(connection.options.domain_aliases, function(d){ return user.email.indexOf(d) >= 0;

the code (above) would return true given emails such as:

  • user.domain.com@not-domain.com

  • "user@domain.com"@not-domain.com (quotes included)

which may not be as desired. Instead, prefer to perform exact matches using code such as:

const emailSplit = user.email.split('@'); const userEmailDomain = emailSplit[emailSplit.length - 1].toLowerCase();

To learn more, see the Check if user email domain matches configured domain rule template on GitHub, or navigate to Auth0 Dashboard > Auth Pipeline > Rules, and select Create.

Contextual bypass for Multi-Factor Authentication

Multi-Factor Authentication (MFA) provides an additional layer of security in order to guard against unauthorized access. From a user experience perspective, this typically requires additional user interaction to provide a second authentication factor—i.e., typically presenting some additional credential(s) or authorizing some form of access request.

There are situations, though, when it may be desirable to bypass MFA for a user who has been designated as requiring multi-factor authentication. For instance, it maybe desirable to bypass MFA if a user has already presented both primary and secondary factors as part of authentication in the current browser context. Contextual checking in this way can help improve the user experience. However, if not done properly, it can open up serious security loop-holes which could lead to subsequent security breaches due to MFA being skipped. We therefore recommend that you observe the following guidance when considering whether to employ contextual bypass of MFA or not.

As a recommended best practice, use of allowRememberBrowser or context.authentication should be the only options considered for contextual bypass when using out-of-box MFA. Setting allowRememberBrowser to true lets users check a box so they will only be prompted for MFA periodically, whereas context.authentication can be used safely and accurately to determine when MFA was last performed in the current browser context; you can see some sample use of context.authentication in the out-of-box supplied rule, Require MFA once per session.

  • Do not perform MFA bypass based on conditional logic related to silent authentication (e.g., context.request.query.prompt === 'none')

  • Do not perform MFA bypass based on conditional logic using some form of device fingerprinting (e.g., where user.app_metadata.lastLoginDeviceFingerPrint === deviceFingerPrint)

  • Do not perform MFA bypass based on conditional logic using geographic location (e.g., where user.app_metadata.last_location === context.request.geoip.country_code)

Context checking when using custom MFA providers

In a similar fashion to that already discussed, we recommend following guidance provided in the items listed above for any rules that redirect users to custom multi-factor authentication providers. For example, for custom providers, there's no safe way to effectively bypass MFA during silent authentication because redirection (required for custom MFA) will always fail in silent authentication situations.

Learn more