loginos

Which auth providers support roles and permissions out of the box?

loginos teamUpdated 9 min read

Short answer

Auth0, Clerk, WorkOS, Kinde and Logto have both roles and permissions built in, and Keycloak, Zitadel and FusionAuth focus on roles. Amazon Cognito only has groups, while Firebase Auth and Supabase leave roles to custom claims you set yourself. The providers differ most in whether roles reach the token by default and whether they are per organization. loginos puts roles and permissions in every token.

You are choosing an identity provider, or checking the one you have, and you don’t want to build authorization from scratch. Almost every provider mentions RBAC (role-based access control: people get roles, and roles grant permissions). The real differences are where you define roles, whether they reach the token without extra code, and whether one person can hold a different role in each customer organization.

Each entry below was checked against the vendor’s documentation in October 2026. Products change often, so confirm the details in their docs before you commit.

Which auth providers have roles and permissions built in?

The table compares eleven providers and loginos. “Per organization” means a person can hold a different role in each organization or tenant inside one user pool.

ProviderRoles in a UIPermissionsIn the token by defaultPer organizationChange by API
Auth0YesYes, per APIWith an API setting or an ActionWith OrganizationsYes
ClerkYesYesYesOnly per organizationYes
WorkOSYesYesYesOnly per organizationYes
KindeYesYesPermissions yes, roles with a settingOnly per organizationYes
LogtoYesYesWith a scopeGlobal and per organizationYes
KeycloakYesRoles, plus Authorization ServicesIn the access tokenThrough organization groupsYes
ZitadelYesRoles onlyWith a setting or scopePer organizationYes
FusionAuthYesRoles onlyIn the access tokenPer applicationYes
Amazon CognitoGroupsGroups onlyYes, as cognito:groupsNoYes
Firebase AuthNoNoWith custom claimsNoYes, custom claims
Supabase AuthNoNoWith a hookNoYes, app_metadata
loginosYesYesYesNo, per platformYes

How do the providers differ?

Auth0

Auth0 defines permissions on APIs, like read:reports on a reports API, and groups them into roles in the dashboard. Turn on Enable RBAC and Add Permissions in the Access Token in an API’s settings, and access tokens for that API carry a permissions array. Roles never appear by default. A post-login Action reads event.authorization.roles and adds them with api.idToken.setCustomClaim or api.accessToken.setCustomClaim.

Auth0 recommends namespaced claim names to avoid collisions. You can’t name the claim roles anyway: roles, permissions and groups are restricted, so use a name like https://example.com/roles. Auth0 Organizations let a member hold different roles in each organization, and roles that exist only inside one organization are in Early Access.

Clerk and WorkOS

Clerk has roles and permissions only inside Organizations. It ships two default roles, org:admin and org:member, and custom permissions follow the pattern org:<feature>:<permission>. A member holds one role per organization. The session token’s o claim carries the role (o.rol) and permission names (o.per) for the active organization, and Clerk’s nine system permissions are left out. For app-wide roles, Clerk documents a workaround: keep the role in publicMetadata and add it to the session token as a custom claim.

WorkOS also ties roles to organization memberships, so there is no global role. Roles and permissions defined in the dashboard are shared by all organizations, and each organization can add custom roles. The AuthKit access token carries org_id, role and permissions for the organization selected at sign-in, and you refresh the session with another organization_id to switch. WorkOS can also assign roles from your customers’ directory or SSO groups.

Kinde and Logto

Kinde defines roles once for your business and assigns them per organization, so one person can be an admin in one organization and a member in another. People with no known organization go into a default one. The access token carries permissions for the organization the person signed in to. A roles claim appears only after you switch on Roles (array) in the application’s token settings.

Logto has both kinds: global roles, whose permissions belong to API resources, and organization roles from one shared organization template. An access token for an API resource lists the granted permissions in its scope claim. The ID token and userinfo include roles and organization_roles when you request the matching scopes. Logto is open source and can be self-hosted.

Keycloak, Zitadel and FusionAuth

Keycloak is an open-source server you run yourself. It has realm roles, client roles scoped to one application, composite roles that contain other roles, and default roles for new users. Its built-in roles client scope puts them in the access token as realm_access.roles and resource_access.<client>.roles, but not in the ID token unless you change the mapper. For permissions on resources, its Authorization Services module adds policies. Each realm is a separate user pool, and since version 26.7, roles can be assigned to organization groups.

Zitadel defines roles per project, and a role is a key with a display name, without a list of permissions. Roles are assigned to a user in an organization, and the claim urn:zitadel:iam:org:project:{projectId}:roles maps each role to the organizations where the person holds it. Roles appear only when configured or requested: turn on Assert Roles on Authentication for the project and the role setting on the app, or request a roles scope.

FusionAuth defines roles per application, with default roles for new registrations. A registration links a user to one application and holds the user’s roles there. The access token has a roles claim for the application the person signed in to, but the ID token stopped carrying roles in version 1.24.0. You change roles by updating the registration with PUT /api/user/registration/{userId}/{applicationId}.

Amazon Cognito, Firebase Auth and Supabase Auth

Amazon Cognito has groups, not roles or permissions. Groups can’t be nested, and each can map to an IAM role. Cognito adds cognito:groups to the ID and access tokens when the user is in at least one group, with up to 100 groups per user. Other claims need a pre token generation Lambda trigger, and changing access tokens needs trigger version 2 or 3, which not every feature plan includes.

