Build a B2B SaaS application
Availability: this walkthrough describes a service template currently available through beta repository access. 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.
Before you start
You need an accessible Udibo tenant, a development application configured as a 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
Use http://localhost:8007 as the app origin and register
http://localhost:8007/auth/callback exactly. Set these environment values in
your local runtime:
| Variable | Value |
|---|---|
UDIBO_ISSUER | Your tenant's issuer origin |
APP_ORIGIN | http://localhost:8007 |
UDIBO_CLIENT_ID | The registered application's client ID |
UDIBO_CLIENT_SECRET | A secret created for that application |
UDIBO_WEBHOOK_SECRET | The signing secret for your webhook endpoint |
If you have the template checkout, run deno task dev from
templates/b2b-saas. The application requires all five values at startup. Keep
secrets on the server.
Register a webhook endpoint at your reachable HTTPS application origin plus
/webhooks/udibo. A hosted service cannot deliver to your laptop's localhost:
use a development tunnel or a deployed preview for that part of the trial.
Follow webhook registration and select the organization events
you want to receive.
1. Sign in and establish a session
The server discovers the tenant's endpoints, starts the authorization-code flow, exchanges the code, and keeps tokens behind a backend-for-frontend. The browser carries an application session cookie.
Login, signup, and consent run on the tenant's hosted pages. Configure sign-in methods there instead of rebuilding those pages inside the example.
Checkpoint: you return to the app after login, and a protected request is validated through introspection.
2. Keep customer organizations separate
Create two organizations. Give your test user
projects:read and projects:archive through roles in one organization, then
give them different access in the other.
The project list reads the validated token's permissions and active org_id,
then restricts its own rows to that organization. A credential answers for at
most one active organization. Permission to read in one must not expose
another's projects.
The archive action obtains a project's organization from the application's own
data and asks POST /api/check about projects:archive in that organization.
It does not accept an organization supplied by the browser as proof of access.
Checkpoint: a user can read or archive only where their role allows it. Test a project in the other organization and an unknown project ID.
3. Receive organization changes
Verify the webhook signature over raw request bytes before parsing JSON. Enforce the timestamp tolerance and deduplicate deliveries. The activity list must also filter events to the reader's organization.
Read the delivery contract for retry behavior and the current private-beta worker cadence. Treat webhook data as eventually delivered; authorize protected requests from current validated identity.
Make it your product
Replace the example's in-memory project and activity stores with durable storage. Keep organization ownership on each application row, and retain the cross-organization denial tests.
The example links to hosted organization-management pages. Plan the return journey back into your application. It does not call administrator-only tenant-management APIs using an end user's token.
Embedding those screens is an option the example does not take. Your tenant's
own host answers /api/organizations for the person signed in to your
application: it creates organizations, invites members, and manages memberships
and the organization's own roles, authorized by that person's own accepted
membership rather than by any credential of yours — no scope, no dashboard
access, and a machine (client_credentials) token refused on every verb
including create. See organizations for the address, the
verbs and the authority tiers.
Continue with permissions and the production checklist.
Last verified 2026-09-14.

