Roles and permissions
Applications often include many capabilities. Not every user should be able to perform every action (for example, only owners should manage billing or payments). Scalekit helps you restrict access cleanly with roles and permissions so users get only the access they need.
Scalekit’s Full Stack Auth (FSA) lets your users sign in to your app. Once integrated, Scalekit acts as access middleware: it verifies identity and shares the user’s role and permissions with your application so you can allow or restrict actions.
Understand roles and permissions
Section titled “Understand roles and permissions”- Role: A convenient bundle of actions. Examples: Admin, Manager, Editor, Viewer. Roles state what a user can and cannot do.
- Permission: A single allowed action in your system, typically rendered as
resource:action
(for example,projects:create
,tasks:read
). Use permissions when you need fine-grained control.
Default roles
Section titled “Default roles”Scalekit automatically assigns default roles when setting up your organization:
- Organization creator: The first user who creates your Scalekit organization gets the
creator
role automatically - New members: When you invite users to your organization, they’re automatically assigned the
member
role
These default roles provide a starting point for access control. You can customize what permissions these roles have and change the default role assignments at any time.
Model roles for your app
Section titled “Model roles for your app”Design roles that reflect how your users work. For a project management app (similar to Linear), you might define:
Role | Permissions |
---|---|
Admin | org:manage , billing:manage , projects:create , projects:write , projects:read , tasks:create , tasks:write , tasks:read , members:manage |
Project owner | projects:create , projects:write , projects:read , tasks:create , tasks:write , tasks:read |
Editor | projects:write , projects:read , tasks:create , tasks:write , tasks:read |
Member | projects:read , tasks:create , tasks:read |
Viewer | projects:read , tasks:read |
Keep headers short and permissions focused. Expand this table to match your product.
Manage roles and permissions
Section titled “Manage roles and permissions”You can register roles and permissions from the Scalekit dashboard:
-
Define permissions for your application
Section titled “Define permissions for your application”- Go to User management → Permissions
- Create granular permissions like
projects:create
,tasks:read
,billing:manage
- Think about your app’s resources and actions when defining permissions
- Use consistent naming patterns:
resource:action
(e.g.,projects:create
,projects:read
,projects:update
,projects:delete
)
-
Create roles
Section titled “Create roles”- Go to Authorization → Roles
- Click + Add role and provide:
- Display name: Human-readable name (e.g., “Project Owner”)
- Name (key): Machine-friendly identifier (e.g.,
project_owner
) - Description: Clear explanation of what users with this role can do
- Use short, machine-friendly keys for the Name field
-
Understand role inheritance Optional
Section titled “Understand role inheritance ”Role inheritance lets you build permission hierarchies by having one role inherit all permissions from another role, then adding or removing specific permissions. This approach reduces duplication and makes permission management more maintainable.
How inheritance works:
- A role automatically gets all permissions from its base role
- You can then add new permissions specific to the inheriting role
- You can also remove specific permissions if needed
- Changes to the base role automatically propagate to all inheriting roles
Example hierarchy:
viewer (base role)├── projects:read├── tasks:read└── comments:readeditor (inherits from viewer)├── [inherits all viewer permissions]├── projects:write├── tasks:create└── tasks:writeproject_owner (inherits from editor)├── [inherits all editor permissions]├── projects:create└── members:inviteadmin (inherits from project_owner)├── [inherits all project_owner permissions]├── org:manage├── billing:manage└── members:manageBenefits of inheritance:
- Easier maintenance: Update permissions in one place and they automatically apply to all inheriting roles
- Consistent access levels: Ensures users with higher roles always have at least the same access as base roles
- Reduced duplication: No need to repeatedly assign the same permissions to multiple roles
- Logical structure: Clear progression from basic access to full administrative capabilities
-
Map permissions to roles
Section titled “Map permissions to roles”- Assign the permissions you created to each role
- Consider using inheritance to build permission sets efficiently
- Test your permission model to ensure users have appropriate access levels
- Document your permission structure for your development team
-
Set defaults
Section titled “Set defaults”- Confirm default role for the environment creator is
creator
- Confirm default role for new members is
member
- You can change these defaults at any time in Authorization → Roles
- Confirm default role for the environment creator is
Token contents and sessions
Section titled “Token contents and sessions”When your app is integrated with Full Stack Auth and a user signs in, Scalekit issues an access token and a refresh token, and creates a session to track access duration.
Token claim availability:
- Access token: Contains both
roles
andpermissions
claims - ID token: Contains only the
roles
claim
The roles
claim contains the role name (not the display name). For example, if the role name is editor
and the display name is “Content Editor,” the token includes "editor"
.
The permissions
claim contains the permission name (not the description). For example, if the permission name is projects:create
and the description is “Create new projects,” the token includes "projects:create"
.
Here’s an example of decoded tokens containing roles
and permissions
claims:
{"amr": ["conn_123456789012345678"],"aud": ["skc_987654321098765432"],"azp": "skc_987654321098765432","client_id": "skc_987654321098765432","email": "john.doe@example.com","email_verified": true,"exp": 1753441845,"family_name": "Doe","given_name": "John","iat": 1750849845,"iss": "http://example.localhost:8889","name": "John Doe","oid": "org_987654321098765432","roles": ["member"],"sid": "ses_987654321098765432","sub": "usr_987654321098765432"}
{"aud": ["skc_987654321098765432"],"client_id": "skc_987654321098765432","exp": 1750850145,"iat": 1750849845,"iss": "http://example.localhost:8889","jti": "tkn_987654321098765432","nbf": 1750849845,"roles": ["member"],"oid": "org_69615647365005430","permissions": ["emails:read", "inbox:read"],"sid": "ses_987654321098765432","sub": "usr_987654321098765432","xuid": "john.doe"}
With roles and permissions available in tokens, enforce access in your backend and UI. Example checks:
// Example: check roleconst isProjectOwner = token.roles?.includes('project_owner');
// Example: check permissionconst canCreateProject = token.permissions?.includes('projects:create');