Build a client portal
Availability: this walkthrough describes a service template available through repository access arranged with Udibo. It is not a public standalone starter. You can follow the same design in your own application using the first-login guide. For runnable public package examples, use starter projects.
The template is a React Router application served by Hono, with its projects, documents and sessions in Postgres through drizzle. It needs Node.js 26, pnpm and a Postgres database.
What the walkthrough covers
One person can read and revise the entire workspace. Others see only one project through their organization, one directly shared document, or nothing. The portal reports sharing permission but does not yet write grants.
Before you start
You need an accessible Udibo Identity tenant, a development application configured as a first-party, confidential authorization-code client with refresh tokens, and a client secret. Choose opaque access tokens for these walkthroughs; they validate through introspection. See application registration for the exact settings.
The app requests openid profile email. Define application permissions in your
tenant's permission catalog and assign roles separately; permission names are
not additional OAuth scopes.
Set up the example
Register http://localhost:8009/auth/callback and configure:
| Variable | Value |
|---|---|
UDIBO_ISSUER | Your tenant's issuer origin, https://<tenant-id>.udibo.com |
APP_ORIGIN | http://localhost:8009; the registered callback is {APP_ORIGIN}/auth/callback |
UDIBO_CLIENT_ID | The registered application's client ID |
UDIBO_CLIENT_SECRET | A secret created for that application |
DATABASE_URL | The Postgres database the projects, documents and sessions live in |
SESSION_SECRET | At least 32 bytes of entropy that encrypts sessions, sign-ins in progress and page cursors; new,old rotates it |
PORT | Optional. Where the server listens; 8009 by default |
Every required value is checked at startup, and a missing one stops the server with its name. Keep secrets on the server.
SESSION_SECRET is required. It encrypts the sessions the app stores in its
database, the browser cookie that holds a sign-in in progress, and the cursors
the document list pages with. Generate one with
node -e "console.log(require('node:crypto').randomBytes(32).toString('base64url'))".
It accepts a comma-separated list, and every entry needs at least 32 bytes. The
first entry encrypts new sessions, the sign-in cookie and the cursors, and keys
the document list's order; the entries after it only open sessions created
before a rotation. To rotate without signing everyone out, set
SESSION_SECRET=<new>,<old> and drop the old entry after 14 days, the session
lifetime. A sign-in started before the rotation and finished after it fails, and
the person signs in again; a half-read document list starts again from its first
page.
In your copy of the template, install, migrate the database and start it.
pnpm db:seed is optional and adds a sample workspace of two projects and their
documents:
pnpm install --frozen-lockfile
cp .env.example .env # then fill it in
pnpm db:migrate
pnpm db:seed
pnpm dev # http://localhost:8009Define documents:read, documents:write, and documents:share in your
permission catalog, and roles carrying them. Register the document and
project resource types. Place resource grants in your tenant's dashboard under
Access control → Resources, or from your own backend with an application
credential (see What sharing can do today).
Permissions explains the
resource and grant API shapes.
1. Ask about the document being accessed
A token cannot enumerate every document a person may access. The application gets candidate documents from its own data store and asks Udibo about those specific resources.
The list uses POST /api/check/batch: one set of checks for documents and
another for their projects, chunked to the endpoint's limit of 100 candidates.
It returns only documents allowed by the resulting decisions.
A direct read checks the same access boundary. Updating a document additionally
requires documents:write. Keep the read and write decisions separate.
Checkpoint: a user granted one document sees that document and cannot read another by guessing its ID.
2. Model inheritance deliberately
Udibo does not store your application's document-to-project hierarchy. The portal knows that relationship, checks both resources, and accepts an appropriate grant on either.
A project grant therefore reaches its documents because the application applies that rule. It does not automatically reach arbitrary descendants in Udibo.
Checkpoint: grant a project and verify access to its documents, then confirm a different project's documents remain inaccessible.
3. Share with people or organizations
A resource grant can name a person or an organization. An organization grant resolves through accepted membership and can restrict which member roles may exercise it.
The active organization on a token is not a substitute for a resource check. An
organization-scoped documents:read role does not by itself grant access to
every document resource. Conversely, a person with a direct resource grant may
have no permissions claim on their token and still be allowed to read it.
Checkpoint: test a direct recipient, an organization recipient, a member outside the grant's role restriction, and someone with a similarly named organization permission but no resource grant.
What sharing can do today
The example reads documents:share to decide who should be offered a sharing
option. That application permission does not authorize a write to Udibo's
tenant-management API.
The example writes no resource grants itself. Your application can: on your
tenant's host, POST /api/resource-grants accepts your application's machine
credential once a tenant administrator assigns it resource_grants.write and
its token carries the identity:organizations:write scope. See
permissions
for placing a grant. Machine tokens still do not provide general
tenant-management access.
Replace the example's workspace tables with your own data, preserve consistent responses for missing and inaccessible documents, and use the production checklist before launch.
Last verified 2026-10-13.

