Protect a JSON API and automate a grant report
Availability: this walkthrough describes a template available through beta repository access. It is not a public standalone starter. You need an accessible Udibo tenant and the template checkout; the OAuth package resolves from that workspace until its first release.
The template contains a Deno and Hono API serving sample reports for one organization, plus a separate command that reads grants on one resource in your tenant. The API accepts people with the right permission. The command uses its own machine credentials and an explicit administrator-assigned permission.
Register the API's client
Choose the API's canonical URL ending in /reports, such as
https://reports.example.com/reports. Register the reports:read customer
permission, create a role containing it, and assign the role to people inside
the organization the API will serve. Follow
roles and permissions for that setup.
Register a public, first-party application with the authorization-code grant,
JWT access tokens, the exact API URL as audience, and your client's callback
URL. Follow application registration. Your OAuth client must
use PKCE S256 and state, request openid profile, and select the organization
using the hosted authorization request's organization slug parameter.
Set three values in the template's .env:
UDIBO_ISSUER: your tenant origin, such ashttps://<tenant-id>.udibo.com.API_RESOURCE_URL: the API's canonical URL ending in/reports.API_ORGANIZATION_ID: the exact ID of the organization to serve.
Run deno task serve from templates/api-service in the checkout. The included
development listener uses 127.0.0.1:8011. Set DENO_SERVE_ADDRESS for your
deployment, terminate HTTPS at the proxy, and preserve the canonical Host
header. HTTP URLs are accepted only for loopback development. This starter
supports backend/native clients and same-origin requests; it does not configure
cross-origin browser access.
Checkpoint: GET /reports without a bearer token returns 401. Sign in as a
permitted member and send the access token in the Authorization header. The
response contains the configured organization ID and one sample quarterly
report. An unpermitted member gets 403. Selecting another organization also gets
403, even when the person has the permission tenant-wide.
Register the reporting bot
Create a separate confidential application with the client-credentials grant and
an explicit identity:organizations:read scope allowlist. Keep the token format
and audience at their tenant defaults. Generate a client secret and store it
only on the backend running the command.
A tenant owner or administrator must assign resource_grants.read using PUT
/api/identity/{tenantId}/applications/{id}/machine-permissions on the Udibo
host, authenticated as that administrator. Its JSON body has a permissions
array containing resource_grants.read. This is separate from customer roles:
registering a customer key of the same name confers no management authority. See
resource-grant management for the assignment API and
authentication requirements.
Register a resource type, such as report. For a nonempty report, use an
administrator's Management API credential to place a customer-role grant on a
resource such as quarterly. The template itself performs no writes.
Add four values to the command's environment:
UDIBO_BOT_CLIENT_ID: the reporting application's client ID.UDIBO_BOT_CLIENT_SECRET: its secret; never expose it to a browser.REPORT_RESOURCE_TYPE: the registered type, such asreport.REPORT_RESOURCE_ID: your resource identifier, such asquarterly.
The command shares UDIBO_ISSUER with the API but does not need the API's other
configuration. Run deno task report from the template directory. It obtains
its own machine token and reads /api/resource-grants on your tenant origin. It
does not call the operator's /api/identity/* routes with that token.
Checkpoint: stdout contains JSON naming the configured resource and its
grant holders. An unshared resource produces an empty grants array. Revoke the
bot's read permission and rerun: the command exits unsuccessfully and emits no
report. Protect successful output as authorization data. Errors use a generic
diagnostic, without logging credentials.
Extend the template safely
Replace the API's sample data with your own storage, keeping each query scoped to the verified organization ID. JWT signature, issuer, audience and expiry are checked locally; permission and organization checks remain your application's responsibility. Local JWT validation does not check immediate revocation, so access may last until the token expires. Keep lifetimes short and clocks synchronized.
The API is read-only, and the reporting command reads grants only. It does not create organizations, invite members, or change sharing. Scheduling, persistent storage and production request logging are application choices. Use the production checklist before exposing your application.
Last verified 2026-09-20.

