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

> 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.

| Provider | Roles in a UI | Permissions | In the token by default | Per organization | Change by API |
| --- | --- | --- | --- | --- | --- |
| Auth0 | Yes | Yes, per API | With an API setting or an Action | With Organizations | Yes |
| Clerk | Yes | Yes | Yes | Only per organization | Yes |
| WorkOS | Yes | Yes | Yes | Only per organization | Yes |
| Kinde | Yes | Yes | Permissions yes, roles with a setting | Only per organization | Yes |
| Logto | Yes | Yes | With a scope | Global and per organization | Yes |
| Keycloak | Yes | Roles, plus Authorization Services | In the access token | Through organization groups | Yes |
| Zitadel | Yes | Roles only | With a setting or scope | Per organization | Yes |
| FusionAuth | Yes | Roles only | In the access token | Per application | Yes |
| Amazon Cognito | Groups | Groups only | Yes, as `cognito:groups` | No | Yes |
| Firebase Auth | No | No | With custom claims | No | Yes, custom claims |
| Supabase Auth | No | No | With a hook | No | Yes, `app_metadata` |
| loginos | Yes | Yes | Yes | No, per platform | Yes |

## 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](https://auth0.com/docs/get-started/apis/enable-role-based-access-control-for-apis), 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](https://firebase.google.com/docs/auth/admin/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](https://supabase.com/docs/guides/database/postgres/custom-claims-and-role-based-access-control-rbac) 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](https://loginos.io/answers/multi-tenant-login-custom-domains.md).
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](https://loginos.io/answers/roles-permissions-in-jwt.md) 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](https://loginos.io/answers/manage-roles-across-apps.md).
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.

- **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.

```json title="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](https://loginos.io/docs/api.md). The [roles guide](https://loginos.io/docs/roles.md) and the [claims reference](https://loginos.io/docs/oidc.md#claims) 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.

---

Updated 2026-10-14. Web page: https://loginos.io/answers/auth-providers-with-roles-permissions
