Skip to main content

Overview

SuperTokens Core provides a flexible role-based access control (RBAC) system that supports:
  • Hierarchical role and permission structure
  • Multi-tenant role assignments
  • Role inheritance across user pool
  • Bulk role operations

Core Concepts

Roles vs Permissions

Roles

Named collections of permissions (e.g., “admin”, “editor”, “viewer”)

Permissions

Granular access rights (e.g., “user:read”, “post:write”, “admin:delete”)
Relationship: A role contains multiple permissions. A user can have multiple roles.
User → Roles → Permissions

Storage Architecture

From io/supertokens/userroles/UserRoles.java:46-51:
Roles are stored in the public tenant storage (app-level), while role-to-user mappings are stored in each tenant’s storage. This allows roles to be shared across all tenants in an app.

Creating Roles

From io/supertokens/userroles/UserRoles.java:124-161:
public static boolean createNewRoleOrModifyItsPermissions(
    AppIdentifier appIdentifier,
    Storage storage,
    String role,
    String[] permissions
) {
    UserRolesSQLStorage userRolesStorage = 
        StorageUtils.getUserRolesStorage(storage);
    
    return userRolesStorage.startTransaction(con -> {
        // Create role if it doesn't exist
        boolean wasANewRoleCreated = userRolesStorage
            .createNewRoleOrDoNothingIfExists_Transaction(
                appIdentifier, con, role
            );
        
        // Add permissions to role
        if (permissions != null) {
            for (String permission : permissions) {
                userRolesStorage
                    .addPermissionToRoleOrDoNothingIfExists_Transaction(
                        appIdentifier, con, role, permission
                    );
            }
        }
        
        userRolesStorage.commitTransaction(con);
        return wasANewRoleCreated;
    });
}

Example: Create Role with Permissions

// Create admin role with all permissions
boolean created = UserRoles.createNewRoleOrModifyItsPermissions(
    appIdentifier,
    storage,
    "admin",
    new String[]{
        "user:read",
        "user:write",
        "user:delete",
        "post:read",
        "post:write",
        "post:delete"
    }
);

if (created) {
    System.out.println("Admin role created");
}

// Create editor role with limited permissions
UserRoles.createNewRoleOrModifyItsPermissions(
    appIdentifier,
    storage,
    "editor",
    new String[]{
        "user:read",
        "post:read",
        "post:write"
    }
);

// Create viewer role
UserRoles.createNewRoleOrModifyItsPermissions(
    appIdentifier,
    storage,
    "viewer",
    new String[]{"post:read"}
);

Assigning Roles to Users

From io/supertokens/userroles/UserRoles.java:41-60:
public static boolean addRoleToUser(
    Main main,
    TenantIdentifier tenantIdentifier,
    Storage storage,
    String userId,
    String role
) throws UnknownRoleException {
    // Verify role exists in app storage
    Storage appStorage = StorageLayer.getStorage(
        tenantIdentifier.toAppIdentifier().getAsPublicTenantIdentifier(),
        main
    );
    
    if (!doesRoleExist(tenantIdentifier.toAppIdentifier(), appStorage, role)) {
        throw new UnknownRoleException();
    }
    
    try {
        StorageUtils.getUserRolesStorage(storage)
            .addRoleToUser(tenantIdentifier, userId, role);
        return true;  // Role added
    } catch (DuplicateUserRoleMappingException e) {
        return false;  // User already has role
    }
}

Example: Assign Roles

try {
    // Assign admin role
    boolean added = UserRoles.addRoleToUser(
        main,
        tenantIdentifier,
        storage,
        userId,
        "admin"
    );
    
    if (added) {
        System.out.println("Admin role assigned");
    } else {
        System.out.println("User already has admin role");
    }
    
} catch (UnknownRoleException e) {
    System.out.println("Role doesn't exist. Create it first.");
}

Bulk Role Assignment

