Skip to content

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:

VariableValue
UDIBO_ISSUERYour tenant's issuer origin
APP_ORIGINhttp://localhost:8007
UDIBO_CLIENT_IDThe registered application's client ID
UDIBO_CLIENT_SECRETA secret created for that application
UDIBO_WEBHOOK_SECRETThe 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.