GXS Internal
Identity server

Check permissions

Ask identity whether the current user may act, then enforce the answer in your API.

The Graphxsource authorization plugin answers permission checks for the caller. These endpoints extend Better Auth. They are separate from OAuth scopes and from Better Auth's native organization roles and permissions.

Your API first validates the access token, including its audience and scopes. It then asks identity for any required user permission and acts only when the response explicitly allows it.

Register permissions before checking them

Ask an identity administrator to register your permission catalog, activate the application and definitions, and assign grants through the relevant roles. An OAuth client registration does not create a permission catalog or grant permissions to its users.

A permission catalog application has a slug, supported planes, and a status. It can link to an OAuth client or resource. A permission key names its application, resource, and action, for example gxsio.asset.write. Use the keys actually registered for your service. Examples below assume that key exists.

PlaneUseOrganization required
adminSuite-wide operations, such as administering identity applicationsNo
organizationActions within one organizationYes

The same key can exist in both planes with separate grants. Choose the plane that matches the operation. Unknown keys and plane mismatches deny access. The current HTTP check endpoints accept exactly three dot-separated segments of lowercase letters, digits, and underscores. Catalog keys with additional sub-action segments are not accepted by these HTTP endpoints.

Check one action

Send the user's access token, not the API's client credentials token. The body contains only a plane and a permission. Identity derives the user and organization from the credential.

curl --fail-with-body \
  --request POST "$IDENTITY_ORIGIN/api/auth/authorization/check" \
  --header "Authorization: Bearer $USER_ACCESS_TOKEN" \
  --header 'Content-Type: application/json' \
  --data '{"plane":"organization","permission":"gxsio.asset.write"}'

An illustrative denial:

{
  "allowed": false,
  "reason": "deny_not_member",
  "mode": "enforce"
}

A denied permission returns HTTP 200 with allowed: false. Checking only response.ok would grant access by mistake. Require allowed === true; otherwise refuse the operation. Treat timeouts, malformed responses, and server errors as a failed authorization check.

response = identity.check(user_access_token, plane, permission)
if response.http_status != 200 or response.body.allowed != true:
    refuse_operation()
perform_operation_in_verified_organization()

The token's active_organization.id supplies organization context. A request body cannot select a different user or organization. Identity checks current membership, so removing a user from the organization can deny a request even while the JWT has time left.

Check several capabilities for a UI

Use a batch to decide which controls to show. Send between 1 and 50 permissions on one plane. Duplicate keys collapse in the response.

curl --fail-with-body \
  --request POST "$IDENTITY_ORIGIN/api/auth/authorization/batch-check" \
  --header "Authorization: Bearer $USER_ACCESS_TOKEN" \
  --header 'Content-Type: application/json' \
  --data '{
    "plane": "organization",
    "permissions": ["gxsio.asset.read", "gxsio.asset.write"]
  }'
{
  "results": {
    "gxsio.asset.read": {
      "allowed": true,
      "reason": "allow",
      "mode": "enforce"
    },
    "gxsio.asset.write": {
      "allowed": false,
      "reason": "deny_default",
      "mode": "enforce"
    }
  }
}

This response illustrates the shape, not grants for an existing user. Recheck on the server when the user performs the action. Do not reuse a UI capability result as a long-lived authorization grant.

Interpret the response

allowed is the decision your service must enforce. Log reason and mode for diagnosis, but do not recreate the evaluator in your application.

ModeWho decides
legacyThe pre-registry permission check
shadowThe pre-registry check, while the registry evaluates for comparison
enforceThe permission registry

The global configuration and each permission's enforcement setting determine the mode. Two permissions in a batch can resolve to different modes. reason and mode can be null, and new reason strings can appear. Keep a default case.

ResponseWhat to do
200, allowed: falseRefuse the action; inspect reason and role grants.
400, NO_ACTIVE_ORGANIZATIONObtain a user token with the intended active organization.
401Reauthenticate or renew the user token.
403, UNSUPPORTED_SUBJECTThe token acts for a client. Use a service authorization policy instead of a user check.
500, AUTHORIZE_NOT_CONFIGUREDReport a server configuration problem and refuse the action.

See the generated authorization API reference for request and response schemas.

On this page