From io/supertokens/userroles/UserRoles.java:62-109: For bulk imports and batch operations:
public static void addMultipleRolesToMultipleUsers(
    Main main,
    AppIdentifier appIdentifier,
    Storage storage,
    Map<TenantIdentifier, Map<String, List<String>>> rolesToUserByTenant
) {
    Storage appStorage = StorageLayer.getStorage(
        appIdentifier.getAsPublicTenantIdentifier(), main
    );
    
    UserRolesSQLStorage publicRoleStorage = 
        StorageUtils.getUserRolesStorage(appStorage);
    
    // Verify all roles exist
    publicRoleStorage.startTransaction(con -> {
        Set<String> allRoles = new HashSet<>();
        for (Map<String, List<String>> tenantRoles : rolesToUserByTenant.values()) {
            for (List<String> roles : tenantRoles.values()) {
                allRoles.addAll(roles);
            }
        }
        
        List<String> rolesFound = publicRoleStorage
            .doesMultipleRoleExist_Transaction(
                appIdentifier, con, new ArrayList<>(allRoles)
            );
        
        // Check if any roles are missing
        if (!new HashSet<>(rolesFound).containsAll(allRoles)) {
            throw new UnknownRoleException();
        }
        
        return null;
    });
    
    // Add role-to-user mappings
    UserRolesSQLStorage userRolesStorage = 
        StorageUtils.getUserRolesStorage(storage);
    
    userRolesStorage.startTransaction(con -> {
        userRolesStorage.addRolesToUsers_Transaction(
            con, rolesToUserByTenant
        );
        userRolesStorage.commitTransaction(con);
        return null;
    });
}

Example: Bulk Assignment

// Prepare role assignments by tenant
Map<TenantIdentifier, Map<String, List<String>>> rolesToUserByTenant = 
    new HashMap<>();

TenantIdentifier tenant1 = new TenantIdentifier(
    connectionUriDomain, appId, "tenant1"
);

// User1: admin + editor
// User2: editor + viewer
Map<String, List<String>> tenant1Users = new HashMap<>();
tenant1Users.put("user1", Arrays.asList("admin", "editor"));
tenant1Users.put("user2", Arrays.asList("editor", "viewer"));

rolesToUserByTenant.put(tenant1, tenant1Users);

// Bulk assign
UserRoles.addMultipleRolesToMultipleUsers(
    main, appIdentifier, storage, rolesToUserByTenant
);

Retrieving User Roles

From io/supertokens/userroles/UserRoles.java:223-226:
public static String[] getRolesForUser(
    TenantIdentifier tenantIdentifier,
    Storage storage,
    String userId
) {
    return StorageUtils.getUserRolesStorage(storage)
        .getRolesForUser(tenantIdentifier, userId);
}

Example: Get User’s Roles

String[] roles = UserRoles.getRolesForUser(
    tenantIdentifier,
    storage,
    userId
);

System.out.println("User has " + roles.length + " roles:");
for (String role : roles) {
    System.out.println("  - " + role);
}

// Check if user has specific role
boolean isAdmin = Arrays.asList(roles).contains("admin");

Managing Permissions

Get Permissions for Role

From io/supertokens/userroles/UserRoles.java:257-270:
public static String[] getPermissionsForRole(
    AppIdentifier appIdentifier,
    Storage storage,
    String role
) throws UnknownRoleException {
    UserRolesSQLStorage userRolesStorage = 
        StorageUtils.getUserRolesStorage(storage);
    
    if (!userRolesStorage.doesRoleExist(appIdentifier, role)) {
        throw new UnknownRoleException();
    }
    
    return userRolesStorage.getPermissionsForRole(appIdentifier, role);
}

Example: Check User Permissions

// Get all user's roles
String[] roles = UserRoles.getRolesForUser(
    tenantIdentifier, storage, userId
);

// Collect all permissions
Set<String> allPermissions = new HashSet<>();
for (String role : roles) {
    String[] permissions = UserRoles.getPermissionsForRole(
        appIdentifier, storage, role
    );
    allPermissions.addAll(Arrays.asList(permissions));
}

// Check specific permission
if (allPermissions.contains("post:delete")) {
    System.out.println("User can delete posts");
}

Delete Permissions from Role

From io/supertokens/userroles/UserRoles.java:280-308:
public static void deletePermissionsFromRole(
    AppIdentifier appIdentifier,
    Storage storage,
    String role,
    String[] permissions  // null to delete all
) throws UnknownRoleException {
    UserRolesSQLStorage userRolesStorage = 
        StorageUtils.getUserRolesStorage(storage);
    
    userRolesStorage.startTransaction(con -> {
        if (!userRolesStorage.doesRoleExist_Transaction(
                appIdentifier, con, role)) {
            throw new UnknownRoleException();
        }
        
        if (permissions == null) {
            // Delete all permissions
            userRolesStorage.deleteAllPermissionsForRole_Transaction(
                appIdentifier, con, role
            );
        } else {
            // Delete specific permissions
            for (String permission : permissions) {
                userRolesStorage.deletePermissionForRole_Transaction(
                    appIdentifier, con, role, permission
                );
            }
        }
        
        userRolesStorage.commitTransaction(con);
        return null;
    });
}

Removing Roles

Remove Role from User

