Change a user email when using Custom Database

I am using a custom Database and when I tried changing email address, I get an error as “Change_email Script doesn’t exist”. How can we get this accomplished?

You can change your users’ email addresses for custom databases, but you must implement the get_user and change_email scripts. You can find the former in the dashboard under Get User; however, right now the dashboard doesn’t allow you to configure the change_email script. To implement this script, you need to add it to the connection via the management API. You need to use the Update a connection endpoint with the options parameters in this format:

{
"options": {
    ...,
    "customScripts": {
      "login": "...",
      "get_user": "...",
      "change_email": "...",
      ...
    }
  }
}

Keep in mind that the options object provided will overwrite whatever was there before, so before doing the PATCH you will have to do a GET to retrieve the full object and amend the options.change_email value accordingly. The change_email script requires this signature:

function changeEmail(oldEmail, newEmail, emailVerified, callback) {
  // change the email and email_verified flags in your internal DB if needed
  ...]
  // notify if the change was done
  var changeWasSuccessful = true;
  callback(null, changeWasSuccessful);
}

I understand without the UI this may be a little cumbersome, but you can achieve what you need. We are aware of this limitation and have this feature (configuring via UI) in our backlog, but there’s no ETA on when we’ll implement it.

1 Like

Thanks Richard.
Received the format from @jmangelo and I can perform that action.

Hi, sorry for resurrecting an old thread.

Can we include the built in email verification step in the change_email script somehow? Ideally we would like to ensure a user can access their email when they request the change - or would we have to build a custom solution outside auth0?

I’m trying to do this today and it’s not updating my connection. I can’t figure out what I’m doing wrong. Here’s my options:

{
"options": {
        "mfa": {
            "active": true,
            "return_enroll_settings": true
        },
        "passwordPolicy": "good",
        "disable_signup": true,
        "requires_username": false,
        "brute_force_protection": true,
        "strategy_version": 2,
        "enabledDatabaseCustomization": true,
        "customScripts": {
            "login": "function login(email, password, callback) {\n  //this example uses the \"tedious\" library\n  //more info here: http://pekim.github.io/tedious/index.html\n  const bcrypt = require('bcrypt');\n  const sqlserver = require('tedious@1.11.0');\n\n  const Connection = sqlserver.Connection;\n  const Request = sqlserver.Request;\n  const TYPES = sqlserver.TYPES;\n\n  const connection = new Connection({\n    userName:  '{username}',\n    password:  '{password}',\n    server:    '{sqlserveraddress}',\n    options:  {\n      database: '{dbname}',\n      encrypt: true,\n      rowCollectionOnRequestCompletion: true\n    }\n  });\n\n  const query = 'SELECT Id, Height, Weight, Position, Experience, Name, Given_Name, Family_Name, Role, OrganizationId, Email, Email_Verified, Password FROM dbo.Users WHERE Email = @Email';\n\n  connection.on('debug', function (text) {\n    console.log(text);\n  }).on('errorMessage', function (text) {\n    console.log(JSON.stringify(text, null, 2));\n  }).on('infoMessage', function (text) {\n    console.log(JSON.stringify(text, null, 2));\n  });\n\n  connection.on('connect', function (err) {\n    if (err) return callback(err);\n\n    //console.log(\"No error\");\n    \n    const request = new Request(query, function (err, rowCount, rows) {\n      if (err || rowCount < 1) { \n        console.log(\"Wrong username or password\");\n        return callback(err || new WrongUsernameOrPasswordError(email));\n      }\n      \n//      console.log(\"Row count good\");\n//      console.log(password);\n//      console.log(rows[0][12].value);\n      \n      bcrypt.compare(password, rows[0][12].value, function (err, isValid) {\n        if (err || !isValid) {\n          console.log(\"Wrong username or password\");\n          return callback(err || new WrongUsernameOrPasswordError(email));\n        }\n        \n        //console.log(\"Password good\");\n        \n        //console.log(rows[0][11].value);\n        \n        callback(null, { \n          //user_profile: {\n          \tuser_id: rows[0][0].value,\n            user_metadata: {\n          \t  height: rows[0][1].value,\n              weight: rows[0][2].value,\n              position: rows[0][3].value,\n              experience: rows[0][4].value\n            },\n          \tname: rows[0][5].value,\n          \tgiven_name: rows[0][6].value,\n          \tfamily_name: rows[0][7].value,\n            app_metadata: {\n              role: rows[0][8].value,\n          \t  organizationid: rows[0][9].value\n            },\n          \temail: rows[0][10].value,\n            email_verified: rows[0][11].value\n          //}\n        });\n      });\n    });\n\n    request.addParameter('Email', TYPES.VarChar, email);\n    \n    //console.log(\"What's wrong?\");\n    \n    connection.execSql(request);\n  });\n}\n",
            "create": "function create(user, callback) { \n  //this example uses the \"tedious\" library\n  //more info here: http://pekim.github.io/tedious/index.html\n  //const bcrypt = require('bcrypt');\n  const sqlserver = require('tedious@1.11.0');\n\n  const Connection = sqlserver.Connection;\n  const Request = sqlserver.Request;\n  const TYPES = sqlserver.TYPES;\n\n  const connection = new Connection({\n    userName:  '{username}',\n    password:  '{password}',\n    server:    '{sqlserveraddress}',\n    options:  {\n      database: '{dbname}',\n      encrypt: true\n    }\n  });\n\n  const query = \"INSERT INTO users (Email, Given_Name, Family_Name, Name, Height, Weight, Position, Experience, Role, OrganizationId, Password, Email_Verified) VALUES (@Email, @Given_Name, @Family_Name, @Name, @Height, @Weight, @Position, @Experience, @Role, @OrganizationId, @Password, @Email_Verified)\";\n\n  connection.on('debug', function(text) {\n    // Uncomment next line in order to enable debugging messages\n    //console.log(text);\n  }).on('errorMessage', function(text) {\n    console.log(JSON.stringify(text, null, 2));\n  }).on('infoMessage', function(text) {\n    // Uncomment next line in order to enable information messages\n    //console.log(JSON.stringify(text, null, 2));\n  });\n\n  connection.on('connect', function (err) {\n    if (err) {\n      //console.log(err);\n      return callback(err);\n    }\n\n    const request = new Request(query, function (err, rows) {\n      if (err) {\n        //console.log(err);\n        return callback(err);\n      }\n      //console.log('rows: ' + rows);\n      callback(null);\n    });\n    \n    console.log(user);\n\n    bcrypt.hash(user.password, 10, function(err, hash) {\n      if (err) {\n        //console.log(err);\n        return callback(err);\n      }\n      request.addParameter('Email', TYPES.VarChar, user.email);\n      request.addParameter('Given_Name', TYPES.VarChar, user.given_name);\n      request.addParameter('Family_Name', TYPES.VarChar, user.family_name);\n      request.addParameter('Name', TYPES.VarChar, user.name);\n      request.addParameter('Height', TYPES.Int, user.user_metadata.height);\n      request.addParameter('Weight', TYPES.Int, user.user_metadata.weight);\n      request.addParameter('Position', TYPES.VarChar, user.user_metadata.position);\n      request.addParameter('Experience', TYPES.VarChar, user.user_metadata.experience);\n      request.addParameter('Role', TYPES.Int, user.metadata.role);\n      request.addParameter('OrganizationId', TYPES.VarChar, user.metadata.organizationId);\n      //request.addParameter('Picture', TYPES.VarChar, user.picture);\n      request.addParameter('Password', TYPES.VarChar, hash);\n      request.addParameter('Email_Verified', TYPES.Bit, user.email_verified);\n      connection.execSql(request);\n    });\n  });\n}\n",
            "verify": "function verify (email, callback) {  \n  //this example uses the \"tedious\" library\n  //more info here: http://pekim.github.io/tedious/index.html\n  const sqlserver = require('tedious@1.11.0');\n\n  const Connection = sqlserver.Connection;\n  const Request = sqlserver.Request;\n  const TYPES = sqlserver.TYPES;\n\n  const connection = new Connection({\n    userName:  '{username}',\n    password:  '{password}',\n    server:    '{sqlserveraddress}',\n    options:  {\n      database: '{dbname}',\n      encrypt: true\n    }\n  });\n  \n  const query = 'UPDATE dbo.Users SET Email_Verified = \\'TRUE\\' ' + \n        'WHERE Email_Verified = \\'FALSE\\' AND Email=@Email';\n\n  connection.on('debug', function(text) {\n    console.log(text);\n  }).on('errorMessage', function(text) {\n    console.log(JSON.stringify(text, null, 2));\n  }).on('infoMessage', function(text) {\n    console.log(JSON.stringify(text, null, 2));\n  });\n\n  connection.on('connect', function (err) {\n    if (err) return callback(err);\n    \n    const request = new Request(query, function (err, rows) {\n      if (err) return callback(err);\n      // console.log('rows: ' + rows);\n      callback(null, rows > 0);\n    });\n\n    request.addParameter('Email', TYPES.VarChar, email);\n\n    connection.execSql(request);\n    \n    var resetRequest = require(\"request\");\n    \n    var options = {\n      method: 'POST',\n      url: 'https://monarc.auth0.com/dbconnections/change_password',\n      headers: {'content-type': 'application/json'},\n      body: {\n        client_id: 'kzwH25vA6SpN1e4CtQbCQLhmCoD7O2AM',\n        email: `${email}`,\n        connection: 'MonarcDB'\n      },\n      json: true\n    };\n    \n    resetRequest(options, function (error, response, body) {\n      if (error) throw new Error(error);\n      console.log(body);\n    });\n  });\n}\n",
            "change_password": "function changePassword (email, newPassword, callback) {\n  //this example uses the \"tedious\" library\n  //more info here: http://tediousjs.github.io/tedious/\n  const bcrypt = require('bcrypt');\n  const sqlserver = require('tedious@1.11.0');\n\n  const Connection = sqlserver.Connection;\n  const Request = sqlserver.Request;\n  const TYPES = sqlserver.TYPES;\n\n  const connection = new Connection({\n    userName:  '{username}',\n    password:  '{password}',\n    server:    '{sqlserveraddress}',\n    options:  {\n      database: '{dbname}',\n      encrypt: true\n    }\n  });\n\n  const query = 'UPDATE dbo.Users SET Password = @NewPassword WHERE Email = @Email';\n\n  connection.on('debug', function(text) {\n    console.log(text);\n  }).on('errorMessage', function(text) {\n    console.log(JSON.stringify(text, null, 2));\n  }).on('infoMessage', function(text) {\n    console.log(JSON.stringify(text, null, 2));\n  });\n\n  connection.on('connect', function (err) {\n    if (err) return callback(err);\n\n    const request = new Request(query, function (err, rows) {\n      if (err) return callback(err);\n      // console.log('rows: ' + rows);\n      callback(null, rows > 0);\n    });\n\n    bcrypt.hash(newPassword, 10, function (err, hash) {\n      if (err) return callback(err);\n      request.addParameter('NewPassword', TYPES.VarChar, hash);\n      request.addParameter('Email', TYPES.VarChar, email);\n      connection.execSql(request);\n    });\n  });\n}\n",
            "get_user": "function getByEmail(email, callback) {\n  //this example uses the \"tedious\" library\n  //more info here: http://pekim.github.io/tedious/index.html\n  const sqlserver = require('tedious@1.11.0');\n\n  const Connection = sqlserver.Connection;\n  const Request = sqlserver.Request;\n  const TYPES = sqlserver.TYPES;\n\n  const connection = new Connection({\n    userName:  '{username}',\n    password:  '{password}',\n    server:    '{sqlserveraddress}',\n    options:  {\n      database: '{dbname}',\n      encrypt: true,\n      rowCollectionOnRequestCompletion: true\n    }\n  });\n\n  const query = 'SELECT Email FROM dbo.Users WHERE Email = @Email';\n\n  connection.on('debug', function (text) {\n    console.log(text);\n  }).on('errorMessage', function (text) {\n    console.log(JSON.stringify(text, null, 2));\n  }).on('infoMessage', function (text) {\n    console.log(JSON.stringify(text, null, 2));\n  });\n\n  connection.on('connect', function (err) {\n    if (err) return callback(err);\n\n    const request = new Request(query, function (err, rowCount, rows) {\n      if (err) return callback(err);\n\t  \n      if ( typeof rows[0] != 'undefined' && rows[0]) {\n        callback(null, {\n          email: rows[0][0].value\n        });\n      } else {\n       \tcallback(null); \n      }\n    });\n\n    request.addParameter('Email', TYPES.VarChar, email);\n    connection.execSql(request);\n  });\n}\n",
            "change_email": "function changeEmail (email, newEmail, emailVerified, callback) {\n  //this example uses the \"tedious\" library\n  //more info here: http://tediousjs.github.io/tedious/\n   const sqlserver = require('tedious@1.11.0');\n\n  const Connection = sqlserver.Connection;\n  const Request = sqlserver.Request;\n  const TYPES = sqlserver.TYPES;\n\n  const connection = new Connection({\n    userName:  '{username}',\n    password:  '{password}',\n    server:    '{sqlserveraddress}',\n    options:  {\n      database: '{dbname}',\n      encrypt: true\n    }\n  });\n\n  const query = 'UPDATE dbo.Users SET Email = @NewEmail WHERE Email = @Email';\n\n connection.on('debug', function(text) {\n    console.log(text);\n  }).on('errorMessage', function(text) {\n    console.log(JSON.stringify(text, null, 2));\n  }).on('infoMessage', function(text) {\n    console.log(JSON.stringify(text, null, 2));\n  });\n\n  connection.on('connect', function (err) {\n    if (err) return callback(err);\n\n    const request = new Request(query, function (err, rows) {\n      if (err) return callback(err);\n\t  \n      callback(null, rows > 0);\n    });\n\n    request.addParameter('NewEmail', TYPES.VarChar, newEmail);\n    request.addParameter('Email', TYPES.VarChar, email);\n    connection.execSql(request);\n  });\n}",
            "delete": "function remove(id, callback) {\n  // this example uses the \"tedious\" library\n  // more info here: http://pekim.github.io/tedious/index.html\n  const sqlserver = require('tedious@1.11.0');\n\n  const Connection = sqlserver.Connection;\n  const Request = sqlserver.Request;\n  const TYPES = sqlserver.TYPES;\n\n  const connection = new Connection({\n    userName:  '{username}',\n    password:  '{password}',\n    server:    '{sqlserveraddress}',\n    options:  {\n      database: '{dbname}',\n      encrypt: true\n    }\n  });\n\n  const query = 'DELETE FROM dbo.Users WHERE id = @UserId';\n\n  connection.on('debug', function (text) {\n    console.log(text);\n  }).on('errorMessage', function (text) {\n    console.log(JSON.stringify(text, null, 2));\n  }).on('infoMessage', function (text) {\n    console.log(JSON.stringify(text, null, 2));\n  });\n\n  connection.on('connect', function (err) {\n    if (err) return callback(err);\n\n    const request = new Request(query, function (err) {\n      if (err) return callback(err);\n      callback(null);\n    });\n\n    request.addParameter('UserId', TYPES.VarChar, id);\n\n    connection.execSql(request);\n  });\n}\n"
        },
        "configuration": {},
        "import_mode": false,
        "password_no_personal_info": {
            "enable": false
        },
        "password_dictionary": {
            "enable": false,
            "dictionary": []
        },
        "password_history": {
            "enable": false,
            "size": 5
        },
        "password_complexity_options": {
            "min_length": 8
        }
    }
}

Note I replaced my actual username, password, sqlserveraddress, and dbname with {}. That’s not my actual code for those parts.

Can anyone spot what I’m doing wrong here?

Is this still the recommended method?

Hello,

I faced with the case I need update Custom DB (NopC Sql Server DB) - when user change email in Auth0, I need update email in the DB.
I have GetUser script that property connects to the DB and check if user exist or not by email.
I implemented simple “change_email” script, assigned via Management API and try to check logs (use console.log with dummy text inside) to make sure “change_email” function triggers when I try to change email.
But it does not work - I don’t see any logs from “change_email” script.

Here is the function:
function changeEmail(email, newEmail, verified, callback) { console.log(‘Nopc : Change Email called’); return callback(null); }

I doubled checked - connection has this script.

Is this method to change custom DB email is still valid or something change since 2018?

Hello, I am also trying to sort out the change email script. I can sort of change the email one time but the second time I try change it back I get this error.

Is there any chance someone can send a sample change email script so I can learn from as the boiler plate on the auth0 website I can’t really work off of

{
    "statusCode": 400,
    "error": "Bad Request",
    "message": "User with old email does not exist in Auth0 database",
    "errorCode": "auth0_idp_error"
}

Thanks in advance for reading this

Ran into this same problem. We were trying to update the user’s email and username (both are the same value for us) and we have a custom database. You need to create the change_email and change_username scripts. However, it seems Auth0 doesn’t actually call the change_username script - but you still must have it otherwise Auth0 will throw an error when you call their endpoint. The solution for us was to use the change_email script to update both the email and username fields in our database (as well as setting the emailVerified field to false). Then you can first call the Auth0 endpoint once to update the email (this will change the email in Auth0 and the email and username in your custom database). Then make a second call to the Auth0 endpoint to update the username (this will change the username in Auth0).