Hello
This is wee bit complex.We have an existing environment of hundreds of apps and services that use different IdPs. Now we want to migrate to Auth0 as we expect to have too many users for our old solution. However we would like to preserve how we work with application roles - the roles are contained in the access token for machine-to-machine flow and in the identity token for user flows. The claim we use is simply “{someprefix}roles” so the token for a user or an application looks like this:
{
"myroles": [
"operator",
"projects:read",
"projects:write"
],
…
other claims
}
The only way I found so far is having logical APIs for every application where the permissions will be defined as a representation of the application roles. For users there will be also roles that will be mapped to permissions 1:1. And there are two Actions, one for user flows and one for machine flows, that inject these permissions/roles to the custom roles claim.
Example for user flow (JSON for better readability)
Application
{
"AppName": "TestAppUserFlow",
"ClientId": "AAAAA"
}
Logical API
{
"ApiName": "TestAppUserFlow",
"ApiIdentifier": "testAppUserFlow",
"Permissions": [ "operator", "projects:read", "projects:write"]
}
Roles
{
{
"RoleName": "AAAAA::operator",
"Permissions":
[
"operator"
]
},
{
"RoleName": "AAAAA::projects:read",
"Permissions":
[
"projects:read"
]
},
{
"RoleName": "AAAAA::projects:write",
"Permissions":
[
"projects:write"
]
}
}
User
{
"RolesAssigned":
[
"AAAAA::operator",
"AAAAA::projects:read"
]
}
Action
{
exports.onExecutePostLogin = async (event, api) => {
var clientId = event.client.client_id;
if (event.authorization)
{
var clientWiseRoles = new Array();
event.authorization.roles.forEach(role =>
{
if (role.startsWith(clientId + "::"))
{
clientWiseRoles.push(role.split("::")[1])
}
});
api.idToken.setCustomClaim("approles", clientWiseRoles);
api.accessToken.setCustomClaim("approles", clientWiseRoles);
}
};
}
Code request
{
"client_id":"AAAAA",
"response_type":"code",
"redirect_uri":"https://localhost/",
"response_mode":"query",
"scope":"openid offline_access profile",
"state":"12345",
"audience":"testAppUserFlow"
}
Token request
{
"client_id":"AAAAA",
"scope":"openid offline_access profile",
"code":"EWKJSJi56MHCZQurFly_2ydEGVilx-9H3gKEJHJaJIdei",
"redirect_uri":"https://localhost/",
"grant_type":"authorization_code",
"client_secret":"****************************************",
"audience":"testAppUserFlow"
}
Token response
{
"approles": [
"projects:read",
"operator"
],
"nickname": "john.doe",
"name": "john.doe@contoso.com",
"updated_at": "2024-01-29T08:08:19.212Z",
"iss": "https://somedomain.eu.auth0.com/",
"aud": "6wwXydkYjkshb3u7*****",
"iat": 1706520431,
"exp": 1706556431,
"sub": "auth0|6499a4d532*******",
"sid": "NOy_qXCPJ_lG4sAc*********"
}
Example for machine flow (json for better readability)
Application
{
"AppName": "TestAppMachineFlow",
"ClientId": "BBBBB"
}
Logical API
{
"ApiName": "TestAppMachineFlow",
"ApiIdentifier": "testAppMachineFlow",
"Permissions": [ "operator", "projects:read", "projects:write" ]
}
Client application
{
"PermissionsAssigned": [ "projects:write"]
}
Action
{
exports.onExecuteCredentialsExchange = async (event, api) => {
var clientId = event.client.client_id;
var clientWiseRoles = new Array();
event.accessToken.scope.forEach(role =>
{
if (role.startsWith(clientId + "::"))
{
clientWiseRoles.push(role.split("::")[1])
}
});
api.accessToken.setCustomClaim("approles", clientWiseRoles);
};
}
Token request
{
"client_id":"BBBBB",
"scope":"",
"grant_type":"client_credentials",
"client_secret":"*************************************",
"audience":"testAppMachineFlow"
}
Token
{
"approles": [
"owner"
],
"iss": "https://somedomain.eu.auth0.com/",
"sub": "s475wirEc9LclpiW*******************",
"aud": "testAppMachineFlow",
"iat": 1706520822,
"exp": 1706607222,
"azp": "s475wirEc9Lc********",
"scope": "s475wirEc9Lc***********::owner",
"gty": "client-credentials"
}
Risks
- As I use permissions of the logical API for application roles and it is only possible to use the audience for just one API, I basically cannot use application roles and scopes of real APIs simultaneously.
- I need to use Actions. Potential performance issue with hundreds thousands users?
- Feels like a workaround for something Auth0 might support natively somehow.
Is there a better way how to work with application roles with Auth0?