Converting Rule to Action

Hey there @bbarnell welcome to the community!

We unfortunately don’t have a pre-existing example of the same functionality in an Action, but I was able to just test this code in my own environment and it seems to be functioning as expected:

const { ManagementClient } = require('auth0');

exports.onExecutePostLogin = async (event, api) => {
  const fieldMapping = {
    family_name: 'family_name',
    given_name: 'given_name',
    name: 'name',
    nickname: 'nickname',
    picture: 'picture'
  };

  if (needMigration(event.user)) {
    const management = new ManagementClient({
        domain: event.secrets.domain,
        clientId: event.secrets.clientID,
        clientSecret: event.secrets.clientSecret,
    });

    try {
      const updatedUser = await management.updateUser(
        { id: event.user.user_id },
        generateUserPayload(event.user)
      );
      updateActionUser(event.user, updatedUser);
    } catch (err) {
      console.error(err);
      throw new Error('Failed to migrate user attributes.');
    }
  }

  function needMigration(user) {
    if (user.user_metadata) {
      for (const key in fieldMapping) {
        if (typeof user.user_metadata[fieldMapping[key]] === 'string') {
          return true;
        }
      }
    }
    return false;
  }

  function generateUserPayload(user) {
    const payload = { user_metadata: {} };
    const userMetadata = user.user_metadata;

    for (const key in fieldMapping) {
      generateUserPayloadField(userMetadata, payload, key, fieldMapping[key]);
    }

    return payload;
  }

  function updateActionUser(user, updatedUser) {
    for (const key in fieldMapping) {
      if (typeof user.user_metadata[fieldMapping[key]] === 'string') {
        user[key] = updatedUser[key];
        delete user.user_metadata[fieldMapping[key]];
      }
    }
  }

  function generateUserPayloadField(userMetadata, payload, rootField, metadataField) {
    if (typeof userMetadata[metadataField] === 'string') {
      payload[rootField] = userMetadata[metadataField];
      payload.user_metadata[metadataField] = null;
    }
  }
};

I attempted to mirror the Rule code as closely as possible - I will note that it might be worth looking into caching the Management API access token as well:

Hope this helps!