perm=a|b+c now means (a or b) and c; repeated perm args remain ANDed. Out-of-spec values (empty alternatives, chars outside the scope charset, stray %2B) are rejected with 400 instead of being silently misparsed; extra spaces between groups are tolerated. Forward endpoint 400/500 details name /auth/api/forward as origin without echoing query args. Wildcards are now filename-like: * stays within a :- or /-separated segment, ** spans segments, partial segments allowed. Slash added to allowed scope characters for path-based permissions.
3.7 KiB
The perm argument
API overview · /auth/api/validate · /auth/api/forward
The perm argument is used by endpoints that validate sessions to require one or more permission scopes. It is supported by:
Passing the argument
Repeat the query parameter for each required scope:
?perm=myapp:read&perm=myapp:write
You can also pass multiple scopes in one parameter by separating them with whitespace (a literal space, i.e. + or %20 in the query string):
?perm=myapp:read%20myapp:write
Both forms produce the same result.
Semantics
The perm argument uses AND semantics between groups: every listed scope group must be satisfied by the effective permissions for the request to succeed. If any required group is not satisfied, the endpoint returns 403.
Within a single group, use | to list alternatives with OR semantics — the group is satisfied when any one of the alternatives is present:
?perm=myapp:read|myapp:write+myapp:login
This requires myapp:login and (myapp:read or myapp:write). No whitespace is allowed around the | operator.
Syntax errors
Parsing is strict: anything out of spec is rejected with 400 Bad Request rather than guessed at. This includes:
- Empty values or alternatives (
?perm=,?perm=a||b,?perm=|a,?perm=a|) - Whitespace around
|(?perm=a+|+b) - Characters not allowed in scopes other than the operators (space and
|); scopes match^[A-Za-z0-9:._~/-]+$plus the*wildcard. In particular a literal+in the decoded value (from a%2Bin the query string) is rejected — use+or%20to encode a space, never%2B.
Extra spaces between groups (leading, trailing, or repeated) are tolerated, since they can easily result from URL formatting and carry no ambiguity — they only ever add required permissions, never remove them. The | operator is parsed strictly: ?perm=foo&perm=|bar is an error, never a way to make foo optional.
Wildcards
Wildcards work like filenames, with : and / acting as path separators:
*matches any sequence of characters within a single segment (it never crosses a:or/)**matches any sequence of characters, across separators- Part of a segment can be wildcarded, with required text on either or both sides
?perm=myapp:*
This matches myapp:read and myapp:write, but not myapp:read:all — use myapp:** for that. Partial wildcards like myapp:re* or myapp:r*d match myapp:read. The same applies to path-based scopes: myapp:path:/api/* matches myapp:path:/api/clients but not myapp:path:/api/v2/clients — use myapp:path:/api/** to span path segments.
Effective permissions
The permissions available to a session are determined as follows:
- Role permissions — the role assigned to the user contains a set of permission UUIDs.
- Org grantable permissions — only permissions that the user's organization is allowed to grant are effective.
- Domain filtering — a permission can be restricted to a specific domain via its domain field. If the request's Host header does not match that domain, the permission is excluded.
The result is the set of effective permission scopes used for the perm check. Domain-restricted permissions let you grant a scope only for a specific site or subdomain without making it global.
Examples
Require a single permission:
?perm=myapp:login
Require two permissions:
?perm=myapp:login&perm=myapp:api
Require any scope under myapp:
?perm=myapp:*
Require myapp:login and either myapp:read or myapp:write:
?perm=myapp:login&perm=myapp:read|myapp:write