Custom Database Action Script Execution Best Practices

A custom database connection type allows you to configure action scripts, which contain custom code Auth0 uses to interface with your legacy identity store. Action scripts are a named JavaScript function that accepts a predefined set of parameters.

Webtask containers

Action scripts execute within an individual Webtask container with an execution limit of approximately 20 seconds. After the functions execute, the container is recycled.

When a container is recycled, it terminates the action script’s pending operations. This may result in an error condition being returned and a potential reset of the global object. For more information on the global object, read Custom Database Action Script Environment Best Practices.

Asynchronous features

Asynchronous features of JavaScript, including Promise objects and async functions, are supported in action scripts.

You can use asynchronous features to execute non-blocking operations within an action script, but make sure that these operations do not exceed the execution limit of the Webtask container. When the Webtask container is recycled, it will terminate any pending operations, which may result in unexpected behavior or an error.

If you are making a call to an external service or API within your action script, set the function to time out after a reasonable duration, and return an error if the external service or API cannot be reached.

Examples

Auth0 supports using Promise objects and async functions within action scripts.

Promise object

This example uses the built-in JavaScript fetch method, which gets a resource from a network then returns a Promise object, written using promise chains.

function login(userNameOrEmail, password, callback) {
	const hashedPassword = hash(password);
	const apiEndpoint = 'https://example.com/api/authenticate';
	const options = {
		method: 'POST',
		body: {
			email: userNameOrEmail,
			password: hashedPassword
		}
	};

	fetch(apiEndpoint, options) 
		.then((response) => {
			if (!response.ok) {
				return callback(new Error(`HTTP error! Status: ${response.status}`));
			}

			return response.json();
		})
		.then((response) => {
			if (response.err) {
				return callback(new Error(`Error authenticating user: ${err}`));
			}

			let profile = {
				email: response.profileData.email,
				username: response.profileData.username
			};

			return callback(null, profile);
		})
		.catch((err) => {
			return callback(new Error(`An error occurred: ${err}`));
		});
  }

Was this helpful?

/

Async function

This example uses the built-in JavaScript fetch method, which gets a resource from a network then returns a Promise object, written using async functions.

async function login(userNameOrEmail, password, callback) {
	const hashedPassword = hash(password);
	const apiEndpoint = 'https://example.com/api/authenticate';
	const options = {
		method: 'POST',
		body: {
			email: userNameOrEmail,
			password: hashedPassword
		}
	};

	const response = await fetch(apiEndpoint, options);

	if (!response.ok) {
		return callback(new Error(`HTTP error! Status: ${response.status}`));
	}

	const result = response.json();

	if (result.err) {
		return callback(new Error(`Error authenticating user: ${err}`));
	}

	let profile = {
		email: response.profileData.email,
		username: response.profileData.username
	};

	return callback(null, profile);
}

Was this helpful?

/

Callback function

The callback function signals the action script’s operation is complete and must be called exactly once. An action script should complete immediately after a call to the callback function, preferably by explicitly using the return statement.

Asynchronous processing

If an action script uses asynchronous processing, then the callback function must be called after all asynchronous operations complete.

Parameters

If the callback function is called with no parameters, it will be executed as if a null parameter had been provided.

Size

The total size of implementation for any action script must not exceed 100kB. This size limitation excludes imported npm modules. For more information on npm modules, read Custom Database Action Script Environment Best Practices.

The larger the size of a script, the more latency is introduced based on the packaging and transport process employed by the Webtask platform. The size impacts the performance of the system.

Anonymous functions

Action scripts can be implemented as anonymous functions, but it is not recommended that you do so. Anonymous functions make it difficult to debug the action script and interpret the call-stack generated as a result of any exceptional error condition. To learn more about anonymous functions, read IIFE on MDN Web Docs.

Error handling

Pass an Error object to the callback function with a descriptive message of the error:

return callback(new Error('My custom error message'));

Was this helpful?

/

Security

Database interface vs. API

Ensure to secure all communications between Auth0 and your legacy identity store. If your legacy identity store does not already have an API implemented, it is highly recommended that you do so.

If your legacy identity store has an API available, you can register the API through Auth0, and create an Action to restrict access from end users.

If your legacy identity store does not have an API available—and implementing one is not feasible—you can still communicate with your database directly. Make sure to add Auth0 IP addresses to your firewall’s allow list to allow inbound traffic from Auth0.

Identity provider tokens

If the user object returns the access_token and refresh_token properties, Auth0 handles them differently from other types of user information. Auth0 stores them in the user object's identities property:

{
    "email": "you@example.com",
    "updated_at": "2019-03-15T15:56:44.577Z",
    "user_id": "auth0|some_unique_id",
    "nickname": "a_nick_name",
    "identities": [ 
        {
            "user_id": "some_unique_id",
            "access_token": "e1b5.................92ba",
            "refresh_token": "a90c.................620b",
            "provider": "auth0", "connection": "custom_db_name",
            "isSocial": false 
        }
  ], 
  "created_at": "2019-03-15T15:56:44.577Z",
  "last_ip": "192.168.1.1",
  "last_login": "2019-03-15T15:56:44.576Z",
  "logins_count": 3
}

Was this helpful?

/

If you want to retrieve either of these properties with the Auth0 Management API, include the read:user_idp_tokens scope when requesting an Access Token.