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:
| Field | Set it to | Why |
|---|---|---|
| Name | A name you recognize, such as “My app (dev)” | Distinguishes this registration from production |
| Environment | Development (the default is Production) | Permits an http://localhost callback |
| Grants | Authorization code and Refresh token | Signs people in and renews their access |
| Confidential client | Checked (unchecked by default) | Your backend can protect a client secret |
| Redirect URIs | Your app's complete callback URL, one a line | The 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.
| Value | Where it comes from |
|---|---|
| Issuer | Issuer on the application's page: https://{tenant-id}.udibo.com |
| Client ID | Client ID on the application's page |
| Client secret | The value shown once by Create new secret |
| Callback URL | The redirect URI you registered, exactly |
| Scopes | openid, plus profile and email when you need those identity claims |
| Application secret | A 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:
| Variable | Value |
|---|---|
AUTH_ISSUER | Issuer |
AUTH_CLIENT_ID | Client ID |
AUTH_CLIENT_SECRET | Client secret |
AUTH_CALLBACK_URL | Callback URL |
AUTH_SCOPES | Scopes, space-separated, such as openid profile email |
AUTH_REQUEST_SECRET | Application 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:
Your backend mounts
HonoBffat/auth, exchanges the authorization code, and stores the application's session.Your API validates the access token. With opaque tokens, use introspection against the issuer, as shown in Protect an API.
Your browser uses
BffClientto 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=createThrough @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=createwithprompt=noneis refused withinvalid_requeston 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 goal | Guide |
|---|---|
| Choose sign-in methods, sign-up, and MFA | Sign-in methods |
| Put your name and logo on the hosted pages | Brand your hosted pages |
| Define sign-out and device controls | Sessions and sign-out |
| Give customers their own workspace | Organizations |
| Control access to features or records | Permissions and roles |
| Prepare a launch | Production checklist |
Last verified 2026-10-28.

