> ## Documentation Index
> Fetch the complete documentation index at: https://docs-dev-docs-ia-custom-database-connections.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Best Practices for Database Action Scripts

> View our recommendations for performance and reliability when using custom database action scripts.

## Secure access to your external user store

When you use a custom database connection, you need to provide an interface to your user store so Auth0 can connect to it.

If you do this by making your user store broadly accessible via the internet, you incur significant risks. For example, interfaces for SQL and other databases expose significant functionality, and making them generally available violates the [principle of least privilege](https://en.wikipedia.org/wiki/Principle_of_least_privilege).

### Provide access with a protected API

We recommend providing access with an API that has discrete number of protected endpoints to perform only the user management functionality required for the custom database connection, like read user and change password.

Protecting this API with use of an <Tooltip tip="Access Token: Authorization credential, in the form of an opaque string or JWT, used to access an API." cta="View Glossary" href="/docs/glossary?term=access+token">access token</Tooltip> allows you to use the client credentials grant flow from within an action script. You can subsequently cache the token for re-use within the `global` object to improve performance.

If your external user store has an API available, or if you implement one yourself, you can [register the API](/docs/get-started/auth0-overview/set-up-apis) through Auth0, and [create an Action](/docs/manage-users/access-control/sample-use-cases-actions-with-authorization#deny-access-to-anyone-calling-an-api) to restrict access from end users.

By default, Auth0 can give you a token for any API if you authenticate successfully and include the appropriate <Tooltip tip="Audience: Unique identifier of the audience for an issued token. Named aud in a token, its value contains the ID of either an application (Client ID) for an ID Token or an API (API Identifier) for an Access Token." cta="View Glossary" href="/docs/glossary?term=audience">audience</Tooltip>. Restricting access to your user store's API by restricting access token allocation prevents unauthorized usage by only granting access using specific client credentials. This mitigates a number of attack vector scenarios, such as a bad actor intercepting redirects to `/authorize` and adding the audience to the API.

If your external user store does not have an API available and implementing one is not feasible, you can still write your action scripts to [communicate with it directly](#restrict-network-access).

### Restrict network access

We recommend restricting access to your external user store with an IP allowlist including [Auth0's outbound IP addresses](/docs/secure/security-guidance/data-security/allowlist) to allow inbound traffic from Auth0.

Because Auth0's outbound IP addresses are shared between all Auth0 tenants in the given region, we recommend against using such an allowlist as the sole method of securing access to your user store. Instead, use an allowlist as one of several security measures.

## Time out async and external calls

If you call 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.

<AccordionGroup>
  <Accordion title="Promise object example">
    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](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises#chaining).

    ```javascript lines expandable theme={null}
    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}`));
    		});
      }
    ```
  </Accordion>

  <Accordion title="Async function example">
    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](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function).

    ```js lines expandable theme={null}
    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);
    }
    ```
  </Accordion>
</AccordionGroup>

You can return errors from custom database action scripts by passing them to [the `callback` function](/docs/authenticate/database-connections/custom-db/custom-database-connections-scripts#callback). We recommend using descriptive error messages to help with troubleshooting and debugging.

## Avoid anonymous functions

You can implement action scripts as [anonymous functions](https://developer.mozilla.org/en-US/docs/Glossary/IIFE), but we recommend using function names because anonymous functions can make it difficult to interpret the call stack when debugging [error conditions](./error-handling).

## Retrieve 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:

```json lines theme={null}
{
	"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
}
```

To retrieve either of these properties with the Auth0 <Tooltip tip="Management API: A product to allow customers to perform administrative tasks." cta="View Glossary" href="/docs/glossary?term=Management+API">Management API</Tooltip>, include the `read:user_idp_tokens` scope when [requesting an Access Token](/docs/secure/tokens/access-tokens/management-api-access-tokens/get-management-api-access-tokens-for-production).
