Test 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
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.
Create a test tenant and application
Use the Auth0 Dashboard to create a new tenant.
Navigate to Applications > Applications and create a Machine-to-Machine application.
Enable both the Password and Client Credential grant for this application (both grants should be enabled for this test).
To authorize your application, navigate to Applications > APIs.
Select Management API.
Under the Machine-to-Machine Applications tab, use the toggle to authorize your test application.
Select the drop-down menu to enable the following Auth0 Management API scopes:
read:users
update:users
delete:users
create:users
read:users_app_metadata
update:users_app_metadata
create:users_app_metadata
Create test database connections
After you create a tenant and an application in Dashboard, create a source database connection and a target database connection.
Navigate to Authentication > Database to create a new database connection to be the source.
Name your test source connection, enable Requires Username, and select Create.
Create a second database to be the target with the same configuration from step 2.
In your target database, switch to the Custom Database view and toggle on Use my own database.
Switch to the Settings view and enable Import Users to Auth0.
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:
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. Update the Login and 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.
Select Save and Try on each script. You should monitor the Real-time Webtask Logs Extension
console.log
output. To learn more, read Real-time Webtask Logs Extension.Select Try Connection to test the connection live.
Test without Import Users enabled
Repeat steps in Create a test tenant and application.
Create one test database connection.
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.
Update all database actions scripts with the samples below.
Get User script
The Get User script implements an executable function that determines the current state of a user.
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.
The Get User script also runs when a user attempts to:
Change a user's email address (Change Email script)
Log in (Login script)
Change a user's password (Change Password script)
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.
The Get User script also runs when a user attempts to:
Create a user (Create script)
Change a user's email address (Change Email script)
Change a user's password (Change Password script)
Example
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() {
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));
}
}
Was this helpful?
Login script
The 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
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));
}
}
Was this helpful?
Create script
The Create script 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
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));
}
}
Was this helpful?
Delete script
The Delete script 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
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));
}
}
Was this helpful?
Verify script
The Verify script 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
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`);
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);
}
});
/* -- 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));
}
}
Was this helpful?
Change Password script
The Change Password script 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
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}`,
}
};
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.`);
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);
}
});
/* -- 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));
}
}
Was this helpful?
Change Email script
The 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 endpoint and provide the options.customScripts.change_email
property.
Example
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) {
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);
}
});
/* -- 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));
}
}
Was this helpful?