Rule Syntax

A Rule is a function with the following arguments:

Due to the asynchronous nature of Node.js, you must call the callback function. If not, this acts as a blocker and the next rule will not execute. The entire rules sequence must complete within 20 seconds, otherwise the process times out.

Global Functions

If you need to use a function in multiple Rules, you can place the shared function in its own Rule. Within this rule, assign the function to the global object so that you can later call the function in a separate rule.

if (!global.foo) {
  global.foo = function () { };
}

if (!global.bar) {
  global.bar = function (baz) { };
}

Rules containing shared functions should be placed at the top of the Rules list in the Management Dashboard. If this is not the case, calling these functions results in an undefined function error when the Rules execute.

Examples

To create a Rule, or try the examples below, go to New Rule in the Rule Editor on the dashboard.

Hello World

This rule will add a hello attribute to all users authenticating through any provider.

function (user, context, callback) {
  user.hello = 'world';
  console.log('===> set "hello" for ' + user.name);
  callback(null, user, context);
}

You can add console.log lines for debugging or use the Real-time Webtask Logs Extension.

Add roles to a user

In this example, all authenticated users will get a guest role, but johnfoo@gmail.com will also be an admin:

function (user, context, callback) {
  user.roles = [];
  // only johnfoo is admin
  if (user.email === 'johnfoo@gmail.com') {
    user.roles.push('admin');
  }

  // all users are guest
  user.roles.push('guest');

  callback(null, user, context);
}

At the beginning of the rules pipeline, John's user object will be:

{
  "email": "johnfoo@gmail.com",
  "family_name": "Foo",
  "user_id": "google-oauth2|103547991597142817347"
  //... other properties ...
}

The context object will be:

{
  "clientID": "...client_id_of_the_app...",
  "clientName": "my app",
  "clientMetadata": {
    "myKey1": "myValue2",
    "myKey2": "myValue2"
  }
  "connection": "google-oauth2"
}

After the rule executes, the output that the application will receive is the following user object:

{
  "email": "johnfoo@gmail.com",
  "family_name": "Foo",
  "user_id": "google-oauth2|103547991597142817347",

  //... other props ...

  "roles": ["guest", "admin"]  // NEW PROPERTY ADDED BY THE RULE
}

Properties added in a rule are not persisted in the Auth0 user store. Persisting properties requires calling the Auth0 Management API.

Deny access based on a condition

In addition to adding and removing properties from the user object, you can return an access denied error.

function (user, context, callback) {
  if (user.roles.indexOf('admin') === -1) {
    return callback(new UnauthorizedError('Only admins can use this'));
  }

  callback(null, user, context);
}

This will cause a redirect to your callback URL with an error querystring parameter containing the message you set. (such as https://yourapp.com/callback?error=unauthorized&error_description=Only%20admins%20can%20use%20this). Make sure to call the callback with an instance of UnauthorizedError (not Error).

Error reporting to the app depends on the protocol. OpenID Connect (OIDC) apps will receive the error in the querystring. SAML apps will receive the error in a SAMLResponse.

Create Rules with the Management API

Rules can also be created by creating a POST request to /api/v2/rules using the Management APIv2.

This will creates a new rule according to the following input arguments:

Example of a body schema:

{
  "name": "my-rule",
  "script": "function (user, context, callback) {\n  callback(null, user, context);\n}",
  "order": 2,
  "enabled": true
}

Use this to create the POST request:


curl --request POST \
  --url 'https://YOUR_DOMAIN/api/v2/rules' \
  --header 'content-type: application/json' \
  --data '{"name":"my-rule","script":"function (user, context, callback) {callback(null, user, context);}","order":2,"enabled":true}'
var client = new RestClient("https://YOUR_DOMAIN/api/v2/rules");
var request = new RestRequest(Method.POST);
request.AddHeader("content-type", "application/json");
request.AddParameter("application/json", "{\"name\":\"my-rule\",\"script\":\"function (user, context, callback) {callback(null, user, context);}\",\"order\":2,\"enabled\":true}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
package main

import (
	"fmt"
	"strings"
	"net/http"
	"io/ioutil"
)

func main() {

	url := "https://YOUR_DOMAIN/api/v2/rules"

	payload := strings.NewReader("{\"name\":\"my-rule\",\"script\":\"function (user, context, callback) {callback(null, user, context);}\",\"order\":2,\"enabled\":true}")

	req, _ := http.NewRequest("POST", url, payload)

	req.Header.Add("content-type", "application/json")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := ioutil.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
HttpResponse<String> response = Unirest.post("https://YOUR_DOMAIN/api/v2/rules")
  .header("content-type", "application/json")
  .body("{\"name\":\"my-rule\",\"script\":\"function (user, context, callback) {callback(null, user, context);}\",\"order\":2,\"enabled\":true}")
  .asString();
var request = require("request");

var options = {
  method: 'POST',
  url: 'https://YOUR_DOMAIN/api/v2/rules',
  headers: {'content-type': 'application/json'},
  body: {
    name: 'my-rule',
    script: 'function (user, context, callback) {callback(null, user, context);}',
    order: 2,
    enabled: true
  },
  json: true
};

request(options, function (error, response, body) {
  if (error) throw new Error(error);

  console.log(body);
});
#import <Foundation/Foundation.h>

NSDictionary *headers = @{ @"content-type": @"application/json" };
NSDictionary *parameters = @{ @"name": @"my-rule",
                              @"script": @"function (user, context, callback) {callback(null, user, context);}",
                              @"order": @2,
                              @"enabled": @YES };

NSData *postData = [NSJSONSerialization dataWithJSONObject:parameters options:0 error:nil];

NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"https://YOUR_DOMAIN/api/v2/rules"]
                                                       cachePolicy:NSURLRequestUseProtocolCachePolicy
                                                   timeoutInterval:10.0];
[request setHTTPMethod:@"POST"];
[request setAllHTTPHeaderFields:headers];
[request setHTTPBody:postData];

NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request
                                            completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
                                                if (error) {
                                                    NSLog(@"%@", error);
                                                } else {
                                                    NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;
                                                    NSLog(@"%@", httpResponse);
                                                }
                                            }];
