How to protect an API with edge — Versola Docs
VersolaVersola/docs
Pre-1.00.7.1versola.kzGitHub

How to protect an API with edge

Put an internal API behind edge. Register the resource, grant permissions through a role, add an authorization preset for browser login, add a CEL rule and verify with curl, including a 403.

This guide puts one upstream API behind edge and lets a browser app call it with a session cookie. At the end a user can read their own orders, cannot read anyone else’s, and you have the curl commands that prove it. You need a running installation with a tenant linked to an edge, and your API reachable from edge over the network.

The example uses these names. Replace them with yours.

ThingValue
Public address of edge (edge-url)https://edge.example.com
Public address of authhttps://id.example.com
Your browser apphttps://app.example.com
Upstream APIhttp://orders.internal.example.com:8080
Resource IDorders-api
Clientacme-web
Permission, roleorders:read, customer
Presetweb

Prerequisites

  • An installation with central, auth and edge (Run Versola locally).
  • A tenant on the profile you want, linked to your edge in Tenants. See Security profiles.
  • central has a CA backend for client certificates, because a web client fronted by edge gets an issued certificate (Edge client certificates). The CLI targets configure one.
  • An upstream API with two routes: GET /orders and GET /users/{userId}/orders.

1. Register the resource

  1. Open Resources and press + Create Resource.

  2. Set Resource ID to orders-api and Absolute resource URI to http://orders.internal.example.com:8080. The URI has no path, query or fragment.

  3. Under Audience, add the client acme-web once it exists (step 3). You can come back to this field.

  4. Choose the type internal. edge then calls your API with HTTP Basic credentials and never forwards the user’s token.

  5. Press Add endpoint and add GET /orders. Leave the rules empty.

  6. Add a second endpoint, GET /users/{userId}/orders. In Allow (CEL) enter:

    request.path.params.userId == token.sub
  7. Press Create Resource and copy the secret the banner shows. It appears once. Configure your API to accept Basic authentication with orders-api as the user and that secret as the password. More detail is in How to register a resource.

The rule compares the userId in the URL with the sub claim of the caller’s token. Other fields and variables are on Endpoint policies.

The endpoint card of the resource form. The guide's rule goes into Allow (CEL)
Resource endpoint with Allow, step-up and Inject rule.

2. Grant access through a permission and a role

An endpoint answers 403 until a permission covers it.

  1. Open Permissions, create orders:read and attach both endpoints of orders-api (How to register a permission).
  2. Open Roles, create customer and add orders:read (How to register a role).
  3. Open Users and give your test user the role customer.

3. Create the client and the authorization preset

  1. Open Clients and press the create button. In the wizard choose Web app and give the client ID acme-web.

  2. Add the redirect URI https://edge.example.com/complete. This is the /complete route of edge, which receives the code from auth.

  3. Finish the wizard and go back to the Audience field of the resource to add acme-web if you skipped it (How to register a client).

  4. On the client, open Authorization Presets and press + Add Preset:

    FieldValue
    Preset IDweb
    Redirect URIhttps://edge.example.com/complete
    Scopesopenid
    Response Typecode
    Post-login redirect URIhttps://app.example.com/
    Post-logout redirect URI (optional)https://app.example.com/logged-out
    Session cookie domain, path (optional)leave empty for a cookie on the edge host, path /
  5. Press Save Presets. edge loads presets with the rest of its configuration, so allow up to configuration-cache-refresh-interval (generated: 5 minutes, 1 minute locally).

4. Point the browser app at edge

  • Sign in. Send the browser to https://edge.example.com/login/web. After sign-in it comes back to https://app.example.com/ and holds an EDGE_SESSION cookie that JavaScript cannot read. To require a recent login or a stronger one, add acr_values or max_age to the URL.
  • Call the API. Request https://edge.example.com/resources/orders-api/orders with credentials: 'include'. The part after /resources/orders-api is matched against the endpoints you registered.
  • Same site, same origin. The cookie is SameSite=Strict, so the app and edge must be on the same site. edge sends no CORS headers, so serve the app and edge from one origin, for example by routing /resources/* and /login/* from your site’s proxy to edge.
  • Expired session. When the cookie cannot be refreshed, edge answers 401 with a Location: /login/web header. Navigate there.
  • Sign out. Send the browser to https://edge.example.com/logout/web.

5. Verify with curl

Sign in in the browser, then copy the EDGE_SESSION cookie value from the developer tools. It has the form web:<access token>. Take the user ID, the sub claim, from the token or from Users. A token bound to edge’s certificate works only from the cookie, so send it as a cookie and not as Authorization: Bearer.

Read your own orders:

curl -i -H "Cookie: EDGE_SESSION=web:<access token>" \
  https://edge.example.com/resources/orders-api/users/<your-user-id>/orders
HTTP/1.1 200 OK
...

The body is whatever your API returns. Now ask for someone else’s orders:

curl -i -H "Cookie: EDGE_SESSION=web:<access token>" \
  https://edge.example.com/resources/orders-api/users/<another-user-id>/orders
HTTP/1.1 403 Forbidden

The rule evaluated to false. edge logs access_rule_denied for this request and sends no body. The upstream never saw the call. Without a credential:

curl -i https://edge.example.com/resources/orders-api/orders
HTTP/1.1 401 Unauthorized

A path no endpoint matches, with a valid session, answers 404.

If it does not work

You seeCheck
404 on /login/webThe preset is not saved, or edge has not reloaded. Wait one refresh interval
403 on every callThe role lacks the permission, or the permission lacks the endpoint. A permission_denied code in the edge log confirms it. Role and permission changes also take up to one refresh interval
403 with audience_denied in the logThe client is missing from the resource’s Audience
403 on your own orderstoken.sub is not the user ID you put in the URL
500A rule does not evaluate. Look for access_rule_failed in the log
Your API answers 401It does not accept Basic credentials orders-api:<secret>

Next steps