diff --git a/main/config/navigation/authenticate.json b/main/config/navigation/authenticate.json index f5baa710fe..cbfa1d7df0 100644 --- a/main/config/navigation/authenticate.json +++ b/main/config/navigation/authenticate.json @@ -281,15 +281,14 @@ "pages": [ { "group": "Custom Database Connections", + "directory": "accordion", "root": "docs/authenticate/database-connections/custom-db", "pages": [ - "docs/authenticate/database-connections/custom-db/overview-custom-db-connections", "docs/authenticate/database-connections/custom-db/create-db-connection", - "docs/authenticate/database-connections/custom-db/test-custom-database-connections", - "docs/authenticate/database-connections/custom-db/error-handling", { - "group": "Action Script Templates", - "root": "docs/authenticate/database-connections/custom-db/templates", + "group": "Custom Database Action Scripts", + "root": "docs/authenticate/database-connections/custom-db/custom-database-connections-scripts", + "expanded": true, "pages": [ "docs/authenticate/database-connections/custom-db/templates/change-password", "docs/authenticate/database-connections/custom-db/templates/create", @@ -297,17 +296,15 @@ "docs/authenticate/database-connections/custom-db/templates/get-user", "docs/authenticate/database-connections/custom-db/templates/login", "docs/authenticate/database-connections/custom-db/templates/verify", - "docs/authenticate/database-connections/custom-db/templates/change-email" + "docs/authenticate/database-connections/custom-db/templates/change-email", + "docs/authenticate/database-connections/custom-db/action-script-best-practices" ] }, { - "group": "Custom Database and Action Script Best Practices", - "root": "docs/authenticate/database-connections/custom-db/custom-database-connections-scripts", + "group": "Troubleshooting", "pages": [ - "docs/authenticate/database-connections/custom-db/custom-database-connections-scripts/anatomy", - "docs/authenticate/database-connections/custom-db/custom-database-connections-scripts/environment", - "docs/authenticate/database-connections/custom-db/custom-database-connections-scripts/execution", - "docs/authenticate/database-connections/custom-db/custom-database-connections-scripts/connection-security" + "docs/authenticate/database-connections/custom-db/test-custom-database-connections", + "docs/authenticate/database-connections/custom-db/error-handling" ] } ] diff --git a/main/config/redirects.json b/main/config/redirects.json index f91fd21a0f..513f4ed669 100644 --- a/main/config/redirects.json +++ b/main/config/redirects.json @@ -1,4 +1,20 @@ [ + { + "source": "/docs/authenticate/database-connections/custom-db/custom-database-connections-scripts/anatomy", + "destination": "/docs/manage-users/user-migration/configure-automatic-migration-from-your-database" + }, + { + "source": "/docs/authenticate/database-connections/custom-db/custom-database-connections-scripts/*", + "destination": "/docs/authenticate/database-connections/custom-db/action-script-best-practices" + }, + { + "source": "/docs/authenticate/database-connections/custom-db/overview-custom-db-connections", + "destination": "/docs/authenticate/database-connections/custom-db" + }, + { + "source": "/docs/authenticate/database-connections/custom-db/templates", + "destination": "/docs/authenticate/database-connections/custom-db/custom-database-connections-scripts" + }, { "source": "/docs/manage-users/user-search/retrieve-users-with-get-users-endpoint", "destination": "/docs/manage-users/user-search/list-and-search-users" diff --git a/main/docs/authenticate/database-connections/custom-db.mdx b/main/docs/authenticate/database-connections/custom-db.mdx index cead2ac0fe..639381b143 100644 --- a/main/docs/authenticate/database-connections/custom-db.mdx +++ b/main/docs/authenticate/database-connections/custom-db.mdx @@ -1,21 +1,27 @@ --- title: Custom Database Connections -description: Learn about authenticating users using your database as an identity provider. +description: Use a custom database connection to connect Auth0 to your external user store for authentication, migration, or multi-tenant architectures. validatedOn: 2026-02-25 --- -Use a custom database connection when you want to provide Auth0 with access to your own independent (legacy) identity data store primarily for authentication (filling the role of an identity provider) and for migrating user data to Auth0's data store. +Custom database connections let you connect Auth0 to an external user store. This supports use cases like: -Auth0 [Extensibility](/docs/customize/extensions) allows you to add custom logic to build out last-mile solutions for Identity and Access Management (IdAM). Auth0 provides extensibility through [Actions](/docs/customize/actions) and [scripts](/docs/authenticate/database-connections/custom-db/templates) for both custom database connections and custom database migration. Each is implemented using [Node.js](https://nodejs.org/en/) running on the Auth0 platform in an Auth0 tenant. +* **Authenticating users using your own external user store** as the identity provider instead of Auth0's user store. -Auth0 extensibility executes at different points in the IAM pipeline: + In this application, after a user's first successful login authenticating with the external user store, Auth0 creates a [user account](/docs/manage-users/user-accounts) but does not store a hash of their passwords and never authenticates users with the Auth0 user store. -* **Actions** are secure, tenant-specific, versioned functions written in Node.js that execute at certain points within the Auth0 platform. Actions are used to customize and extend Auth0’s capabilities with custom logic. To learn more, read [Understand how Auth0 Actions Work](/docs/customize/actions/actions-overview). +* **Incrementally [migrating users](/docs/manage-users/user-migration/configure-automatic-migration-from-your-database) to Auth0** from your external user store (sometimes called *trickle* or *lazy* migration). -* **Custom database action scripts** can be used to integrate with an existing user identity store, or can be used where [automatic user migration](/docs/manage-users/user-migration) from a legacy identity store is required. Various [templates](/docs/authenticate/database-connections/custom-db/templates) are provided. + In this application, when a user tries to log in, Auth0 always tries to authenticate them with the Auth0 user store first. If the user does not exist there, Auth0 tries to authenticate them with the external user store and, if successful, creates the user in the Auth0 user store with the same ID and password used to log in. -Whatever the use case, Auth0 extensibility allows you to tailor IAM operations to your exact requirements. However, if not used in the right way, this can open up the potential for improper or unintended use which can lead to problematic situations down the line. In an attempt to address matters ahead of time, Auth0 provides [best practice guidance](/docs/authenticate/database-connections/custom-db/custom-database-connections-scripts) to both designers and implementers, and we recommend reading it in its entirety at least once, even if you've already started your journey with Auth0. +* **Proxying access to an Auth0 tenant** in [multi-tenant architecture scenarios](/docs/get-started/auth0-overview/create-tenants/multi-tenant-apps-best-practices). - -Your Auth0 plan or custom agreement affects whether this feature is available. To learn more, read [Pricing](https://auth0.com/pricing). - + +For example, as part of the Universal Login workflow, custom database connections obtain user identity information from your external user store: + +![Custom Database Connections Anatomy](/docs/images/cdy7uua7fh8z/2lHqvZKFiEbAXURU2gmchc/626cac94211c266f2135a41456b2e49d/custom-database-connections.png) + + +You control how Auth0 interfaces with your external user store by writing [database action scripts](/docs/authenticate/database-connections/custom-db/custom-database-connections-scripts), which are Node.js functions that Auth0 calls during functionality like logins and password changes. + +You can customize the behavior of database action scripts to suit your specific use case. We provide several templates for each database action script to help you get started. diff --git a/main/docs/authenticate/database-connections/custom-db/action-script-best-practices.mdx b/main/docs/authenticate/database-connections/custom-db/action-script-best-practices.mdx new file mode 100644 index 0000000000..93f07c8edf --- /dev/null +++ b/main/docs/authenticate/database-connections/custom-db/action-script-best-practices.mdx @@ -0,0 +1,149 @@ +--- +title: Best Practices for Database Action Scripts +sidebarTitle: Best Practices +description: 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 access token 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 audience. 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. + + + +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 +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}`)); + }); + } +``` + + + +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 +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); +} +``` + + + +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 +{ + "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 Management API, 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). diff --git a/main/docs/authenticate/database-connections/custom-db/create-db-connection.mdx b/main/docs/authenticate/database-connections/custom-db/create-db-connection.mdx index 126429e7cb..f8df1d2d4c 100644 --- a/main/docs/authenticate/database-connections/custom-db/create-db-connection.mdx +++ b/main/docs/authenticate/database-connections/custom-db/create-db-connection.mdx @@ -1,222 +1,182 @@ --- -description: Learn how to create a database connection. -title: Create Custom Database Connections -validatedOn: 2026-02-25 +title: How to Create Custom Database Connections +sidebarTitle: Create Custom Database Connections +description: Use your own external user store as an IdP in Auth0 by creating a custom database connection. +validatedOn: 2026-07-27 --- -If you have your own user database, you can use it as an identity provider in Auth0 to authenticate users. Follow the process to create the custom database connection, create database action scripts, and add configuration parameters. +If you have your own external user store, you can use it as an identity provider in Auth0 to authenticate users. - +To do so, first you connect Auth0 to your external user store by creating a custom database connection, then you write database action scripts to define how Auth0 interfaces with your user store. We provide database action script templates for several common databases: -Make sure that your database has the appropriate fields to store [user profiles attributes](/docs/manage-users/user-accounts/user-profiles/user-profile-structure#user-profile-attributes), such as `id`, `nickname`, `email`, and `password`. To learn about Auth0's user profile schema, read [Normalized User Profiles](/docs/manage-users/user-accounts/user-profiles/normalized-user-profiles). To learn more about updating user profiles, read [Update User Profiles Using Your Database](/docs/manage-users/user-accounts/user-profiles/update-user-profiles-using-your-database). - - - -Auth0 allows you to create connections and scripts for most of the commonly-used databases, including: - -* ASP.NET Identity * MongoDB * MySQL * PostgreSQL -* SQLServer -* Windows Azure SQL Database -* Web services accessed via Basic Auth - -You can connect to any kind of database or web service with a properly-configured custom script. Note that input sanitization must be completed on the customer end; Auth0 does not sanitize or validate any username/password combination passed by a custom database. Custom Database scripts are subject to Auth0's [Rate Limit Policy](/docs/troubleshoot/customer-support/operational-policies/rate-limit-policy). - - - -If you are behind a firewall, this feature may require that you add the appropriate Auth0 IP addresses to the Allow List to work properly. - - - -## Create the connection in the Auth0 Dashboard - -1. Navigate to [Auth0 Dashboard > Authentication > Database](https://manage.auth0.com/#/connections/database), and select **Create DB Connection**. -2. Configure the connection's settings, and select **Create**: - -| Parameter | Definition | -| --- | --- | -| **Name** | The name of the connection. The name must start and end with an alphanumeric character, contain only alphanumeric characters and dashes, and not exceed 35 characters. | -| **Choose one or more attributes as user identifiers** | Choose the [identifier attribute(s)](/docs/authenticate/database-connections/activate-and-configure-attributes-for-flexible-identifiers) for the connection. | -| **Choose one or more authentication methods** | Choose an authentication method to challenge users. | -| **Use my own database** | Toggle on if you have a database you want to connect with Auth0. | -| **Disable Sign Ups** | Prevents new user signups from public endpoints. | -| **Promote Connection to Domain Level** | Promote this connection to domain level to be used with third-party applications. | - - Once Auth0 creates your connection, you'll have the following views (in addition to the **Settings** view): - * Attributes - * Authentication Methods - * Custom Database - * Applications -3. Select the **Custom Database** view, and enable the **Use my own database** switch. - - ![Enable Custom Database Use My Own Database Option](/docs/images/cdy7uua7fh8z/3kgHDpBFdVWNq9XOfhsTXI/2efa43e44793b8dd69b9f9e8b54bf752/2025-02-25_10-20-20.png) +* Microsoft SQL Server +* ASP.NET Identity +* Web services accessed with Basic Auth -## Create database action scripts +You can modify these templates or write your own entirely to connect to any kind of external user store. -Toggling the **Use my own database** switch enables the **Database Action Scripts** area where you will create scripts to configure how authentication works when using your database. You can write your database action scripts, or you can select a template from the **Templates** dropdown and modifying it as necessary. +## Instructions -You **must** configure a `login` script; additional scripts for user functionality are optional. + + +* Your external user store must be reachable from Auth0's servers. If it is behind a firewall, allow inbound traffic from [Auth0's outbound IP addresses](/docs/secure/security-guidance/data-security/allowlist). -The available database action scripts are: +* Your external user store must have the appropriate fields to store [normalized user profile attributes](/docs/manage-users/user-accounts/user-profiles/normalized-user-profile-schema), such as `id`, `nickname`, `email`, and `password`. -| Name | Description | Parameters | -| --- | --- | --- | -| **Login**
Required | Executes each time a user attempts to log in. | `email`, `password` | -| **Create** | Executes when a user signs up. | `user` | -| **Verify** | Executes after a user follows the verification link. | `email` | -| **Change Password** | Executes when a user clicks on the confirmation link after a reset password request. | `email`, `newPassword` | -| **Get User** | Retrieves a user profile from your database without authenticating the user. | `email` | -| **Delete** | Executes when a user is deleted using the API or Auth0 Dashboard. | `id` | +* The `id` (or `user_id`) value returned from your database action scripts must be unique across all custom database connections because Auth0 uses this value to [identify users](/docs/manage-users/user-accounts/identify-users). -To learn more about the scripts, read [Custom Database Action Script Templates](/docs/authenticate/database-connections/custom-db/templates) and [Custom Database Action Script Execution Best Practices](/docs/authenticate/database-connections/custom-db/custom-database-connections-scripts/execution). + To avoid user ID collisions when using multiple custom database connections, we recommend prefixing `id` values with the connection name (omitting whitespace). +
-### Create a Login script + +From [**Auth0 Dashboard > Authentication > Database**](https://manage.auth0.com/#/connections/database), select **+ Create DB Connection**. On [the **New Database Connection** page](https://manage.auth0.com/#/connections/database/new), enter the following configuration. - +* Enter a name, which must: -The `id` (or alternatively `user_id`) property in the returned user profile will be used by Auth0 to identify the user. + * Contain only alphanumeric characters and dashes. + * Start and end with an alphanumeric character + * Not exceed 35 characters. -If you are using multiple custom database connections, then `id` value must be unique across all the custom database connections to avoid user ID collisions. Our recommendation is to prefix the value of `id` with the connection name (omitting any whitespace). To learn more about user IDs, read [Identify Users](/docs/manage-users/user-accounts/identify-users). +* Choose one or more [attributes](/docs/authenticate/database-connections/activate-and-configure-attributes-for-flexible-identifiers) as user identifiers. - +* Choose one or more authentication methods (like [passwords](/docs/authenticate/database-connections/flexible-password-policy) or [passkeys](/docs/authenticate/database-connections/passkeys)). -The following steps use an example for a MySQL database login script +* Enable the **Use my own database** toggle. -1. After toggling the **Use my own database** switch, the **Database Action Scripts** area is enabled. Make sure you are on the **Login** tab. -2. Use the **Templates** dropdown to select the MySQL database script template. +When you're done, select **Create**. Auth0 creates the database connection and redirects you to its configuration page. + - ```javascript lines expandable - function login(email, password, callback) { - var bcrypt = require('bcrypt'); - var mysql = require('mysql'); - var connection = mysql.createConnection({ - host: 'localhost', - user: 'me', - password: 'secret', - database: 'mydb' - }); - connection.connect(); - var query = "SELECT id, nickname, email, password " + - "FROM users WHERE email = ?"; - connection.query(query, [email], function (err, results) { - if (err) return callback(err); - if (results.length === 0) return callback(new WrongUsernameOrPasswordError(email)); - var user = results[0]; - bcrypt.compare(password, user.password, function (err, isValid) { - if (err) { - callback(err); - } else if (!isValid) { - callback(new WrongUsernameOrPasswordError(email)); - } else { - callback(null, { - // This prefix (replace with your own custom DB name) - // ensure uniqueness across different custom DBs if there's the - // possibility of collisions (e.g. if the user ID is an email address or an integer) - id: 'MyConnection1|' + user.id.toString(), - nickname: user.nickname, - email: user.email - }); - } - }); - }); - } - ``` + +Once you create your custom database connection, you need to write [database action scripts](/docs/authenticate/database-connections/custom-db/custom-database-connections-scripts) to define how Auth0 interfaces with your external user store for functionality like login, sign up, email verification, password resets, and user deletion. - The above script connects to a MySQL database and executes a query to retrieve the first user with `email == user.email`. - With the `bcrypt.compareSync` method, it then validates that the passwords match, and if successful, returns an object containing the user profile information including `id`, `nickname`, and `email`. - This script assumes that you have a `users` table containing these columns. The `id` returned by Login script is used to construct the user ID attribute of the user profile. -3. Select **Save and Try**. -4. In the pop-up modal, enter your email and password and click **Try**. This provides a dropdown to select from a list of available runtimes, with your current runtime on top. +At minimum, you must implement a [Login script](/docs/authenticate/database-connections/custom-db/templates/login): - ![Modal to try the login script.](/docs/images/cdy7uua7fh8z/8uBGZhHYZwOcO19ljJNNp/ccd69d84fdbdff90dc779a6b1ea8a6fd/db-connection-try-login-script.png) +1. From the database connection's page, select the **Custom Database** tab. -Script templates are not used by Auth0 until you select **Save** or **Try**. This is true even if you only modify one script and haven't made changes to any others. You must choose **Save** at least once for all the scripts to be in place. +2. In the **Database Action Scripts** section, select **Login**. -## Custom database support for Organizations - -If you use custom database connections in conjunction with Organizations, you can use the `context` parameter in the script signature to make Organization details like `id`, `name`, and `metadata` available to your custom database action scripts. Organization data is passed in the context object when the associated action happens in the context of an organization; for example, when a user authenticates on an organization’s login prompt, the login action script is passed. To learn more, read [Database Connections](/docs/authenticate/database-connections). +3. Write your own database action script with the provided function signature, or use the **Load Template** drop-down menu to select a template to start from. -### Enable context object + + For example, the MySQL Login action script template looks like this: -1. Navigate to [Auth0 Dashboard > Authentication > Database](https://manage.auth0.com/dashboard/us/#/connections/database/). -2. Select your database connection. -3. Under the **Custom Database** tab, find **Context objects in database scripts** and select **Enable**. - + ```js expandable Login database action script template for MySQL + function login(identifierValue, password, callback) { + const mysql = require('mysql'); + const bcrypt = require('bcrypt'); - This feature cannot be disabled once toggled. + const connection = mysql.createConnection({ + host: 'localhost', + user: 'me', + password: 'secret', + database: 'mydb' + }); - -4. On the confirmation prompt, select **Confirm**. + connection.connect(); -### Use Organization context + const query = 'SELECT id, nickname, email, password FROM users WHERE email = ?'; -Once the context object is enabled in Dashboard, you can add a `context` object to your custom database action scripts, inserted directly preceding the callback parameter. When events are triggered with Organization context, the corresponding data is made available to your custom database action scripts in the format below: + connection.query(query, [ identifierValue ], function(err, results) { + if (err) return callback(err); + if (results.length === 0) return callback(new WrongUsernameOrPasswordError(identifierValue)); + const user = results[0]; - + bcrypt.compare(password, user.password, function(err, isValid) { + if (err || !isValid) return callback(err || new WrongUsernameOrPasswordError(identifierValue)); -```json lines -{ - "organization": { - "display_name": "My Organization", - "id": "org_XXXXXX", - "metadata": { - "example": "value" - }, - "name": "my-organization" + callback(null, { + user_id: user.id.toString(), + nickname: user.nickname, + email: user.email + }); + }); + }); } -} -``` + ``` - + This example assumes the database contains a `users` table containing the appropriate columns. The script connects to the database, uses a query to retrieve the first user with `email == user.email`, validates the entered passwords with `bcrypt.compareSync` and, if successful, returns an object containing the user profile information (including `id`, `nickname`, and `email`). + -```json lines -{ - "organization": { - "display_name": "My Organization", - "id": "org_XXXXXX", - "metadata": { - "example": "value" - }, - "name": "my-organization" - } -} -``` +All database action scripts must call the Auth0-supplied [`callback` function](/docs/authenticate/database-connections/custom-db/custom-database-connections-scripts#callback) exactly once, immediately before it completes (either implicitly or explicitly with a `return` statement). + + +In the **Configuration Parameters** section (below **Database Action Scripts**), you can store values that are available to all database action scripts in the global `configuration` object. - +Values are encrypted, so you can also use configuration parameters to store sensitive information such as credentials or API keys for accessing external identity stores or define variables with tenant-specific values. -```json lines -{ - "organization": { - "display_name": "My Organization", - "id": "org_XXXXXX", - "metadata": { - "example": "value" - }, - "name": "my-organization" - } -} +To extend the previous MySQL example, if you add the **Key** `MYSQL_PASSWORD` with the **Value** as the MySQL password, you can use that configuration parameter to connect: + +```javascript MySQL connection configuration parameter example +const connection = mysql.createConnection({ + host: 'localhost', + user: 'me', + password: 'secret', // [!code --] + password : configuration.MYSQL_PASSWORD, // [!code ++] + database: 'mydb' +}); ``` +Treat the `configuration` object as read-only and use it to avoid hard-coding values in your action scripts. + - + +1. To test your database action script, select **Save and Try** above the code block. -```json lines -{ - "organization": { - "display_name": "My Organization", - "id": "org_XXXXXX", - "metadata": { - "example": "value" - }, - "name": "my-organization" - } -} +2. In the **Try the login script** window that opens, enter the user credentials you want to use to test. + + ![Try the login script window.](/docs/images/cdy7uua7fh8z/8uBGZhHYZwOcO19ljJNNp/ccd69d84fdbdff90dc779a6b1ea8a6fd/db-connection-try-login-script.png) + +3. Select **Try** to open the runtime selector. Choose your runtime to execute the script. + + + You can [change the default NodeJS runtime](/docs/get-started/tenant-settings#extensibility) in your tenant settings. + + +When you're done, close the **Try the login script** window, then select **Save** to save the script. + +
+ +## Next steps + +### Enable automatic migration + +You can continue using your external user store as your IdP. If you want to transition to Auth0's user store instead, you can [incrementally migrate users to Auth0](/docs/manage-users/user-migration/configure-automatic-migration-from-your-database) (sometimes called *trickle* or *lazy* migration). + +### Enable Organization context + +If you use custom database connections with [Organizations](/docs/manage-users/organizations), you can make Organization details like `id`, `name`, and `metadata` available to your database action scripts by enabling [the `context` object](/docs/authenticate/database-connections/custom-db/custom-database-connections-scripts#context-object) + +Doing so passes an additional `context` argument to custom database scripts containing Organization data. For example, when a user authenticates on an Organization’s login prompt, the login database action script is passed. + + +You cannot disable the `context` object after you enable it. + + +First, enable the `context` object: + +1. From to [Auth0 Dashboard > Authentication > Database](https://manage.auth0.com/dashboard/us/#/connections/database/), select your database connection. + +2. Select the **Custom Database** tab. + +3. Next to **Context object in database scripts**, select **Enable**. + +4. In the **Are you absolutely sure?** window that opens, select **Confirm**. + +Next, add the `context` object to your database action scripts. This parameter directly precedes the `callback` parameter. For example, in the Login script function signature: + +```js Original Login script function signature +function login(email, password, callback) { ... } +``` + +```js Login script function signature with the context object +function login(email, password, context, callback) { ... } ``` - +The `context` object contains Organization data in the following format: ```json lines { @@ -231,42 +191,11 @@ Once the context object is enabled in Dashboard, you can add a `context` object } ``` - - - - - - - -An empty context will always be passed to the `delete` script. - - - -## Add configuration parameters - -You can store parameters, like the credentials required to connect to your database, in the **Settings** section below the script editor. These will be available for all of your scripts, and you can access the parameter values using the `configuration` object in your database action scripts (i.e., `configuration.MYSQL_PASSWORD`). - -Use the added parameters in your scripts to configure the connection. For example, you might add the following to the MySQL Login script: - -```javascript lines -function login (username, password, callback) { - var mysql = require('mysql'); - var connection = mysql.createConnection({ - host : configuration.MYSQL_HOST, - user : 'me', - password : configuration.MYSQL_PASSWORD, - database : 'mydb' - }); -} -``` - - - +When events are triggered with Organization context, the corresponding data is made available to all database action scripts except the [Delete script](/docs/authenticate/database-connections/custom-db/templates/delete), which is always passed an empty `context` object. +## Limits and considerations +* Auth0 does not sanitize or validate any username/password combination passed by a custom database. -## Learn more +* Database action scripts are subject to Auth0's [Rate Limit Policy](/docs/troubleshoot/customer-support/operational-policies/rate-limit-policy). -* [Custom Database Connection and Action Script Best Practices](/docs/authenticate/database-connections/custom-db/custom-database-connections-scripts) -* [Troubleshoot Custom Databases](/docs/authenticate/database-connections/custom-db/error-handling) -* [Import and Export Users](/docs/manage-users/user-migration) \ No newline at end of file diff --git a/main/docs/authenticate/database-connections/custom-db/custom-database-connections-scripts.mdx b/main/docs/authenticate/database-connections/custom-db/custom-database-connections-scripts.mdx index da2aae5885..39f4089a41 100644 --- a/main/docs/authenticate/database-connections/custom-db/custom-database-connections-scripts.mdx +++ b/main/docs/authenticate/database-connections/custom-db/custom-database-connections-scripts.mdx @@ -1,37 +1,72 @@ --- -description: Learn about best practices for custom database connections and database action scripts. -title: Custom Database Connection and Action Script Best Practices +title: Database Action Scripts +description: Configure and customize how Auth0 interfaces with your external user store by writing database action scripts. --- - -Both your specific login implementation and your Auth0 plan or custom agreement affect whether this feature is available. To learn more, read [Pricing](https://auth0.com/pricing). +## Usage - +Database action scripts are Node.js functions that you write for custom database connections. They define how Auth0 interfaces with your external user store during functionality like logins, password changes, and user deletion. -Extensibility allows admins to add custom login in Auth0 as a mechanism for building out last-mile solutions for Identity and Access Management (IdAM). Each extensibility type uses Node.js and runs on the Auth0 platform in an Auth0 tenant. +Database action scripts execute as a series of called JavaScript functions in an instance of a serverless Webtask container. -Auth0 Extensibility comes in several forms: +### Completion and the `callback` function {#callback} -* [Actions](/docs/customize/actions): Actions are secure, tenant-specific, versioned functions written in Node.js that execute at certain points within the Auth0 platform. Use actions to customize and extend Auth0's capabilities with custom login. -* [Rules](/docs/customize/rules): Run when artifacts for user authenticity are generated. For example: +The function signature for each database action script includes a `callback` function as its final parameter. Auth0 supplies this `callback` function, which signals completion of operation. - + ID Token in OpenID Connect (OIDC) - + Access Token in OAuth 2.0 - + An assertion in SAML -* [Hooks](/docs/customize/hooks): Provide additional extensibility when there is an exchange of non-user-related artifacts and when user identities are created such as pre-user registration and post-user registration. +Every database action script must call its `callback` function exactly once, immediately before it completes (either implicitly or explicitly with a `return` statement). Failure to execute the `callback` function results in a stall of execution and ultimately returns an error. -Below are some best practices for using Auth0 Extensibility products: +For troubleshooting, you can use return errors from your database action scripts by passing an `Error` object to the `callback` function: -* Scripts for both custom database connections and migrations are used to integrate with an existing user identity store or where automatic user migration from an independent or legacy identity store are required. For more more configuration information, review [Create Custom Database Connections.](/docs/authenticate/database-connections/custom-db/create-db-connection) -* To avoid user duplications, set a consistent `user_id` on each returned user profile. Additionally, ensure that you don't return the same email address for two different users in the `get_user` or `login` script and make sure both scripts return the same user. -* To support backwards compatibility, `app_metadata` is called `metadata` in custom DB scripts. Since data transfer can be interrupted, use an Action to fetch the metadata that should be in `app_metadata` or `user_metadata` if it's missing. -* Ensure you restrict access to the audience with an [Action](/docs/customize/actions). +```js lines +return callback(new Error('My custom error message')); +``` -Auth0 Extensibility is a comprehensive and sophisticated capability to tailor IdAM operations, but can also expose your database to improper or unintended use. The documents in this section highlight best practices to both designers and implementers; we recommend reading them in their entirety at least once, even if you've already started your journey with Auth0. +Calling the `callback` function with no parameters, as in `callback()`, is equivalent to calling it with `callback(null)`. -## Learn more +### Asynchronous JavaScript -* [Custom Database Connection Anatomy Best Practices](/docs/authenticate/database-connections/custom-db/custom-database-connections-scripts/anatomy) -* [Custom Database Action Script Environment Best Practices](/docs/authenticate/database-connections/custom-db/custom-database-connections-scripts/environment) -* [Custom Database Action Script Execution Best Practices](/docs/authenticate/database-connections/custom-db/custom-database-connections-scripts/execution) -* [Custom Database Connection Security Best Practices](/docs/authenticate/database-connections/custom-db/custom-database-connections-scripts/connection-security) +Database action scripts support asynchronous JavaScript features, including [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) objects and [async functions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function). You can use asynchronous features to execute non-blocking operations from an action script. + +If your action script uses asynchronous processing, you must defer the call to the `callback` function until asynchronous processing completes. Asynchronous operations must complete within the [execution limit](#limits) of the Webtask container. + +### Public npm modules + +`npm` modules reduce the overall size of action scripts and provide access to a wide range of pre-built functionality. + +Auth0 serverless Webtask containers can use many [supported public `npm` modules](https://auth0-extensions.github.io/canirequire/). If you require another module, make a request with your Auth0 representative or [open a support ticket](https://support.auth0.com/). + +Action scripts do not support `npm` modules from private repositories. + +## Provided artifacts + +We provide a specific environment with a number of artifacts supplied by both the Webtask container and the Auth0 authentication server (your Auth0 tenant) itself. + +### `global` object + +Auth0 serverless Webtask containers are provisioned from a pool associated with each Auth0 tenant. All action scripts that execute within a container instance can access the `global` object, which acts as a global variable unique to the container. + +You can use the `global` object to define information or functions used across all action scripts that run in the container instance or cache expensive resources, like storing an Access Token for a third-party (logging) API or your own API defined in Auth0 and obtained via the [Client Credentials Flow](/docs/get-started/authentication-and-authorization-flow/client-credentials-flow). + + +Do not store user-specific information the `global` object. Because there is no container affinity for action script execution in Auth0, action scripts may execute in any running container instance or in a new container instance added to the pool. + + +All assignment declarations in the `global` object should also include provision for initialization because the `global` object is reset when a Webtask container is recycled or instantiated. + +### `configuration` object + +You can [set configuration parameters in database action scripts](/docs/authenticate/database-connections/custom-db/create-db-connection#add-configuration-parameters) to make values available to all scripts through the `configuration` object. Values are encrypted. + +Treat the `configuration` object as read-only and use it to avoid hard-coding values in your action scripts. For example, you can store sensitive information such as credentials or API keys for accessing external identity stores or define variables with tenant-specific values. + +### `context` object + +If you use [Organizations](/docs/manage-users/organizations), you can make Organization data available to your database action scripts by enabling the `context` object. This passes an additional `context` argument to custom database scripts containing Organization details like `id`, `name`, and `metadata`. + +You cannot disable the `context` object after you enable it. The [Delete script](/docs/authenticate/database-connections/custom-db/templates/delete) is always passed an empty `context` object. + +## Limits + +* The total size of an action script must not exceed 100 KB, excluding imported `npm` modules. Larger sizes introduce more latency due to the Webtask platform's packaging and transport process, which impacts the performance of the system. + +* Each action script's Webtask container has an execution limit of approximately 20 seconds after which the container is recycled. This terminates the action script's pending operations and may result in errors. diff --git a/main/docs/authenticate/database-connections/custom-db/custom-database-connections-scripts/anatomy.mdx b/main/docs/authenticate/database-connections/custom-db/custom-database-connections-scripts/anatomy.mdx deleted file mode 100644 index 243166b58b..0000000000 --- a/main/docs/authenticate/database-connections/custom-db/custom-database-connections-scripts/anatomy.mdx +++ /dev/null @@ -1,39 +0,0 @@ ---- -description: Learn about best practices for custom database connection anatomy. -title: Custom Database Connection Anatomy Best Practices ---- -You typically use a custom database connection to provide access to your own legacy identity store for authentication (sometimes referred to as **legacy authentication**) or [perform user import through automatic migration](/docs/manage-users/user-migration/configure-automatic-migration-from-your-database) (often referred to as **trickle** or **lazy** migration). You can also use custom database connections to proxy access to an Auth0 tenant in scenarios where you use Auth0 multi-tenant architecture. To learn more, read [Multi-Tenant Applications Best Practices](/docs/get-started/auth0-overview/create-tenants/multi-tenant-apps-best-practices). - -You typically create and configure custom database connections using the Auth0 Dashboard. You create a database connection and then toggle **Use my own database** to enable editing of the database action scripts. A custom database connection can also be created and configured with the Auth0 Management API [Create a connection endpoint](https://auth0.com/docs/api/management/v2#!/Connections/post_connections) and the `auth0` strategy. - -![Auth0 Dashboard Authentication Database Connection Custom Database Settings Use Own Database Enabled](/docs/images/cdy7uua7fh8z/11HPAdVwJMmnWbzMVjHCJ8/f75ff47f613a41778f266643f29ab2b1/2025-02-27_17-25-36.png) - -As shown below, you use custom database connections as part of a login workflow to obtain user identity information from your own legacy identity store for authentication or user import. - -![Custom Database Connection Flow](/docs/images/cdy7uua7fh8z/1H2Ky1WyPlZ9zHZrEVlFhP/4e4856395fac6d6339c883c35d4b4ac0/custom-database-connections.png) - -In addition to artifacts common for all database connection types, a custom database connection allows you to configure action scripts—custom code used when interfacing with legacy identity stores. The scripts you choose to configure depend on whether you are creating a connection for legacy authentication or for automatic migration. - -Action scripts can be implemented as anonymous functions; however, anonymous functions make it hard to debug when it comes to interpreting the call-stack generated as a result of any exceptional error conditions. For convenience, we recommend providing a function name for each action script. To see some recommended names, read [Custom Database Action Script Execution Best Practices](/docs/authenticate/database-connections/custom-db/custom-database-connections-scripts/execution). - -In a legacy authentication scenario, no new user record is created; the user remains in the legacy identity store and Auth0 uses the identity it contains when authenticating the user. Custom database connections are also used outside of the Universal Login workflow. For example, a connection's `changePassword` action script is called when a password change operation occurs for a user that resides in a legacy identity store. - -## Automatic migration - -During automatic migration, Auth0 creates a new user in an identity store (database) managed by Auth0. Auth0 uses the identity in the Auth0-managed identity store when authenticating the user. For this to occur, Auth0 first requires the user to be authenticated against the legacy identity store, and only if this succeeds will the new user be created in the Auth0 managed database. Auth0 creates the new user using the same ID and password that was supplied during authentication. - -User creation in an automatic migration scenario typically occurs after the Login action script completes. We recommend that you do not attempt to delete users from a legacy identity store as an inline operation within the Login script, but instead as an independent process. This prevents accidental user deletion should an error condition occur during migration. - -With automatic migration, users remain in the legacy identity store and can be deleted or archived if required. A side-effect can occur where a user is deleted from Auth0 but remains in the legacy data store. In this case, a login made by the deleted user could result in either the Login or Get User script executing and the user again migrating from the legacy identity store. - -We recommend marking legacy store user identities as migrated before either Login or Get User scripts complete and prior to any eventual legacy store deletion to prevent the unintentional recreation of intentionally-deleted users. - -## Size - -We recommend that the total size of any action script not exceed 100 KB. The larger the size, the more latency is introduced due to the packaging and transport process employed by the Auth0 platform, and this will have an impact on the performance of your system. Note that the 100 KB limit does not include any `npm` modules that may be referenced as part of any `require` statements. - -## Learn more - -* [Custom Database Connection Security Best Practices](/docs/authenticate/database-connections/custom-db/custom-database-connections-scripts/connection-security) -* [Custom Database Action Script Execution Best Practices](/docs/authenticate/database-connections/custom-db/custom-database-connections-scripts/execution) -* [Custom Database Action Script Environment Best Practices](/docs/authenticate/database-connections/custom-db/custom-database-connections-scripts/environment) \ No newline at end of file diff --git a/main/docs/authenticate/database-connections/custom-db/custom-database-connections-scripts/connection-security.mdx b/main/docs/authenticate/database-connections/custom-db/custom-database-connections-scripts/connection-security.mdx deleted file mode 100644 index d1875a1380..0000000000 --- a/main/docs/authenticate/database-connections/custom-db/custom-database-connections-scripts/connection-security.mdx +++ /dev/null @@ -1,34 +0,0 @@ ---- -description: Learn about best practices for custom database connection security. -title: Custom Database Connection Security Best Practices ---- -## Access legacy identity storage via custom API - -We recommend that you implement an API to provide least privilege to your legacy identity storage rather than simply opening up general access via the internet. - -Protecting legacy identity storage from general access is a recommended best practice. Exposing a (legacy identity) database directly to the internet, for example, can be extremely problematic: database interfaces for SQL and the like are extremely open in terms of functionality, which violates the principle of least privilege when it comes to security. - -The alternative is to create a simple (custom) API—protected via use of an access token—that each action script can call. This would act as the interface to the legacy identity store. Client credentials grant flow can then be used to obtain an access token from within a script, and this can be subsequently cached for re-use within the `global` object to improve performance. The API can then provide a discrete number of protected endpoints that perform only the legacy (identity) management functionality required (e.g., read user, change password). - -By default, Auth0 will give you a token for any API if you authenticate successfully and include the appropriate audience. Restricting access to the legacy identity store API by restricting access token allocation via the use of a rule will prevent unauthorized usage and will mitigate a number of attack vector scenarios, such as where redirect to `/authorize` is intercepted and the audience to the API is added. - - - -Restricting access to the API via Rule will mitigate attack vector scenarios, such as where redirect to `/authorize` is intercepted and the audience to the API is added, and will ensure that only access using specific client credentials is granted. - - - -## Access to legacy identity storage - -Whether managing access to legacy identity storage via custom API or using the native interface provided, you should restrict access to the list of IP addresses associated with your Auth0 tenant. To see the list of IP addresses, read [Auth0 IP Addresses for AllowLists](/docs/secure/security-guidance/data-security/allowlist). Adding URLs to the AllowList constrains access to the legacy identity store and ensures that only custom database actions scripts defined in Auth0 are permitted. - - - -The Auth0 IP address AllowList is shared between all Auth0 tenants defined to a region. Never use the allow list as the sole method of securing access to your legacy identity store; doing so could open up potential security vulnerabilities allowing unauthorized access to your users. - - - -## Learn more - -* [Secure](/docs/secure) -* [Token Best Practices](/docs/secure/tokens/token-best-practices) \ No newline at end of file diff --git a/main/docs/authenticate/database-connections/custom-db/custom-database-connections-scripts/environment.mdx b/main/docs/authenticate/database-connections/custom-db/custom-database-connections-scripts/environment.mdx deleted file mode 100644 index eca63c271d..0000000000 --- a/main/docs/authenticate/database-connections/custom-db/custom-database-connections-scripts/environment.mdx +++ /dev/null @@ -1,61 +0,0 @@ ---- -description: Learn about best practices for the custom database action script environment. -title: Custom Database Action Script Environment Best Practices ---- -Action scripts execute as a series of called JavaScript functions in an instance of a serverless Webtask container. As part of this, a specific environment is provided, together with a number of artifacts supplied by both the Webtask container and the Auth0 authentication server (your Auth0 tenant) itself. - -## npm modules - -Auth0 serverless Webtask containers can make use of a wide range of [`npm`](https://www.npmjs.com/) modules; `npm` modules not only reduce the overall size of action script code implementation, but also provide access to a wide range of pre-built functionality. - -By default, a large list of publicly-available `npm` modules are supported out-of-the-box. This list has been compiled and vetted for any potential security concerns. To see which `npm` modules are supported, read [Can I require: Auth0 Extensibility](https://auth0-extensions.github.io/canirequire/). - -If you require an `npm` module that is not supported out-of-the-box, then you can make a request via the [Auth0 Extensibility portal](https://auth0-extensions.github.io/canirequire/) or through your Auth0 representative. Auth0 will evaluate your request to determine suitability, if you need to escalate an unresolved request open a support ticket via the [Auth0 support portal](https://support.auth0.com/). There is currently no support in Auth0 for the use of `npm` modules from private repositories. - -## Variables - -Auth0 action scripts support the notion of environment variables, accessed via what is defined as the globally-available `configuration` object. The `configuration` object should be treated as read-only and should be used for storing sensitive information, such as credentials or API keys for accessing external identity stores. This mitigates having security-sensitive values hardcoded in an action script. - -The `configuration` object can also be used to support whatever Software Development Life Cycle (SDLC) best practice strategies you employ, such as [setting up multiple environments](/docs/get-started/auth0-overview/create-tenants/set-up-multiple-environments), by allowing you to define variables that have tenant-specific values. This mitigates hardcoded values in an action script, which may change depending upon which tenant is executing it. - -## global object - -Auth0 serverless Webtask containers are provisioned from a pool that is associated with each Auth0 tenant. Each container instance makes available a global object, which can be accessed across all action scripts that execute within it (the container instance). The global object acts as a global variable that is unique to the container and that can be used to define information—or even functions—that can be used across all action scripts that run in it (the container instance). - -This means that the global object can be used to cache expensive resources, as long as those resources are not user-specific. For example, an Access Token for a third-party (e.g., logging) API that provides non user-specific functionality could be stored. Or it could be used to store an Access Token to your own non user-specific API defined in Auth0 and obtained via use of the Client Credentials flow. - - - -An action script may execute in any of the container instances already running or in a newly-created container instance (which may subsequently be added to the pool). There is no container affinity for action script execution in Auth0. This means that you should avoid storing any user-specific information in the global object and should always ensure that any declaration made within the global object provides for initialization too. - - - -Each time a Webtask container is recycled, or for each instantiation of a new Webtask container, the global object it defines is reset. Thus, any declaration of assignment within the global object associated with a container should also include provision for initialization. To provide performance flexibility, serverless Webtask containers are provisioned in Auth0 on an ad-hoc basis and are also subject to various recycle policies. In general, we recommend that you do not consider the life of a global object to be anything more than 20 minutes. - -## Custom database connection environment checklist - -* Make sure that your database has the appropriate fields to store user profiles attributes, such as **id**, **nickname**, **email**, and **password**. To learn more about Auth0's user profile schema and expected fields, read [Normalized User Profiles](/docs/manage-users/user-accounts/user-profiles/normalized-user-profiles). To learn how to update user profiles, read [Update User Profiles Using Your Database](/docs/manage-users/user-accounts/user-profiles/update-user-profiles-using-your-database). -* You can use return errors resulting from your custom database connection for troubleshooting purposes. -* The `id` (or alternatively `user_id`) property in the returned user profile will be used by Auth0 to identify the user. If you are using multiple custom database connections, then the **id** value **must be unique across all the custom database connections** to avoid **user ID** collisions. Our recommendation is to prefix the value of **id** with the connection name (omitting any whitespace). To learn more about user IDs, read [Identify Users](/docs/manage-users/user-accounts/identify-users). -* Latency will be greater compared to Auth0-hosted user stores. -* The database or service must be reachable from the Auth0 servers. You will need to configure inbound connections if your store is behind a firewall. - -## Testing a specific runtime version - -The runtime version for custom Database scripts is defined at **Dashboard > Settings > Advanced** within the **Extensibility** section. - -Follow these steps to test a single custom database script on a specific runtime version: - -1. Navigate to **Authentication > Database.** -2. Select the Database connection where you have defined a custom script. -3. From within the selected database connection's page, select the **Custom Database** tab. -4. Scroll to **Database Action Scripts** section of the page. -5. Select the specific script you want to verify (eg. Login, Create etc) on the respective tab. -6. Select **Save and Try**. This loads a modal with specific test parameters and sample context details. Update as needed. -7. Select **Try** from the modal to open a dropdown selector for a specific Node version. -8. The test would trigger upon selecting the desired Node version and results would show up in a message on the same screen. - -## Learn more - -* [Custom Database Action Script Execution Best Practices](/docs/authenticate/database-connections/custom-db/custom-database-connections-scripts/execution) -* [Custom Database Connection Security Best Practices](/docs/authenticate/database-connections/custom-db/custom-database-connections-scripts/connection-security) diff --git a/main/docs/authenticate/database-connections/custom-db/custom-database-connections-scripts/execution.mdx b/main/docs/authenticate/database-connections/custom-db/custom-database-connections-scripts/execution.mdx deleted file mode 100644 index ec3517804c..0000000000 --- a/main/docs/authenticate/database-connections/custom-db/custom-database-connections-scripts/execution.mdx +++ /dev/null @@ -1,199 +0,0 @@ ---- -description: Learn about best practices for custom database action script execution. -title: 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](/docs/authenticate/database-connections/custom-db/custom-database-connections-scripts/environment). - -## Asynchronous features - -Asynchronous features of JavaScript, including [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) objects and [async functions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function), 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](#error-handling) if the external service or API cannot be reached. - -### Examples - -Auth0 supports using [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) objects and [async functions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function) 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](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises#chaining). - -```javascript lines expandable -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}`)); - }); - } -``` - - - - - - -#### 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](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function). - -```js lines expandable -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); -} -``` - - - - - - -## 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. - - - -If the `callback` function is called more than once, then it may lead to unexpected results and/or errors. - -If the `callback` function is not called at all, then execution of the action script will stall, and an error will be returned when the Webtask container is recycled. - - - -### 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](/docs/authenticate/database-connections/custom-db/custom-database-connections-scripts/environment). - -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](https://developer.mozilla.org/en-US/docs/Glossary/IIFE). - -## Error handling - -Pass an `Error` object to the `callback` function with a descriptive message of the error: - -```js lines -return callback(new Error('My custom error message')); -``` - - - - - - -## 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](/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. - -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](/docs/secure/security-guidance/data-security/allowlist) 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: - -```json lines -{ - "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 -} -``` - - - - - - -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](/docs/secure/tokens/access-tokens/management-api-access-tokens/get-management-api-access-tokens-for-production). diff --git a/main/docs/authenticate/database-connections/custom-db/error-handling.mdx b/main/docs/authenticate/database-connections/custom-db/error-handling.mdx index 0185f7ba9a..f74d074a3a 100644 --- a/main/docs/authenticate/database-connections/custom-db/error-handling.mdx +++ b/main/docs/authenticate/database-connections/custom-db/error-handling.mdx @@ -1,41 +1,34 @@ --- -title: Troubleshoot Custom Databases -description: How to handle errors and troubleshoot when using your own database as an identity provider. +title: Error Handling for Custom Database Connections +sidebarTitle: Error Handling +description: Learn more about error handling when using custom database connections. --- -You may receive errors from custom dataabaseses. You can also return errors from your custom database connection action scripts for troubleshooting. +You may receive errors from custom databases. You can also return errors from database action scripts for troubleshooting. ## Custom database error types Database connections may return the following errors: -| Error | Login Script | Description | +| Error | Action Script | Occurs when... | |-------|--------------|-------------| -| `new WrongUsernameOrPasswordError(`
` , `
`)` | Login | Occurs when the user's credentials are invalid. Passing no arguments or a falsey first argument will result in the error being logged as an `fu` event (invalid username/email) with an empty string for a `user_id`. Providing a truthy first argument will result in the error being logged as an `fp` event (the user exists, but the password is invalid) with a `user_id` value of `auth0\|`. To learn more, read [Log Event Type Codes](/docs/deploy-monitor/logs/log-event-type-codes). | -| `new ValidationError(`
` , `
`)` | Create | Occurs when a user already exists in your database. | -| `callback(,)` | Change Password | Occurs when the user's password was not updated. | -| `callback()` | Get User | Occurs when the user is not found. | -| `new UnauthorizedError()` | All Login Scripts | Occurs when something went wrong while trying to reach the database. Preferred way of communicating errors back from custom databases to front-end. | -| `new Error()` | All Login Scripts | Occurs when something went wrong while trying to reach the database. | -| `error code: auth0_idp_error;` `user already exists` | Create | Occurs in legacy identity store when you attempt to create a user that already exists in your legacy identity store or in Auth0. | -| `error code: auth0_idp_error;` `user already exists` | Delete | Occurs in legacy identity store when you attempt to delete a user with custom database scripts. Error could also manifest as "Error! There was a problem deleting `user@example.com`" or "Sandbox Error: Please implement the Delete script for this database connection at `https://manage.auth0.com/#/connections/database`". | +| `new WrongUsernameOrPasswordError(`
` , `
`)` | Login | The user's credentials are invalid. The [Log Event Type Codes](/docs/deploy-monitor/logs/log-event-type-codes) are:
  • `fu` (invalid username/email) with an empty string for `user_id`, if passed no arguments or a falsey first argument.
  • `fp` (user exists, password invalid) with a `user_id` value of `auth0\|`, if passed a truthy first argument.
| +| `new ValidationError(`
` , `
`)` | Create | The user already exists in your database. | +| `callback(,)` | Change Password | The user's password was not updated. | +| `callback()` | Get User | The user is not found. | +| `new UnauthorizedError()` | All login scripts | Something went wrong while trying to reach the database. Preferred way of communicating errors back from custom databases to front-end. | +| `new Error()` | All login scripts | Something went wrong while trying to reach the database. | +| `error code: auth0_idp_error;` `user already exists` | Create | Automatic migration to Auth0 is disabled and you attempt to create a user that already exists in your external user store or in Auth0. | +| `error code: auth0_idp_error;` `user already exists` | Delete | Automatic migration to Auth0 is disabled and you attempt to delete a user with custom database scripts.

Error could also manifest as "Error! There was a problem deleting `user@example.com`" or "Sandbox Error: Please implement the Delete script for this database connection" | ## Return errors -To return an error, call the `callback()` function and pass `error` as the first parameter: - -```js lines -callback(error); -``` - -For example: +You can return errors from database action scripts using [the `callback` function](/docs/authenticate/database-connections/custom-db/custom-database-connections-scripts#callback). For example: ```js wrap lines callback(new ValidationError('email-too-long', 'Email is too long.')); ``` -### Return errors when using Lock - If you use Lock, you can [customize the error messages](/docs/customize/login-pages/classic-login/customize-lock-error-messages) by adding them to the dictionary. ## Troubleshoot errors @@ -44,4 +37,4 @@ Test the script using the **Try** button. If you do not get the expected result or you receive an error, use [Actions Real-time Logs](/docs/customize/actions/actions-real-time-logs), use `console.log()` statements in your script, and try the connection again. The output of `console.log()` prints to the Actions Real-time Logs window. -You may experience errors using action scripts to import, create, or delete users. Use `console.logs` to verify the error is a result of using action scripts. To learn more, read [Custom Database Action Script Templates](/docs/authenticate/database-connections/custom-db/templates). +You may experience errors using action scripts to import, create, or delete users. Use `console.logs` to verify the error is a result of using action scripts. diff --git a/main/docs/authenticate/database-connections/custom-db/overview-custom-db-connections.mdx b/main/docs/authenticate/database-connections/custom-db/overview-custom-db-connections.mdx deleted file mode 100644 index 810f8fd5b5..0000000000 --- a/main/docs/authenticate/database-connections/custom-db/overview-custom-db-connections.mdx +++ /dev/null @@ -1,153 +0,0 @@ ---- -description: Learn about authenticating users using your database as an identity provider. -title: Authenticate with Your Own User Store ---- - - -Your Auth0 plan or custom agreement affects whether this feature is available. To learn more, read [Pricing](https://auth0.com/pricing). - - - -Use a custom database connection when you want to provide access to your own independent (legacy) identity data store for the following purposes: - -* **Authentication**: Use your database as an identity provider in Auth0 to authenticate users. (Referred to as legacy authentication.) -* **Import Users**: Use automatic migration (trickle or lazy migration) -* **Proxy access to an Auth0 tenant**: Use Auth0 multi-tenant architecture. - -You can create and configure a custom database connection by doing one of the following tasks: - -1. Use the [Create connections](https://auth0.com/docs/api/management/v2#!/Connections/post_connections) endpoint with the `auth0` strategy. -2. Navigate to [Auth0 Dashboard > Authentication > Database](https://manage.auth0.com/#/connections/database), create the connection, and enable the **Use my own database** option to allow database action script editing. - - ![Enable Custom Database Use My Own Database Option](/docs/images/cdy7uua7fh8z/3kgHDpBFdVWNq9XOfhsTXI/2efa43e44793b8dd69b9f9e8b54bf752/2025-02-25_10-20-20.png) - -## How it works - -As shown in the diagram below, you use custom database connections as part of Universal Login workflow in order to obtain user identity information from your own, legacy identity store. - -![Custom Database Connections Anatomy](/docs/images/cdy7uua7fh8z/2lHqvZKFiEbAXURU2gmchc/626cac94211c266f2135a41456b2e49d/custom-database-connections.png) - -In addition to artifacts common for all [database connection](/docs/authenticate/database-connections) types, a custom database connection allows you to configure action scripts: custom code that’s used when interfacing with your legacy identity store. Auth0 provides [custom database action script templates](/docs/authenticate/database-connections/custom-db/templates) for configuration, and the ones you use will depend on whether you are creating a custom database connection for legacy authentication or for automatic migration. - - - -Action scripts can be implemented as anonymous functions, however anonymous functions make it hard in debugging situations when it comes to interpreting the call-stack generated as a result of any exceptional error condition. For convenience, we recommend providing a function name for each action script. - - - -### Legacy authentication scenario - -In a legacy authentication scenario, a new user record is created within Auth0 during the user's first login, but Auth0 does not store a hash of the user's password. Auth0 will always use the legacy identity store and the identity it contains when authenticating the user. - - - -Custom database connections are also used outside of Universal Login workflow. For example, a connection's `changePassword` action script is called when a password change operation occurs for a user that resides in a legacy identity store. - - - -### Automatic migration scenario - -During automatic or trickle migration, Auth0 creates a new user in an identity store (database) managed by Auth0. From then on, Auth0 always uses the identity in the Auth0 managed identity store for authenticating the user. For this to occur, first Auth0 requires the user be authenticated against the legacy identity store and only if this succeeds will the new user be created. The new user will be created using the same id and password that was supplied during authentication. - - - -Creation of a user in an automatic migration scenario typically occurs after the `login` action script completes. We therefore recommend that you do not attempt any deletion of a user from a legacy identity store as an inline operation (i.e., within the `login` script) but perform the deletion as an independent process. This will prevent accidental deletion of a user should an error condition occur during the migration process. - - - -In an automatic migration scenario, users remain in the legacy identity store and can be deleted or archived if required. One side effect of this can occur if a user is deleted from Auth0 but still remains present in the legacy identity store. In this case, a login actioned by the deleted user could result in either the `login` and/or `getUser` script being executed and the user being migrated from the legacy identity store once again. - - - -We recommend marking any legacy user identity as having been migrated before either `login` or `getUser` completes and prior to any eventual legacy store deletion. - - - -## Size - -The total size of implementation for any action script should not exceed 100 kB. The larger the size the more latency is introduced due to the packaging and transport process employed by the Auth0 serverless Webtask platform, and this will have an impact on the performance of your system. - - - -The 100 kB limit does not include any `npm` modules that may be referenced as part of any require statements. - - - -## Environment - -Action scripts execute as a series of called JavaScript functions in an instance of a serverless Webtask container. As part of this, a specific environment is provided, together with a number of artifacts supplied by both the Webtask container and the Auth0 authentication server (your Auth0 tenant) itself. - -### npm modules - -Auth0 serverless Webtask containers can make use of a [wide range of npm modules](https://auth0-extensions.github.io/canirequire/); `npm` modules not only reduce the overall size of action script code implementation, but also provide access to a wide range of pre-built functionality. - -Many publicly available `npm` modules are supported out-of-the-box. The list has been compiled and vetted for any potential security concerns. If you require an `npm` module that is not supported out-of-the-box, then you can make a request through the [Auth0 support portal](https://support.auth0.com) or your Auth0 representative. Auth0 will evaluate your request to determine suitability. There is currently no support in Auth0 for the user of `npm` modules from private repositories. - -### Variables - -Auth0 action scripts support environment variables, accessed via what is known as the globally-available `configuration` object. To learn more, read the Add Configuration Parameters section in [Create Custom Database Connections](/docs/authenticate/database-connections/custom-db/create-db-connection). - - - -The `configuration` object should be treated as read-only, and should be used for storing sensitive information such as credentials or API keys for accessing external identity stores. This mitigates having security sensitive values hard coded in an action script. - - - -The configuration object can also be used to support whatever [Software Development Life Cycle (SDLC)](/docs/get-started/auth0-overview/create-tenants/set-up-multiple-environments) strategies you employ by allowing you to define variables that have tenant specific values. This mitigates hard coded values in an action script which may change depending upon which tenant is executing it. - -### global object - -Auth0 serverless Webtask containers are provisioned from a pool that's associated with each Auth0 tenant. Each container instance makes available the `global` object, which can be accessed across all action scripts that execute within the container instance. The `global` object acts as a global variable that’s unique to the container, and that can be used to define information or functions used across all action scripts that run in the container instance. - -This means that the `global` object can be used to cache expensive resources as long as those resources are not user-specific. For example, you could use it to store an Access Token for a third-party (logging) API that provides non-user-specific functionality. Or you could store an Access Token to your own non-user-specific API defined in Auth0 and obtained via the [Client Credentials Flow](/docs/get-started/authentication-and-authorization-flow/client-credentials-flow). - - - -An action script may execute in any of the container instances already running, or in a newly created container instance (which may subsequently be added to the pool). There is no container affinity for action script execution in Auth0. This means that you should avoid storing any user-specific information in the `global` object, and should always ensure that any declaration made within the `global` object provides for initialization too. - - - -Each time a Webtask container is recycled, or for each instantiation of a new Webtask container, the `global` object it defines is reset. Thus, any declaration of assignment within the `global` object associated with a container should also include provision for initialization too. - - - -To provide performance flexibility, serverless Webtask containers are provisioned in Auth0 on an ad-hoc basis and are also subject to various recycle policies. In general, we recommend that you do not consider the life of a `global` object to be anything more than 20 minutes. - - - -## Security - -### Access legacy identity storage via custom API - -Protecting legacy identity storage from general access is a recommended best practice. Exposing a database directly to the internet, for example, can be extremely problematic: database interfaces for SQL and the like are extremely open in terms of functionality, which violates the principle of least privilege when it comes to security. - - - -We recommend that you implement an API to provide least privilege to your legacy identity store (database), rather than simply opening up general access via the internet. - - - -The alternative is to create a simple (custom) API, protected via use of an access token, that each action script can call. This would act as the interface to the legacy identity store. Client credentials grant flow can then be used to obtain an access token from within a script, and this can subsequently be cached for reuse in the `global` object in order to improve performance. The API can then provide a discrete number of protected endpoints that perform only the legacy management functionality required (for example, `read user`, `change password`). - - - -By default, Auth0 will give you a token for any API if you authenticate successfully and include the appropriate audience. Restricting access to the legacy identity store API by restricting access token allocation via use of a Rule, will prevent unauthorized usage and mitigate a number of attack vector scenarios, such as where redirect to `/authorize` is intercepted and the audience to the API is added. - - - -### Allowlist access to legacy identity storage - -Whether managing access to a legacy identity store via custom API, or using the native interface provided, restricting access to the list of IP addresses associated with your Auth0 tenant. Allowlisting constrains access to the legacy identity store ensuring that only custom database action scripts defined in Auth0 are permitted. - - - -The Auth0 IP address allowlist is shared among all Auth0 tenants defined to a region. Never use the allowlist as the sole method of securing access to your legacy identity store; doing so could open up potential security vulnerabilities allowing unauthorized access to your users. - - - -## Learn more - -* [Create Custom Database Connections](/docs/authenticate/database-connections/custom-db/create-db-connection) -* [Custom Database Action Script Templates](/docs/authenticate/database-connections/custom-db/templates) -* [Troubleshoot Custom Databases](/docs/authenticate/database-connections/custom-db/error-handling) -* [Import and Export Users](/docs/manage-users/user-migration) diff --git a/main/docs/authenticate/database-connections/custom-db/templates.mdx b/main/docs/authenticate/database-connections/custom-db/templates.mdx deleted file mode 100644 index 2ea81ea935..0000000000 --- a/main/docs/authenticate/database-connections/custom-db/templates.mdx +++ /dev/null @@ -1,56 +0,0 @@ ---- -description: Learn about custom database action script templates. -title: Custom Database Action Script Templates ---- - - -Your Auth0 plan or custom agreement affects whether this feature is available. To learn more, read [Pricing](https://auth0.com/pricing). - - -If you have your own database (known as a legacy data store in Auth0) containing user identity data, you can use it as an identity provider to authenticate users. -You create and configure the connection to your legacy data store as a custom database in Auth0. You can choose to migrate data to Auth0's data store from your legacy database incrementally over time, or you can continue to use it without migrating data. We provide script templates to perform functions on the custom database that you can use and customize. - -There are two different types of custom database scripts: - -* **Automatic Migration**: Whenever a user logs into Auth0, if the user is not yet in Auth0, the script will check the legacy database to see if the user exists there. If found and the **Import users to Auth0** flag is turned on, the user data migrates the user to Auth0 data store. This capability is sometimes called **trickle migration** or **lazy migration**. -* **Legacy Database**: Auth0 will always query the underlying database when a user tries to log in, is created, changes their password, verifies their email, or is deleted. If found and the **Import users to Auth0** flag is **not** turned on, user data stays in the legacy database and does **not** migrate to Auth0. - -Auth0 provides the following custom database action scripts: - -* [Change Password](/docs/authenticate/database-connections/custom-db/templates/change-password) -* [Create User](/docs/authenticate/database-connections/custom-db/templates/create) -* [Delete User](/docs/authenticate/database-connections/custom-db/templates/delete) -* [Get User](/docs/authenticate/database-connections/custom-db/templates/get-user) -* [Login](/docs/authenticate/database-connections/custom-db/templates/login) -* [Verify User](/docs/authenticate/database-connections/custom-db/templates/verify) -* [Change Email](/docs/authenticate/database-connections/custom-db/templates/change-email) - - -If you are behind a firewall, this feature may require that you add the appropriate Auth0 IP addresses to the Allow List to work properly. - - -## Script execution - -As described in [Custom Database Connections](/docs/authenticate/database-connections/custom-db), a custom database connection type allows you to configure action scripts: custom code that is used when interfacing with your legacy identity store. Each action script is essentially a named JavaScript function that is passed a number of parameters, with the name of the function and the parameters passed depending on the script. - -### Limits - -Action script execution supports the asynchronous nature of JavaScript, and constructs such as [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) objects can be used. Asynchronous processing effectively results in suspension pending completion of an operation, and an Auth0 serverless Webtask container typically has a 20-second execution limit, after which the container may be recycled. Recycling a container due to this limit will prematurely terminate operation, ultimately resulting in an error condition being returned (as well as resulting in a potential reset of the `global` object). - -### Completion and the callback function - -The `callback` function supplied to each action script effectively acts as a signal to indicate completion of operation. An action script should complete immediately following a call to the `callback` function (either implicitly or by explicitly executing a JavaScript return statement) and should refrain from any other operation. - - -The Auth0 supplied `callback` function must be called exactly **once**; calling the function more than once within an action script will lead to unpredictable results and/or errors. - - - -Where `callback` is executed with no parameters, as in `callback()`, the implication is that function has been called as though `callback(null)` had been executed. - - -If an action script uses asynchronous processing, then a call to the `callback` function must be deferred to the point where asynchronous processing completes, and must be the final thing called. Asynchronous execution will result in a JavaScript `callback` being executed after the asynchronous operation is complete; this callback is typically fired at some point after the main (synchronous) body of a JavaScript function completes. - - -Failure to execute the `callback` function will result in a stall of execution, and ultimately in an error condition being returned. The action script must call the `callback` function exactly once: the `callback` function must be called at least once in order to prevent stall of execution, however it must not be called more than once otherwise unpredictable results and/or errors will occur. - diff --git a/main/docs/authenticate/database-connections/custom-db/templates/change-email.mdx b/main/docs/authenticate/database-connections/custom-db/templates/change-email.mdx index 07563247a8..45cf0f4924 100644 --- a/main/docs/authenticate/database-connections/custom-db/templates/change-email.mdx +++ b/main/docs/authenticate/database-connections/custom-db/templates/change-email.mdx @@ -1,7 +1,9 @@ --- -description: Describes the custom database action script for changing a user's email. -title: Change Email Script Template +title: Change Email Database Action Script and Template +sidebarTitle: Change Email +description: The Change Email script runs when a user's email address or their email address verification status changes. --- + The Change Email script implements the defined function when a user's email address or their email address verification status changes. We recommend naming this function `changeEmail`. The script is only used in a [legacy authentication scenario](/docs/authenticate/database-connections/custom-db/overview-custom-db-connections), and is required if you want to update a user's email address (and/or email address verification status) in Auth0 and your external database in the same operation. @@ -9,18 +11,16 @@ The script is only used in a [legacy authentication scenario](/docs/authenticate The Change Email script is not configurable through the Auth0 Dashboard. To manage this script, you must use the Auth0 Management API [Create a connection](https://auth0.com/docs/api/management/v2#!/Connections/post_connections) or [Update a connection](https://auth0.com/docs/api/management/v2#!/Connections/patch_connections_by_id) endpoint, or the [Auth0 Deploy CLI](/docs/deploy-monitor/deploy-cli-tool). - If you manage a custom database connection's Database Action Scripts through the Auth0 Dashboard, the Change Email script will be automatically deleted upon saving. To ensure that the Change Email script is not removed accidentally, use the Auth0 Management API or Auth0 Deploy CLI when managing the connection. If you manually change a user's email in your custom database, you must patch the user separately; Auth0 will not automatically detect the change. - ## ChangeEmail function -The changeEmail function should: +The `changeEmail` function should: * Update the user's email address in the external database. * Return an error if the operation failed or an error occurred. @@ -33,11 +33,6 @@ The `changeEmail` function accepts four parameters and returns a `callback` func changeEmail(email, newEmail, verified, callback): function ``` - - - - - | Parameter | Type | Description | | --- | --- | --- | | `email` | String | User's current email address. | @@ -45,8 +40,6 @@ changeEmail(email, newEmail, verified, callback): function | `verified` | Boolean | Email verification status of the new email address. | | `callback` | Function | Used to pass error data through the pipeline. | -### Example - This is a pseudo-JavaScript example of how you could implement the `changeEmail` function: ```javascript lines expandable @@ -78,26 +71,16 @@ function (email, newEmail, verified, callback) { } ``` - - - - - ## Callback function The `callback` function accepts two parameters and returns a function. ### Definition -```js lines +```js callback(error, operationResult): function ``` - - - - - | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `error` | Object | Required | Contains error data. | @@ -107,46 +90,22 @@ callback(error, operationResult): function If the change email operation succeeded, return the `callback` function, and pass a `null` value as the `error` parameter and a `true` value as the `operationResult` parameter: -```js lines +```js return callback(null, true); ``` - - - - - ### Return a failure If the change email operation failed, return the `callback` function, and pass a `null` value as the `error` parameter and a `false` value as the `operationResult` parameter: -```js lines +```js return callback(null, false); ``` - - - - - ### Return an error If an error occurred, return the `callback` function, and pass relevant error information as the `error` parameter: -```js lines +```js return callback(new Error("My custom error message.")); ``` - - - - - - -## Learn more - -* [Change Password Script Templates](/docs/authenticate/database-connections/custom-db/templates/change-password) -* [Create Script Templates](/docs/authenticate/database-connections/custom-db/templates/create) -* [Delete Script Templates](/docs/authenticate/database-connections/custom-db/templates/delete) -* [Get User Script Templates](/docs/authenticate/database-connections/custom-db/templates/get-user) -* [Login Script Templates](/docs/authenticate/database-connections/custom-db/templates/login) -* [Verify Script Templates](/docs/authenticate/database-connections/custom-db/templates/verify) diff --git a/main/docs/authenticate/database-connections/custom-db/templates/change-password.mdx b/main/docs/authenticate/database-connections/custom-db/templates/change-password.mdx index 1379cfb146..dbf26d662c 100644 --- a/main/docs/authenticate/database-connections/custom-db/templates/change-password.mdx +++ b/main/docs/authenticate/database-connections/custom-db/templates/change-password.mdx @@ -1,7 +1,9 @@ --- -description: Describes custom database action script templates for changing a user's password. -title: Change Password Script Templates +title: Change Password Database Action Script and Templates +sidebarTitle: Change Password +description: The Change Password script runs during when you or the user start a password change workflow. --- + The Change Password script implements the defined function to change the specified user's password in the external database. We recommend naming this function `changePassword`. The script is used only in a [legacy authentication scenario](/docs/authenticate/database-connections/custom-db/overview-custom-db-connections) and is required if you want to change a user's password in the external database. It will execute when a user performs a [password reset workflow](/docs/customize/login-pages/classic-login/customize-password-reset-page), or when a password change workflow is started from the Auth0 Dashboard or the Auth0 Management API. @@ -23,11 +25,6 @@ The `changePassword` function accepts three parameters and returns a `callback` changePassword(email, newPassword, callback): function ``` - - - - - | Parameter | Type | Description | | --- | --- | --- | | `email` | String | User's email address in Auth0 and external database. | @@ -74,22 +71,11 @@ function changePassword(email, newPassword, callback) { } ``` - - - - - ### Encryption - - -Avoid logging, storing, or transporting the password credential anywhere in its unencrypted form. - - +To prevent any potential data leak, encrypt the password value using a cryptographic hash encryption library such as `bcrypt`. Avoid logging, storing, or transporting the password credential anywhere in its unencrypted form. -To prevent any potential data leak, encrypt the password value using a cryptographic hash encryption library such as `bcrypt`. - -#### Example +For example: ```js lines bcrypt.hash(password, 10, function (err, hash) { @@ -101,11 +87,6 @@ bcrypt.hash(password, 10, function (err, hash) { }); ``` - - - - - ## Callback function The `callback` function accepts two parameters and is used to pass error data or indicate the result of the operation. @@ -116,11 +97,6 @@ The `callback` function accepts two parameters and is used to pass error data or callback(error, operationResult | resultObj): function ``` - - - - - | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `error` | Object | Required | Contains error data. | @@ -129,80 +105,42 @@ callback(error, operationResult | resultObj): function ### Return a success -If the change password operation succeeded, return the `callback` function, and pass a `null` value as the `error` parameter and a `true` value as the `operationResult` parameter. - -#### Example +If the change password operation succeeded, return the `callback` function, and pass a `null` value as the `error` parameter and a `true` value as the `operationResult` parameter. For example: ```js lines return callback(null, true); ``` - - - - - ### Return a success and update last_password_reset attribute -If the change password operation succeeded, return the `callback` function, and pass a `null` value as the `error` parameter and an object value as the `profile` parameter. If `last_password_reset` attribute is provided in the object, it will be updated on the user's profile. - -#### Example +If the change password operation succeeded, return the `callback` function, and pass a `null` value as the `error` parameter and an object value as the `profile` parameter. If `last_password_reset` attribute is provided in the object, it will be updated on the user's profile. For example: ```js lines return callback(null, { "last_password_reset": Date.now() }); ``` - - - - - ### Return a failure -If the change password operation failed, return the `callback` function, and pass a `null` value as the `error` parameter and a `false` value as the `operationResult` parameter. - -#### Example +If the change password operation failed, return the `callback` function, and pass a `null` value as the `error` parameter and a `false` value as the `operationResult` parameter. For example: ```js lines return callback(null, false); ``` - - - - - ### Return an error -If an error occurs, return `callback` function, and pass relevant error information as the `error` parameter. - -#### Example +If an error occurs, return `callback` function, and pass relevant error information as the `error` parameter. For example: ```js lines return callback(new Error("My custom error message.")); ``` - - - - - ## Language-specific script examples Auth0 provides sample scripts for use with the following languages/technologies: -* [JavaScript](#javascript) -* [ASP.NET Membership Provider (MVC3 - Universal Providers)](#asp-net-membership-provider-mvc3-universal-providers-) -* [ASP.NET Membership Provider (MVC4 - Simple Membership)](#asp-net-membership-provider-mvc4-simple-membership-) -* [MongoDB](#mongodb) -* [MySQL](#mysql) -* [PostgreSQL](#postgresql) -* [SQL Server](#sql-server) -* [Windows Azure SQL Database](#windows-azure-sql-database) - -### JavaScript - -```javascript lines expandable + +```javascript JavaScript lines expandable function changePassword(email, newPassword, callback) { // This script should change the password stored for the current user in your // database. It is executed when the user clicks on the confirmation link @@ -231,14 +169,8 @@ function changePassword(email, newPassword, callback) { } ``` - - - - - -### ASP.NET Membership Provider (MVC3 - Universal Providers) - -```javascript lines expandable +```javascript ASP.NET MVC3 lines expandable +// For ASP.NET Membership Provider (MVC3 - Universal Providers): function changePassword(email, newPassword, callback) { var crypto = require('crypto'); var Connection = require('tedious').Connection; @@ -309,14 +241,8 @@ function changePassword(email, newPassword, callback) { } ``` - - - - - -### ASP.NET Membership Provider (MVC4 - Simple Membership) - -```javascript lines expandable +```javascript ASP.NET MVC4 lines expandable +// For ASP.NET Membership Provider (MVC4 - Simple Membership): function changePassword(email, newPassword, callback) { var crypto = require('crypto'); var Connection = require('tedious').Connection; @@ -425,14 +351,7 @@ function changePassword(email, newPassword, callback) { } ``` - - - - - -### MongoDB - -```javascript lines +```javascript MongoDB lines function changePassword(email, newPassword, callback) { const bcrypt = require('bcrypt'); const MongoClient = require('mongodb@3.1.4').MongoClient; @@ -456,14 +375,7 @@ function changePassword(email, newPassword, callback) { } ``` - - - - - -### MySQL - -```javascript lines +```javascript MySQL lines function changePassword(email, newPassword, callback) { const mysql = require('mysql'); const bcrypt = require('bcrypt'); @@ -485,14 +397,7 @@ function changePassword(email, newPassword, callback) { } ``` - - - - - -### PostgreSQL - -```javascript lines +```javascript PostgreSQL lines function changePassword (email, newPassword, callback) { //this example uses the "pg" library //more info here: https://github.com/brianc/node-postgres @@ -515,14 +420,7 @@ function changePassword (email, newPassword, callback) { } ``` - - - - - -### SQL Server - -```javascript lines expandable +```javascript SQL Server lines expandable function changePassword (email, newPassword, callback) { //this example uses the "tedious" library //more info here: http://tediousjs.github.io/tedious/ @@ -563,14 +461,7 @@ function changePassword (email, newPassword, callback) { } ``` - - - - - -### Windows Azure SQL Database - -```javascript lines expandable +```javascript Azure SQL Database lines expandable function changePassword (email, newPassword, callback) { //this example uses the "tedious" library //more info here: http://pekim.github.io/tedious/index.html @@ -614,17 +505,4 @@ function changePassword (email, newPassword, callback) { }); } ``` - - - - - - -## Learn more - -* [Create Script Templates](/docs/authenticate/database-connections/custom-db/templates/create) -* [Delete Script Templates](/docs/authenticate/database-connections/custom-db/templates/delete) -* [Get User Script Templates](/docs/authenticate/database-connections/custom-db/templates/get-user) -* [Login Script Templates](/docs/authenticate/database-connections/custom-db/templates/login) -* [Verify Script Templates](/docs/authenticate/database-connections/custom-db/templates/verify) -* [Change Email Script Template](/docs/authenticate/database-connections/custom-db/templates/change-email) + diff --git a/main/docs/authenticate/database-connections/custom-db/templates/create.mdx b/main/docs/authenticate/database-connections/custom-db/templates/create.mdx index dc7c15579e..4cdc9c13bd 100644 --- a/main/docs/authenticate/database-connections/custom-db/templates/create.mdx +++ b/main/docs/authenticate/database-connections/custom-db/templates/create.mdx @@ -1,7 +1,9 @@ --- -description: Describes custom database action script templates for user creation. -title: Create Script Templates +title: Create User Database Action Script and Templates +sidebarTitle: Create User +description: The Create script runs when a user signs up through Universal Login or when you create a user through the Auth0 Dashboard or Management API. --- + The Create script implements the defined function when a user is created. We recommend naming this function `create`. This script is optional. If it's enabled, when a user signs up through Universal Login or is created through the Auth0 Dashboard or Auth0 Management API, Auth0 will run the script to create a corresponding user record in the external database. @@ -23,23 +25,16 @@ The `create` function should: The `create` function accept two parameters and returns a `callback` function: -```js lines +```js create(user, callback): function ``` - - - - - | Parameter | Description | | --- | --- | | `user` | Object. Contains [user profile data](#user-object-parameter) sourced from the user creation process. | | `callback` | Function. Used to pass error data through the pipeline. | -### Example - -This is a pseudo-JavaScript example of how you could implement the `create` function. For language-specific examples, read [Language-specific script examples](#language-specific-script-examples). +This is a pseudo-JavaScript example of how you could implement the `create` function: ```javascript lines expandable function create(user, callback) { @@ -70,24 +65,13 @@ function create(user, callback) { } ``` - - - - - ### Encryption - +Encrypt the password value using a cryptographic hash encryption library such as `bcrypt` to prevent any potential data leak. Avoid logging, storing, or transporting the password credential anywhere in its unencrypted form. -Avoid logging, storing, or transporting the password credential anywhere in its unencrypted form. +For example: - - -Encrypt the password value using a cryptographic hash encryption library such as `bcrypt` to prevent any potential data leak. - -#### Example - -```js lines +```js bcrypt.hash(password, 10, function (err, hash) { if (err) { return callback(err); @@ -97,85 +81,54 @@ bcrypt.hash(password, 10, function (err, hash) { }); ``` - - - - - ## Callback function The `callback` function accepts one parameter and returns a function. ### Definition -```js lines +```js callback(error): function ``` - - - - - | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `error` | Object | Required | Contains error data. | -### Return a success - -If the user creation operation succeeded, return the `callback` function, and pass a `null` value as the `error` parameter. - -#### Example - -```js lines -return callback(null); -``` - - - - - - -### Return an error - -If an error occurs, return the callback function, and pass relevant error information to the the `error` parameter. - -#### ValidationError type object +### ValidationError type object The `ValidationError` custom error type object allows you to pass data that will be displayed in your [Tenant Logs](/docs/deploy-monitor/logs). -##### Constructor - The `ValidationError` constructor accepts up to two parameters: -```js lines +```js new ValidationError(errorCode[, message]): ValidationError ``` - - - - - | Parameter | Description | | --- | --- | | `errorCode` | (Required) String. Specifies the type of error. | | `message` | (Optional) String. Contains information about the error. | -#### Return error that user already exists - -If you return an error with the value of `user_exists` for the `errorCode` parameter, Auth0 will record an `fs` tenant log event. +### Return a success -##### Example +If the user creation operation succeeded, return the `callback` function, and pass a `null` value as the `error` parameter: -```js wrap lines -return callback(new ValidationError("user_exists", "My custom error message.")); +```js +return callback(null); ``` +### Return an error +If an error occurs, return the callback function, and pass relevant error information to the the `error` parameter. +For example, you can return an error with the value of `user_exists` for the `errorCode` parameter: +```js wrap lines +return callback(new ValidationError("user_exists", "My custom error message.")); +``` +If you do, Auth0 records an `fs` tenant log event. | Tenant Log Event Field | Value | | --- | --- | @@ -208,11 +161,9 @@ The `user_metadata` and `app_metadata` properties do not need to be stored in yo ### Custom sign up fields -If you [create and use custom fields](/docs/libraries/custom-signup#using-the-api) during the sign up process, they will be included in the `user` object. - -### Example +If you [create and use custom fields](/docs/libraries/custom-signup#using-the-api) during the sign up process, they will be included in the `user` object. For example: -```js lines +```json { client_id: "8tkMo6n1QkKOazqPcSQd8wU7LzXYibgK", tenant: "{yourAuth0Tenant}", @@ -228,28 +179,12 @@ If you [create and use custom fields](/docs/libraries/custom-signup#using-the-ap } ``` - - - - - ## Language-specific script examples Auth0 provides sample scripts for use with the following languages/technologies: -* [JavaScript](#javascript) -* [ASP.NET Membership Provider (MVC3 - Universal Providers)](#asp-net-membership-provider-mvc3-universal-providers-) -* [ASP.NET Membership Provider (MVC4 - Simple Membership)](#asp-net-membership-provider-mvc4-simple-membership-) -* [MongoDB](#mongodb) -* [MySQL](#mysql) -* [PostgreSQL](#postgresql) -* [SQL Server](#sql-server) -* [Windows Azure SQL Database](#windows-azure-sql-database) -* [Request with Basic Auth](#request-with-basic-auth) - -### JavaScript - -```javascript lines expandable + +```javascript JavaScript lines expandable function create(user, callback) { // This script should create a user entry in your existing database. It will // be executed when a user attempts to sign up, or when a user is created @@ -279,14 +214,8 @@ function create(user, callback) { } ``` - - - - - -### ASP.NET Membership Provider (MVC3 - Universal Providers) - -```javascript lines expandable +```javascript ASP.Net MVC3 lines expandable +// For ASP.NET Membership Provider (MVC3 - Universal Providers) function create(user, callback) { const crypto = require('crypto'); const sqlserver = require('tedious@1.11.0'); @@ -388,14 +317,8 @@ function create(user, callback) { } ``` - - - - - -### ASP.NET Membership Provider (MVC4 - Simple Membership) - -```javascript lines expandable +```javascript ASP.Net MVC4 lines expandable +// For ASP.NET Membership Provider (MVC4 - Simple Membership) function create(user, callback) { const crypto = require('crypto'); const sqlserver = require('tedious@1.11.0'); @@ -472,14 +395,7 @@ function create(user, callback) { } ``` - - - - - -### MongoDB - -```javascript lines expandable +```javascript MongoDB lines expandable function create(user, callback) { const bcrypt = require('bcrypt'); const MongoClient = require('mongodb@3.1.4').MongoClient; @@ -510,14 +426,7 @@ function create(user, callback) { } ``` - - - - - -### MySQL - -```javascript lines +```javascript MySQL lines expandable function create(user, callback) { const mysql = require('mysql'); const bcrypt = require('bcrypt'); @@ -544,14 +453,7 @@ function create(user, callback) { } ``` - - - - - -### PostgreSQL - -```javascript lines +```javascript PostgreSQL lines expandable function create(user, callback) { //this example uses the "pg" library //more info here: https://github.com/brianc/node-postgres @@ -574,14 +476,7 @@ function create(user, callback) { } ``` - - - - - -### SQL Server - -```javascript lines expandable +```javascript SQL Sever lines expandable function create(user, callback) { //this example uses the "tedious" library //more info here: http://pekim.github.io/tedious/index.html @@ -622,14 +517,7 @@ function create(user, callback) { } ``` - - - - - -### Windows Azure SQL Database - -```javascript lines expandable +```javascript Azure SQL Database lines expandable function create (user, callback) { //this example uses the "tedious" library //more info here: http://pekim.github.io/tedious/index.html @@ -673,14 +561,7 @@ function create (user, callback) { } ``` - - - - - -### Request with Basic Auth - -```javascript lines +```javascript Basic Auth request lines function create(user, callback) { const request = require('request'); request.post({ @@ -694,25 +575,12 @@ function create(user, callback) { }); } ``` - - - - - + ## Troubleshoot -If you are unable to create a user in either your legacy database or Auth0: +If you are unable to create a user in either your external user store or Auth0: 1. Check the `console.log()` statements with [Actions Real-Time Logs](/docs/customize/actions/actions-real-time-logs). 2. Find the user in your legacy database and delete accordingly. If the partial user state is in Auth0, use the Management API's [Delete a User](https://auth0.com/docs/api/management/v2#!/Users/delete_users_by_id) endpoint or [Delete a Connection User](https://auth0.com/docs/api/management/v2#!/Connections/delete_users_by_email) endpoint. 3. Make sure **Import Mode** is disabled, then configure the create script. - -## Learn more - -* [Login Script Templates](/docs/authenticate/database-connections/custom-db/templates/login) -* [Get User Script Templates](/docs/authenticate/database-connections/custom-db/templates/get-user) -* [Delete Script Templates](/docs/authenticate/database-connections/custom-db/templates/delete) -* [Verify Script Templates](/docs/authenticate/database-connections/custom-db/templates/verify) -* [Change Password Script Templates](/docs/authenticate/database-connections/custom-db/templates/change-password) -* [Change Email Script Template](/docs/authenticate/database-connections/custom-db/templates/change-email) diff --git a/main/docs/authenticate/database-connections/custom-db/templates/delete.mdx b/main/docs/authenticate/database-connections/custom-db/templates/delete.mdx index 40cb2e37dd..eb645acf66 100644 --- a/main/docs/authenticate/database-connections/custom-db/templates/delete.mdx +++ b/main/docs/authenticate/database-connections/custom-db/templates/delete.mdx @@ -1,7 +1,9 @@ --- -description: Describes custom database action script templates for user deletion. -title: Delete Script Templates +title: Delete User Database Action Script and Templates +sidebarTitle: Delete User +description: The Delete script runs when you need to delete a specified user from your external user store, and is required if you want to additionally delete the user from Auth0 in the same operation. --- + The Delete script implements the defined function to delete a specified user from an external database. We recommend naming this function `deleteUser`. The script is only used in a [legacy authentication scenario](/docs/authenticate/database-connections/custom-db/overview-custom-db-connections), and is required if you want to delete a user from Auth0 and your external database in the same operation. @@ -22,19 +24,12 @@ The `deleteUser` function accepts two parameters and returns a `callback` functi deleteUser(id, callback): function ``` - - - - - | Parameter | Type | Description | | --- | --- | --- | | `id` | String | User’s identity `user_id`. Does not start with `auth0`. | | `callback` | Function | Used to pass error or profile data through the pipeline. | -### Example - -This is a pseudo-JavaScript example of how you could implement the `deleteUser` function. For language-specific examples, read [Language-specific script examples](/docs/authenticate/database-connections/custom-db/templates/delete#language-specific-script-examples). +This is a pseudo-JavaScript example of how you could implement the `deleteUser` function: ```javascript lines function deleteUser(id, callback) { @@ -58,11 +53,6 @@ function deleteUser(id, callback) { } ``` - - - - - ## Callback function The `callback` function accepts one parameter and returns a function. @@ -73,61 +63,32 @@ The `callback` function accepts one parameter and returns a function. callback(error): function ``` - - - - - | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `error` | Object | Required | Contains error data. | ### Return a success -If the user was successfully deleted in your external database, return the `callback` function, and pass a `null` value for the `error` parameter. - -#### Example +If the user was successfully deleted in your external database, return the `callback` function, and pass a `null` value for the `error` parameter: ```js lines return callback(null); ``` - - - - - ### Return an error -If an error occurs, return the `callback` function, and pass relevant error information to the `error` parameter. - -#### Example +If an error occurs, return the `callback` function, and pass relevant error information to the `error` parameter: ```js lines return callback(new Error("My custom error message.")); ``` - - - - - ## Language-specific script examples Auth0 provides sample scripts for use with the following languages/technologies: -* [JavaScript](#javascript) -* [ASP.NET Membership Provider (MVC3 - Universal Providers)](#asp-net-membership-provider-mvc3-universal-providers-) -* [ASP.NET Membership Provider (MVC4 - Simple Membership)](#asp-net-membership-provider-mvc4-simple-membership-) -* [MongoDB](#mongodb) -* [MySQL](#mysql) -* [PostgreSQL](#postgresql) -* [SQL Server](#sql-server) -* [Windows Azure SQL Database](#windows-azure-sql-database) - -### JavaScript - -```javascript lines + +```javascript JavaScript lines function remove (id, callback) { // This script remove a user from your existing database. // It is executed whenever a user is deleted from the Management API or Auth0 dashboard. @@ -143,14 +104,8 @@ function remove (id, callback) { } ``` - - - - - -### ASP.NET Membership Provider (MVC3 - Universal Providers) - -```javascript lines expandable +```javascript ASP.NET MVC3 lines expandable +// For ASP.NET Membership Provider (MVC3 - Universal Providers) function remove(id, callback) { const sqlserver = require('tedious@1.11.0'); const Connection = sqlserver.Connection; @@ -195,14 +150,8 @@ function remove(id, callback) { } ``` - - - - - -### ASP.NET Membership Provider (MVC4 - Simple Membership) - -```javascript lines expandable +```javascript ASP.NET MVC4 lines expandable +// For ASP.NET Membership Provider (MVC4 - Simple Membership) function remove(id, callback) { const sqlserver = require('tedious@1.11.0'); const Connection = sqlserver.Connection; @@ -247,14 +196,7 @@ function remove(id, callback) { } ``` - - - - - -### MongoDB - -```javascript lines +```javascript MongoDB lines function remove(id, callback) { const MongoClient = require('mongodb@3.1.4').MongoClient; const client = new MongoClient('mongodb://user:pass@mymongoserver.com'); @@ -271,14 +213,7 @@ function remove(id, callback) { } ``` - - - - - -### MySQL - -```javascript lines +```javascript MySQL lines function remove(id, callback) { const mysql = require('mysql'); const connection = mysql({ @@ -296,14 +231,7 @@ function remove(id, callback) { } ``` - - - - - -### PostgreSQL - -```javascript lines +```javascript PostgreSQL lines function remove(id, callback) { //this example uses the "pg" library //more info here: https://github.com/brianc/node-postgres @@ -322,14 +250,7 @@ function remove(id, callback) { } ``` - - - - - -### SQL Server - -```javascript lines expandable +```javascript SQL Server lines expandable function remove(id, callback) { // this example uses the "tedious" library // more info here: http://pekim.github.io/tedious/index.html @@ -365,14 +286,7 @@ function remove(id, callback) { } ``` - - - - - -### Windows Azure SQL Database - -```javascript lines expandable +```javascript Azure SQL Database lines expandable function remove (id, callback) { // this example uses the "tedious" library // more info here: http://pekim.github.io/tedious/index.html @@ -407,17 +321,4 @@ function remove (id, callback) { }); } ``` - - - - - - -## Learn more - -* [Change Password Script Templates](/docs/authenticate/database-connections/custom-db/templates/change-password) -* [Create Script Templates](/docs/authenticate/database-connections/custom-db/templates/create) -* [Get User Script Templates](/docs/authenticate/database-connections/custom-db/templates/get-user) -* [Login Script Templates](/docs/authenticate/database-connections/custom-db/templates/login) -* [Verify Script Templates](/docs/authenticate/database-connections/custom-db/templates/verify) -* [Change Email Script Template](/docs/authenticate/database-connections/custom-db/templates/change-email) + diff --git a/main/docs/authenticate/database-connections/custom-db/templates/get-user.mdx b/main/docs/authenticate/database-connections/custom-db/templates/get-user.mdx index 1a3abfe01c..ca15a39b2b 100644 --- a/main/docs/authenticate/database-connections/custom-db/templates/get-user.mdx +++ b/main/docs/authenticate/database-connections/custom-db/templates/get-user.mdx @@ -1,23 +1,28 @@ --- -description: Describes custom database action script templates for user search. -title: Get User Script Templates +title: Get User Database Action Script and Templates +sidebarTitle: Get User +description: The Get User script runs when Auth0 needs to determine the current state of existence of a user. --- + The Get User script implements the function executed to determine the current state of existence of a user. We recommend naming this function `getUser`. -This script is required for [automatic migration](/docs/manage-users/user-migration/configure-automatic-migration-from-your-database), and conditionally required for legacy authentication depending on the operations configured for the connection. Auth0 recommends you set permanent `user_id` on the returned user profile to avoid creating duplicate users. +This script is required for [automatic migration to Auth0](/docs/manage-users/user-migration/configure-automatic-migration-from-your-database), and conditionally required without migration enabled depending on the operations configured for the connection. Auth0 recommends you set permanent `user_id` on the returned user profile to avoid creating duplicate users. + +When **Import Users to Auth0** is enabled, the Get User script runs when a user attempts to sign up to check if the user already exists in the external user store. -If automatic migration is configured for the connection and the user profile has not yet been created, the script is executed whenever the following operations occur: +The Get User script also runs when a user attempts to: -* Change email -* Sign up -* Password reset +* Change their email address ([Change Email](/docs/authenticate/database-connections/custom-db/templates/change-email) script) +* Log in ([Login](/docs/authenticate/database-connections/custom-db/templates/login) script) +* Change their password ([Change Password](/docs/authenticate/database-connections/custom-db/templates/change-password) script) -If legacy authentication is configured for the connection, the script is executed whenever the following operations occur: +When **Import Users to Auth0** is disabled, the Get User script runs when a user attempts to sign up to check if the user already exists in the external user store. If a user already exists in the external user store, it does not execute the Create script. -* Create user -* Change email -* Change password -* Password reset +The Get User script also runs when a user attempts to: + +* Sign up ([Create](/docs/authenticate/database-connections/custom-db/templates/create) script) +* Change their email address ([Change Email](/docs/authenticate/database-connections/custom-db/templates/change-email) script) +* Change or reset their password ([Change Password](/docs/authenticate/database-connections/custom-db/templates/change-password) script) ## Get User function @@ -35,19 +40,12 @@ The `getUser` function accepts two parameters and returns a `callback` function: getUser(email, callback): function ``` - - - - - | Parameter | Type | Description | | --- | --- | --- | | `email` | String | The user's email address. | | `callback` | Function | Used to pass error or profile data through the pipeline | -### Example - -This is a pseudo-Javascript example of how you could implement the `getUser` function. For language-specific examples, read [Language-specific script examples](#language-specific-script-examples). +This is a pseudo-JavaScript example of how you could implement the `getUser` function: ```javascript lines expandable function getUser(email, callback) { @@ -80,11 +78,6 @@ function getUser(email, callback) { } ``` - - - - - ## Callback function The `callback` function is used to pass user profile data or error data through the pipeline. @@ -97,11 +90,6 @@ The `callback` function accepts up to two parameters and returns a function: callback(error[,profile]): function ``` - - - - - | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `error` | Object | Required | Contains error data. | @@ -110,14 +98,10 @@ callback(error[,profile]): function ### Return the user profile (user found) - The profile data returned by the Get User script for a user must be consistent with the profile data returned by the Login script. - -If the user is found, pass a `null` value to the `error` parameter, and pass the user's profile data to the `profile` parameter in [normalized form](/docs/manage-users/user-accounts/user-profiles/normalized-user-profile-schema). In addition to the standard fields, you can include the `user_metadata`, `app_metadata`, and `mfa_factors` fields. - -#### Example +If the user is found, pass a `null` value to the `error` parameter, and pass the user's profile data to the `profile` parameter in [normalized form](/docs/manage-users/user-accounts/user-profiles/normalized-user-profile-schema): ```js lines return callback(null, { @@ -141,59 +125,33 @@ return callback(null, { }); ``` - - - - +In addition to the standard fields, you can include the `user_metadata`, `app_metadata`, and `mfa_factors` fields. ### Return no user profile (user not found) -If the user is not found, pass a `null` value to the `error` parameter, and omit the `profile` parameter. - -#### Example +If the user is not found, pass a `null` value to the `error` parameter, and omit the `profile` parameter: ```js lines return callback(null); ``` - - - - - ### Return an error -If an error occurs, pass error data to the `error` parameter with relevant information about what went wrong. For more information, read [Troubleshoot Custom Databases](/docs/authenticate/database-connections/custom-db/error-handling). - -#### Example +If an error occurs, pass error data to the `error` parameter with relevant information about what went wrong: ```js lines return callback(new Error("My custom error message.")); ``` - - - - +For more information, read [Troubleshoot Custom Databases](/docs/authenticate/database-connections/custom-db/error-handling). ## Language-specific script examples Auth0 provides sample scripts for use with the following languages/technologies: -* [JavaScript](#javascript) -* [ASP.NET Membership Provider (MVC3 - Universal Providers)](#asp-net-membership-provider-mvc3-universal-providers-) -* [ASP.NET Membership Provider (MVC4 - Simple Membership)](#asp-net-membership-provider-mvc4-simple-membership-) -* [MongoDB](#mongodb) -* [MySQL](#mysql) -* [PostgreSQL](#postgresql) -* [SQL Server](#sql-server) -* [Windows Azure SQL Database](#windows-azure-sql-database) -* [Axios](#axios) -* [Stormpath](#stormpath) - -### JavaScript + -```javascript lines +```javascript Javascript lines function getByEmail(email, callback) { // This script should retrieve a user profile from your existing database, // without authenticating the user. @@ -214,14 +172,8 @@ function getByEmail(email, callback) { } ``` - - - - - -### ASP.NET Membership Provider (MVC3 - Universal Providers) - -```javascript lines expandable +```javascript ASP.NET MVC3 lines expandable +// For ASP.NET Membership Provider (MVC3 - Universal Providers) function getByEmail(email, callback) { const sqlserver = require('tedious@1.11.0'); const Connection = sqlserver.Connection; @@ -269,14 +221,8 @@ function getByEmail(email, callback) { } ``` - - - - - -### ASP.NET Membership Provider (MVC4 - Simple Membership) - -```javascript lines expandable +```javascript ASP.NET MVC4 lines expandable +// For ASP.NET Membership Provider (MVC4 - Simple Membership) function getByEmail(email, callback) { const sqlserver = require('tedious@1.11.0'); const Connection = sqlserver.Connection; @@ -323,14 +269,7 @@ function getByEmail(email, callback) { } ``` - - - - - -### MongoDB - -```javascript lines +```javascript MongoDB lines function getByEmail(email, callback) { const MongoClient = require('mongodb@5.1.0').MongoClient; const client = new MongoClient('mongodb://user:pass@mymongoserver.com'); @@ -352,14 +291,7 @@ function getByEmail(email, callback) { } ``` - - - - - -### MySQL - -```javascript lines +```javascript MySQL lines function getByEmail(email, callback) { const mysql = require('mysql'); const connection = mysql({ @@ -382,14 +314,7 @@ function getByEmail(email, callback) { } ``` - - - - - -### PostgreSQL - -```javascript lines +```javascript PostgreSQL lines function loginByEmail(email, callback) { //this example uses the "pg" library //more info here: https://github.com/brianc/node-postgres @@ -414,14 +339,7 @@ function loginByEmail(email, callback) { } ``` - - - - - -### SQL Server - -```javascript lines expandable +```javascript SQL Server lines expandable function getByEmail(email, callback) { //this example uses the "tedious" library //more info here: http://pekim.github.io/tedious/index.html @@ -461,14 +379,7 @@ function getByEmail(email, callback) { } ``` - - - - - -### Windows Azure SQL Database - -```javascript lines +```javascript Azure SQL Database lines function getByEmail (name, callback) { var profile = { user_id: "103547991597142817347", @@ -482,14 +393,7 @@ function getByEmail (name, callback) { } ``` - - - - - -### Axios - -```js lines expandable +```js Axios lines expandable async function getUserAsync(email, callback) { //should be updated as new versions of axios are made available (https://auth0-extensions.github.io/canirequire/#axios) const axios = require("axios@0.22.0"); @@ -539,14 +443,7 @@ async function getUserAsync(email, callback) { } ``` - - - - - -### Stormpath - -```javascript lines expandable +```javascript Stormpath lines expandable function getByEmail(email, callback) { // Replace the {yourStormpathClientId} with your Stormpath ID var url = 'https://api.stormpath.com/v1/applications/{yourStormpathClientId}/accounts'; @@ -577,17 +474,4 @@ function getByEmail(email, callback) { }); } ``` - - - - - - -## Learn more - -* [Change Password Script Templates](/docs/authenticate/database-connections/custom-db/templates/change-password) -* [Create Script Templates](/docs/authenticate/database-connections/custom-db/templates/create) -* [Delete Script Templates](/docs/authenticate/database-connections/custom-db/templates/delete) -* [Login Script Templates](/docs/authenticate/database-connections/custom-db/templates/login) -* [Verify Script Templates](/docs/authenticate/database-connections/custom-db/templates/verify) -* [Change Email Script Template](/docs/authenticate/database-connections/custom-db/templates/change-email) + diff --git a/main/docs/authenticate/database-connections/custom-db/templates/login.mdx b/main/docs/authenticate/database-connections/custom-db/templates/login.mdx index 513fb31576..838386fbe8 100644 --- a/main/docs/authenticate/database-connections/custom-db/templates/login.mdx +++ b/main/docs/authenticate/database-connections/custom-db/templates/login.mdx @@ -1,7 +1,9 @@ --- -description: Describes custom database action script templates for user login. -title: Login Script Templates +title: Login Database Action Script and Templates +sidebarTitle: Login +description: The Login script runs when a user tries to log in. --- + The Login script implements the function executed each time a user is required to authenticate. We recommend naming this function `login`. This script is required for both legacy authentication and for [automatic migration](/docs/manage-users/user-migration/configure-automatic-migration-from-your-database). If automatic migration is configured for the connection, the migration process triggers after the first time the user logs in successfully. Auth0 recommends you set permanent `user_id` on the returned user profile to avoid creating duplicate users. @@ -22,20 +24,13 @@ The `login` function accepts three parameters and returns a `callback` function: login(userNameOrEmail, password, callback): function ``` - - - - - | Parameter | Type | Description | | --- | --- | --- | | `userNameOrEmail` | String | The user's username or email. | | `password` | String | The user's password in plain text. | | `callback` | Function | Used to pass error or profile data through the pipeline. | -### Example - -This is a pseudo-JavaScript example of how you could implement the `login` function. For language-specific examples, read [Language-specific script examples](#language-specific-script-examples). +This is a pseudo-JavaScript example of how you could implement the `login` function: ```javascript lines expandable function login(userNameOrEmail, password, callback) { @@ -68,22 +63,9 @@ function login(userNameOrEmail, password, callback) { } ``` - - - - - ### Encryption - - -Avoid logging, storing, or transporting the password credential anywhere in its unencrypted form. - - - -Encrypt the `password` value using a cryptographic hash encryption library such as `bcrypt` to prevent any potential data leak. - -#### Example +Encrypt the `password` value using a cryptographic hash encryption library such as `bcrypt` to prevent any potential data leak. Avoid logging, storing, or transporting the password credential anywhere in its unencrypted form. For example: ```js lines bcrypt.hash(password, 10, function (err, hash) { @@ -95,11 +77,6 @@ bcrypt.hash(password, 10, function (err, hash) { }); ``` - - - - - ## Callback function The `callback` function is used to pass user profile data or error data through the pipeline. @@ -112,11 +89,6 @@ The `callback` function accepts up to two parameters and returns a function: callback(error[, profile]): function ``` - - - - - | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `error` | Object | Required | Contains error data. | @@ -125,14 +97,10 @@ callback(error[, profile]): function ### Return the user profile - The profile data returned by the Login script for a user must be consistent with the profile data returned by the Get User script. - -If the user authenticates successfully, their profile data must be returned in the `profile` object in [normalized form](/docs/manage-users/user-accounts/user-profiles/normalized-user-profile-schema). In addition to the standard fields, you can include the `user_metadata`, `app_metadata`, and `mfa_factors` fields. - -#### Example +If the user authenticates successfully, their profile data must be returned in the `profile` object in [normalized form](/docs/manage-users/user-accounts/user-profiles/normalized-user-profile-schema). For example: ```js lines return callback(null, { @@ -156,51 +124,35 @@ return callback(null, { }); ``` - - - - - -### Return an error - -If an error occurs, the `error` parameter should contain relevant information about what went wrong. - -#### WrongUsernameOrPasswordError type object +### WrongUsernameOrPasswordError type object The `WrongUsernameOrPasswordError` custom error type object allows you to pass data that will be displayed in your [Tenant Logs](/docs/deploy-monitor/logs). -##### Constructor - The `WrongUsernameOrPasswordError` constructor accepts up to two parameters: ```js wrap lines new WrongUsernameOrPasswordError(userNameOrEmail[, message]): WrongUsernameOrPasswordError ``` - - - - - | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `userNameOrEmail` | String | Required | Contains the user's username or email, or a `null` value. | | `message` | String | Optional | Contains information about the error. | -#### Return error with username or email -If you return an error with a value for the `userNameOrEmail` field, Auth0 will record an `fp` tenant log event. - -##### Example - -```js wrap lines -return callback(new WrongUsernameOrPasswordError(userNameOrEmail, "My custom error message")); -``` +In addition to the standard fields, you can include the `user_metadata`, `app_metadata`, and `mfa_factors` fields. +### Return an error +If an error occurs, the `error` parameter should contain relevant information about what went wrong. +For example, you can return an error with a value for the `userNameOrEmail` field +```js wrap +return callback(new WrongUsernameOrPasswordError(userNameOrEmail, "My custom error message")); +``` +In this case, Auth0 records an `fp` tenant log event. | Tenant Log Event Field | Value | | --- | --- | @@ -208,20 +160,13 @@ return callback(new WrongUsernameOrPasswordError(userNameOrEmail, "My custom err | **Event** | Failed Login (Incorrect Password) | | **Description** | `My custom error message` | -#### Return error without username or email - -If you return an error with a `null` value for the `userNameOrEmail` field, Auth0 will record an `fu` tenant log event. +You can also return an error with a `null` value for the `userNameOrEmail` field: -##### Example - -```js wrap lines +```js wrap return callback(new WrongUsernameOrPasswordError(null, "My custom error message")); ``` - - - - +In this case, Auth0 records an `fu` tenant log event. | Tenant Log Event Field | Value | | --- | --- | @@ -239,20 +184,8 @@ If you do not enable this setting, the values returned from the external databas Auth0 provides sample scripts for use with the following languages/technologies: -* [JavaScript](#javascript) -* [ASP.NET Membership Provider (MVC3 - Universal Providers)](#asp-net-membership-provider-mvc3-universal-providers-) -* [ASP.NET Membership Provider (MVC4 - Simple Membership)](#asp-net-membership-provider-mvc4-simple-membership-) -* [MongoDB](#mongodb) -* [MySQL](#mysql) -* [PostgreSQL](#postgresql) -* [SQL Server](#sql-server) -* [Windows Azure SQL Database](#windows-azure-sql-database) -* [Axios](#axios) -* [Stormpath](#stormpath) - -### JavaScript - -```javascript lines expandable + +```javascript JavaScript lines expandable function login(email, password, callback) { // This script should authenticate a user against the credentials stored in // your database. @@ -292,14 +225,8 @@ function login(email, password, callback) { } ``` - - - - - -### ASP.NET Membership Provider (MVC3 - Universal Providers) - -```javascript lines expandable +```javascript ASP.NET MVC3 lines expandable +// For ASP.NET Membership Provider (MVC3 - Universal Providers) function login(email, password, callback) { const crypto = require('crypto'); const sqlserver = require('tedious@11.0.3'); @@ -393,14 +320,8 @@ function login(email, password, callback) { } ``` - - - - - -### ASP.NET Membership Provider (MVC4 - Simple Membership) - -```javascript lines expandable +```javascript ASP.NET MVC4 lines expandable +// For ASP.NET Membership Provider (MVC4 - Simple Membership) function login(email, password, callback) { const crypto = require('crypto'); const sqlserver = require('tedious@11.0.3'); @@ -508,14 +429,7 @@ function login(email, password, callback) { } ``` - - - - - -### MongoDB - -```javascript lines +```javascript MongoDB lines function login(email, password, callback) { const bcrypt = require('bcrypt'); const MongoClient = require('mongodb@3.1.4').MongoClient; @@ -543,14 +457,7 @@ function login(email, password, callback) { } ``` - - - - - -### MySQL - -```javascript lines +```javascript MySQL lines function login(email, password, callback) { const mysql = require('mysql'); const bcrypt = require('bcrypt'); @@ -578,14 +485,7 @@ function login(email, password, callback) { } ``` - - - - - -### PostgreSQL - -```javascript lines expandable +```javascript PostgreSQL lines expandable function login(email, password, callback) { //this example uses the "pg" library //more info here: https://github.com/brianc/node-postgres @@ -614,14 +514,7 @@ function login(email, password, callback) { } ``` - - - - - -### SQL Server - -```javascript lines expandable +```javascript SQL Server lines expandable function login(email, password, callback) { //this example uses the "tedious" library //more info here: http://pekim.github.io/tedious/index.html @@ -666,14 +559,7 @@ function login(email, password, callback) { } ``` - - - - - -### Windows Azure SQL Database - -```javascript lines expandable +```javascript Azure SQL Database lines expandable function login(email, password, callback) { //this example uses the "tedious" library //more info here: http://pekim.github.io/tedious/index.html @@ -729,14 +615,7 @@ function login(email, password, callback) { } ``` - - - - - -### Axios - -```js lines expandable +```js Axios lines expandable async function loginAsync(email, password, callback) { //should be updated as new versions of axios are made available (https://auth0-extensions.github.io/canirequire/#axios) const axios = require("axios@0.22.0"); @@ -789,14 +668,7 @@ async function loginAsync(email, password, callback) { } ``` - - - - - -### Stormpath - -```javascript lines expandable +```javascript Stormpath lines expandable function login(username, password, callback) { // Replace the {yourStormpathClientId} with your Stormpath ID var url = 'https://api.stormpath.com/v1/applications/{yourStormpathClientId}/loginAttempts'; @@ -848,17 +720,4 @@ function login(username, password, callback) { }); } ``` - - - - - - -## Learn more - -* [Change Password Script Templates](/docs/authenticate/database-connections/custom-db/templates/change-password) -* [Create Script Templates](/docs/authenticate/database-connections/custom-db/templates/create) -* [Delete Script Templates](/docs/authenticate/database-connections/custom-db/templates/delete) -* [Get User Script Templates](/docs/authenticate/database-connections/custom-db/templates/get-user) -* [Verify Script Templates](/docs/authenticate/database-connections/custom-db/templates/verify) -* [Change Email Script Template](/docs/authenticate/database-connections/custom-db/templates/change-email) + diff --git a/main/docs/authenticate/database-connections/custom-db/templates/verify.mdx b/main/docs/authenticate/database-connections/custom-db/templates/verify.mdx index 6f83cdd518..e280439542 100644 --- a/main/docs/authenticate/database-connections/custom-db/templates/verify.mdx +++ b/main/docs/authenticate/database-connections/custom-db/templates/verify.mdx @@ -1,7 +1,9 @@ --- -description: Describes custom database action script templates for user email verification. -title: Verify Script Templates +title: Verify Database Action Script and Templates +sidebarTitle: Verify +description: The Verify script runs when a user follows the link in Auth0's verification email. --- + The Verify script implements the function executed to mark the verification status of a user’s email address in an external database. We recommend naming this function `verify`. This script is only used in legacy authentication scenarios, and is required to support user email address verification. A verified email address is critical to a number of workflow scenarios in Auth0, and implementing this script will provide support for these out of the box. @@ -20,23 +22,16 @@ The `verify` function should: The `verify` function accepts two parameters and returns a `callback` function: -```js lines +```js verify(email, callback): function ``` - - - - - | Parameter | Type | Description | | --- | --- | --- | | `email` | String | User's email address. | | `callback` | Function | Used to pass error or profile data through the pipeline. | -### Example - -This is a pseudo-JavaScript example of how you could implement the `login` function. For language-specific examples, read [Language-specific script examples](/docs/authenticate/database-connections/custom-db/templates/verify#language-specific-script-examples). +This is a pseudo-JavaScript example of how you could implement the `login` function: ```javascript lines function verify(email, callback) { @@ -60,11 +55,6 @@ function verify(email, callback) { } ``` - - - - - ## Callback function The `callback` function is used to pass user profile data or error data through the pipeline. @@ -73,15 +63,10 @@ The `callback` function is used to pass user profile data or error data throug The `callback` function accepts up to two parameters and returns a function: -```js lines +```js callback(error, [verified]): function ``` - - - - - | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `error` | Object | Required | Contains error data. | @@ -89,58 +74,32 @@ callback(error, [verified]): function ### Return a success -If the user's verification status was updated successfully in the external database, pass a `null` value for the `error` parameter, and a `true` value for the `verified` parameter. +If the user's verification status was updated successfully in the external database, pass a `null` value for the `error` parameter, and a `true` value for the `verified` parameter: - +```js +callback(null, true); +``` + The Verify script does not change the value of the `email_verified` attribute in the user's Auth0 profile, regardless of what value is returned in the `callback` function. To update the `email_verified` attribute in the user's Auth0 profile, you must include the `email_verified` attribute and its value in the user profile object returned in the [Login](/docs/authenticate/database-connections/custom-db/templates/login) and [Get User](/docs/authenticate/database-connections/custom-db/templates/get-user) scripts. - -#### Example - -```js lines -callback(null, true); -``` - - - - - - ### Return an error -If an error occurs, the `error` parameter should contain relevant information about what went wrong. - -#### Example +If an error occurs, the `error` parameter should contain relevant information about what went wrong: -```js lines +```js return callback(new Error("My custom error message.")); ``` - - - - - ## Language-specific script examples Auth0 provides sample scripts for use with the following languages/technologies: -* [JavaScript](#javascript) -* [ASP.NET Membership Provider (MVC3 - Universal Providers)](#asp-net-membership-provider-mvc3-universal-providers-) -* [ASP.NET Membership Provider (MVC4 - Simple Membership)](#asp-net-membership-provider-mvc4-simple-membership-) -* [MongoDB](#mongodb) -* [MySQL](#mysql) -* [PostgreSQL](#postgresql) -* [SQL Server](#sql-server) -* [Windows Azure SQL Database](#windows-azure-sql-database) - -### JavaScript - -```javascript lines + +```javascript JavaScript lines function verify(email, callback) { // This script should mark the current user's email address as verified in // your database. @@ -167,14 +126,8 @@ function verify(email, callback) { } ``` - - - - - -### ASP.NET Membership Provider (MVC3 - Universal Providers) - -```javascript lines expandable +```javascript ASP.NET MVC3 lines expandable +// For ASP.NET Membership Provider (MVC3 - Universal Providers) function verify(email, callback) { const sqlserver = require('tedious@1.11.0'); const Connection = sqlserver.Connection; @@ -222,14 +175,8 @@ function verify(email, callback) { } ``` - - - - - -### ASP.NET Membership Provider (MVC4 - Simple Membership) - -```javascript lines expandable +```javascript ASP.NET MVC4 lines expandable +// For ASP.NET Membership Provider (MVC4 - Simple Membership) function verify (email, callback) { const sqlserver = require('tedious@1.11.0'); const Connection = sqlserver.Connection; @@ -291,14 +238,7 @@ function verify (email, callback) { } ``` - - - - - -### MongoDB - -```javascript lines +```javascript MongoDB lines function verify (email, callback) { const MongoClient = require('mongodb@3.1.4').MongoClient; const client = new MongoClient('mongodb://user:pass@mymongoserver.com'); @@ -316,14 +256,7 @@ function verify (email, callback) { } ``` - - - - - -### MySQL - -```javascript lines +```javascript MySQL lines function verify(email, callback) { const mysql = require('mysql'); const connection = mysql({ @@ -341,14 +274,7 @@ function verify(email, callback) { } ``` - - - - - -### PostgreSQL - -```javascript lines +```javascript PostgreSQL lines function verify (email, callback) { //this example uses the "pg" library //more info here: https://github.com/brianc/node-postgres @@ -367,14 +293,7 @@ function verify (email, callback) { } ``` - - - - - -### SQL Server - -```javascript lines expandable +```javascript SQL Server lines expandable function verify (email, callback) { //this example uses the "tedious" library //more info here: http://pekim.github.io/tedious/index.html @@ -410,14 +329,7 @@ function verify (email, callback) { } ``` - - - - - -### Windows Azure SQL Database - -```javascript lines expandable +```javascript Azure SQL Database lines expandable function verify (email, callback) { //this example uses the "tedious" library //more info here: http://pekim.github.io/tedious/index.html @@ -457,17 +369,4 @@ function verify (email, callback) { }); } ``` - - - - - - -## Learn more - -* [Login Script Templates](/docs/authenticate/database-connections/custom-db/templates/login) -* [Get User Script Templates](/docs/authenticate/database-connections/custom-db/templates/get-user) -* [Create Script Templates](/docs/authenticate/database-connections/custom-db/templates/create) -* [Delete Script Templates](/docs/authenticate/database-connections/custom-db/templates/delete) -* [Change Password Script Templates](/docs/authenticate/database-connections/custom-db/templates/change-password) -* [Change Email Script Template](/docs/authenticate/database-connections/custom-db/templates/change-email) + diff --git a/main/docs/authenticate/database-connections/custom-db/test-custom-database-connections.mdx b/main/docs/authenticate/database-connections/custom-db/test-custom-database-connections.mdx index 05b16b2a03..e99924630b 100644 --- a/main/docs/authenticate/database-connections/custom-db/test-custom-database-connections.mdx +++ b/main/docs/authenticate/database-connections/custom-db/test-custom-database-connections.mdx @@ -1,29 +1,29 @@ --- -description: Describes how to test your custom database connections in Auth0 Dashboard. -title: Test Custom Database Connections +title: How to Test Custom Database Connections +sidebarTitle: Test Custom Database Connections +description: Describes how to test your custom database connections. --- -If you use the Auth0-hosted database structure to store your users, you can test the connection by authenticating users against the same or a separate tenant. -## Test with import users to Auth0 enabled +You can use the Auth0 Dashboard test a custom database connection by authenticating users against the same or a separate tenant. -When import users is enabled, users are gradually migrated from your external user store to Auth0 user store. In the process, users will be authenticated against the external store the first time they successfully log in. All further login attempts will be executed against the Auth0 user store. To learn more, read about automatic migrations in [Import and Export Users](/docs/manage-users/user-migration). +Whether you have [enabled importing users](/docs/manage-users/user-migration/configure-automatic-migration-from-your-database) from your user store or not, start by creating a test tenant and application. -### Create a test tenant and application +## Create a test tenant and application -1. Use the Auth0 Dashboard to [create a new tenant](/docs/get-started/auth0-overview/create-tenants). -2. Navigate to [Applications > Applications](https://manage.auth0.com/#/applications/applications) and create a [Machine-to-Machine](https://manage.auth0.com/#/applications/applications/create) application. +1. [Create a new tenant](/docs/get-started/auth0-overview/create-tenants) to use for testing. - +2. [Create a Machine-to-Machine application](https://manage.auth0.com/#/applications/applications/create). - You need to note the **Client ID** and **Client Secret** when you create the custom database scripts. You need to note the **Client ID** to enable scopes in Management API. + You need the application's client ID and secret to enable scopes in the Management API and when you create custom database action scripts. - -3. Enable both the **Password** and **Client Credential** grant for this application (both grants should be enabled for this test). +3. Enable both the **Password** and **Client Credential** grant for this application. ![Auth0 Dashboard > Applications > Advanced Settings](/docs/images/cdy7uua7fh8z/4caAveuhYurCDb5F8mdImm/38496efe6f670d828542b02a56e5ca64/Grant_Types_-_English.png) -4. To authorize your application, navigate to [Applications > APIs](https://manage.auth0.com/#/apis). -5. Select **Management API**. -6. Under the **Machine-to-Machine Applications** tab, use the toggle to authorize your test application. + +4. To authorize your application, navigate to [Applications > APIs](https://manage.auth0.com/#/apis) and select **Management API**. + +5. Under the **Machine-to-Machine Applications** tab, use the toggle to authorize your test application. + 7. Select the drop-down menu to enable the following Auth0 Management API scopes: * `read:users` @@ -34,647 +34,572 @@ When import users is enabled, users are gradually migrated from your external us * `update:users_app_metadata` * `create:users_app_metadata` - ![Auth0 Dashboard > APIs > Management API > M2M Tab > App Permissions](/docs/images/cdy7uua7fh8z/2vW8zD1iIObuqb0i59yw77/c43bafa05b271a4343af51cb5c8ce1ce/2024-03-14_10-28-19.png) - -### Create test database connections - -After you create a tenant and an application in Dashboard, create a source database connection and a target database connection. - -1. Navigate to [Authentication > Database](https://manage.auth0.com/#/connections/database) to create a new database connection to be the source. -2. Name your test source connection, enable **Requires Username**, and select **Create**. - - - - You can create users to use in your test by navigating to [User Management > Users](https://manage.auth0.com/#/users). Once you select **Create** **Users** and populate the necessary fields, select the source database under the **Connection** field. - - -3. Create a second database to be the target with the same configuration from step 2. +## Test with import users to Auth0 enabled - +To test a custom database connection with [import users enabled](/docs/manage-users/user-migration/configure-automatic-migration-from-your-database), after you create a tenant and an application, create a source database connection and a target database connection. - You can set the **Password Policy** to **Non-empty password required** in both target and source databases so you can use simple passwords in your tests. +1. From [**Dashboard > Authentication > Database**](https://manage.auth0.com/#/connections/database), create two database connections (one to be the source and the other to be the target) with **Required Username** enabled. If you want to test using Organizations, enable **Context object in database scripts**. - -4. In your target database, switch to the **Custom Database** view and toggle on **Use my own database**. + + You can set the **Password Policy** to **Non-empty password required** in both target and source databases so you can use simple passwords in your tests. + - +2. Create test users in your source database connection. From [**Dashboard > User Management > Users**](https://manage.auth0.com/#/users), select **+Create User > **Create via UI**. - If you want to test using Organizations with your custom databases, enable **Context object in database scripts**. + In the **Create user** window that opens, for **Connection**, choose your source database connection. Fill in the necessary fields, then select **Create** (or **Create Another**). - -5. Switch to the **Settings** view and enable **Import Users to Auth0**. -6. Switch to the **Custom Database** view, and then locate the **Database settings** section. Add the following information from your source database created in step 1: +3. Configure the target database connection: -| Key | Value | -| --- | --- | -| `client_id` | Client ID of the application you created. | -| `client_secret` | Client Secret of the application you created. | -| `auth0_domain` | Your tenant name in the Auth0 domain: `yourTenant.us.auth0.com`. | -| `source_database` | Name of the source connection. | -7. Update the [Login](/docs/authenticate/database-connections/custom-db/test-custom-database-connections#login) and [Get User](/docs/authenticate/database-connections/custom-db/test-custom-database-connections#get-user) database action scripts in your target database. To learn more about best practices using database action scripts, read [Custom Database Connection and Action Scripts Best Practices](/docs/authenticate/database-connections/custom-db/custom-database-connections-scripts). -8. Select **Save and Try** on each script. You should monitor Actions Real-time Logs `console.log` output. To learn more, read [Actions Real-time Logs](/docs/customize/actions/actions-real-time-logs). -9. Select **Try Connection** to test the connection live. + * On the **Custom Database** tab, toggle **Use my own database**. -## Test without Import Users enabled + * On the **Settings** tab, enable **Import Users to Auth0**. -1. Repeat steps in [Create a test tenant and application](/docs/authenticate/database-connections/custom-db/test-custom-database-connections#create-a-test-tenant-and-application). -2. Create one test database connection. -3. Make sure **Import Users to Auth0** under your source database settings is disabled. Users will authenticate against the external user store during each login attempt. -4. Update all database actions scripts with the samples below. + * On the **Custom Database** tab, in the **Database settings** section, add the following information from the source database connection: -### Get User script + | Key | Value | + | --- | --- | + | `client_id` | Client ID of the application you created. | + | `client_secret` | Client Secret of the application you created. | + | `auth0_domain` | Your tenant name in the Auth0 domain: `yourTenant.us.auth0.com`. | + | `source_database` | Name of the source connection. | -The [Get User](/docs/authenticate/database-connections/custom-db/templates/get-user) script implements an executable function that determines the current state of a user. +4. Update the [Login](/docs/authenticate/database-connections/custom-db/templates/login) and [Get User](/docs/authenticate/database-connections/custom-db/templates/get-user) database action scripts in your target database. -When **Import Users to Auth0** is enabled, the Get User script runs when a user attempts to sign up to check if the user already exists in the external user store. +5. Select **Save and Try** on each script. You should monitor [Actions Real-time Logs](/docs/customize/actions/actions-real-time-logs) `console.log` output. -The Get User script also runs when a user attempts to: +6. Select **Try Connection** to test the connection live. -* Change a user's email address ([Change Email](/docs/authenticate/database-connections/custom-db/templates/change-email) script) -* Log in ([Login](/docs/authenticate/database-connections/custom-db/templates/login) script) -* Change a user's password ([Change Password](/docs/authenticate/database-connections/custom-db/templates/change-password) script) +## Test with import users to Auth0 disabled -When **Import Users to Auth0** is disabled, the Get User script runs when a user attempts to sign up to check if the user already exists in the external user store. If a user already exists in the external user store, it will not execute the Create script. +To test a custom database connection with import users to Auth0 disabled, after you create a tenant and an application, create a source database connection and a target database connection. -The Get User script also runs when a user attempts to: +1. From [**Dashboard > Authentication > Database**](https://manage.auth0.com/#/connections/database), create one test database connection to be the target. -* Create a user ([Create](/docs/authenticate/database-connections/custom-db/templates/create) script) -* Change a user's email address ([Change Email](/docs/authenticate/database-connections/custom-db/templates/change-email) script) -* Change a user's password ([Change Password](/docs/authenticate/database-connections/custom-db/templates/change-password) script) +2. Update the database action scripts with the samples below. -#### Example + + + Update the [Get User script](/docs/authenticate/database-connections/custom-db/templates/get-user) with the following sample: -```js lines expandable -async function getUser(user, context, callback) { - log(`Script started.`); - log(`Requesting an Access Token from "${configuration.auth0_domain}".`); - let accessToken = await getAccessToken(); - accessToken = accessToken.access_token; - if (!accessToken) return log(`Failed to get an Access Token from "${configuration.auth0_domain}".`, true); - log(`The Access Token is available. Searching for user "${user}" in "${configuration.source_database}"`); - user = user.toLowerCase(); - const searchQuery = encodeURI(`identities.connection:"${configuration.source_database}"+AND+(email:${user} OR username:${user})`); - var options = { - method: `GET`, - url: `https://${configuration.auth0_domain}/api/v2/users?q=${searchQuery}`, - headers: { - Authorization: `Bearer ${accessToken}`, - } - }; - request(options, function (error, response) { - if (error) return log(`Cannot connect to "${configuration.source_database}" database.`, true); - let search_results = JSON.parse(response.body); - let profile = null; - if (search_results.length > 0) { - log(`A user "${user}" is FOUND in "${configuration.source_database}" database.`); - profile = { - user_id: search_results[0].user_id.toString(), - nickname: search_results[0].nickname, - username: search_results[0].username, - email: search_results[0].email - }; - } else { - log(`A user "${user}" is NOT FOUND in "${configuration.source_database}" database.`); - } - log(`Script completed!`); - return callback(null, profile); - }); - /* -- GET ACCESS TOKEN VIA CLIENT CREDENTIALS -- */ - async function getAccessToken() { + ```js lines expandable + async function getUser(user, context, callback) { + log(`Script started.`); + log(`Requesting an Access Token from "${configuration.auth0_domain}".`); + let accessToken = await getAccessToken(); + accessToken = accessToken.access_token; + if (!accessToken) return log(`Failed to get an Access Token from "${configuration.auth0_domain}".`, true); + log(`The Access Token is available. Searching for user "${user}" in "${configuration.source_database}"`); + user = user.toLowerCase(); + const searchQuery = encodeURI(`identities.connection:"${configuration.source_database}"+AND+(email:${user} OR username:${user})`); var options = { - method: `POST`, - url: `https://${configuration.auth0_domain}/oauth/token`, + method: `GET`, + url: `https://${configuration.auth0_domain}/api/v2/users?q=${searchQuery}`, headers: { - "Content-Type": `application/x-www-form-urlencoded`, - }, - form: { - grant_type: `client_credentials`, - client_id: configuration.client_id, - client_secret: configuration.client_secret, - audience: `https://${configuration.auth0_domain}/api/v2/` - }, - json: true + Authorization: `Bearer ${accessToken}`, + } }; - return new Promise(function (resolve) { - request(options, function (error, response) { - resolve(error || response.body); - }); + request(options, function (error, response) { + if (error) return log(`Cannot connect to "${configuration.source_database}" database.`, true); + let search_results = JSON.parse(response.body); + let profile = null; + if (search_results.length > 0) { + log(`A user "${user}" is FOUND in "${configuration.source_database}" database.`); + profile = { + user_id: search_results[0].user_id.toString(), + nickname: search_results[0].nickname, + username: search_results[0].username, + email: search_results[0].email + }; + } else { + log(`A user "${user}" is NOT FOUND in "${configuration.source_database}" database.`); + } + log(`Script completed!`); + return callback(null, profile); }); + /* -- GET ACCESS TOKEN VIA CLIENT CREDENTIALS -- */ + async function getAccessToken() { + var options = { + method: `POST`, + url: `https://${configuration.auth0_domain}/oauth/token`, + headers: { + "Content-Type": `application/x-www-form-urlencoded`, + }, + form: { + grant_type: `client_credentials`, + client_id: configuration.client_id, + client_secret: configuration.client_secret, + audience: `https://${configuration.auth0_domain}/api/v2/` + }, + json: true + }; + return new Promise(function (resolve) { + request(options, function (error, response) { + resolve(error || response.body); + }); + }); + } + /* -- LOGGING -- */ + function log(message, error = false) { + const script_name = `GET USER`; + const error_label = error ? `(ERROR)` : ``; + const return_message = `${script_name}: ${error_label} ${message}`; + console.log(return_message); + if (error) return callback(new Error(return_message)); + } } - /* -- LOGGING -- */ - function log(message, error = false) { - const script_name = `GET USER`; + ``` + + + + Update the [Login script](/docs/authenticate/database-connections/custom-db/templates/login) with the following sample: + + ```javascript lines expandable + function login(usernameOrEmail, password, context, callback) { + log(`Script started.`); + const jwt = require('jsonwebtoken'); + const options = { + method: `POST`, + url: `https://${configuration.auth0_domain}/oauth/token`, + headers: { "Content-Type": `application/x-www-form-urlencoded` }, + json: true, + form: { + grant_type: `http://auth0.com/oauth/grant-type/password-realm`, + client_id: configuration.client_id, + client_secret: configuration.client_secret, + username: usernameOrEmail, + password: password, + realm: `${configuration.source_database}` + } + }; + request(options, function (error, response, body) { + log(`Attempting to authenticate a user "${usernameOrEmail}" against "${configuration.source_database}" database in "${configuration.auth0_domain}" tenant.`); + if (error) return log(`Cannot connect to "${configuration.auth0_domain}" database.`, true); + if (response.statusCode !== 200) { + console.log(`LOGIN: (ERROR) ${response.body.error_description}`); + return callback(new WrongUsernameOrPasswordError(usernameOrEmail, `LOGIN: (ERROR) ${response.body.error_description}`)); + } + log(`Successfuly authenticated user "${usernameOrEmail}" against "${configuration.source_database}" database in "${configuration.auth0_domain}" tenant.`); + const decoded_id_token = jwt.decode(body.id_token); + const profile = { + user_id: decoded_id_token.sub, + nickname: decoded_id_token.nickname, + username: decoded_id_token.username, + email: decoded_id_token.email + }; + log(`Script completed.`); + return callback(null, profile); + }); + /* -- LOGGING -- */ + function log(message, error = false) { + const script_name = `LOGIN`; const error_label = error ? `(ERROR)` : ``; const return_message = `${script_name}: ${error_label} ${message}`; console.log(return_message); if (error) return callback(new Error(return_message)); + } } -} -``` - - - - - - -### Login script - -The [Login](/docs/authenticate/database-connections/custom-db/templates/login) script implements an executable function that authenticates a user when a user logs in. If the user exists in the target database (Auth0), it authenticates them using that record. Otherwise, it authenticates the user with their record in the source database (external). - -#### Example - -```javascript lines expandable -function login(usernameOrEmail, password, context, callback) { - log(`Script started.`); - const jwt = require('jsonwebtoken'); - const options = { - method: `POST`, - url: `https://${configuration.auth0_domain}/oauth/token`, - headers: { "Content-Type": `application/x-www-form-urlencoded` }, - json: true, - form: { - grant_type: `http://auth0.com/oauth/grant-type/password-realm`, - client_id: configuration.client_id, - client_secret: configuration.client_secret, - username: usernameOrEmail, - password: password, - realm: `${configuration.source_database}` - } - }; - request(options, function (error, response, body) { - log(`Attempting to authenticate a user "${usernameOrEmail}" against "${configuration.source_database}" database in "${configuration.auth0_domain}" tenant.`); - if (error) return log(`Cannot connect to "${configuration.auth0_domain}" database.`, true); - if (response.statusCode !== 200) { - console.log(`LOGIN: (ERROR) ${response.body.error_description}`); - return callback(new WrongUsernameOrPasswordError(usernameOrEmail, `LOGIN: (ERROR) ${response.body.error_description}`)); - } - log(`Successfuly authenticated user "${usernameOrEmail}" against "${configuration.source_database}" database in "${configuration.auth0_domain}" tenant.`); - const decoded_id_token = jwt.decode(body.id_token); - const profile = { - user_id: decoded_id_token.sub, - nickname: decoded_id_token.nickname, - username: decoded_id_token.username, - email: decoded_id_token.email - }; - log(`Script completed.`); - return callback(null, profile); - }); - /* -- LOGGING -- */ - function log(message, error = false) { - const script_name = `LOGIN`; - const error_label = error ? `(ERROR)` : ``; - const return_message = `${script_name}: ${error_label} ${message}`; - console.log(return_message); - if (error) return callback(new Error(return_message)); - } -} -``` - - - - - - -### Create script - -The [Create script](/docs/authenticate/database-connections/custom-db/templates/create) implements an executable function that creates a corresponding user record in the external database when a user signs up through Universal Login, or is created in the Auth0 Dashboard or with the Auth0 Management API. - -#### Example - -```js lines expandable -async function create(user, context, callback) { - log(`Script started.`); - log(`Requesting an Access Token from "${configuration.auth0_domain}".`); - let accessToken = await getAccessToken(); - if (!accessToken.access_token) return log(`Failed to get an Access Token from "${configuration.auth0_domain}".`, true); - accessToken = accessToken.access_token; - log(`The Access Token is available. Attempting to create a user "${user.email}" in "${configuration.source_database}"`); - const options = { - method: `POST`, - url: `https://${configuration.auth0_domain}/api/v2/users`, - headers: { - Authorization: `Bearer ${accessToken}`, - "Content-Type": `application/x-www-form-urlencoded` - }, - form: { - connection: configuration.source_database, - email: user.email, - password: user.password, - username: user.username - }, - json: true - }; - request(options, function (error, response) { - if (error) return log(`Cannot connect to "${configuration.source_database}" database.`, true); - switch (response.statusCode) { - case 201: - log(`The user "${user.email}" is successfuly created in "${configuration.source_database}" database.`); - return callback(null); - case 409: - return callback(new ValidationError(`user_exists`, `The user already exists in "${configuration.source_database}" database.`)); - default: - return log(`Failed to create a user "${user.email}" in "${configuration.source_database}" database. Error: "${response.statusCode}, ${response.body.message}"`, true); - } - }); - /* -- GET ACCESS TOKEN VIA CLIENT CREDENTIALS -- */ - async function getAccessToken() { - var options = { - method: `POST`, - url: `https://${configuration.auth0_domain}/oauth/token`, - headers: { - "Content-Type": `application/x-www-form-urlencoded`, - }, - form: { - grant_type: `client_credentials`, - client_id: configuration.client_id, - client_secret: configuration.client_secret, - audience: `https://${configuration.auth0_domain}/api/v2/` - }, - json: true - }; - return new Promise(function (resolve) { - request(options, function (error, response) { - resolve(error || response.body); - }); - }); - } - /* -- LOGGING -- */ - function log(message, error = false) { - const script_name = `CREATE`; - const error_label = error ? `(ERROR)` : ``; - const return_message = `${script_name}: ${error_label} ${message}`; - console.log(return_message); - if (error) return callback(new Error(return_message)); - } -} -``` - - - - - - -### Delete script - -The [Delete script](/docs/authenticate/database-connections/custom-db/templates/delete) implements an executable function that deletes a user from Auth0 and the external database in the same operation when a user is deleted in the Auth0 Dashboard or with the Auth0 Management API. - -#### Example - -```js lines expandable -async function deleteUser(user, context, callback) { - log(`Script started.`); - log(`Requesting an Access Token from "${configuration.auth0_domain}".`); - let accessToken = await getAccessToken(); - if (!accessToken.access_token) return log(`Failed to get an Access Token from "${configuration.auth0_domain}".`, true); - accessToken = accessToken.access_token; - log(`The Access Token is available. Attempting to delete a user "${user}" from "${configuration.source_database}"`); - const options = { - method: `DELETE`, - url: `https://${configuration.auth0_domain}/api/v2/users/${user}`, - headers: { - Authorization: `Bearer ${accessToken}`, - "Content-Type": `application/x-www-form-urlencoded` - }, - json: true - }; - request(options, function (error, response) { - if (error) return log(`Cannot connect to "${configuration.source_database}" database.`, true); - switch (response.statusCode) { - case 204: - log(`The user "${user}" is successfuly deleted from "${configuration.source_database}" database.`); - return callback(null); - default: - return log(`Failed to delete a user "${user}" from "${configuration.source_database}" database. Error: "${response.statusCode}, ${response.body.message}"`, true); - } - }); - /* -- GET ACCESS TOKEN VIA CLIENT CREDENTIALS -- */ - async function getAccessToken() { - var options = { - method: `POST`, - url: `https://${configuration.auth0_domain}/oauth/token`, - headers: { - "Content-Type": `application/x-www-form-urlencoded`, - }, - form: { - grant_type: `client_credentials`, - client_id: configuration.client_id, - client_secret: configuration.client_secret, - audience: `https://${configuration.auth0_domain}/api/v2/` - }, - json: true - }; - return new Promise(function (resolve) { - request(options, function (error, response) { - resolve(error || response.body); - }); - }); - } - /* -- LOGGING -- */ - function log(message, error = false) { - const script_name = `DELETE`; - const error_label = error ? `(ERROR)` : ``; - const return_message = `${script_name}: ${error_label} ${message}`; - console.log(return_message); - if (error) return callback(new Error(return_message)); - } -} -``` - - - - - - -### Verify script - -The [Verify script](/docs/authenticate/database-connections/custom-db/templates/verify) implements an executable function that marks the verification status of a user’s email address in the external database when a user clicks on the link in the verification email sent by Auth0. - -#### Example - -```js lines expandable -async function verify(user, context, callback) { - log(`Script started.`); - log(`Requesting an Access Token from "${configuration.auth0_domain}".`); - let accessToken = await getAccessToken(); - if (!accessToken.access_token) return log(`Failed to get an Access Token from "${configuration.auth0_domain}".`, true); - accessToken = accessToken.access_token; - log(`The Access Token is available. Searching for user "${user}" in "${configuration.source_database}"`); - user = user.toLowerCase(); - const searchQuery = encodeURI(`identities.connection:"${configuration.source_database}"+AND+(email:${user} OR username:${user})`); - var options = { - method: `GET`, - url: `https://${configuration.auth0_domain}/api/v2/users?q=${searchQuery}`, - headers: { - Authorization: `Bearer ${accessToken}`, - } - }; - request(options, function (error, response) { - if (error) return log(`Cannot connect to "${configuration.source_database}" database.`, true); - let search_results = JSON.parse(response.body); - if (search_results.length > 0) { - log(`A user "${user}" is found in "${configuration.source_database}" database.`); - const user_id = search_results[0].user_id.toString(); - log(`Attempting to mark user "${user_id}" as verified in "${configuration.source_database}" database`); + ``` + + + + Update the [Create script](/docs/authenticate/database-connections/custom-db/templates/create) to the following sample: + + ```js lines expandable + async function create(user, context, callback) { + log(`Script started.`); + log(`Requesting an Access Token from "${configuration.auth0_domain}".`); + let accessToken = await getAccessToken(); + if (!accessToken.access_token) return log(`Failed to get an Access Token from "${configuration.auth0_domain}".`, true); + accessToken = accessToken.access_token; + log(`The Access Token is available. Attempting to create a user "${user.email}" in "${configuration.source_database}"`); const options = { - method: `PATCH`, - url: `https://${configuration.auth0_domain}/api/v2/users/${user_id}`, + method: `POST`, + url: `https://${configuration.auth0_domain}/api/v2/users`, headers: { Authorization: `Bearer ${accessToken}`, "Content-Type": `application/x-www-form-urlencoded` }, form: { - email_verified: true, + connection: configuration.source_database, + email: user.email, + password: user.password, + username: user.username }, json: true }; request(options, function (error, response) { if (error) return log(`Cannot connect to "${configuration.source_database}" database.`, true); switch (response.statusCode) { - case 200: - log(`The user "${user}" is marked as verified in "${configuration.source_database}" database.`); - return callback(null, true); + case 201: + log(`The user "${user.email}" is successfuly created in "${configuration.source_database}" database.`); + return callback(null); + case 409: + return callback(new ValidationError(`user_exists`, `The user already exists in "${configuration.source_database}" database.`)); default: - return log(`Failed to mark a user "${user}" as verified in "${configuration.source_database}" database. Error: "${response.statusCode}, ${response.body.message}"`, true); + return log(`Failed to create a user "${user.email}" in "${configuration.source_database}" database. Error: "${response.statusCode}, ${response.body.message}"`, true); } }); - } else { - log(`A user "${user}" is not found in "${configuration.source_database}" database. Unable to verify.`, true); - } - }); - /* -- GET ACCESS TOKEN VIA CLIENT CREDENTIALS -- */ - async function getAccessToken() { - var options = { - method: `POST`, - url: `https://${configuration.auth0_domain}/oauth/token`, - headers: { - "Content-Type": `application/x-www-form-urlencoded`, - }, - form: { - grant_type: `client_credentials`, - client_id: configuration.client_id, - client_secret: configuration.client_secret, - audience: `https://${configuration.auth0_domain}/api/v2/` - }, - json: true - }; - return new Promise(function (resolve) { - request(options, function (error, response) { - resolve(error || response.body); - }); - }); - } - /* -- LOGGING -- */ - function log(message, error = false) { - const script_name = `VERIFY`; - const error_label = error ? `(ERROR)` : ``; - const return_message = `${script_name}: ${error_label} ${message}`; - console.log(return_message); - if (error) return callback(new Error(return_message)); - } -} -``` - - - - - - -### Change Password script - -The [Change Password script](/docs/authenticate/database-connections/custom-db/templates/change-password) implements an executable function that updates a user's password in the external database when a password change workflow starts from the Auth0 Dashboard or the Auth0 Management API. - -#### Example - -```js lines expandable -async function changePassword(user, newPassword, context, callback) { - log(`Script started.`); - log(`Requesting an Access Token from "${configuration.auth0_domain}".`); - let accessToken = await getAccessToken(); - if (!accessToken.access_token) return log(`Failed to get an Access Token from "${configuration.auth0_domain}".`, true); - accessToken = accessToken.access_token; - log(`The Access Token is available. Searching for user "${user}" in "${configuration.source_database}" database.`); - user = user.toLowerCase(); - const searchQuery = encodeURI(`identities.connection:"${configuration.source_database}"+AND+(email:${user} OR username:${user})`); - var options = { - method: `GET`, - url: `https://${configuration.auth0_domain}/api/v2/users?q=${searchQuery}`, - headers: { - Authorization: `Bearer ${accessToken}`, + /* -- GET ACCESS TOKEN VIA CLIENT CREDENTIALS -- */ + async function getAccessToken() { + var options = { + method: `POST`, + url: `https://${configuration.auth0_domain}/oauth/token`, + headers: { + "Content-Type": `application/x-www-form-urlencoded`, + }, + form: { + grant_type: `client_credentials`, + client_id: configuration.client_id, + client_secret: configuration.client_secret, + audience: `https://${configuration.auth0_domain}/api/v2/` + }, + json: true + }; + return new Promise(function (resolve) { + request(options, function (error, response) { + resolve(error || response.body); + }); + }); + } + /* -- LOGGING -- */ + function log(message, error = false) { + const script_name = `CREATE`; + const error_label = error ? `(ERROR)` : ``; + const return_message = `${script_name}: ${error_label} ${message}`; + console.log(return_message); + if (error) return callback(new Error(return_message)); + } } - }; - request(options, function (error, response) { - if (error) return log(`Cannot connect to "${configuration.source_database}" database.`, true); - let search_results = JSON.parse(response.body); - if (search_results.length > 0) { - log(`A user "${user}" is found in "${configuration.source_database}" database.`); - const user_id = search_results[0].user_id.toString(); - log(`Attempting to change password for user "${user_id}" in "${configuration.source_database}" database.`); + ``` + + + + Update the [Delete script](/docs/authenticate/database-connections/custom-db/templates/delete) to the following sample: + + ```js lines expandable + async function deleteUser(user, context, callback) { + log(`Script started.`); + log(`Requesting an Access Token from "${configuration.auth0_domain}".`); + let accessToken = await getAccessToken(); + if (!accessToken.access_token) return log(`Failed to get an Access Token from "${configuration.auth0_domain}".`, true); + accessToken = accessToken.access_token; + log(`The Access Token is available. Attempting to delete a user "${user}" from "${configuration.source_database}"`); const options = { - method: `PATCH`, - url: `https://${configuration.auth0_domain}/api/v2/users/${user_id}`, + method: `DELETE`, + url: `https://${configuration.auth0_domain}/api/v2/users/${user}`, headers: { Authorization: `Bearer ${accessToken}`, "Content-Type": `application/x-www-form-urlencoded` }, - form: { - password: newPassword, - }, json: true }; request(options, function (error, response) { if (error) return log(`Cannot connect to "${configuration.source_database}" database.`, true); switch (response.statusCode) { - case 200: - log(`The user "${user}" password successfully changed in "${configuration.source_database}" database.`); - return callback(null, true); + case 204: + log(`The user "${user}" is successfuly deleted from "${configuration.source_database}" database.`); + return callback(null); default: - return log(`Failed to change password for "${user}" in "${configuration.source_database}" database. Error: "${response.statusCode}, ${response.body.message}"`, true); + return log(`Failed to delete a user "${user}" from "${configuration.source_database}" database. Error: "${response.statusCode}, ${response.body.message}"`, true); } }); - } else { - log(`A user "${user}" is not found in "${configuration.source_database}" database. Unable to change password.`, true); + /* -- GET ACCESS TOKEN VIA CLIENT CREDENTIALS -- */ + async function getAccessToken() { + var options = { + method: `POST`, + url: `https://${configuration.auth0_domain}/oauth/token`, + headers: { + "Content-Type": `application/x-www-form-urlencoded`, + }, + form: { + grant_type: `client_credentials`, + client_id: configuration.client_id, + client_secret: configuration.client_secret, + audience: `https://${configuration.auth0_domain}/api/v2/` + }, + json: true + }; + return new Promise(function (resolve) { + request(options, function (error, response) { + resolve(error || response.body); + }); + }); + } + /* -- LOGGING -- */ + function log(message, error = false) { + const script_name = `DELETE`; + const error_label = error ? `(ERROR)` : ``; + const return_message = `${script_name}: ${error_label} ${message}`; + console.log(return_message); + if (error) return callback(new Error(return_message)); + } } - }); - /* -- GET ACCESS TOKEN VIA CLIENT CREDENTIALS -- */ - async function getAccessToken() { - var options = { - method: `POST`, - url: `https://${configuration.auth0_domain}/oauth/token`, - headers: { - "Content-Type": `application/x-www-form-urlencoded`, - }, - form: { - grant_type: `client_credentials`, - client_id: configuration.client_id, - client_secret: configuration.client_secret, - audience: `https://${configuration.auth0_domain}/api/v2/` - }, - json: true - }; - return new Promise(function (resolve) { + ``` + + + + Update the [Verify script](/docs/authenticate/database-connections/custom-db/templates/verify) to the following sample: + + ```js lines expandable + async function verify(user, context, callback) { + log(`Script started.`); + log(`Requesting an Access Token from "${configuration.auth0_domain}".`); + let accessToken = await getAccessToken(); + if (!accessToken.access_token) return log(`Failed to get an Access Token from "${configuration.auth0_domain}".`, true); + accessToken = accessToken.access_token; + log(`The Access Token is available. Searching for user "${user}" in "${configuration.source_database}"`); + user = user.toLowerCase(); + const searchQuery = encodeURI(`identities.connection:"${configuration.source_database}"+AND+(email:${user} OR username:${user})`); + var options = { + method: `GET`, + url: `https://${configuration.auth0_domain}/api/v2/users?q=${searchQuery}`, + headers: { + Authorization: `Bearer ${accessToken}`, + } + }; request(options, function (error, response) { - resolve(error || response.body); + if (error) return log(`Cannot connect to "${configuration.source_database}" database.`, true); + let search_results = JSON.parse(response.body); + if (search_results.length > 0) { + log(`A user "${user}" is found in "${configuration.source_database}" database.`); + const user_id = search_results[0].user_id.toString(); + log(`Attempting to mark user "${user_id}" as verified in "${configuration.source_database}" database`); + const options = { + method: `PATCH`, + url: `https://${configuration.auth0_domain}/api/v2/users/${user_id}`, + headers: { + Authorization: `Bearer ${accessToken}`, + "Content-Type": `application/x-www-form-urlencoded` + }, + form: { + email_verified: true, + }, + json: true + }; + request(options, function (error, response) { + if (error) return log(`Cannot connect to "${configuration.source_database}" database.`, true); + switch (response.statusCode) { + case 200: + log(`The user "${user}" is marked as verified in "${configuration.source_database}" database.`); + return callback(null, true); + default: + return log(`Failed to mark a user "${user}" as verified in "${configuration.source_database}" database. Error: "${response.statusCode}, ${response.body.message}"`, true); + } + }); + } else { + log(`A user "${user}" is not found in "${configuration.source_database}" database. Unable to verify.`, true); + } }); - }); - } - /* -- LOGGING -- */ - function log(message, error = false) { - const script_name = `CHANGE PASSWORD`; - const error_label = error ? `(ERROR)` : ``; - const return_message = `${script_name}: ${error_label} ${message}`; - console.log(return_message); - if (error) return callback(new Error(return_message)); - } -} -``` - - - - - - -### Change Email script - -The [Change Email](/docs/authenticate/database-connections/custom-db/templates/change-email) script implements an executable function that updates a user's email address when the user changes their email address or their email address verification status. - -This script is not available in the Auth0 Dashboard. You must call the Management API [Update a connection](https://auth0.com/docs/api/management/v2/connections/patch-connections-by-id) endpoint and provide the `options.customScripts.change_email` property. - -#### Example - -```js lines expandable -async function changeEmail(user, newEmail, verified, callback) { - log(`Script started.`); - log(`Requesting an Access Token from "${configuration.auth0_domain}".`); - let accessToken = await getAccessToken(); - if (!accessToken.access_token) return log(`Failed to get an Access Token from "${configuration.auth0_domain}".`, true); - accessToken = accessToken.access_token; - log(`The Access Token is available. Searching for user "${user}" in "${configuration.source_database}" database.`); - user = user.toLowerCase(); - const searchQuery = encodeURI(`identities.connection:"${configuration.source_database}"+AND+(email:${user} OR username:${user})`); - var options = { - method: `GET`, - url: `https://${configuration.auth0_domain}/api/v2/users?q=${searchQuery}`, - headers: { - Authorization: `Bearer ${accessToken}`, + /* -- GET ACCESS TOKEN VIA CLIENT CREDENTIALS -- */ + async function getAccessToken() { + var options = { + method: `POST`, + url: `https://${configuration.auth0_domain}/oauth/token`, + headers: { + "Content-Type": `application/x-www-form-urlencoded`, + }, + form: { + grant_type: `client_credentials`, + client_id: configuration.client_id, + client_secret: configuration.client_secret, + audience: `https://${configuration.auth0_domain}/api/v2/` + }, + json: true + }; + return new Promise(function (resolve) { + request(options, function (error, response) { + resolve(error || response.body); + }); + }); + } + /* -- LOGGING -- */ + function log(message, error = false) { + const script_name = `VERIFY`; + const error_label = error ? `(ERROR)` : ``; + const return_message = `${script_name}: ${error_label} ${message}`; + console.log(return_message); + if (error) return callback(new Error(return_message)); + } } - }; - request(options, function (error, response) { - if (error) return log(`Cannot connect to "${configuration.source_database}" database.`, true); - let search_results = JSON.parse(response.body); - if (search_results.length > 0) { - log(`A user "${user}" is found in "${configuration.source_database}" database.`); - const user_id = search_results[0].user_id.toString(); - log(`Attempting to change email / verified status for user "${user_id}" in "${configuration.source_database}" database.`); - const options = { - method: `PATCH`, - url: `https://${configuration.auth0_domain}/api/v2/users/${user_id}`, + ``` + + + + Update the [Change Password script](/docs/authenticate/database-connections/custom-db/templates/change-password) to the following sample: + + ```js lines expandable + async function changePassword(user, newPassword, context, callback) { + log(`Script started.`); + log(`Requesting an Access Token from "${configuration.auth0_domain}".`); + let accessToken = await getAccessToken(); + if (!accessToken.access_token) return log(`Failed to get an Access Token from "${configuration.auth0_domain}".`, true); + accessToken = accessToken.access_token; + log(`The Access Token is available. Searching for user "${user}" in "${configuration.source_database}" database.`); + user = user.toLowerCase(); + const searchQuery = encodeURI(`identities.connection:"${configuration.source_database}"+AND+(email:${user} OR username:${user})`); + var options = { + method: `GET`, + url: `https://${configuration.auth0_domain}/api/v2/users?q=${searchQuery}`, headers: { Authorization: `Bearer ${accessToken}`, - "Content-Type": `application/x-www-form-urlencoded` - }, - form: { - email: newEmail, - email_verified: verified || false - }, - json: true + } }; request(options, function (error, response) { if (error) return log(`Cannot connect to "${configuration.source_database}" database.`, true); - switch (response.statusCode) { - case 200: - log(`The user "${user}" email / verified status successfully changed in "${configuration.source_database}" database.`); - return callback(null, true); - default: - return log(`Failed to change email / verified status for "${user}" in "${configuration.source_database}" database. Error: "${response.statusCode}, ${response.body.message}"`, true); + let search_results = JSON.parse(response.body); + if (search_results.length > 0) { + log(`A user "${user}" is found in "${configuration.source_database}" database.`); + const user_id = search_results[0].user_id.toString(); + log(`Attempting to change password for user "${user_id}" in "${configuration.source_database}" database.`); + const options = { + method: `PATCH`, + url: `https://${configuration.auth0_domain}/api/v2/users/${user_id}`, + headers: { + Authorization: `Bearer ${accessToken}`, + "Content-Type": `application/x-www-form-urlencoded` + }, + form: { + password: newPassword, + }, + json: true + }; + request(options, function (error, response) { + if (error) return log(`Cannot connect to "${configuration.source_database}" database.`, true); + switch (response.statusCode) { + case 200: + log(`The user "${user}" password successfully changed in "${configuration.source_database}" database.`); + return callback(null, true); + default: + return log(`Failed to change password for "${user}" in "${configuration.source_database}" database. Error: "${response.statusCode}, ${response.body.message}"`, true); + } + }); + } else { + log(`A user "${user}" is not found in "${configuration.source_database}" database. Unable to change password.`, true); } }); - } else { - log(`A user "${user}" is not found in "${configuration.source_database}" database. Unable to change email / verified status.`, true); + /* -- GET ACCESS TOKEN VIA CLIENT CREDENTIALS -- */ + async function getAccessToken() { + var options = { + method: `POST`, + url: `https://${configuration.auth0_domain}/oauth/token`, + headers: { + "Content-Type": `application/x-www-form-urlencoded`, + }, + form: { + grant_type: `client_credentials`, + client_id: configuration.client_id, + client_secret: configuration.client_secret, + audience: `https://${configuration.auth0_domain}/api/v2/` + }, + json: true + }; + return new Promise(function (resolve) { + request(options, function (error, response) { + resolve(error || response.body); + }); + }); + } + /* -- LOGGING -- */ + function log(message, error = false) { + const script_name = `CHANGE PASSWORD`; + const error_label = error ? `(ERROR)` : ``; + const return_message = `${script_name}: ${error_label} ${message}`; + console.log(return_message); + if (error) return callback(new Error(return_message)); + } } - }); - /* -- GET ACCESS TOKEN VIA CLIENT CREDENTIALS -- */ - async function getAccessToken() { - var options = { - method: `POST`, - url: `https://${configuration.auth0_domain}/oauth/token`, - headers: { - "Content-Type": `application/x-www-form-urlencoded`, - }, - form: { - grant_type: `client_credentials`, - client_id: configuration.client_id, - client_secret: configuration.client_secret, - audience: `https://${configuration.auth0_domain}/api/v2/` - }, - json: true - }; - return new Promise(function (resolve) { + ``` + + + + Update the [Change Email script](/docs/authenticate/database-connections/custom-db/templates/change-email) with the following sample: + + ```js lines expandable + async function changeEmail(user, newEmail, verified, callback) { + log(`Script started.`); + log(`Requesting an Access Token from "${configuration.auth0_domain}".`); + let accessToken = await getAccessToken(); + if (!accessToken.access_token) return log(`Failed to get an Access Token from "${configuration.auth0_domain}".`, true); + accessToken = accessToken.access_token; + log(`The Access Token is available. Searching for user "${user}" in "${configuration.source_database}" database.`); + user = user.toLowerCase(); + const searchQuery = encodeURI(`identities.connection:"${configuration.source_database}"+AND+(email:${user} OR username:${user})`); + var options = { + method: `GET`, + url: `https://${configuration.auth0_domain}/api/v2/users?q=${searchQuery}`, + headers: { + Authorization: `Bearer ${accessToken}`, + } + }; request(options, function (error, response) { - resolve(error || response.body); + if (error) return log(`Cannot connect to "${configuration.source_database}" database.`, true); + let search_results = JSON.parse(response.body); + if (search_results.length > 0) { + log(`A user "${user}" is found in "${configuration.source_database}" database.`); + const user_id = search_results[0].user_id.toString(); + log(`Attempting to change email / verified status for user "${user_id}" in "${configuration.source_database}" database.`); + const options = { + method: `PATCH`, + url: `https://${configuration.auth0_domain}/api/v2/users/${user_id}`, + headers: { + Authorization: `Bearer ${accessToken}`, + "Content-Type": `application/x-www-form-urlencoded` + }, + form: { + email: newEmail, + email_verified: verified || false + }, + json: true + }; + request(options, function (error, response) { + if (error) return log(`Cannot connect to "${configuration.source_database}" database.`, true); + switch (response.statusCode) { + case 200: + log(`The user "${user}" email / verified status successfully changed in "${configuration.source_database}" database.`); + return callback(null, true); + default: + return log(`Failed to change email / verified status for "${user}" in "${configuration.source_database}" database. Error: "${response.statusCode}, ${response.body.message}"`, true); + } + }); + } else { + log(`A user "${user}" is not found in "${configuration.source_database}" database. Unable to change email / verified status.`, true); + } }); - }); - } - /* -- LOGGING -- */ - function log(message, error = false) { - const script_name = `CHANGE EMAIL`; - const error_label = error ? `(ERROR)` : ``; - const return_message = `${script_name}: ${error_label} ${message}`; - console.log(return_message); - if (error) return callback(new Error(return_message)); - } -} -``` - - - - - + /* -- GET ACCESS TOKEN VIA CLIENT CREDENTIALS -- */ + async function getAccessToken() { + var options = { + method: `POST`, + url: `https://${configuration.auth0_domain}/oauth/token`, + headers: { + "Content-Type": `application/x-www-form-urlencoded`, + }, + form: { + grant_type: `client_credentials`, + client_id: configuration.client_id, + client_secret: configuration.client_secret, + audience: `https://${configuration.auth0_domain}/api/v2/` + }, + json: true + }; + return new Promise(function (resolve) { + request(options, function (error, response) { + resolve(error || response.body); + }); + }); + } + /* -- LOGGING -- */ + function log(message, error = false) { + const script_name = `CHANGE EMAIL`; + const error_label = error ? `(ERROR)` : ``; + const return_message = `${script_name}: ${error_label} ${message}`; + console.log(return_message); + if (error) return callback(new Error(return_message)); + } + } + ``` + + -## Learn more +3. Select **Save and Try** on each script. You should monitor [Actions Real-time Logs](/docs/customize/actions/actions-real-time-logs) `console.log` output. -* [Password Strength in Auth0 Database Connections](/docs/authenticate/database-connections/password-strength) -* [Custom Database Action Script Environment Best Practices](/docs/authenticate/database-connections/custom-db/custom-database-connections-scripts/environment) -* [Password Options in Auth0 Database Connections](/docs/authenticate/database-connections/password-options) -* [Configure Identity Provider Connection for User Profile Updates](/docs/manage-users/user-accounts/user-profiles/configure-connection-sync-with-auth0) -* [Connection Settings Best Practices](/docs/authenticate/connection-settings-best-practices) -* [Create Custom Database Connections](/docs/authenticate/database-connections/custom-db/create-db-connection) +4. Select **Try Connection** to test the connection live. diff --git a/main/docs/manage-users/user-migration/configure-automatic-migration-from-your-database.mdx b/main/docs/manage-users/user-migration/configure-automatic-migration-from-your-database.mdx index acef24ec49..2e1ed74094 100644 --- a/main/docs/manage-users/user-migration/configure-automatic-migration-from-your-database.mdx +++ b/main/docs/manage-users/user-migration/configure-automatic-migration-from-your-database.mdx @@ -4,15 +4,15 @@ sidebarTitle: Enable Automatic Migration description: Gradually migrate users from your external user store to Auth0 as they log in by enabling import mode on your custom database connection. --- -When using a [custom database connection](/docs/authenticate/database-connections/custom-db), Auth0 supports automatic migration (also known as *trickle migration* or *lazy migration*) of users from your external user store to the Auth0 database. - -Using automatic migration, your users are moved to Auth0 the first time they log in and do not need to reset their password as a result of the migration. +When using a [custom database connection](/docs/authenticate/database-connections/custom-db), Auth0 supports automatic migration (also known as *trickle migration* or *lazy migration*) of users from your external user store to the Auth0 user store. When a user authenticates via a custom database connection marked for import to Auth0, the following process takes place: ![User Migration Diagram](/docs/images/cdy7uua7fh8z/JAHBs6NzVRf0N5iXI7Wby/d0bfa23a1485aeb5d64bd52fe977d4fe/import-export-diagram.png) -For migrated users, Auth0 authenticates against the Auth0 database. For unmigrated users, Auth0 executes your custom login script and, on a successful login, migrates the user to the Auth0 database. New users are added directly to the Auth0 database. +If the user doesn't exist in the Auth0 user store, Auth0 authenticates against your external user store and, if successful, creates the user in the Auth0 user store using the same ID and password supplied during authentication. + +In this way, your existing users are migrated to Auth0 the first time they log in and do not need to reset their password as a result. New users are added directly to the Auth0 user store. ## Start the migration @@ -26,7 +26,7 @@ To configure automatic migration, you need: From [Auth0 Dashboard > Authentication > Database](https://manage.auth0.com/#/connections/database), select the custom database connection. -In the **Settings** tab, toggle **Import Users to Auth0** and select **Save. +In the **Settings** tab, toggle **Import Users to Auth0** and select **Save**. @@ -42,8 +42,19 @@ When you enable automatic migration, you must implement two scripts: * The [`getUser()` script](/docs/authenticate/database-connections/custom-db/templates/get-user), which executes in scenarios like sign-ups and password changes. The data returned from the `getUser()` script is migrated into the new Auth0 user profile. + +## Manage external user store data during migration + +We recommend the following best practices for handling user data in your external user store during automatic migration: + +* Users remain in your external user store unless you explicitly archive or delete them. If a user is migrated to Auth0 and then deleted from Auth0 but remains in your external user store, it's possible that the deleted user could try to log in and be migrated again. + + We recommend marking user identities in your external user store as migrated before the Login or Get Users custom database action scripts execute to prevent the unintentional recreation of users deleted intentionally. + +* Typically, users are created in the Auth0 user store after the [Login action script](/docs/authenticate/database-connections/custom-db/templates/login) completes. Instead of deleting users from your external user store as an inline operation in the Login script, we recommend deleting users as an independent process to avoid accidental user deletion in the event of an error during migration. + ## Complete the migration @@ -65,7 +76,7 @@ Once you confirm that all users are migrated, you can disconnect your external u Leave the **Import Users to Auth0** option on. This setting is what tells Auth0 to use the Auth0 user store for your custom database connection (instead of using only your external user store via your database action scripts). -From [Auth0 Dashboard > Authentication > Database](https://manage.auth0.com/#/connections/database), select your database connection. On the **Custom Database** tab, find the **Database Action Scripts** section +From [Auth0 Dashboard > Authentication > Database](https://manage.auth0.com/#/connections/database), select your database connection. On the **Custom Database** tab, find the **Database Action Scripts** section. Update the **Login** script: