Skip to main content

Time Based Role Example

Grant a user a role that expires at a set time, per tenant, with an attribute-based access control (ABAC) user set in Permit.io. This guide is for developers who need temporary access, such as an admin role for a support session, without removing the role assignment when the time is up.

A user set (also called a dynamic role, and userset in the API) is a condition set that matches users by their attributes. This page uses the term user set throughout.

How the time-based role works

  • The user has the admin role in a tenant, and a time_based_groups user attribute. Each entry in time_based_groups names a role, a tenant, and an expiration time.
  • Each tenant has a key tenant attribute that stores the tenant's own key. Permit doesn't add the tenant key to the tenant attributes for you, so you set the key attribute when you create the tenant.
  • Your application passes the current time as the current_time user attribute in each permit.check() call.
  • The temp-admin user set matches the user when the user has the admin role in the tenant of the check, and an entry in time_based_groups has an expires value greater than or equal to current_time and a tenant value equal to the key tenant attribute.
  • When the expiration time passes, the user set stops matching, and the check returns false.

Prerequisites

  • A Permit.io account.
  • A role with the key admin.
  • A resource with the key internal_api, with the actions you want to grant temporarily. The check in this guide uses the patch action.
  • Your environment API key. See Get your API key.
  • A container policy decision point (PDP) running at http://localhost:7766. The Cloud PDP doesn't evaluate ABAC policies. See Cloud PDP capabilities.
  • The Permit Node.js SDK (npm install permitio). The examples use top-level await, so run them in an ES module. The steps in 2. Test the time-based role run in one file: the permit client created in the first of those steps serves the later steps too.

1. Configure the attributes and the user set

1

Create the key tenant attribute

  1. Open Tenant Attributes in the Permit dashboard.
  2. Create a tenant attribute named key with the type String.
  3. Save the attribute. You set the value for each tenant in Create the coke tenant.

Tenant Attributes settings with the key attribute

2

Create the time_based_groups and current_time user attributes

  1. Open User Attributes.
  2. Create a user attribute named time_based_groups with the type Object Array.
  3. Create a user attribute named current_time with the type Number. Your application passes the current_time value in each check, so you don't store a value for it.
  4. Save the attributes.

User Attributes settings with the time_based_groups attribute

3

Create the temp-admin user set with the API

The temp-admin user set matches a user based on:

  • The user's role in the tenant of the check (admin).
  • The expires value in time_based_groups, compared with current_time.
  • The tenant value in time_based_groups, compared with the key tenant attribute.

Create the user set with the Permit API. The URL path contains the project key default and the environment key production: replace them with your project and environment keys. Replace <YOUR_API_KEY> with your environment API key.

curl --location 'https://api.permit.io/v2/schema/default/production/condition_sets' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'Authorization: Bearer <YOUR_API_KEY>' \
--data '{
"key": "temp-admin",
"name": "Temporary Admin",
"type": "userset",
"conditions": {
"allOf": [
{
"allOf": [
{ "user.roles": { "array_contains": "admin" } },
{ "user.time_based_groups": { "any_match": { "match": {
"expires": { "greater-than-equals": { "ref": "user.current_time" } },
"tenant": { "equals": { "ref": "tenant.key" } }
}}}}
]
}
]
}
}'

The API responds with a JSON object for the created user set, with "key": "temp-admin" and "type": "userset".

4

Grant permissions to the temp-admin user set

  1. Open the Policy Editor.
  2. Find the Temporary Admin user set, and select the internal_api actions it can perform, including patch.
  3. Save your changes.

Policy Editor with actions selected for the Temporary Admin user set

2. Test the time-based role

1

Create the coke tenant

Create a tenant with the key coke, and set its key tenant attribute to the same value. Replace <YOUR_API_KEY> with your environment API key.

import { Permit } from "permitio";

const permit = new Permit({
token: "<YOUR_API_KEY>",
pdp: "http://localhost:7766",
});

await permit.api.tenants.create({
key: "coke",
name: "Coke",
attributes: {
key: "coke",
},
});

To use the time-based role in a tenant that already exists, set the key tenant attribute with permit.api.tenants.update(), or in the dashboard. See Define stored tenant attributes.

2

Create a user with the admin role and a time_based_groups entry

Create the user george@test.com with a time_based_groups entry for the coke tenant that expires one hour from now, and assign the user the admin role in the coke tenant:

const ONE_HOUR_MS = 60 * 60 * 1000;

await permit.api.users.sync({
key: "george@test.com",
attributes: {
time_based_groups: [
{
role: "admin",
tenant: "coke",
expires: Date.now() + ONE_HOUR_MS,
},
],
},
});

await permit.api.users.assignRole({
user: "george@test.com",
role: "admin",
tenant: "coke",
});
Time format

expires is a Unix timestamp in milliseconds, the format Date.now() returns. Pass current_time in permit.check() in the same format. If the two values use different units, the comparison gives wrong results.

3

Run permit.check() with the current time

Run a check for george@test.com in the coke tenant. The resource type is the key of your resource (internal_api). The call passes the current time as the current_time user attribute:

const permitted = await permit.check(
// User object
{
key: "george@test.com",
attributes: {
current_time: new Date().getTime(),
},
},
// Action
"patch",
// Resource
{
type: "internal_api",
tenant: "coke",
}
);

permitted is true, because the coke entry of time_based_groups expires one hour after you created the user.

4

Verify that the role expires

Set the expires value of the coke entry to a time in the past, then run the same check again:

await permit.api.users.update("george@test.com", {
attributes: {
time_based_groups: [
{
role: "admin",
tenant: "coke",
expires: Date.now() - 1000,
},
],
},
});

After the PDP receives the updated user data, the check returns false. The following table lists the results of the check:

Condition at check timeResult
The coke entry's expires is later than current_timetrue
The coke entry's expires is earlier than current_timefalse
The user has no time_based_groups entry for the tenant of the checkfalse
The tenant of the check has no key tenant attributefalse
Set expiration times from your application

When you grant temporary access in production, update time_based_groups with the Permit API, and set expires to the current time plus the access duration.

Next steps