Fetching and displaying user lists
This guide focuses on how to build a user experience that potentially supports complex tenant and user management for your users via query and list endpoints using Authress Access Records. Here, we pick up from the previous guide Onboarding Users.
Scenarioโ
You are extending your application to give your users the ability to configure their account. Users will want to specify who has access to their account's resources and to grant new access to existing members. For user invites and onboarding new users, check out the Onboarding Users guide. This means we'll need to provide the users a dialog which includes users as well as their permissions via the available tenants. The result will be the right users have access to right resources in your Authress account.
- When the term
User
is used in this guide, we refer to your application's users, one of your customers. - Likewise,
Account
refers to your user's customer configuration in your application. Authress account
will always mean your account in the Authress management portal, used for configuration of your Authress account.Authress tenant
ortenant
will refer the the Authress authentication configuration for logging a user in.
User storiesโ
There are multiple possible ways to fetch users and review access, so it helps to think in terms of concrete user stories. There are two core user stories
related to user management that we will tackle throughout this guide:
1. Assigning Permissions User Story - A user is the Account owner of a resource in your product. After inviting other users to the Account, the Account owner wants to configure the permissions other users have. Examples might be setting other users to also be owners of the Account, or granting them read only permissions. The Account owner will want to fetch a list of all users for the tenant, and then set the permissions associated with each of those users. Some admin users will have access to multiple tenants and the users in each of those tenants.
2. Reviewing Access User Story - The Account owner wants to achieve yearly compliance by ensuring only the right users have access to the resources owned by their Account. Frequently they'll want to fetch the list of users that have access to the Account, and review which users have which access.
Inviting users is a flow handled in the Onboarding Users guide.
The rest of the guide will review an implementation for how to deliver the above user stories. And since permissions are controlled by Access Records in Authress, we'll see a heavy use of Access Records below. The pieces required are:
- 1. Get all the users in a tenant
- 2. Get user accounts
- 3. List multiple accounts' users
- 4. Grant access to multiple accounts
- 5. Accounts versus Tenants
- 6. Auditing user access
Authress permissions modelโ
In this guide, we'll be discussing access to accounts, the users in an account, and the permissions those users have. This means it is important to review the following recommended Authress access record model:
- Each user's permissions for an account will be stored in a single access record
- Since a user can have access to multiple accounts, we will create access records with IDs in the following format
rec_user_${userId}
. Accounts/{accountId}
is theresourceUri
that represents the whole account resource. We will use access toAccounts/{accountId}
to represent access to an Application Customer Account.
Reminder: Authress resources are prefixed by the namespace Authress:
, access to change an Authress connection is Authress:Connections/{connectionId}
, and access to change the Authress configuration for a tenant in Authress is Authress:Tenants/{tenantId}
. Access to Accounts/{accountId}
represents access for your users to a Customer Account, and access to Accounts:Tenants/{tenantId}
represents access to the Authress configuration.
Implementation stepsโ
1. Get all users in a tenantโ
import { AuthressClient } from '@authress/sdk';
const authressClient = new AuthressClient({ authressApiUrl: 'https://auth.yourdomain.com' });
const userResponse = await authressClient.users.getUsers({
tenantId: accountId
});
const userList = userResponse.data.users;
/*
userList = [{
name: 'User Name',
userId: 'User ID',
picture: 'https://www.gravatar.com/avatar/?d=identicon',
email: 'user@customer.domain.com'
}];
*/
The returned user list will only include users that have logged in through the tenant configuration specified by the tenantId
. Using this list allows you to populate default drop downs and displays that include multiple users. This API is restricted to service clients that have users:read
permission on Authress:Users
, For more information on the permissions that Authress APIs require review the Authress management resources permissions.
You'll notice in the above code block that there is no access check for whether or not users should be returned. And further we need to actually know which Account users we should display for the user. To do this we'll check that user's access using the Authress Authorization Check:
const userIdentity = await TokenVerifier('https://auth.yourdomain.com', userToken);
await authressClient.userPermissions.authorizeUser(userIdentity.sub, `Accounts/${accountId}`, 'accounts:read');
In this case, when we know which customer account we want to fetch the users for, we can use the user's token to validate access to the specified account. The accounts:read
permission as well as the Accounts/{accountId}
are properties defined by you in your Authress Account, assigned to your users via access records, and then checked here.
2. Get the tenant a user has access toโ
It isn't required to always know the user's tenant a priori. We can instead use the Authress User Permissions List endpoint to fetch the accounts the user has access to. Since your application will assign users access in accordance with the above permissions model, the relevant resources will be of the form Accounts/account_001
, Accounts/account_002
, etc...
const userIdentity = await TokenVerifier('https://auth.yourdomain.com', userToken);
const resourceResponse = await authressClient.userPermissions.getUserResources(userIdentity.sub, 'Accounts', 10, null, 'accounts:read', 'INCLUDE_NESTED');
const accounts = resourceResponse.data.resources;
// And then fetch the users for relevant account:
const userResponse = await authressClient.users.getUsers({
tenantId: accounts[0].resourceUri;
});
The result will be a list of users, which have logged in with that specific Authress Tenant, for each Tenant that matches a Customer Account for which the user has the accounts:read
permission.
3. Fetch users for multiple accountsโ
Some users in your application might have access to multiple customer Accounts. When populating a user related drop down selection, great care should be used in deciding whether or not to display users or resources across multiple tenants. The Authress recommendation is only display users or resources for one tenant at a time. This prevents administrative users from accidentally selecting the wrong users or the wrong resources. In any list that includes users or resources from multiple tenants, please ensure that the Account ID is clearly indicated in all displays.
For rare cases where a user admin is logged in that should be able to see multiple users from multiple tenants at the same time, but only a subset of all the users present in your Authress account, Interleaving the results from multiple getUsers
api calls is optimal:
const listsAsync = accounts.map(account => authressClient.users.getUsers({
tenantId: accounts.resourceUri;
}));
const userResponseForTenants = await Promise.all(listAsync);
const paginatedUsers = userResponseForTenants.map(tenantResponse => tenantResponse.data.users).flat();
return paginatedUsers;
4. Grant a user access to multiple accountsโ
To actually grant a user permissions to multiple accounts, we can review the above permissions model to see that we should have a dedicated Access Record for the user.
await authressClient.accessRecords.createRecord({
name: `User: ${userId}`,
recordId: `rec_user_${userId}`,
users: [{ userId: userId }],
statements: [{
roles: ['Admin'],
resources: [
{ resourceUri: `/Accounts/account_001` },
{ resourceUri: `/Accounts/account_002` }
]
}]
});
When a user is assigned multiple resources, each of these resources will be returned in the earlier getUserResources()
request above.
5. When to use Authress:Tenants versus Accountsโ
Authress:Tenants
and Accounts
represent two different concepts in Authress:
Authress:Tenants
is the Authress resource that represents the Authress tenant configuration in Authress. This configuration enables users to log in with their Corporate SSO IdP. Usually this configuration can only be changed by an Authress Account Admin or an Authress Service Client that you have given sufficient access to. TheID
of the tenant matches your customerAccounts
and is the value passed as thetenantId
in both authentication requests as well asuser queries
.Accounts
represents your application customers accounts. Authress actually doesn't need to know what your internal identifiers are called. Your application might call these tenants or customers or organizations. As long as all the above API calls and Access Records in Authress use a consistent resourceUri such asAccounts/
, then the functionality will work as expected. For this reason this guide usesAccounts/
, however you could theoretically replace every instance ofAccounts/
in this usage guide withOrganizations/
and everything would still work asAccounts
is not a reserved concept in Authress, but rather one on the application side.
Further, your end users are not usually given direct access to the Authress:Tenants
resource, because this would allow them to directly change the Authress configuration in a way that might not be preferred by your application. An example might be DELETE Authress:Tenants/tenant_001
. Deleting a tenant is likely something that you don't want to allow your users to do. It is common however to enable users the ability to update the tenant SSO configuration based on the needs of that customer. In these cases, Authress recommends to create a permission that matches this need, and assign it to users as necessary. An example might be:
import { AuthressClient } from '@authress/sdk';
const authressClient = new AuthressClient({ authressApiUrl: 'https://auth.yourdomain.com' });
const userIdentity = await authressClient.verifyToken(userToken);
const accountId = 'account_001';
await authressClient.userPermissions.authorizeUser(userIdentity.sub, `Accounts/${accountId}/SSO`, 'accounts:sso:update');
await authressClient.tenants.updateTenant(accountId, {
tenantLookupIdentifier: 'Tenant-Lookup-Identifier',
connection: {
connectionId: 'google'
},
domains: [{
domain: 'customer.domain.com'
}],
data: {
name: 'Tenant Name'
}
});
6. Get all users that have access to a resourceโ
As part of being an account admin, one of your users might require asking who are all the users that have access to a customer resource. That resource could be a specific document or element in your application, or it could be access to the whole tenant itself. An example might be who are all the account admin of my account? The goal could be display something similar to this:
To get the list, we can use the resources.getUsers()
endpoint and specify the resource that we want to see who has access to that resource.
In the case where an account admin would like to know which users have full account access, you might embed a call to Authress to generate that view above:
import { AuthressClient } from '@authress/sdk';
const authressClient = new AuthressClient({ authressApiUrl: 'https://auth.yourdomain.com' });
const accountId = 'account_001';
const userIdentity = await authressClient.verifyToken(userToken);
await authressClient.userPermissions.authorizeUser(userIdentity.sub, `Accounts/${accountId}`, 'accounts:users:read');
const resourceUsersResponse = await authressClient.resourcePermissions.getResourceUsers(`Accounts/${accountId}`);
const users = resourceUsersResponse.data.users;
/*
users = [{
userId: 'User ID',
roles: [{ roleId: 'Admin' }, { roleId: 'Editor' }]
}];
*/
// And then optionally combine that with the user list endpoint:
const userResponse = await authressClient.users.getUsers({
tenantId: accountId
});
const userPaginatedList = users.map(u => userResponse.data.users.find(userData => userData.userId === u.userId));
return userPaginatedList;
One note related to permissions management for tenants is the usage of Authress Groups. Groups also can be given direct permissions as well. They help by creating a layer of indirection and abstraction for aspects that are commonly associated with a group. For instance, you might have a customer account where the customer admin keeps track of third party contractors. Rather than individual access for each contractor, you might allow that customer to create an Authress group for all their contractors.