Versola is organized around the three actors of OAuth 2.0: the user (the OAuth resource owner) who owns the data, the client that wants to act on their behalf, and the resource that holds the data. Everything else in the admin console — tenants, scopes, permissions and roles — exists to describe how these three are allowed to interact.
The big picture
flowchart TB
subgraph tenant["🏢 Tenant — configuration boundary"]
client["📱 Client<br/><i>an application</i><br/>redirect URIs, scopes,<br/>permissions, auth flow"]
resource["🔌 Resource<br/><i>an upstream API</i><br/>resource URI, audience,<br/>endpoints"]
scope["🏷️ Scope<br/><i>claims released<br/>to the client</i>"]
permission["🔑 Permission<br/><i>a set of<br/>resource endpoints</i>"]
role["👥 Role<br/><i>a set of<br/>permissions</i>"]
end
owner["👤 Resource Owner<br/><i>the end user</i><br/>global identity + claims"]
owner -->|holds roles in| tenant
owner -->|grants access to| client
client -->|requests| scope
client -->|may request| resource
resource -->|lists allowed clients in| client
role -->|bundles| permission
permission -->|unlocks endpoints of| resource
client -->|carries own| permission
style owner fill:#166534,stroke:#166534,color:#fff
style client fill:#155e75,stroke:#e6edf3,color:#fff
style resource fill:#155e75,stroke:#e6edf3,color:#fff
style scope fill:#334155,stroke:#e6edf3,color:#fff
style permission fill:#92400e,stroke:#92400e,color:#fff
style role fill:#92400e,stroke:#92400e,color:#fff
| Entity | What it is | Where it lives |
|---|---|---|
| User | An end user with an identity and claims (the OAuth resource owner) | auth (global, not tenant-scoped) |
| Client | An application requesting tokens | central, inside a tenant |
| Resource | An upstream API protected by edge | central, inside a tenant |
| Tenant | The namespace owning everything below | central |
| Scope | Claims a client may receive about the user | central, inside a tenant |
| Permission | A named set of resource endpoints | central, inside a tenant |
| Role | A named set of permissions, assigned to users | central, inside a tenant |
Tenant — the configuration boundary
A tenant is the namespace that owns clients, resources, scopes,
permissions and roles. Two tenants can each have a web-app client with no interference. A
tenant may be bound to an edge deployment, and every client and resource inside it
inherits that binding.
Users are deliberately not tenant-scoped: one person has one identity across the whole platform. What is tenant-scoped is their roles — a user can be an administrator in one tenant and an ordinary user in another.
The entities
- User — the end user: their identifiers, claims, credentials and
per-tenant roles, stored once in
auth. - Client — an application requesting tokens: its identity, scopes, permissions, auth flow and logout configuration.
- Resource — an upstream API behind
edge: its identity, audience, internal/public type and the endpoints that make it up. - Tenant — the namespace owning clients, resources, scopes, permissions and roles.
- Scope — a named bundle of claims a client may request about a user.
- Permission — a named set of resource endpoints, referenced by roles and clients.
- Role — a named set of permissions, assigned to a user per tenant.
For step-by-step registration instructions, see How To.
How a request flows through them
sequenceDiagram
participant Owner as 👤 Resource Owner
participant Client as 📱 Client
participant Auth as auth
participant Edge as edge
participant Resource as 🔌 Resource
Owner->>Client: Uses the application
Client->>Auth: /authorize (scope, resource)
Auth->>Auth: Authenticate the owner<br/>(auth flow of this client)
Auth->>Auth: Check requested resources:<br/>client must be in resource.audience
Auth-->>Client: Access token (aud = resource URIs)
Client->>Edge: GET /resources/{resourceId}/orders<br/>+ access token
Edge->>Edge: Verify signature, aud, expiry
Edge->>Edge: Match method + path → endpoint
Edge->>Edge: Permission check<br/>(owner roles or client permissions)
Edge->>Edge: Evaluate allow (CEL),<br/>step-up, max age
Edge->>Resource: Proxy request<br/>+ injected headers/query/body
Resource-->>Edge: Response
Edge-->>Client: Response
The two authorization gates are worth calling out separately:
- At token issuance (
auth) — the requestedresourcevalues are checked against the registry: the resource must exist in the client’s tenant, and the client must be listed in its audience. Only then does the resource URI land in the token’saudclaim. - At request time (
edge) — the token’saudmust cover the resource, the matched endpoint must be reachable through the caller’s roles (or, forclient_credentials, the client’s own permissions), and the endpoint’sallowexpression must returntrue.
Because the second gate re-reads configuration from central on a refresh interval,
revoking a permission or a role takes effect without reissuing any tokens.