Rules
Rules are functions written in JavaScript that are executed in Auth0 as part of the transaction every time a user authenticates to your application. Rules allow you to easily customize and extend Auth0's capabilities. Rules can be chained together for modular coding and can be turned on and off individually.
- Step 1: An app initiates an authentication request to Auth0.
- Step 2: Auth0 routes the request to an Identity Provider through a configured connection.
- Step 3: The user authenticates successfully.
- Step 4: The
user
object representing the logged in user is passed through the Rules pipeline, and returned to the app.
Among many possibilities, Rules can be used to:
- Profile enrichment: query for information on the user from a database/API, and add it to the user profile object.
- Create authorization rules based on complex logic (anything that can be written in JavaScript).
- Normalize attributes from different providers beyond what is provided by Auth0.
- Reuse information from existing databases or APIs for migration scenarios.
- Keep a white-list of users and deny access based on email.
- Notify other systems through an API when a login happens in real-time.
- Enable counters or persist other information. (For information on storing user data, see: Metadata in Rules.)
- Enable multifactor authentication, based on context (e.g. last login, IP address of the user, location, etc.).
NOTE: You can find more examples of common Rules on Github at auth0/rules.
Video: Using Rules
Watch this video learn all about rules in just a few minutes.
Rule Syntax
A Rule is a function with the following arguments:
-
user
: the user object as it comes from the identity provider (For a complete list of the user properties, see: User Profile Structure). -
context
: an object containing contextual information of the current authentication transaction, such as user's IP address, application, location. (A complete list of context properties is available here: Context Argument Properties in Rules.) -
callback
: a function to send back the potentially modifieduser
andcontext
objects back to Auth0 (or an error).
NOTE: Because of the async nature of node.js, it is important to always call the callback
function, or else the script will timeout.
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);
}
NOTE: 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. (e.g.: 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 apps will receive the error in the querystring. SAML apps will receive the error in a
SAMLResponse
.
Creating a new Rule using 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 JSON object received in body, which contains:
name: A string
value, this field is the name of the rule. Can only contain alphanumeric characters, spaces and '-'. Can neither start nor end with '-' or spaces.
script: A string
value this is the script that contains the rule's code, as seen in some of the examples on this page. This is the same as what you would enter when creating a new rule using the dashboard.
order: This field is optional and contains a number
. This number represents the rule's order in relation to other rules. A rule with a lower order than another rule executes first. If no order is provided it will automatically be one greater than the current maximum.
enabled: This field can contain an optional boolean
. If true if the rule will be turned on, false otherwise.
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_AUTH0_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_AUTH0_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_AUTH0_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_AUTH0_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 settings = {
"async": true,
"crossDomain": true,
"url": "https://YOUR_AUTH0_DOMAIN/api/v2/rules",
"method": "POST",
"headers": {
"content-type": "application/json"
},
"processData": false,
"data": "{\"name\":\"my-rule\",\"script\":\"function (user, context, callback) {callback(null, user, context);}\",\"order\":2,\"enabled\":true}"
}
$.ajax(settings).done(function (response) {
console.log(response);
});
var request = require("request");
var options = { method: 'POST',
url: 'https://YOUR_AUTH0_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_AUTH0_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_AUTH0_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_AUTH0_DOMAIN/api/v2/rules", payload, headers)
res = conn.getresponse()
data = res.read()
print(data.decode("utf-8"))
require 'uri'
require 'net/http'
url = URI("https://YOUR_AUTH0_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
]
let postData = NSJSONSerialization.dataWithJSONObject(parameters, options: nil, error: nil)
var request = NSMutableURLRequest(URL: NSURL(string: "https://YOUR_AUTH0_DOMAIN/api/v2/rules")!,
cachePolicy: .UseProtocolCachePolicy,
timeoutInterval: 10.0)
request.HTTPMethod = "POST"
request.allHTTPHeaderFields = headers
request.HTTPBody = postData
let session = NSURLSession.sharedSession()
let dataTask = session.dataTaskWithRequest(request, completionHandler: { (data, response, error) -> Void in
if (error != nil) {
println(error)
} else {
let httpResponse = response as? NSHTTPURLResponse
println(httpResponse)
}
})
dataTask.resume()
Debugging
You can add console.log
lines in the rule's code for debugging. The Rule Editor provides two ways for seeing the output:
- 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 the Rule being run with those two objects as input.
console.log
output will be displayed too.
-
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. -
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.
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.
Caching 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 based on webtask.io 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.