How to Troubleshoot Common Authentication Issues in Logto: A Complete Developer’s Guide
Most Logto authentication failures stem from three root causes: missing interaction states in session storage, invalid or expired one-time tokens, or misconfigured SSO connectors.
When users can’t sign in to your application, the issue usually traces back to how Logto manages the sign-in flow state, validates temporary credentials, or communicates with external identity providers. Understanding the exact error patterns in the logto-io/logto source code lets you diagnose problems in minutes rather than hours.
Diagnosing Interaction State Failures
Logto’s authentication flow relies on interaction objects stored in session storage. When a user initiates sign-in, the server creates an interaction of type InteractionEvent.SignIn. If subsequent requests can’t retrieve this object, the flow breaks immediately.
What Happens When Interactions Go Missing
The submit-interaction.ts handler expects to find a stored interaction before processing authentication results. If the session storage lacks the expected record, Logto returns a 404 error with the code session.not_found. This commonly occurs when:
- The client calls the submit endpoint without first creating an interaction via the SSO authorization endpoint.
- A server restart or cache flush clears the session storage before the flow completes.
- Multiple concurrent requests corrupt the stored state.
Where the Logic Resides
According to the Logto source code, the critical paths are:
- Submission handler:
packages/core/src/routes/interaction/actions/submit-interaction.tscallsassignInteractionResultsafter processing. - Storage helpers:
packages/core/src/routes/interaction/utils/interaction.tscontainsstoreInteractionResultandclearInteractionStorage. - SSO routes:
packages/core/src/routes/interaction/single-sign-on.tslogs interaction updates viacreateLog(Interaction.SignIn.Update).
How to Diagnose Interaction Errors
Check the oidc_model_instances table directly when you suspect state corruption:
SELECT * FROM oidc_model_instances WHERE id = '<interaction-id>';
Verify the interaction exists via API before submitting:
curl http://localhost:3001/api/interaction/1234 \
-H "Cookie: your-session-cookie"
# Expect 200 if valid, 404 if missing
Review server logs for the specific interaction event:
// Look for this log pattern in your output
createLog(`Interaction.SignIn.Update`);
Quick Fixes for Interaction Issues
- Ensure proper flow order: The client must call
POST /interaction/sso/:connectorId/authorization-urlbefore posting authentication results. This endpoint creates the interaction object. - Clear stale state: Use the
clearInteractionStoragehelper (line 96 insubmit-interaction.ts) or restart the development server to wipe corrupted sessions. - Verify interaction type: Confirm the stored JSON shows
"event": "SignIn"if you expect a sign-in flow, not"Register"or"ForgotPassword".
Resolving One-Time Token (OTT) Validation Errors
Logto uses one-time tokens for auto-consent flows and passwordless email login. The koa-consent-guard middleware validates these tokens against the current session, but strict validation rules often cause unexpected redirects to the switch-account page.
Common OTT Failure Modes
The consent guard in packages/core/src/middleware/koa-consent-guard.ts enforces three strict conditions:
one_time_token.token_consumed: The token was already used (lines 44-48).- Mismatched
login_hint: The session’s primary email differs from the URL parameter (lines 38-41). - Missing parameters: The request lacks either
one_time_tokenorlogin_hint(lines 31-40).
How to Diagnose Token Problems
Inspect the primary email comparison logic when users see unexpected account-switch prompts:
// From koa-consent-guard.ts
if (primaryEmail !== loginHint && !hasLoginPrompt(prompt) && !hasMatchingLastSubmittedLogin) {
// Redirects to switch-account
}
Check token consumption status using the library directly:
import { checkOneTimeToken } from '@logto/core/libraries';
const tokenInfo = await checkOneTimeToken(token, loginHint);
console.log('Consumed:', tokenInfo.consumed);
Look for redirect patterns in your network logs:
// Error pages appear as redirects to:
/experience/one-time-token?errorMessage=one_time_token.token_consumed
Quick Fixes for OTT Issues
- Regenerate tokens: Call
/api/experience/one-time-tokento create a fresh token after consumption. - Match email hints: Ensure the
login_hintquery parameter exactly matches the user’s primary email in Logto. Typos in the domain or casing cause immediate rejection. - Prevent double-submission: Clear the token from your UI state immediately after a successful login to prevent users from clicking "Back" and resubmitting consumed tokens.
Fixing SSO Connector Lookup Problems
When users select an enterprise identity provider (Google, Azure AD, Okta), Logto must resolve the connector configuration and generate an authorization URL. Failures here typically manifest as 404 Not Found or 422 Unprocessable Entity responses.
Connector Resolution Failures
The SSO flow in packages/core/src/routes/interaction/single-sign-on.ts fails when:
- The connector ID doesn’t exist in the database.
- The connector lacks the requested operation (e.g., missing
authorization-urlroute). - The callback payload from the identity provider violates the
authorizationUrlPayloadGuardschema.
Where the Logic Resides
Key files for connector troubleshooting:
- SSO route handlers:
packages/core/src/routes/interaction/single-sign-on.ts(creates auth URLs and processes callbacks). - Library functions:
libraries/sso-connector.ts(line 175 handles default prompt selection). - Authorization URI builder:
packages/core/src/routes/connector/authorization-uri.ts. - OIDC connector implementation:
packages/core/src/sso/OidcConnector/index.tscontains thegetAuthorizationUrlmethod.
How to Diagnose Connector Issues
Verify connector existence in the database:
SELECT * FROM connectors WHERE id = '<connector-id>' AND enabled = true;
Check domain filtering (affects connector availability):
// The SSO selection endpoint filters by email domain
GET /interaction/sso/connectors?email=user@company.com
Validate payload structure against the guard:
// authorizationUrlPayloadGuard expects specific fields
koaGuard({ body: authorizationUrlPayloadGuard })
Inspect the connector’s URL generation:
// From OidcConnector/index.ts
return `${oidcConfig.authorizationEndpoint}?${queryParameters.toString()}`;
Quick Fixes for SSO Problems
- Refresh connector configuration: Run
pnpm cli connector link -p .from the Logto root to re-link connector packages and update the database entries. - Validate domain lists: Ensure the connector’s
domainsfield in the database includes the user’s email domain, or the connector won’t appear in the selection list. - Check credentials: Verify the connector’s
configJSON contains validclientIdandclientSecretvalues; missing credentials cause silent failures during the token exchange phase.
Step-by-Step Authentication Debugging Workflow
When you encounter an unexplained authentication failure, follow this systematic approach using Logto’s internal libraries:
// 1️⃣ Reproduce the failure
const response = await fetch('http://localhost:3001/api/interaction/12345/submit', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ event: 'signIn', identifier: { ... } }),
});
// 2️⃣ Verify interaction existence
const interaction = await queries.interactions.findInteractionById('12345');
if (!interaction) {
console.error('Interaction missing — client likely skipped SSO auth step');
}
// 3️⃣ Check one-time token status (if applicable)
const tokenStatus = await libraries.oneTimeTokens.checkOneTimeToken(token, loginHint);
if (tokenStatus.consumed) {
console.error('Token already used, generate new token');
}
// 4️⃣ Validate SSO connector (if using enterprise login)
const connector = await libraries.ssoConnectors.getSsoConnectorById(connectorId);
if (!connector) {
console.error(`Connector ${connectorId} not found in database`);
}
// 5️⃣ Inspect logs for interaction updates
// Look for: Interaction.SignIn.Update in your logging pipeline
Summary
- Interaction state errors trigger
session.not_found(404) when the sign-in flow starts without proper session initialization or when storage clears prematurely. - One-time token failures occur when tokens are consumed twice,
login_hintparameters mismatch the user’s primary email, or required parameters are missing from the request. - SSO connector issues result from invalid connector IDs, domain mismatches, or corrupted configuration stored in the
connectorstable. - Diagnostic locations focus on
packages/core/src/routes/interaction/actions/submit-interaction.ts,packages/core/src/middleware/koa-consent-guard.ts, andpackages/core/src/routes/interaction/single-sign-on.ts.
Frequently Asked Questions
Why does Logto return "session not found" even though the user just started signing in?
This error indicates the interaction ID provided to the submit endpoint has no corresponding record in the session storage. According to the Logto source code in submit-interaction.ts, the server expects an existing InteractionEvent object created by prior calls to POST /interaction or the SSO authorization URL endpoint. If your frontend calls the submit endpoint directly without the preliminary interaction setup, or if the server restarted and cleared Redis/DB sessions, this error appears immediately.
How can I debug why one-time tokens keep showing as "consumed"?
The koa-consent-guard.ts middleware marks tokens as consumed immediately after validation to prevent replay attacks. If users see this error, check for double-submit scenarios where the browser sends the request twice (common with aggressive form submissions or "Back" button usage). You can verify token status by calling libraries.oneTimeTokens.checkOneTimeToken(token, loginHint) from your debug console. To fix, generate a fresh token via the /api/experience/one-time-token endpoint and ensure your UI clears the token from state immediately after the first successful submission.
What causes "Connector not found" errors when using SSO?
This error originates in packages/core/src/routes/interaction/single-sign-on.ts when the provided connectorId doesn’t match any record in the connectors database table. First, query SELECT * FROM connectors WHERE id = '<your-id>' to confirm the connector exists and enabled = true. If missing, run pnpm cli connector link to re-register the connector. Also verify the connector supports the requested operation—some legacy connectors may lack the getAuthorizationUrl method required by the SSO flow, causing the route handler to return 404 during the authorization phase.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →