Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions docs/authorization/fragments/_handle-user-permission-change.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
### Optional: Handle user permission change \{#optional-handle-user-permission-change}

User permissions can change at any time. Because Logto issues JWTs for RBAC, permission updates only appear in newly issued tokens, and never modify existing JWTs.

:::info Scope subset rule
An access token can only include scopes that were requested in the original OAuth authorization flow.
Even if a user gains new permissions, the token issued later can only contain a subset of the originally requested scopes.
To access newly granted scopes that were not part of the initial request, the client must run a new authorization flow.
:::

#### Downscoped permissions

When a user loses permissions:

- Newly issued tokens immediately reflect the reduced scopes.
- Existing JWTs keep the old scopes until they expire.
- Your API should always validate scopes and rely on short token lifetimes.

If you need faster reactions, implement your own signal that tells clients to refresh their tokens.

#### Enlarged permissions

{props.type === "global" && <p>For global API resources, when a user gains permissions, enlarged permissions will NOT show up through refresh. The client must perform a new OAuth authorization flow to obtain a token that includes the new scopes. This can happen silently if the user has an active Logto session.</p>}

{props.type === "organization" && <p>For organization tokens, when a user gains permissions, enlarged permissions will show up on the next issuance (refresh or token request). A new token is still required, but no full re-auth is needed unless the new scopes exceed the original request set.</p>}

#### Recommendations

- Validate scopes in your API on every call.
- Keep token expiration short.
- Add optional notifications if you need immediate permission-change propagation.
7 changes: 2 additions & 5 deletions docs/authorization/global-api-resources.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import illustration from '@site/docs/authorization/assets/rbac-global-api-resour
import AuthorizationRequestExample from '@site/docs/authorization/fragments/AuthorizationRequestExample';
import ClientCredentialsRequestExample from '@site/docs/authorization/fragments/ClientCredentialsRequestExample';
import TokenRequestExample from '@site/docs/authorization/fragments/TokenRequestExample';
import HandleUserPermissionChange from '@site/docs/authorization/fragments/_handle-user-permission-change.mdx';
import TabItem from '@theme/TabItem';
import Tabs from '@theme/Tabs';

Expand Down Expand Up @@ -192,11 +193,7 @@ When your API receives a request with a Logto-issued access token, you should:

For step-by-step and language-specific guides, see [How to validate access tokens](/authorization/validate-access-tokens).

### Optional: Handle user permission change \{#optional-handle-user-permission-change}

:::info
👷 Work in progress. 🚧
:::
<HandleUserPermissionChange type="global" />

## Best practices and security tips \{#best-practices-and-security-tips}

Expand Down
3 changes: 3 additions & 0 deletions docs/authorization/organization-level-api-resources.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import illustration from '@site/docs/authorization/assets/rbac-organization-leve
import AuthorizationRequestExample from '@site/docs/authorization/fragments/AuthorizationRequestExample';
import ClientCredentialsRequestExample from '@site/docs/authorization/fragments/ClientCredentialsRequestExample';
import TokenRequestExample from '@site/docs/authorization/fragments/TokenRequestExample';
import HandleUserPermissionChange from '@site/docs/authorization/fragments/_handle-user-permission-change.mdx';
import TabItem from '@theme/TabItem';
import Tabs from '@theme/Tabs';

Expand Down Expand Up @@ -210,6 +211,8 @@ When your app receives an organization token, you should:

For step-by-step and language-specific guides, see [How to validate access tokens](/authorization/validate-access-tokens).

<HandleUserPermissionChange type="organization" />

## Best practices and security tips \{#best-practices-and-security-tips}

- **Always validate the organization context:** Don’t trust just the token; check the `organization_id` claim for every organization-scoped API call.
Expand Down
3 changes: 3 additions & 0 deletions docs/authorization/organization-permissions.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import illustration from '@site/docs/authorization/assets/rbac-organization-perm
import AuthorizationRequestExample from '@site/docs/authorization/fragments/AuthorizationRequestExample';
import ClientCredentialsRequestExample from '@site/docs/authorization/fragments/ClientCredentialsRequestExample';
import TokenRequestExample from '@site/docs/authorization/fragments/TokenRequestExample';
import HandleUserPermissionChange from '@site/docs/authorization/fragments/_handle-user-permission-change.mdx';
import TabItem from '@theme/TabItem';
import Tabs from '@theme/Tabs';

Expand Down Expand Up @@ -198,6 +199,8 @@ When your app receives an organization token, you should:

For step-by-step and language-specific guides, see [How to validate access tokens](/authorization/validate-access-tokens).

<HandleUserPermissionChange type="organization" />

## Best practices and security tips \{#best-practices-and-security-tips}

- **Never rely solely on UI enforcement:** Always validate permissions on the backend for critical actions.
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@
"glob@>=10.2.0 <10.5.0": "^10.5.0"
},
"onlyBuiltDependencies": [
"@swc/core",
"core-js",
"core-js-pure",
"sharp"
Expand Down
Loading