# Troubleshoot sign-in and API access Follow the request from the browser to the identity service, back through your callback, and into the protected API. Fix the first boundary that fails. Keep state, PKCE, token verification, and CSRF enabled while diagnosing the problem. For problems a person reports about their own account, start with [a user cannot sign in or recover](https://www.udibo.com/docs/identity/troubleshooting#a-user-cannot-sign-in-or-recover). ## Start with the symptom - [The sign-in page does not open](https://www.udibo.com/docs/identity/troubleshooting#the-sign-in-page-does-not-open) - [Sign-in succeeds but the callback fails](https://www.udibo.com/docs/identity/troubleshooting#the-user-signs-in-but-the-callback-fails) - [The app looks signed in but the API refuses](https://www.udibo.com/docs/identity/troubleshooting#the-app-looks-signed-in-but-the-api-refuses) - [A role change seems to do nothing](https://www.udibo.com/docs/identity/troubleshooting#a-role-change-seems-to-do-nothing) - [Refresh or logout behaves unexpectedly](https://www.udibo.com/docs/identity/troubleshooting#refresh-or-logout-behaves-unexpectedly) - [A user cannot sign in or recover](https://www.udibo.com/docs/identity/troubleshooting#a-user-cannot-sign-in-or-recover) - [Email does not arrive](https://www.udibo.com/docs/identity/troubleshooting#email-does-not-arrive) - [Webhook deliveries fail](https://www.udibo.com/docs/identity/troubleshooting#webhook-deliveries-fail) If these checks do not isolate the problem, [collect a minimal support report](https://www.udibo.com/docs/identity/troubleshooting#bring-useful-information-to-support). ## The sign-in page does not open | Symptom | Check next | | ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | | No tenant or application access | Confirm your Udibo account and ask a tenant owner for [dashboard access](https://www.udibo.com/docs/identity/team-access) | | Discovery fails | Copy **Issuer** from the application's page and fetch its `/.well-known/oauth-authorization-server` document from the backend | | Redirect URI rejected | Compare scheme, host, port, path, and query in the registration and application configuration | | Local HTTP callback refused | Use a Development registration for a localhost web callback | | `invalid_client` | Check the client ID, secret, confidential-client setting, and environment; creating the registration does not create its secret | | Sign-up unavailable | Check the tenant's sign-up mode and enabled sign-in methods; do not bypass a closed or waitlisted policy | The issuer is not the dashboard URL. A social provider's callback returns to the identity service; your application's callback receives the result from that service. They are two different registrations. ## The user signs in but the callback fails **Unknown state or missing transient cookie:** check whether the callback uses the same browser and application host that started the flow. Check cookie attributes, the pending-state secret, and whether every backend instance uses the same configuration and shared storage. Expired state should start a new login rather than being accepted without verification. **`invalid_grant` during the code exchange:** an authorization code may be expired, already consumed, or paired with the wrong callback or PKCE verifier. Start a fresh flow and inspect the request's configuration. Do not replay a captured code or weaken verification to make it pass. **Login works on one instance only:** the callback may arrive at a different instance. A process-local session or pending-state store cannot reliably serve that flow. See [session readiness](https://www.udibo.com/docs/identity/production#make-sessions-durable). ## The app looks signed in but the API refuses First identify whether the request goes through your BFF or directly to a bearer-token API. A BFF cookie request uses your application's session; a direct API request presents an access token. For Udibo APIs, [choose the right API](https://www.udibo.com/docs/identity/apis) to check the host and credentials it accepts. Tenant-host APIs need the access token even when the person is already signed in on the hosted pages. | Result | Check next | | ---------------------------------------------- | ---------------------------------------------------------------------------------------------------------- | | `401` on a BFF cookie request | Confirm the request carries the application's session cookie and the backend can validate its access token | | `401` on a direct bearer API request | Send the access token in `Authorization: Bearer …`; check its expiry and the API's origin | | `403` with `csrf_validation_failed` | For a BFF cookie request, use `BffClient` or supply the configured CSRF header from a same-origin client | | Valid token but missing API scope | Compare the requested scope with the client's allowed scope and the route's requirement | | Valid token but missing application permission | Check the role assignment and the organization or resource the operation actually targets | | A profile field is missing | Read UserInfo with `profile` or `email` as appropriate; an access token is not a full profile | | A JWT is rejected | Check the expected issuer, audience, signing key, algorithm, and expiry | In a BFF integration, keep tokens on the backend. Do not copy a bearer token into browser storage to work around a cookie problem. The [BFF integration guide](https://www.udibo.com/docs/oauth2/guides/use-udibo) shows how the browser calls the application using its session cookie. ## A role change seems to do nothing Check **where the answer came from**. The browser's session projection may still contain the values captured during login. Authorize from the validated request or an appropriate live permission check, not that UI copy. An offline JWT carries the claims from issuance until it expires. An opaque token can be introspected for current state, but your own caching can make that answer older. An active-organization claim is scoped to that organization; it does not answer a question about a different organization or a specific resource. See [permissions and roles](https://www.udibo.com/docs/identity/permissions). ## Refresh or logout behaves unexpectedly For `invalid_grant` during refresh, check expiry, revocation, and whether two requests tried to rotate the same credential. Preserve atomic store updates and the package's rotation behavior. End the failed session and start login again instead of retrying a rejected refresh token indefinitely. For sign-out, distinguish the application session from the identity service's session. A new login can return without another password prompt if the tenant's session is still valid. Test the exact local or SSO logout behavior your UI promises. The [sessions and sign-out guide](https://www.udibo.com/docs/identity/sessions) distinguishes application sessions, tenant sessions, and device revocation, including the expected account-API refusals. ## A user cannot sign in or recover | What the person reports | Check next | | ------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | No social button on the sign-in page | Both **Social sign-in** under **Security** and the provider's own toggle must be on; see [social login](https://www.udibo.com/docs/identity/social-login) | | "Sign up" is refused | Check **Settings → Sign-up → Self-serve sign-up**. Closed or waitlisted registration requires a valid [waitlist invitation](https://www.udibo.com/docs/identity/waitlist#approve-and-re-invite) for a new account; existing users can still sign in. Check whether the link expired, was replaced, or was denied | | Too many failed attempts | A locked account sees the same error as a wrong password; the person can use the emailed unlock link or reset their password; see [account protection](https://www.udibo.com/docs/identity/account-protection) | | Lost their authenticator and recovery codes | An administrator resets MFA from the person's page under **Users**; see [MFA](https://www.udibo.com/docs/identity/mfa#resetting-a-users-mfa) | | Their account is disabled | A disabled account is refused everywhere until an administrator enables it; see [manage users](https://www.udibo.com/docs/identity/users) | | Nowhere to turn on a dead-end page | Set a **Support URL** under **Settings** → **Branding** so those pages offer **Contact support**; see [branding](https://www.udibo.com/docs/identity/branding) | The tenant's [audit log](https://www.udibo.com/docs/identity/audit-log) records failed sign-ins, lockouts, and administrator actions with their times. Filter by the person to see what happened before changing their account. ## Email does not arrive - **Limits.** Mail through Udibo's shared sender has a daily ceiling, and any email limit set on the **Billing** page stops sending when it is reached, sign-in codes and resets included. Check the **Billing** page first. The senders have separate monthly and daily limits; use [usage, limits, and alerts](https://www.udibo.com/docs/identity/usage-limits#respond-to-a-limit-or-alert) to identify the affected sender and period before changing either limit. - **Your own sender.** Under **Settings** → **Email**, check the domain status and any health warning. An unreadable API key falls back to the shared sender; rejected sends mean the key or domain needs attention in Resend. - **Suppressed addresses.** A permanent bounce or spam complaint stops security notices, waitlist emails, and invitations to that address. Codes, links, and resets the person asks for are still sent. - **Failed sends** appear in the audit log as `email.delivery_failed`. [Sending email from your domain](https://www.udibo.com/docs/identity/email) covers every email the service sends and how to test delivery. ## Webhook deliveries fail Open the endpoint's delivery log in the dashboard. It shows each delivery's status, attempts, last response code, and a truncated error. A signature mismatch almost always means the handler verified a parsed and re-serialized body instead of the raw request bytes, or used a rotated secret. See [webhooks](https://www.udibo.com/docs/identity/webhooks#signature-verification) for verification, retries, and duplicate handling. ## Bring useful information to support Include the environment, the failing step, the HTTP status or error code, approximate time, runtime and package versions, and a minimal reproduction. Use the support channel you agreed with Udibo. Redact secrets, passwords, cookies, authorization codes, and token values. Callback URLs can contain credentials in the query string; report the registered URL rather than a captured login URL. **Next:** return to [your first sign-in](https://www.udibo.com/docs/identity/get-started) or [production readiness](https://www.udibo.com/docs/identity/production). _Last verified 2026-10-02._