# Implementing Machine-to-Machine (M2M) Authentication with Logto Client Credentials: A Complete Guide

> Master M2M authentication with Logto client credentials. Learn how Logto's OAuth 2.0 grant enables secure backend service token access and caching for seamless integration.

- Repository: [Logto/logto](https://github.com/logto-io/logto)
- Tags: how-to-guide
- Published: 2026-07-04

---

**Logto implements the OAuth 2.0 client credentials grant as a first-class OIDC flow, enabling backend services to obtain JWT access tokens via the `/oidc/token` endpoint and automatically cache them using the `ClientCredentials` class from `@logto/api`.**

Logto's open-source identity platform (logto-io/logto) provides native support for machine-to-machine (M2M) authentication through the OAuth 2.0 client credentials grant. This implementation allows backend services, CI/CD pipelines, and other non-interactive clients to securely access the Logto Management API or protected resources without user intervention.

## Core Architecture

Logto's M2M implementation consists of four primary components that work together to issue, cache, and consume access tokens.

### OIDC Provider

The server-side implementation resides in [`packages/core/src/oidc/grants/client-credentials.ts`](https://github.com/logto-io/logto/blob/main/packages/core/src/oidc/grants/client-credentials.ts). This module exposes the token endpoint (`/oidc/token`) and validates the `client_id` and `client_secret` provided by M2M clients. It implements a custom `client_credentials` grant that also supports organization-scoped tokens, extending standard OAuth 2.0 functionality.

### ClientCredentials Class

Located in [`packages/api/src/client-credentials.ts`](https://github.com/logto-io/logto/blob/main/packages/api/src/client-credentials.ts), this SDK class serves as a thin wrapper around the token endpoint. It handles HTTP requests, parses responses, and implements intelligent caching with just-in-time refresh. The class stores tokens alongside their absolute expiry timestamp (`expiresAt`) and returns cached tokens until they approach expiration.

### Event Listeners

For audit purposes, Logto registers event listeners in [`packages/core/src/event-listeners/index.ts`](https://github.com/logto-io/logto/blob/main/packages/core/src/event-listeners/index.ts) that track M2M token lifecycle events. The provider emits events via `provider.addListener('client_credentials.*', m2mTokenUsageListener)`, logging token creation, refresh, and revocation activities.

### Management API

The Management API implementation in [`packages/api/src/management.ts`](https://github.com/logto-io/logto/blob/main/packages/api/src/management.ts) demonstrates how protected resources consume these tokens. Endpoints require the `management_api` scope (or application-specific scopes) encoded in the JWT payload.

## How the M2M Flow Works

The authentication sequence follows these steps:

1. **Register an M2M application** in the Logto console to receive a `client_id` and `client_secret`.
2. **Request a token** by calling the `/oidc/token` endpoint with `grant_type=client_credentials`.
3. **Validate credentials** – the OIDC provider verifies the secrets and issues an access token (JWT) with `expires_in`.
4. **Cache the token** – the `ClientCredentials` class stores the token with its expiry timestamp.
5. **Automatic refresh** – subsequent calls to `getAccessToken()` return cached tokens if valid, or request fresh tokens when expiry approaches.
6. **Authorize requests** – include the token in the `Authorization: Bearer <token>` header when calling protected endpoints.

Because tokens are cached **in-process**, the SDK avoids unnecessary network round-trips while using a configurable `accessTokenExpiryLeeway` (default 60 seconds) to prevent race conditions in high-throughput services.

## Implementing the Client Credentials SDK

### Basic Usage

Create a `ClientCredentials` instance with your application credentials and token endpoint:

```typescript
import { ClientCredentials } from '@logto/api/client-credentials';

const logto = new ClientCredentials({
  clientId: 'YOUR_CLIENT_ID',
  clientSecret: 'YOUR_CLIENT_SECRET',
  tokenEndpoint: 'https://logto.example.com/oidc/token',
  tokenParams: { audience: 'https://api.logto.io' },
});

async function callManagementApi() {
  const { value: accessToken } = await logto.getAccessToken();

  const response = await fetch('https://logto.example.com/api/users', {
    headers: {
      Authorization: `Bearer ${accessToken}`,
      'Content-Type': 'application/json',
    },
  });

  const users = await response.json();
  console.log('User list:', users);
}

callManagementApi().catch(console.error);

```

The `getAccessToken()` method automatically refreshes the token when it approaches expiration, eliminating manual token management.

### Custom Leeway and Error Handling

Configure custom expiry leeway and handle specific error types:

```typescript
const logto = new ClientCredentials({
  clientId: 'id',
  clientSecret: 'secret',
  tokenEndpoint: 'https://logto.example.com/oidc/token',
  accessTokenExpiryLeeway: 120, // refresh 2 minutes before expiry
});

try {
  const token = await logto.getAccessToken();
  // use token…
} catch (err) {
  if (err instanceof ClientCredentialsError) {
    console.error('Failed to obtain M2M token:', err.message);
  }
}

```

The `ClientCredentialsError` class provides clear error messages when the token endpoint returns non-2xx status codes or unexpected payloads.

### Integration with Management API

Use the built-in Management client with your credentials provider:

```typescript
import { Management } from '@logto/api/management';

const credentials = new ClientCredentials({
  clientId: process.env.LOGTO_CLIENT_ID!,
  clientSecret: process.env.LOGTO_CLIENT_SECRET!,
  tokenEndpoint: 'https://logto.example.com/oidc/token',
});

const management = new Management({
  accessTokenProvider: () => credentials.getAccessToken(),
});

async function listApplications() {
  const apps = await management.applications.getAll();
  console.log(apps);
}

```

This pattern delegates token management to the `ClientCredentials` class while the Management client focuses on API operations.

## Security Considerations

When implementing M2M authentication with Logto, observe these security practices:

- **Scope restriction** – Only scopes granted to the M2M client during registration are encoded in the JWT payload. Limit scopes to the minimum required for the service's function.
- **Clock skew protection** – The default 60-second leeway handles clock drift between client and server. Increase this value in `accessTokenExpiryLeeway` if your infrastructure experiences significant time synchronization issues.
- **Secret storage** – Store `client_secret` values in environment variables or secure secret managers, never in source code.
- **Token lifecycle monitoring** – Review logs emitted by the event listeners in [`packages/core/src/event-listeners/index.ts`](https://github.com/logto-io/logto/blob/main/packages/core/src/event-listeners/index.ts) to audit token usage patterns and detect anomalous access.

## Summary

- Logto implements M2M authentication via the OAuth 2.0 client credentials grant in [`packages/core/src/oidc/grants/client-credentials.ts`](https://github.com/logto-io/logto/blob/main/packages/core/src/oidc/grants/client-credentials.ts).
- The `ClientCredentials` class in [`packages/api/src/client-credentials.ts`](https://github.com/logto-io/logto/blob/main/packages/api/src/client-credentials.ts) handles token caching, automatic refresh, and expiry management with configurable leeway.
- Tokens are obtained from the `/oidc/token` endpoint and must include appropriate scopes (such as `management_api`) to access protected resources.
- The SDK provides `ClientCredentialsError` for robust error handling when token requests fail.
- Event listeners in the core package enable comprehensive audit logging of token lifecycle events.

## Frequently Asked Questions

### What is the default token expiry leeway in Logto?

The default `accessTokenExpiryLeeway` is **60 seconds**. This means the `ClientCredentials` class considers a token expired 60 seconds before its actual expiry timestamp, preventing race conditions where a token might expire in transit during high-latency requests.

### How does the Logto SDK handle token refresh?

The `ClientCredentials` class automatically requests a new token when `getAccessToken()` is called and the cached token is near expiry (within the configured leeway window). This happens transparently without throwing errors, ensuring continuous service availability while minimizing unnecessary token endpoint calls through in-process caching.

### What scopes are required to access the Logto Management API?

The Management API requires tokens carrying the `management_api` scope (or specific application-defined scopes depending on your resource configuration). These scopes must be granted to the M2M client during application registration in the Logto console; the token endpoint will only issue tokens containing scopes explicitly assigned to the requesting client.

### Where is the client credentials grant implemented server-side?

The server-side implementation resides in [`packages/core/src/oidc/grants/client-credentials.ts`](https://github.com/logto-io/logto/blob/main/packages/core/src/oidc/grants/client-credentials.ts). This file extends the standard OIDC provider to support the `client_credentials` grant type, validates client credentials against the Logto database, and issues signed JWT access tokens with optional organization-specific claims.