How Authentication Connects to Permit.io
This page explains how an authentication provider and Permit.io authorization work together: what each system decides, how a verified identity reaches permit.check(), and which ways you can keep user and role data in sync. It is for developers and architects who plan an integration. To connect a specific provider, go to Connect your authentication.
Authentication and authorization answer different questions
| Authentication (AuthN) | Authorization (AuthZ) | |
|---|---|---|
| Question | Who is this user? | What can this user do? |
| Examples | Passwords, single sign-on (SSO), multi-factor authentication (MFA), passkeys | Roles, attributes, relationships between users and resources |
| Output | A session and a token, such as a JSON Web Token (JWT), that identify the user | An allow or deny decision for one action on one resource |
| Who runs it | Your authentication provider (Auth0, Cognito, Stytch, your own system) | Permit.io: you define policies in Permit, and a policy decision point (PDP) evaluates each check |
Authorization depends on authentication: a permission check is only as trustworthy as the identity passed into it. Permit does not sign users in. Your application verifies the user with your authentication provider first, then asks Permit what that user can do.
The handoff point: from login to permission check
The handoff point is the code in your application that runs right after authentication succeeds. It takes the verified identity from the provider and makes it available to Permit.
1. The user signs in
Your authentication provider verifies the user and issues a session and a token, usually a JWT.
2. Your application verifies the token
Your backend validates the token with the provider's SDK or public keys and reads a stable user identifier, such as the sub claim.
3. Your application syncs the user to Permit
At the handoff point, your application calls syncUser() with the user key and profile data, and assigns roles if the user needs them.
4. Your application checks permissions
Before each protected action, your application calls permit.check() with the same user key. The PDP evaluates roles, attributes, and relationships and returns allow or deny.
The user key links the two systems
Permit identifies a user by a unique user key. Use the same value in three places:
- The
keyyou pass tosyncUser(). - The user in role assignments.
- The user you pass to
permit.check().
Pick a claim that never changes for a user, such as the provider's user ID in the sub claim. An email address works as a key only if users can't change it. If the key in permit.check() doesn't match a synced user, the PDP finds no role assignments for that key, and a role-based check returns deny.
Ways to get user data into Permit
Choose one method or combine several. Most applications call syncUser() at login and use bulk sync once to import existing users.
| Method | When it runs | What reaches Permit | Use it when | Guide |
|---|---|---|---|---|
syncUser() at login | At the handoff point, on each sign-in or on first sign-up | User key, email, name, attributes, and role assignments you add | You want every signed-in user to exist in Permit with current data | Sync users |
Attributes in permit.check() | On each permission check | Attributes you read from the token, used for that one decision | A policy needs a value from the token, such as location, that changes often | Just-in-time (JIT) attributes |
| Bulk sync through the API | Once at migration, or on a schedule | Many users per request | You import an existing user base or run periodic reconciliation | Bulk user operations |
| SCIM provisioning | When the identity provider changes a user | Users, and groups as role assignments, from the identity provider (IdP) | IT manages users in an IdP such as Okta or Microsoft Entra ID | SCIM overview |
For most architectures, start with syncUser() at the handoff point. Add bulk sync to import users who signed up before the integration existed.
Where to manage roles
Authentication providers and IdPs can store roles too. Decide which system owns role assignments before you write the handoff code.
| Approach | How it works | Fits | Tradeoff |
|---|---|---|---|
| Roles managed in the IdP | IT assigns roles in the IdP. Your handoff code or SCIM copies them into Permit role assignments. | Enterprise applications where IT controls access centrally | Roles change only when someone changes them in the IdP. Roles that your application grants at runtime, such as ownership of one document, don't fit this model. |
| Roles managed in Permit | Your application or its admins assign roles in Permit, through the API, SDKs, or dashboard. | Multi-tenant SaaS applications, and policies that use attributes or relationships between users and resources | You sync users into Permit and decide where role assignments happen. |
| Hybrid | The IdP assigns broad roles. Permit adds tenant roles, attribute rules, and relationship-based roles on top. | Organizations that need IT governance and application-level control | Define which system owns each role. Otherwise a role you remove in one system can be assigned again by the next sync from the other. |
Next steps
- Connect your authentication: pick your provider and follow its integration guide.
- Sync users: the
syncUser()parameters and the difference fromcreateUser(). - Check permissions: call
permit.check()with a user, action, and resource. - Bulk user operations: import many users in one request.