When a user logs in to our iOS app for the first time, the JWT access token does not contain any permissions until the second time they login.
From what I have read so far, it seems this is because the Auth0 actions have not fully completed by the time we receive the first access token on the frontend (iOS App).
I notice React or SPA web apps can resolve this easily via the Auth0 SDK by using a getTokenSilently function, however we don’t have something similar in the iOS SDK.
The solution we are considering is if the user’s permissions are empty, we make a request to renew a refresh token, which then contains the permissions we need because the Auth0 actions have completed.
Is there a better way of going about this? Are there any inherent risks to enabling refresh tokens (they are currently disabled) I might not be aware of?
Is it true the only way to receive a refresh token from the /token endpoint is to enable Allow Offline Access in the API Manager?
Other details:
We use Microsoft Active Directory for validating users
Are you attempting something like is outlined in this FAQ to add roles/permission to a user’s token? Which type of Action are you using?
Refresh tokens are encouraged for native mobile apps and are considered a safe approach to improving the user experience. I recommend checking out both this article as well as this blog post to learn more.
That is correct - The only way a refresh token will be granted is if the offline_access scope is included in the authorize request.
Thanks for those resources @tyf ! The information you shared was very helpful!
I apologize, I was incorrect when saying we use actions (not the Auth0 implementer, just the front end consumer). It appears we use Auth Pipeline -> Rules and execute a function to authenticate users.
Here is what our function looks like, are there any room for improvements we could make to allow the Role to be assigned to the access token the first time? Or would we need to migrate to actions?
I removed sensitive data, so it doesn’t exactly match what we implement, but it’s essentially the same.
function authenticateUser(user, context, callback) {
// Auto-populated by auth0-deploy-cli
const groupsAllowed = {"A Group Key":["A collection of group values"]};
const clientGroups = groupsAllowed[context.clientName] || [];
const hasAccess = user.groups.some((group) => clientGroups.includes(group));
if (!hasAccess) {
return callback(
new UnauthorizedError("Access denied. You are not a member of an authorized group. Please contact your administrator.")
);
}
const ManagementClient = require("auth0@2.27.0").ManagementClient;
const management = new ManagementClient({
domain: auth0.domain,
token: auth0.accessToken,
scope: 'read:roles update:users'
});
const params = { id: user.user_id };
const data = { roles: [] };
switch (context.clientName) {
case "Platform 1":
data.roles.push(configuration.ROLE_1);
break;
case "Platform 2":
data.roles.push(configuration.ROLE_2);
break;
case "Platform 3":
if (user.groups.includes("Role 3")) {
data.roles.push(configuration.ROLE_3);
}
if (user.groups.includes("Role 4")) {
data.roles.push(configuration.ROLE_4);
}
if (user.groups.includes("Role 5")) {
data.roles.push(configuration.ROLE_5);
}
// A custom claim for mapping emails to certain locations
const namespace = "https://any-url.com/";
context.accessToken[namespace + 'email'] = user.email;
break;
default:
}
management.assignRolestoUser(params, data, function (err) {
if (err) {
return callback(new Error(err), user, context);
}
return callback(null, user, context);
});
}