From io/supertokens/userroles/UserRoles.java:187-211:
public static boolean removeUserRole(
    TenantIdentifier tenantIdentifier,
    Storage storage,
    String userId,
    String role
) throws UnknownRoleException {
    UserRolesSQLStorage userRolesStorage = 
        StorageUtils.getUserRolesStorage(storage);
    
    return userRolesStorage.startTransaction(con -> {
        // Verify role exists
        boolean doesRoleExist = userRolesStorage
            .doesRoleExist_Transaction(
                tenantIdentifier.toAppIdentifier(), con, role
            );
        
        if (!doesRoleExist) {
            throw new UnknownRoleException();
        }
        
        // Remove role from user
        return userRolesStorage.deleteRoleForUser_Transaction(
            tenantIdentifier, con, userId, role
        );
    });
}

Remove All Roles from User

From io/supertokens/userroles/UserRoles.java:374-378:
public static int deleteAllRolesForUser(
    TenantIdentifier tenantIdentifier,
    Storage storage,
    String userId
) {
    return StorageUtils.getUserRolesStorage(storage)
        .deleteAllRolesForUser(tenantIdentifier, userId);
}

Delete Role Completely

From io/supertokens/userroles/UserRoles.java:336-353: Deletes role and all user associations across all tenants:
public static boolean deleteRole(
    Main main,
    AppIdentifier appIdentifier,
    String role
) {
    Storage[] storages = StorageLayer.getStoragesForApp(
        main, appIdentifier
    );
    
    boolean deletedRole = false;
    
    // Delete user-role associations from all tenants
    for (Storage storage : storages) {
        UserRolesSQLStorage userRolesStorage = 
            StorageUtils.getUserRolesStorage(storage);
        deletedRole = userRolesStorage
            .deleteAllUserRoleAssociationsForRole(appIdentifier, role) 
            || deletedRole;
    }
    
    // Delete role itself from public tenant
    Storage appStorage = StorageLayer.getStorage(
        appIdentifier.getAsPublicTenantIdentifier(), main
    );
    UserRolesSQLStorage userRolesStorage = 
        StorageUtils.getUserRolesStorage(appStorage);
    deletedRole = userRolesStorage.deleteRole(appIdentifier, role) 
        || deletedRole;
    
    return deletedRole;
}
Deleting a role removes it from all users across all tenants in the app. This operation cannot be undone.

Querying Roles

List All Roles

From io/supertokens/userroles/UserRoles.java:362-365:
public static String[] getRoles(
    AppIdentifier appIdentifier,
    Storage storage
) {
    return StorageUtils.getUserRolesStorage(storage)
        .getRoles(appIdentifier);
}

Get Users with Role

From io/supertokens/userroles/UserRoles.java:236-247:
public static String[] getUsersForRole(
    TenantIdentifier tenantIdentifier,
    Storage storage,
    String role
) throws UnknownRoleException {
    UserRolesSQLStorage userRolesStorage = 
        StorageUtils.getUserRolesStorage(storage);
    
    if (!userRolesStorage.doesRoleExist(
            tenantIdentifier.toAppIdentifier(), role)) {
        throw new UnknownRoleException();
    }
    
    return userRolesStorage.getUsersForRole(tenantIdentifier, role);
}

Get Roles with Permission

From io/supertokens/userroles/UserRoles.java:320-325:
public static String[] getRolesThatHavePermission(
    AppIdentifier appIdentifier,
    Storage storage,
    String permission
) {
    return StorageUtils.getUserRolesStorage(storage)
        .getRolesThatHavePermission(appIdentifier, permission);
}

Multi-Tenant Role Management

Tenant-Scoped Role Assignment

// Assign role in tenant1
UserRoles.addRoleToUser(
    main,
    new TenantIdentifier(cuDomain, appId, "tenant1"),
    storage,
    userId,
    "admin"
);

// Same user can have different roles in different tenants
UserRoles.addRoleToUser(
    main,
    new TenantIdentifier(cuDomain, appId, "tenant2"),
    storage,
    userId,
    "viewer"  // Different role in tenant2
);

// Check roles per tenant
String[] tenant1Roles = UserRoles.getRolesForUser(
    new TenantIdentifier(cuDomain, appId, "tenant1"),
    storage,
    userId
);
// Returns: ["admin"]

String[] tenant2Roles = UserRoles.getRolesForUser(
    new TenantIdentifier(cuDomain, appId, "tenant2"),
    storage,
    userId
);
// Returns: ["viewer"]
Roles are defined at the app level but assigned at the tenant level. The same role name has the same permissions across all tenants.

Permission Naming Conventions

