I’m currently working on migrating users from both an on-premises database and an existing Auth0 connection. To do this, I’m using automatic migration via a custom database. The reason for moving users from the existing connection is that automatic migration can only be enabled on an empty connection. To work around this, I’ve set up a new common connection and am migrating both the on-prem and existing Auth0 users into it.
In the custom database login script, when returning the user profile, I append the user’s roles (from the old connection) to app_metadata.roles_to_migrate
. This is so I can assign those roles later using the Management API in a post-login action.
Unfortunately, I haven’t found a way to assign roles directly within the custom database script during user creation. If there’s a better pattern for this, I’d love to hear it.
The Post-Login Role Assignment
In the post-login action, I use a helper function assignMigratedRoles
to:
- Check if
app_metadata.migrate_roles === true
. - If so, use the Management API to assign the roles listed in
roles_to_migrate
. - Clear
migrate_roles
androles_to_migrate
fromapp_metadata
.
However, because we’re using RBAC, the permissions associated with these roles need to be included in the access token. This doesn’t happen on the first login, since the role assignment occurs after the access token is generated.
Current Workaround (Hack)
As a hack, I trigger the post-login actions again by calling:
api.user.setAppMetadata('force_retry', null);
…from within the role assignment function — even though api
isn’t passed to it. Somehow this causes the action to re-run, and the next login includes the correct permissions. Clearly, this is not a reliable or clean solution.
Here’s the relevant code for reference:
exports.onExecutePostLogin = async (event, api) => {
if (event.connection.name === 'Primary') {
await assignMigratedRoles(event, api);
}
};
const assignMigratedRoles = async (event, api) => {
const {
MANAGEMENT_CLIENT_ID,
MANAGEMENT_CLIENT_SECRET,
MANAGEMENT_CLIENT_DOMAIN,
} = event.secrets;
if (event.user.app_metadata.migrate_roles === true) {
const rolesToMigrate = event.user.app_metadata.roles_to_migrate;
const client = new ManagementClient({
clientId: MANAGEMENT_CLIENT_ID,
clientSecret: MANAGEMENT_CLIENT_SECRET,
domain: MANAGEMENT_CLIENT_DOMAIN,
});
const allRoles = await client.getRoles();
const roleIdsToAssign = allRoles
.filter((role) => rolesToMigrate.includes(role.name))
.map((role) => role.id);
if (roleIdsToAssign.length > 0) {
await Promise.all([
client.assignRolestoUser(
{ id: event.user.user_id },
{ roles: roleIdsToAssign },
),
client.updateAppMetadata(
{ id: event.user.user_id },
{
...event.user.app_metadata,
roles_to_migrate: null,
migrate_roles: null,
},
),
]);
// Hack to trigger the post-login action again
api.user.setAppMetadata('force_retry', null);
}
}
};
Questions
- How can I assign roles during login in such a way that the associated permissions are included in the access token right away?
- Is there a better approach to assign roles from within the custom database login script? For example, can I directly assign roles during user creation or call out to the Management API from that script?