[dataTask resume];
$curl = curl_init();

curl_setopt_array($curl, array(
  CURLOPT_URL => "https://YOUR_DOMAIN/api/v2/rules",
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_ENCODING => "",
  CURLOPT_MAXREDIRS => 10,
  CURLOPT_TIMEOUT => 30,
  CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  CURLOPT_CUSTOMREQUEST => "POST",
  CURLOPT_POSTFIELDS => "{\"name\":\"my-rule\",\"script\":\"function (user, context, callback) {callback(null, user, context);}\",\"order\":2,\"enabled\":true}",
  CURLOPT_HTTPHEADER => array(
    "content-type: application/json"
  ),
));

$response = curl_exec($curl);
$err = curl_error($curl);

curl_close($curl);

if ($err) {
  echo "cURL Error #:" . $err;
} else {
  echo $response;
}
import http.client

conn = http.client.HTTPSConnection("")

payload = "{\"name\":\"my-rule\",\"script\":\"function (user, context, callback) {callback(null, user, context);}\",\"order\":2,\"enabled\":true}"

headers = { 'content-type': "application/json" }

conn.request("POST", "/YOUR_DOMAIN/api/v2/rules", payload, headers)

res = conn.getresponse()
data = res.read()

print(data.decode("utf-8"))
require 'uri'
require 'net/http'
require 'openssl'

url = URI("https://YOUR_DOMAIN/api/v2/rules")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE

request = Net::HTTP::Post.new(url)
request["content-type"] = 'application/json'
request.body = "{\"name\":\"my-rule\",\"script\":\"function (user, context, callback) {callback(null, user, context);}\",\"order\":2,\"enabled\":true}"

response = http.request(request)
puts response.read_body
import Foundation

let headers = ["content-type": "application/json"]
let parameters = [
  "name": "my-rule",
  "script": "function (user, context, callback) {callback(null, user, context);}",
  "order": 2,
  "enabled": true
] as [String : Any]

let postData = JSONSerialization.data(withJSONObject: parameters, options: [])

let request = NSMutableURLRequest(url: NSURL(string: "https://YOUR_DOMAIN/api/v2/rules")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()

How to Debug Rules

You can add console.log lines in the rule's code for debugging. The Rule Editor provides two ways for seeing the output:

Rules Editor

  1. TRY THIS RULE: opens a pop-up where you can run a rule in isolation. The tool provides a mock user and context objects. Clicking TRY will result on the Rule being run with those two objects as input. console.log output will be displayed too.

Try this Rule

  1. REALTIME LOGS: an extension that displays all logs in real-time for all custom code in your account. This includes all console.log output, and exceptions.

  2. DEBUG RULE: similar to the above, displays instructions for installing, configuring and running the webtask CLI for debugging rules. Paste these commands into a terminal to see the console.log output and any unhandled exceptions that occur during Rule execution.

Only tenants created prior to 17 July 2018 have access to Webtask.io and the Webtask CLI. If you are an enterprise customer with a newer tenant, please contact your account representative to request access. Other requests can be made through the Auth0 Contact Form and will be evaluated on a case-by-case basis.

For example:

~  npm install -g wt-cli
~  wt init --container "youraccount" --url "https://sandbox.it.auth0.com" --token "eyJhbGci...WMPGI" -p "youraccount-default-logs"
~  wt logs -p "youraccount-default-logs"
[18:45:38.179Z]  INFO wt: connected to streaming logs (container=youraccount)
[18:47:37.954Z]  INFO wt: webtask container assigned
[18:47:38.167Z]  INFO wt: ---- checking email_verified for some-user@mail.com! ----

This debugging method works for rules tried from the dashboard and those actually running during user authentication.

Cache expensive resources

The code sandbox Rules run on allows storing expensive resources that will survive individual execution.

This example, shows how to use the global object to keep a mongodb connection:


  ...

  //If the db object is there, use it.
  if (global.db){
    return query(global.db, callback);
  }

  //If not, get the db (mongodb in this case)
  mongo('mongodb://user:pass@mymongoserver.com/my-db',  function (db){
    global.db = db;
    return query(db, callback);
  });

  //Do the actual work
  function query(db, cb){
    //Do something with db
    ...
  });

  ...

Notice that the code sandbox in which Rules run on, can be recycled at any time. So your code must always check global to contain what you expect.

Available modules

For security reasons, the Rules code runs in a JavaScript sandbox where you can use the full power of the ECMAScript 5 language.

For a list of currently supported sandbox modules, see: Modules Supported by the Sandbox.

Read more

Was this article helpful?
Any suggestion or typo? Edit on GitHub