Logto Authentication and Authorization Flows: A Deep Dive into OIDC Implementation

Logto implements a full OpenID Connect (OIDC) and OAuth 2.0 server that splits authentication and authorization into three logical layers: front-end entry points, core routing logic, and token services, enabling secure Authorization Code flows with PKCE support.

The logto-io/logto repository provides a production-ready identity infrastructure that handles both authentication (verifying user identity) and authorization (granting resource access) through standard-compliant OIDC flows. This guide examines how the TypeScript codebase orchestrates the Authorization Code flow—the most common pattern used by Logto-based applications—from the initial login request through token validation and RBAC enforcement.

Initiating the Login Flow: The /api/authn Endpoint

Every authentication journey begins at the /api/authn endpoint defined in packages/core/src/routes/authn.ts. When a client application redirects a user to Logto, this route performs critical validation and session initialization.

GET https://<logto-host>/api/authn?client_id=...&redirect_uri=...&response_type=code&scope=openid profile email

The implementation verifies the client_id, redirect_uri, response_type, and scope parameters against the database. Upon validation, it generates a state token to prevent CSRF attacks and stores it in the user's session using the session utility located in packages/shared/src/utils/session.ts.

The endpoint then renders the Experience UI—Logto's customizable sign-in interface. If the application requires social login or enterprise SSO, the route triggers the appropriate connector flow while preserving the authentication context.

Handling User Authentication: Connectors and Callbacks

Social and Enterprise Connector Redirects

For external identity providers, Logto utilizes packages/core/src/routes/connector/authorization-uri.ts. This module generates the authorization URL for third-party providers (Google, SAML, OIDC connectors) and handles the redirect to the external authentication page.

After the user authenticates with the external provider, the provider redirects back to Logto's callback endpoint with an authorization code.

The Callback Endpoint and Session Creation

The callback handling logic resides in packages/core/src/routes/callback.ts. This critical component:

  1. Receives the authorization code from the connector
  2. Validates the stored state token against CSRF attacks
  3. Exchanges the external provider's code for a Logto internal session
  4. Sets a short-lived logto_session cookie
  5. Redirects the user to the originally requested redirect_uri, appending the OIDC code that the client will exchange for tokens

The session creation process persists a lightweight entry in PostgreSQL containing the user ID, authentication method, and connector metadata via the utilities in packages/shared/src/utils/session.ts.

Token Exchange: Issuing JWTs at /api/token

Once the client receives the authorization code, it makes a POST request to the token endpoint:

POST https://<logto-host>/api/token
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code
code=<code-from-callback>
redirect_uri=...
client_id=...
client_secret=...

The token issuance logic resides in the same packages/core/src/routes/authn.ts file (POST handler). This route validates the authorization code, retrieves the associated session, and generates three token types:

  • ID Token: A signed JWT containing the user's sub (subject), email, profile claims, and optional custom claims
  • Access Token: A JWT for calling Logto Management APIs or protected resources
  • Refresh Token: A long-lived token used to obtain new access tokens without re-authentication

Token signing utilizes the JWK service implemented in packages/core/src/routes/logto-config/jwt-customizer.ts, which supports automatic key rotation and tenant-specific custom claims. The TTL cache utility in packages/shared/src/utils/ttl-cache.ts optimizes JWK retrieval performance.

The response follows the standard OAuth 2.0 format:

{
  "access_token": "...",
  "id_token": "...",
  "refresh_token": "...",
  "token_type": "Bearer",
  "expires_in": 3600
}

Authorization: Enforcing RBAC and Scope Validation

Resource and Scope Validation

When clients access protected resources, Logto validates the access token against configured permissions. The resource route in packages/core/src/routes/resource.ts handles resource registration, while packages/core/src/routes/role.scope.ts implements the scope-checking logic that maps token claims to permissions.

Access Control Middleware

