Skip to content
Udibo
On this page
View Markdown

Connect your first application

By the end of this guide, a user will sign in on your Udibo Identity tenant, return to your application, and call an API protected by their application session. Start with one development application and one test account.

Before you start

This guide uses a backend-for-frontend (BFF), which keeps tokens and the client secret on your backend. For a browser-only or native client that cannot keep a secret, use a public application with PKCE. If it calls your API, use JWT access tokens with API validation: hosted introspection requires the issuing application's confidential credentials.

You need a Udibo account and a web application with a backend. Start at Udibo for how to get an account. Without one, the local package quickstart runs the same flow against a local identity provider.

Once you are signed in, choose Identity in the dashboard. It opens your tenant, or asks you to create one if you have none. A tenant you create is owned by your active organization, or by you when Personal is active; creating one for an organization takes an owner membership in it. Use a separate tenant for development if you want test users and policy kept apart from production. The tenant switcher in the sidebar lists your active organization's tenants and the ones shared with you; see your active organization.

If a colleague owns the tenant, ask them to give you dashboard access. A role in your application never grants dashboard administration.

For a new project, choose a starter. For an existing app, add this integration alongside your current login and switch it on for test accounts first. Follow the migration plan before moving users.

1. Register the application

Open your tenant's Applications page and choose Create application. For a web app whose backend handles the callback, change these fields from their defaults:

FieldSet it toWhy
NameA name you recognize, such as “My app (dev)”Distinguishes this registration from production
EnvironmentDevelopment (the default is Production)Permits an http://localhost callback
GrantsAuthorization code and Refresh tokenSigns people in and renews their access
Confidential clientChecked (unchecked by default)Your backend can protect a client secret
Redirect URIsYour app's complete callback URL, one a lineThe service returns the browser here after sign-in

Leave Type as First-party, which skips the consent screen for an app you build, and the collapsed Token settings as they are: the Access token format stays Opaque, which your API validates by introspection and which you can revoke instantly.

For example, an app running at http://localhost:8000 with its backend's sign-in routes mounted at /auth uses http://localhost:8000/auth/callback. Match the actual port and path, and use the same URL in your application's configuration.

Save the application. Saving creates the client ID; it does not create a secret. On the application's page, choose Create new secret and store the value in your backend's secret configuration; it is shown once. Managing secrets requires the Reveal application secrets permission.

The application configuration guide explains token formats, scopes, production callbacks, and public clients.

2. Gather the configuration

Keep these values together for this environment. The application's page shows the first two with copy buttons.

ValueWhere it comes from
IssuerIssuer on the application's page: https://{tenant-id}.udibo.com
Client IDClient ID on the application's page
Client secretThe value shown once by Create new secret
Callback URLThe redirect URI you registered, exactly
Scopesopenid, plus profile and email when you need those identity claims
Application secretA high-entropy value you generate to protect pending sign-in state; it is not from Udibo

The issuer is your tenant's own host, not your application's host and not the dashboard URL. Settings → General also shows it as Issuer URL. Its discovery document at {issuer}/.well-known/oauth-authorization-server lists the protocol endpoints, so your client can discover them instead of copying each one.

Use environment variables or your deployment's secret manager for credentials. Do not put the client secret in browser configuration, a public repository, or an agent prompt.

3. Connect the backend and browser

Follow Use Udibo's identity service for the checked Hono backend and browser client code. It reads the values you gathered from these environment variables:

VariableValue
AUTH_ISSUERIssuer
AUTH_CLIENT_IDClient ID
AUTH_CLIENT_SECRETClient secret
AUTH_CALLBACK_URLCallback URL
AUTH_SCOPESScopes, space-separated, such as openid profile email
AUTH_REQUEST_SECRETApplication secret

Using another language or framework? Any OpenID Connect client library that supports the authorization-code flow with PKCE can connect using the same values.

The integration has three pieces:

  1. Your backend mounts HonoBff at /auth, exchanges the authorization code, and stores the application's session.

  2. Your API validates the access token. With opaque tokens, use introspection against the issuer, as shown in Protect an API.

  3. Your browser uses BffClient to start login and make authenticated requests to the same application origin. React applications can use the package's provider and hooks for session presentation.

The guide explicitly marks the session store and resource server as dependencies you supply. A declared variable in a snippet is not a working database adapter. Use a runnable example if you want to inspect all the pieces together before adapting an existing application.

4. Complete a sign-in

Your dashboard account administers the tenant; creating a tenant does not also create a user inside it. Use a test user from the tenant's Users page for this sign-in.

Unless you opened sign-up when creating the tenant, it starts closed, so either choose Create user under Users, or change Self-serve sign-up under Settings. Password, email-code, and magic-link sign-in are on by default; Security controls which methods are offered. A closed sign-up mode does not prevent an existing user from signing in.

Open your application in a browser and start login from the application. The browser should visit the tenant's hosted sign-in page, complete authentication, and return to your registered callback. Your app then reports an authenticated session.

The hosted pages set cookies on your tenant's host while a person signs in. List them in your own privacy notice; cookies on your hosted pages names each one.

Checkpoint: a protected request succeeds while signed in. In a separate signed-out browser context, the same protected request is refused. Signing out ends the application's session; the tenant's SSO session is a separate session, so test the logout behavior you intend to offer.

5. Make the first success repeatable

  • Repeat the flow after reloading the page and after restarting the backend

  • Confirm expired or revoked sessions cannot be restored by a refresh race

  • Confirm the browser has no access or refresh tokens in local storage

  • Test one operation without its required permission; it must be refused

Use troubleshooting if a checkpoint fails. Once it passes, choose sign-in methods, add organizations and permissions where needed, and work through the production checklist.

Optional: start at sign-up instead of sign-in

A consumer product's primary call to action is "Sign up", and an authorization request can say so. Add OIDC's prompt=create and a signed-out person lands on your tenant's sign-up page instead of its sign-in page, carrying the same authorization request — so the account they create completes that request and returns to your callback with a credential, rather than finishing on your tenant's host with no flow in progress.

GET https://{your-tenant-host}/api/oauth2/authorize?…&prompt=create

Through @udibo/oauth2's backend-for-frontend, configure forwardedParams: ["prompt"] on HonoBff, then send the person to /auth/login?prompt=create. Browser-supplied scope and prompt are ignored unless the application explicitly allows them. Keep scope server-configured; it does not need forwarding for sign-up. Allowing prompt also lets the browser choose its other values, so use a fixed extraParams: { prompt: "create" } instead when every login through that BFF should start at sign-up.

Three rules to know:

  • Someone already signed in is sent straight on, as if no prompt were given. The parameter chooses where an unauthenticated request starts; it does not sign anyone out or create a second account.

  • prompt=create with prompt=none is refused with invalid_request on the standard error redirect: the two ask for opposite things.

  • It is not a sign-up-mode check. A tenant whose sign-up mode is closed or waitlisted still receives the person on the sign-up page, which refuses there as it always has — so the parameter never becomes a way to read your tenant's configuration. The sign-up page's own "Sign in" link takes the person to sign-in with the same request intact.

prompt_values_supported in both discovery documents lists the values this authorization server honours. The application configuration guide describes each one; any other value is refused with invalid_request.

Next steps

Your next goalGuide
Choose sign-in methods, sign-up, and MFASign-in methods
Put your name and logo on the hosted pagesBrand your hosted pages
Define sign-out and device controlsSessions and sign-out
Give customers their own workspaceOrganizations
Control access to features or recordsPermissions and roles
Prepare a launchProduction checklist

Last verified 2026-10-28.