Firebase Auth has no roles, only custom claims. Your server sets them with the Admin SDK’s setCustomUserClaims(uid, claims), and the payload may be at most 1,000 bytes. The claims sit at the top level of the ID token, and Security Rules read them as request.auth.token.<claim>.

Supabase Auth has no role or permission builder either. The role claim in its tokens is the Postgres role, like authenticated, not your app’s role. Supabase’s RBAC guide keeps roles and permissions in your own tables and adds a user_role claim with a custom access token hook: a Postgres function or HTTP endpoint that runs before a token is issued. Never authorize on user_metadata, because users can edit it.

How do you choose a provider for roles and permissions?

Work through these five questions before you compare prices or SDKs:

  1. Global or per-organization roles? If your customers are companies with their own admins, you need a role per person per organization. Clerk, WorkOS, Kinde and Zitadel assign roles per organization, and Auth0 and Logto offer both kinds. With the others, you model it with realms, groups or your own tables, as in multi-tenant login.
  2. Do permissions arrive in the token? Code that checks posts:publish keeps working when you reshape roles. Clerk, WorkOS and Kinde send permissions without setup, and Auth0 after one setting per API. Keycloak, Zitadel, FusionAuth and Cognito send roles or groups, so your code maps them to permissions. The JWT answer explains why permission checks age better.
  3. How big will the token get? Every claim travels with every request. Firebase caps custom claims at 1,000 bytes, and Clerk advises keeping custom session claims under 1.2 KB, because browsers limit cookies to 4 KB. If people can hold hundreds of permissions, send roles and expand them on your server.
  4. Can your backend change roles? Check that the management API can assign roles, not only read them. Auth0 adds roles with POST /api/v2/users/{id}/roles, Clerk changes a membership with PATCH /organizations/{organization_id}/memberships/{user_id}, and Firebase replaces all custom claims on every call. Everywhere, a token keeps the old roles until a new one is issued, so plan how your apps re-read them, as in managing roles across apps.
  5. Do you need rules on single objects? Roles answer “may Jane publish posts?”, not “may Jane edit document 42?”. For that, pair your provider with a fine-grained authorization service. OpenFGA, an open-source CNCF incubating project based on Google’s Zanzibar, is one example: such services store relationships, like “Jane is an editor of document 42”, and answer checks against them. Amazon Verified Permissions does similar checks with Cognito tokens.

Where loginos fits

loginos is a hosted OpenID Connect provider for products that run on many domains, and roles are part of every platform. It fits when global roles are enough and you want them in every token without writing a hook.

Jane’s roles

Tap to try your own mix.

{  "sub": "9b1f6c2e-…",  "preferred_username": "jane",  "roles": [    "member"  ],  "permissions": [    "comments:write",    "posts:read"  ]}
1/3Everyone who joins gets the default role, member.
  • Define permissions like posts:publish and roles like editor in the console’s Roles tab, and mark the default roles that everyone who joins gets.
  • Read them without setup: the ID token, the token endpoint’s user object and /oauth/userinfo always carry roles and permissions, whatever scopes you asked for. No setting, Action or hook is needed.
  • Change them from your server with PUT /api/v1/users/{sub}/roles, which replaces the list. Blocking is separate: POST /api/v1/users/{sub}/block stops sign-in on all your domains.
  • Apply them everywhere: roles belong to the platform and are shared by all of its domains.
user, from /oauth/token
{
  "sub": "9b1f6c2e-5d1a-4b7e-9a51-0e6f3f6c1d2a",
  "preferred_username": "jane",
  "roles": ["editor", "member"],
  "permissions": ["comments:write", "posts:publish", "posts:read"]
}

A token shows the roles someone had at sign-in, so your server reads current ones from userinfo or the Management API. The roles guide and the claims reference have the details.

loginos is not the right fit for everyone. A platform has no organizations or tenants, so a person can’t be an admin in one customer workspace and a viewer in another unless your database records it. There are no webhooks and no SCIM, so changes aren’t pushed to your apps. If you need per-organization roles, look at Auth0, Clerk, WorkOS, Kinde, Logto or Zitadel.

Frequently asked questions

Does Auth0 include roles in the ID token?

No. Auth0 adds no roles by default, and its API setting only puts a permissions array in access tokens for that API. To get roles, write a post-login Action that copies event.authorization.roles into a custom claim with a namespaced name, such as https://example.com/roles, because roles is a restricted claim name.

How do I add roles to Firebase Auth?

Set them as custom claims from a trusted server with the Admin SDK, for example setCustomUserClaims(uid, { admin: true }). Each call replaces all of the user’s custom claims, so send the full set every time. The new claims appear only in a newly issued ID token, which the client can force with getIdToken(true).

Does Supabase Auth support roles and permissions?

Not as a built-in feature. The role claim in a Supabase token is the Postgres role, such as authenticated. For app roles, keep roles and permissions in your own tables, add a claim with a custom access token hook, and check it in row level security policies or your API.

Which providers give users different roles in different organizations?

Clerk, WorkOS and Kinde assign roles only inside organizations, and Zitadel assigns them per organization too. Auth0, with Organizations, and Logto offer both global and per-organization roles. Keycloak can attach roles to organization groups since version 26.7. Cognito, Firebase Auth, Supabase Auth and loginos have no organizations inside one user pool, so you keep that membership in your own database.

One account for every domain you run

loginos is an OpenID Connect provider with passkeys, a user pool shared by all of your domains, roles in every token, and account linking for the users you already have. Add your platform and its domains in a few minutes.

Keep reading

All answers