Resource — Versola Docs
VersolaVersola/docs
0.5.0versola.kzGitHub

Resource

The upstream API protected by edge in Versola — identity, audience, internal/public type and endpoints

A resource is an upstream API sitting behind edge. Registering it tells edge where to forward traffic and under what conditions. For the conceptual picture, see Entities.

  • Resource ID — lowercase, immutable, and used in the proxy path: requests to /resources/{resourceId}/* are forwarded to the resource URI. The ID edge is reserved.
  • Resource URI — an absolute URI with an empty path, no query and no fragment. The resource:// scheme is reserved for internal resources.
  • Audience — the list of clients allowed to request this resource. A client that is not in the audience cannot obtain a token targeting it.
  • Internal or public — an internal resource has a generated secret; edge authenticates to it with Authorization: Basic {resourceId}:{secret} and the caller’s own token never reaches it, so it should sit in an internal zone with no access from the web — edge is its only caller. Tokens issued for it carry resource://{resourceId} in the aud claim (RFC 8707). A public resource has no secret; edge runs the same audience, role/permission and allow-expression checks but forwards the caller’s token as-is instead of its own credentials, so the resource is responsible for validating that token itself, and tokens for it carry the resource’s own URI in aud. Internal resource secrets can be rotated in the same way as client secrets.
  • Endpoints — the individual method + path pairs that make up the resource.

Endpoints

An endpoint is the unit of authorization. Everything Versola enforces per request is configured here:

  • Method + relative path — what to match. Segments are literal or {name} parameters matching exactly one segment; matched values reach CEL as request.path.params, and a literal endpoint takes precedence over a parameterized one.
  • Fetch userinfo — load the owner’s claims from auth and expose them to CEL as user.
  • Allow (CEL) — a boolean expression that must return true for the call to be authorized.
  • Inject — rules that write computed values into an upstream header, query parameter, or top-level JSON body field.
  • Step-up condition + ACR — when the condition holds, require the caller to have reached a given assurance level (RFC 9470).
  • Max auth age — reject the call if the owner authenticated longer ago than this.

Permissions reference endpoints by ID, so an endpoint becomes reachable to an owner only once some role they hold includes a permission covering it.

When a CEL expression can’t be evaluated

Nothing is forwarded upstream on the strength of an expression that didn’t produce a value. What the caller gets depends on why:

  • The request doesn’t carry a value the expression reads (e.g. user.plan on a token with no plan claim) — the call is refused with 403, and a step-up condition is treated as met, so the ACR is required rather than skipped. Claims differ legitimately between users, so guard optional ones with has(): has(user.plan) && user.plan == 'premium', or has(user.plan) ? user.plan : '' for an inject rule that may have nothing to inject.
  • The expression itself can’t be evaluated (a division by zero, a numeric overflow, an allow rule returning a non-boolean) — the call fails with 500. This is a configuration problem, not a request problem. Expressions are checked when you save them, so what’s left here is what only shows up against live data.

How to register a resource