Skip to content

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 as https://<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 as report.

  • REPORT_RESOURCE_ID: your resource identifier, such as quarterly.

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.