The enforcement layer lives in packages/core/src/routes/applications/application-access-control.ts. This middleware:

  • Extracts the scp (scope) claim from the access token
  • Matches scopes against the tenant's role-based access control (RBAC) configuration
  • Returns 403 Forbidden for insufficient permissions or allows the request to proceed

This architecture separates authentication (proving identity) from authorization (checking permissions), allowing fine-grained access control without re-authentication.

Session Management and Refresh Token Lifecycle

Logto maintains session state in PostgreSQL through the utilities in packages/shared/src/utils/session.ts. Each session contains the user ID, authentication method, and references to external connector data.

When access tokens expire, clients present the refresh token to /api/token with grant_type=refresh_token. The implementation:

  1. Verifies the refresh token's signature and checks revocation status
  2. Issues a new access token (and optionally a new refresh token)
  3. Updates the persisted session to reflect the latest activity
  4. Returns the new token set to the client

Security Hardening Mechanisms

Logto adds several OIDC-compliant security layers beyond the basic protocol:

  • CSRF State Tokens: Stored in sessions (session.ts) and validated during callback processing (callback.ts) to prevent cross-site request forgery
  • PKCE Support: Required for public clients (SPAs, mobile apps), enforced in authn.ts through code_challenge verification
  • JWK Rotation: Automatic signing key rotation managed in packages/core/src/routes/logto-config/jwt-customizer.ts
  • Replay Attack Mitigation: Each authorization code is single-use only, persisted in the database with strict expiration timestamps in authn.ts
  • Scope-Based RBAC: Fine-grained permission model enforced through role.scope.ts

Implementation Example: Node.js SDK Integration

The following example demonstrates how the Node.js SDK mirrors the server-side flows described above:

import { LogtoClient } from '@logto/client';

// Step 1: Generate the authorization URL
const authUrl = LogtoClient.buildAuthUrl({
  clientId: 'my-app',
  redirectUri: 'https://myapp.com/callback',
  scope: 'openid profile email',
  responseType: 'code',
});

// Step 2: Redirect the user to Logto
// User authenticates → Logto redirects to https://myapp.com/callback?code=...

// Step 3: Exchange the code for tokens
const tokens = await LogtoClient.fetchTokens({
  code: req.query.code,
  redirectUri: 'https://myapp.com/callback',
  clientId: 'my-app',
  clientSecret: 'secret',
});

// tokens.accessToken, tokens.idToken, tokens.refreshToken available

The SDK simply orchestrates the HTTP interactions; all cryptographic operations, session management, and protocol enforcement occur server-side within the routes referenced earlier.

Summary

Frequently Asked Questions

What authentication protocols does Logto support?

Logto supports OpenID Connect (OIDC) and OAuth 2.0 as its core protocols, implementing the Authorization Code flow, PKCE extension for public clients, and Client Credentials flow for machine-to-machine authentication. The implementation resides in packages/core/src/routes/authn.ts and adheres to the OIDC specification for token issuance and validation.

How does Logto prevent CSRF attacks during authentication?

Logto generates a cryptographically random state token in packages/core/src/routes/authn.ts and stores it in the user's session via packages/shared/src/utils/session.ts. When the external provider redirects back to the callback endpoint in packages/core/src/routes/callback.ts, Logto validates that the returned state matches the stored value, rejecting any requests where the state parameter is missing or mismatched.

Where does Logto store session data?

Session data persists in PostgreSQL through the utilities defined in packages/shared/src/utils/session.ts. Each session record contains the user ID, authentication method, connector metadata, and expiration timestamps. This approach allows horizontal scalability while maintaining strong consistency for security-critical session validation during token exchanges.

How does Logto enforce role-based access control (RBAC)?

Logto enforces RBAC through a multi-layer approach: packages/core/src/routes/role.scope.ts defines the scope-to-permission mappings, while packages/core/src/routes/applications/application-access-control.ts implements middleware that extracts the scp claim from JWT access tokens and validates it against the tenant's configured roles. If the required scopes are absent, the middleware returns a 403 Forbidden response before the request reaches protected business logic.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →