# Modify user information

This script will be executed when the administrator modifies the user information or the user modifies the user information. This script is only required in full use of the custom database schema.

# Function definition

The login function is defined as follows:

async function updateUser(id, updates, context) {
  // This script should update a user entry in your existing database. It will
  // be executed when a user attempts to change profile or when a user is updated
  // through the Auth0 dashboard or API.

  // The first argument `id` is the id of user.

  // The second argument `updates` contains following properties:
  // * email: the user's email
  // * username: the user's username
  // * phone: the user's phone number
  // * password: the user's password in plain text format
  // * nickname: the user's nickname
  // * photo: the user's photo
  // * and many other fields

  // The Second argument context contains information about the authentication context.
  // see http://core.authing.cn/connections/custom-db/config-custom-db-connection.html for more information.

  //
  // There are three ways this script can finish:
  // 1. A user was successfully created
  // format: https://docs.authing.co/user/profile.html.
  // return null
  // 2. This user already exists in your database
  // throw new Error("user allready exists")
  // 3. Something went wrong while trying to reach your database:
  // throw new Error("my error message")

  const msg =
    'Please implement the Find User script for this database connection';
  throw new Error(msg);
}
Parameters Type nullable Description
id string / number false User ID
updates object false User fields that need to be modified
updates.email string ture Email, this parameter may be empty.
updates.phone string true Mobile phone number, this parameter may be empty.
updates.username string true User name, this parameter may be empty.
updates.password string true Clear text password. This parameter may be empty. It is strongly recommended to use bcrypt to encrypt user passwords. See below for details.
updates.nickname string true User nickname, this parameter may be empty.
updates.photo string true User avatar, this parameter may be empty.
updates.token string true User token, this parameter may be empty.
updates.emailVerified bool true Whether the mailbox is verified, this parameter may be empty.
updates.phoneVerified bool true Whether the phone number is verified, this parameter may be empty.
updates.loginsCount number true The number of user logins, this parameter may be empty.
updates.xxxx any true Other more user fields, please refer to the detailed format of user information: User Profile Field
context object true request context context

The context contains the following information:

Property name Type Description
userPoolId string User Pool ID
userPoolName string User pool name
userPoolMetadata object User pool configuration information
appId string ID of the current user, **You can distinguish the source of the application requested by the user through appId. **
appName string The name of the current application
appMetadata object Current application configuration information
application string User Pool ID
request object The detailed information of the current request, including:
ip: client IP
geo: client geographic location resolved by IP
body: request body

# Return data convention

# Successfully modified

When the user profile is successfully modified, you need to return the latest user information of the user to Authing. For the detailed format of the user information, please see: User Profile Field. Example:

async function updateUser(id,updates, context) {
  // Implement your logic here
  return {
    id: 1, // must not empty
    email: "test@example.com",
    emailVerified: true,
    nickname: "Nick",
    photo: ""
  }
}

# User does not exist

When the user does not exist, you need to directly throw an error (the error message can be freely defined), for example:

async function updateUser(id, updates, context) {
  // Implement your logic here
  throw new Error('User not exists!');
}

# Other exception errors

When encountering other abnormal errors, you can catch the error and return a more friendly error prompt, for example:

async function updateUser(id, updates, context) {
  try {
    // Implement your logic here
  } catch (error) {
    throw new Error('Something went wrong ...')
  }
}

# Best Practices

# Provide friendly error tips

When encountering an unknown error, we recommend throwing a standard Error object. Authing will catch this error and return it to the end user. For example: throw new Error("My nice error message"), you can see the error log in the log history of the custom database.

# Disconnect the database connection at the end of the function

Please remember to close the connection to the database when the script is executed, such as calling client.end(). For example, it can be executed in try/finallly to ensure that it will always be executed:

try {
  const result = await client.updates("YOUR updates");
} finally {
  // NOTE: always call `client.end()` here to close the connection to the database
  client.end();
}

# Example function

Taking the postgres database as an example, the following points are explained:

-You can get the database connection string through env.DB_CONNECTION_URI to create a database connection. -Dynamically create update SQL statements based on the query conditions passed in updates (updates.id, updates.email, updates.username, updates.phone may be empty, but Will not be empty at the same time). -If insertResult.rowCount is 0, it means that the user does not exist and an exception is thrown. The error message is: User not exists!. -Finally, the user information in the specified format is returned. For the detailed format of the user information, please see: User Profile Field. -Call client.end() in try/finally to disconnect the database.

async function updateUser(id, updates, context) {
  // This example uses the "pg" library
  // more info here: https://github.com/brianc/node-postgres
  const {Client} = require('pg');

  const client = new Client({
    connectionString: env.DB_CONNECTION_URI,
  });

  // Or you can:
  // const client = new Client({
  // host: env.DB_HOST,
  // port: env.DB_PORT,
  // user: env.DB_USERNAME,
  // password: env.DB_PASSWORD,
  // database: env.DB_DATABASE,
  // });

  await client.connect();

  // Authing user attribute to database column
  const userColumnMap = {
    id:'id',
    email:'email',
    name:'name',
    username:'username',
    phone:'phone',
    nickname:'nickname',
    gender:'gender',
    address:'address',
    company:'company',
    birthdate:'birthdate',
    website:'website',
    token:'token',
    password:'password',
    photo:'photo',
    emailVerified:'email_verified',
    phoneVerified:'phone_verified',
    loginsCount:'logins_count',
    lastIp:'last_ip',
  };

  // Make sure to delete cols not exists in your table,
  // or sql might fail.
  for (const key in updates) {
    if (userColumnMap[key] === undefined) {
      delete updates[key];
    }
  }

  // If nothing interested, just return
  if (Object.keys(updates).length === 0) {
    return null;
  }

  function generateQuery(id, cols) {
    const _ = require('lodash');
    // Setup static beginning of query
    var query = ['UPDATE users'];
    query.push('SET');

    // Create another array storing each set command
    // and assigning a number value for parameterized query
    var set = [];
    Object.keys(cols)
      .filter(col => !!userColumnMap[col])
      .forEach(function(key, i) {
        set.push(userColumnMap[key] + '= ($' + (i + 1) +')');
      });
    query.push(set.join(','));

    // Add the WHERE statement to look up by id
    query.push('WHERE id = '+ id);

    // Return all fields
    query.push('RETURNING *');

    // Return a complete query string
    return query.join('');
  }

  // Use bcrypt to encrypt password
  // more info here: https://github.com/kelektiv/node.bcrypt.js
  const bcrypt = require('bcrypt');

  try {
    const query = generateQuery(id, updates);
    const insertResult = await client.query(
      query,
      Object.keys(updates)
        .filter(col => !!userColumnMap[col])
        .map(key => {
          const val = updates[key];
          if (key ==='password') {
            // If key is password, use bcrypt to encrypt it
            return bcrypt.hashSync(val, bcrypt.genSaltSync(10));
          }
          return val;
        }),
    );
    if (insertResult.rowCount === 0) {
      throw new Error('User not exists!');
    }
    const user = insertResult.rows[0];
    return {
      id: user.id,
      email: user.email,
      name: user.name,
      phone: user.phone,
      username: user.username,
      photo: user.photo,
      nickname: user.nickname,
      token: user.token,
      emailVerified: user.email_verified,
      phoneVerified: user.phone_verified,
      loginsCount: user.logins_count,
      lastIp: user.last_ip,
      gender: user.gender,
      address: user.address,
      company: user.company,
      birthdate: user.birthdate,
      website: user.website,
    };
  } catch (error) {
    throw new Error(`Execute query failed: ${error.message}`);
  } finally {
    // NOTE: always call `client.end()` here to close the connection to the databaseclient.end();
   }
}