resource:action
Examples:
  • user:read - Read user data
  • user:write - Create/update users
  • user:delete - Delete users
  • post:read - View posts
  • post:write - Create/edit posts
  • post:delete - Delete posts
  • admin:access - Access admin panel
  • billing:manage - Manage billing

Hierarchical Permissions

// Admin has all permissions
UserRoles.createNewRoleOrModifyItsPermissions(
    appIdentifier, storage, "admin",
    new String[]{"*:*"}  // Wildcard for all
);

// Editor has all post permissions
UserRoles.createNewRoleOrModifyItsPermissions(
    appIdentifier, storage, "editor",
    new String[]{"post:*", "user:read"}
);

// Viewer has read-only
UserRoles.createNewRoleOrModifyItsPermissions(
    appIdentifier, storage, "viewer",
    new String[]{"post:read", "user:read"}
);

Complete Example

// 1. Create roles with permissions
UserRoles.createNewRoleOrModifyItsPermissions(
    appIdentifier, storage, "admin",
    new String[]{"user:*", "post:*", "admin:*"}
);

UserRoles.createNewRoleOrModifyItsPermissions(
    appIdentifier, storage, "editor",
    new String[]{"user:read", "post:*"}
);

UserRoles.createNewRoleOrModifyItsPermissions(
    appIdentifier, storage, "viewer",
    new String[]{"user:read", "post:read"}
);

// 2. Assign roles to users
UserRoles.addRoleToUser(
    main, tenantIdentifier, storage, adminUserId, "admin"
);

UserRoles.addRoleToUser(
    main, tenantIdentifier, storage, editorUserId, "editor"
);

UserRoles.addRoleToUser(
    main, tenantIdentifier, storage, viewerUserId, "viewer"
);

// 3. Check user permissions
String[] userRoles = UserRoles.getRolesForUser(
    tenantIdentifier, storage, editorUserId
);

Set<String> permissions = new HashSet<>();
for (String role : userRoles) {
    String[] rolePermissions = UserRoles.getPermissionsForRole(
        appIdentifier, storage, role
    );
    permissions.addAll(Arrays.asList(rolePermissions));
}

if (permissions.contains("post:delete")) {
    // Allow delete
} else {
    // Deny
}

// 4. Update role permissions
UserRoles.deletePermissionsFromRole(
    appIdentifier, storage, "editor",
    new String[]{"post:delete"}  // Remove delete permission
);

// 5. Remove role from user
UserRoles.removeUserRole(
    tenantIdentifier, storage, editorUserId, "editor"
);

// 6. List all users with a role
String[] admins = UserRoles.getUsersForRole(
    tenantIdentifier, storage, "admin"
);

System.out.println("Admin users: " + admins.length);

Best Practices

Pre-Create Roles

Create all roles before assigning them to users

Use Descriptive Names

Role names should clearly indicate their purpose

Follow Least Privilege

Grant minimum permissions necessary for each role

Document Permissions

Maintain a list of all permissions and their meanings

Regular Audits

Periodically review role assignments and permissions

Tenant Isolation

Remember roles are app-wide but assignments are per-tenant

Common Patterns

Check Permission Helper

public boolean userHasPermission(
    TenantIdentifier tenantIdentifier,
    Storage storage,
    String userId,
    String requiredPermission
) {
    try {
        String[] roles = UserRoles.getRolesForUser(
            tenantIdentifier, storage, userId
        );
        
        for (String role : roles) {
            String[] permissions = UserRoles.getPermissionsForRole(
                appIdentifier, storage, role
            );
            
            if (Arrays.asList(permissions).contains(requiredPermission)) {
                return true;
            }
        }
        
        return false;
    } catch (Exception e) {
        return false;
    }
}

// Usage
if (userHasPermission(tenantIdentifier, storage, userId, "post:delete")) {
    // Allow delete
} else {
    throw new ForbiddenException("Insufficient permissions");
}

Role Inheritance

// Create role hierarchy
UserRoles.createNewRoleOrModifyItsPermissions(
    appIdentifier, storage, "super_admin",
    new String[]{"*:*"}
);

UserRoles.createNewRoleOrModifyItsPermissions(
    appIdentifier, storage, "admin",
    new String[]{"user:*", "post:*"}
);

UserRoles.createNewRoleOrModifyItsPermissions(
    appIdentifier, storage, "moderator",
    new String[]{"post:*", "user:read"}
);

// Assign multiple roles for inheritance
UserRoles.addRoleToUser(
    main, tenantIdentifier, storage, userId, "admin"
);
UserRoles.addRoleToUser(
    main, tenantIdentifier, storage, userId, "moderator"
);
// User now has combined permissions from both roles

Build docs developers (and LLMs) love