Use this file to discover all available pages before exploring further.
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. 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.
This is a pseudo-JavaScript example of how you could implement the login function. For language-specific examples, read Language-specific script examples.
function login(userNameOrEmail, password, callback) { // Send credentials to external database API let hashedPassword = hash(password); let options = { url: "https://example.com/api/authenticate", body: { email: userNameOrEmail, password: hashedPassword } }; send(options, (err, profileData) => { // Return error in callback if authentication is unsuccessful if (err) { return callback(new WrongUsernameOrPasswordError(userNameOrEmail, "My custom error message.")); } else { // Return profile data in callback if authentication is successful let profile = { username: profileData.username, email: profileData.emailAddress, user_id: profileData.userId }; return callback(null, 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. In addition to the standard fields, you can include the user_metadata, app_metadata, and mfa_factors fields.
Enable the Sync user profile attributes at each login setting if you want Auth0 to update the name, nickname, given_name, family_name, and/or picture fields with the values returned from the external database on each login.If you do not enable this setting, the values returned from the external database the first time the user logs in will persist on subsequent logins, even if they’ve changed in the external database.
function login(email, password, callback) { // This script should authenticate a user against the credentials stored in // your database. // It is executed when a user attempts to log in or immediately after signing // up (as a verification that the user was successfully signed up). // // Everything returned by this script will be set as part of the user profile // and will be visible by any of the tenant admins. Avoid adding attributes // with values such as passwords, keys, secrets, etc. // // The `password` parameter of this function is in plain text. It must be // hashed/salted to match whatever is stored in your database. For example: // // var bcrypt = require('bcrypt@0.8.5'); // bcrypt.compare(password, dbPasswordHash, function(err, res)) { ... } // // There are three ways this script can finish: // 1. The user's credentials are valid. The returned user profile should be in // the following format: https://auth0.com/docs/users/normalized/auth0/normalized-user-profile-schema // var profile = { // user_id: ..., // user_id is mandatory // email: ..., // [...] // }; // callback(null, profile); // 2. The user's credentials are invalid // callback(new WrongUsernameOrPasswordError(email, "my error message")); // 3. Something went wrong while trying to reach your database // callback(new Error("my error message")); // // A list of Node.js modules which can be referenced is available here: // // https://tehsis.github.io/webtaskio-canirequire/ const msg = 'Please implement the Login script for this database connection ' + 'at https://manage.auth0.com/#/connections/database'; return callback(new Error(msg));}
ASP.NET Membership Provider (MVC3 - Universal Providers)
function login(email, password, callback) { const crypto = require('crypto'); const sqlserver = require('tedious@11.0.3'); const Connection = sqlserver.Connection; const Request = sqlserver.Request; const TYPES = sqlserver.TYPES; const connection = new Connection({ userName: 'the username', password: 'the password', server: 'the server', options: { database: 'the db name', encrypt: true // for Windows Azure } }); /** * hashPassword * * This function creates a hashed version of the password to store in the database. * * @password {[string]} the password entered by the user * @return {[string]} the hashed password */ function hashPassword(password, salt) { // the default implementation uses HMACSHA256 and since Key length is 64 // and default salt is 16 bytes, Membership will fill the buffer repeating the salt const key = Buffer.concat([salt, salt, salt, salt]); const hmac = crypto.createHmac('sha256', key); hmac.update(Buffer.from(password, 'ucs2')); return hmac.digest('base64'); } connection.on('debug', function(text) { // if you have connection issues, uncomment this to get more detailed info //console.log(text); }).on('errorMessage', function(text) { // this will show any errors when connecting to the SQL database or with the SQL statements console.log(JSON.stringify(text)); }); connection.on('connect', function(err) { if (err) return callback(err); getMembershipUser(email, function(err, user) { if (err || !user || !user.profile || !user.password) return callback(err || new WrongUsernameOrPasswordError(email)); const salt = Buffer.from(user.password.salt, 'base64'); if (hashPassword(password, salt).toString('base64') !== user.password.password) { return callback(new WrongUsernameOrPasswordError(email)); } callback(null, user.profile); }); }); // Membership Provider implementation used on Microsoft.AspNet.Providers NuGet /** * getMembershipUser * * This function gets a username or email and returns a the user membership provider * info, password hashes and salt * * @usernameOrEmail {[string]} the username or email, the method will do a * query on both with an OR * * @callback {[Function]} first argument will be the Error if any, * and second argument will be a user object */ function getMembershipUser(usernameOrEmail, done) { var user = null; const query = 'SELECT Memberships.UserId, Email, Users.UserName, Password ' + 'FROM Memberships INNER JOIN Users ' + 'ON Users.UserId = Memberships.UserId ' + 'WHERE Memberships.Email = @Username OR Users.UserName = @Username'; const getMembershipQuery = new Request(query, function(err, rowCount) { if (err || rowCount < 1) return done(err); done(err, user); }); getMembershipQuery.addParameter('Username', TYPES.VarChar, usernameOrEmail); getMembershipQuery.on('row', function(fields) { user = { profile: { user_id: fields.UserId.value, nickname: fields.UserName.value, email: fields.Email.value, }, password: { password: fields.Password.value, salt: fields.PasswordSalt.value } }; }); connection.execSql(getMembershipQuery); }}
ASP.NET Membership Provider (MVC4 - Simple Membership)
function login(email, password, callback) { const crypto = require('crypto'); const sqlserver = require('tedious@11.0.3'); const Connection = sqlserver.Connection; const Request = sqlserver.Request; const TYPES = sqlserver.TYPES; const connection = new Connection({ userName: 'the username', password: 'the password', server: 'the server', options: { database: 'the db name', encrypt: true // for Windows Azure } }); function fixedTimeComparison(a, b) { var mismatch = (a.length === b.length ? 0 : 1); if (mismatch) { b = a; } for (var i = 0, il = a.length; i < il; ++i) { const ac = a.charCodeAt(i); const bc = b.charCodeAt(i); mismatch += (ac === bc ? 0 : 1); } return (mismatch === 0); } /** * validatePassword * * This function gets the password entered by the user, and the original password * hash and salt from database and performs an HMAC SHA256 hash. * * @password {[string]} the password entered by the user * @originalHash {[string]} the original password hashed from the database * (including the salt). * @return {[bool]} true if password validates */ function validatePassword(password, originalHash, callback) { const iterations = 1000; const hashBytes = Buffer.from(originalHash, 'base64'); const salt = hashBytes.slice(1, 17); const hash = hashBytes.slice(17, 49); crypto.pbkdf2(password, salt, iterations, hash.length, 'sha1', function(err, hashed) { if (err) return callback(err); const hashedBase64 = Buffer.from(hashed, 'binary').toString('base64'); const isValid = fixedTimeComparison(hash.toString('base64'), hashedBase64); return callback(null, isValid); }); } connection.on('debug', function(text) { // if you have connection issues, uncomment this to get more detailed info //console.log(text); }).on('errorMessage', function(text) { // this will show any errors when connecting to the SQL database or with the SQL statements console.log(JSON.stringify(text)); }); connection.on('connect', function(err) { if (err) return callback(err); getMembershipUser(email, function(err, user) { if (err || !user || !user.profile) return callback(err || new WrongUsernameOrPasswordError(email)); validatePassword(password, user.password, function(err, isValid) { if (err || !isValid) return callback(err || new WrongUsernameOrPasswordError(email)); callback(null, user.profile); }); }); }); // Membership Provider implementation used on Microsoft.AspNet.Providers NuGet /** * getMembershipUser * * This function gets a username or email and returns a user info, password hashes and salt * * @usernameOrEamil {[string]} the username or email, the method will do a query * on both with an OR * @callback {[Function]} first argument will be the Error if any, and second * argument will be a user object */ function getMembershipUser(usernameOrEmail, done) { var user = null; const query = 'SELECT webpages_Membership.UserId, UserName, UserProfile.UserName, Password from webpages_Membership ' + 'INNER JOIN UserProfile ON UserProfile.UserId = webpages_Membership.UserId ' + 'WHERE UserProfile.UserName = @Username'; const getMembershipQuery = new Request(query, function(err, rowCount) { if (err || rowCount < 1) return done(err); done(err, user); }); getMembershipQuery.addParameter('Username', TYPES.VarChar, usernameOrEmail); getMembershipQuery.on('row', function(fields) { user = { profile: { user_id: fields.UserId.value, nickname: fields.UserName.value, email: fields.UserName.value, }, password: fields.Password.value }; }); connection.execSql(getMembershipQuery); }}
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"); let response; try { response = await axios.post( //store API url in connection settings to better support SDLC environments configuration.baseAPIUrl + "/login", //user credentials passed as request body { email: email, password: password, }, { timeout: 10000, //end call gracefully if request times out so script can do necessary callback headers: { //securing api call with apiKey stored in connection settings. //quick and easy approach however using M2M tokens is more secure as // a secret must not be shared between client and API. "x-api-key": configuration.apiKey, }, } ); } catch (e) { if (e.response.status === 404) { //assuming api returns 404 when email/username/password invalid return callback( new WrongUsernameOrPasswordError(email, "Invalid credentials provided.") ); } //callback for any other error type return callback(new Error(e.message)); } try { let user = response.data; //if using multiple custom db connections in your tenant prefix the //user_id with a connection specific key ex: "connName|" + user.user_id //this ensures unique user ids across all db connections return callback(null, { user_id: user.user_id, email: user.email, }); } catch (e) { return callback(new Error(e.message)); }}
function login(username, password, callback) { // Replace the {yourStormpathClientId} with your Stormpath ID var url = 'https://api.stormpath.com/v1/applications/{yourStormpathClientId}/loginAttempts'; // Add your Stormpath API Client ID and Secret var apiCredentials = { user : '{yourStormpathApiId}', password: '{yourStormpathApiSecret}' }; // Stormpath requires the user credentials be passed in as a base64 encoded message var credentials = Buffer.from(username + ':' + password).toString('base64'); // Make a POST request to authenticate a user request({ url: url, method: 'POST', auth: apiCredentials, json: { type: 'basic', // Passing in the base64 encoded credentials value: credentials } }, function (error, response, body) { // If response is successful we'll continue if (response.statusCode !== 200) return callback(); // A successful response will return a URL to get the user information var accountUrl = body.account.href; // Make a second request to get the user info. request({ url: accountUrl, auth: apiCredentials, json: true }, function (errorUserInfo, responseUserInfo, bodyUserInfo) { // If we get a successful response, we'll process it if (responseUserInfo.statusCode !== 200) return callback(); // To get the user identifier, we'll strip out the Stormpath API var id = bodyUserInfo.href.replace('https://api.stormpath.com/v1/accounts/', ''); // Finally, we'll set the data we want to store in Auth0 and migrate the user return callback(null, { user_id : id, username: bodyUserInfo.username, email: bodyUserInfo.email, // We set the users email_verified to true as we assume if they were a valid // user in Stormpath, they have already verified their email // If this field is not set, the user will get an email asking them to verify // their account. You can decide how to handle this for your use case email_verified: true // Add any additional fields you would like to carry over from Stormpath }); }); });}