How to Manage API Resources and Scopes in Logto: A Complete Guide
Logto manages API resources and scopes through a database-backed system where resources are identified by unique indicators (audiences) and scopes define granular permissions, accessible via Management API endpoints or the Admin Console UI.
In the logto-io/logto repository, API resources and scopes form the authorization backbone for protecting external APIs. Each API resource represents a logical entity that client applications can request access to, while scopes define the specific operations permitted within that resource. This guide explains the core implementation based on the source code and provides practical examples for managing these entities.
Understanding API Resources and Scopes in Logto
What is an API Resource?
An API resource is a logical representation of an external API that client applications can invoke. According to packages/schemas/src/types/resource.ts, each resource contains:
- Indicator: A unique URL-like identifier (e.g.,
https://myapp.com/api) that serves as the audience (aud) claim in JWT tokens - Name: A human-readable label for the Admin Console
- Default flag: A boolean marking whether this is the default Management API (only one allowed per tenant)
- Permissions: An array of scopes defining available operations
What are Scopes (Permissions)?
A scope (also called a permission) belongs to a single API resource and describes a granular operation. Defined in packages/schemas/src/types/scope.ts, scopes are stored within the resource's permissions array and exposed through the Management API. Common examples include read:user or write:orders.
Creating and Configuring API Resources
Define the Resource Indicator
When creating a resource, you must specify a unique indicator that matches the aud claim expected by your API. The indicator acts as the audience identifier during token validation.
Add Scopes to the Resource
Scopes are attached to resources and validated during token issuance. Each scope requires a name (no spaces) and optional description.
Create an API Resource via Management API
The packages/core/src/routes/api-resource.ts file implements the CRUD endpoints. Below is a TypeScript example for creating a resource:
import fetch from 'node-fetch';
const LOGTO_ADMIN_URL = 'https://logto.example.com/api';
const ACCESS_TOKEN = 'YOUR_MANAGEMENT_API_TOKEN';
async function createApiResource() {
const response = await fetch(`${LOGTO_ADMIN_URL}/api-resources`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${ACCESS_TOKEN}`,
},
body: JSON.stringify({
name: 'My Awesome API',
indicator: 'https://my-awesome-api.com/api',
default: false,
}),
});
const data = await response.json();
console.log('Created API resource →', data);
}
createApiResource();
Add Scopes to an Existing Resource
To add permissions to a resource, POST to the resource-specific permissions endpoint:
async function addScope(resourceId: string) {
const response = await fetch(
`${LOGTO_ADMIN_URL}/api-resources/${resourceId}/permissions`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${ACCESS_TOKEN}`,
},
body: JSON.stringify({
name: 'read:orders',
description: 'Read order information',
}),
}
);
const permission = await response.json();
console.log('Added permission →', permission);
}
Managing Resources Programmatically
Retrieve and Update Resources
The Management API supports full CRUD operations as implemented in packages/core/src/routes/api-resource.ts:
- GET
/api-resources– List all resources with their scopes - PATCH
/api-resources/:id– Update resource name or indicator - DELETE
/api-resources/:id– Remove resource and associated scopes
Example deletion:
async function deleteApiResource(resourceId: string) {
await fetch(`${LOGTO_ADMIN_URL}/api-resources/${resourceId}`, {
method: 'DELETE',
headers: {
Authorization: `Bearer ${ACCESS_TOKEN}`,
},
});
console.log(`API resource ${resourceId} deleted`);
}
Admin Console Implementation
The React-based Admin Console provides UI components for these operations in packages/console/src/pages/api-resources/*, offering a visual interface for creating resources and managing their scopes without writing code.
Token Issuance and Verification
When clients request tokens, Logto validates the resource against stored indicators. The packages/core/src/services/token-service.ts file handles:
- Resource validation: Checking the
resourceparameter against theresourcestable - Scope filtering: Ensuring requested scopes exist in the resource's permissions array
- Audience assignment: Setting the token's
audclaim to the resource indicator
Clients request tokens by specifying the resource indicator:
import { LogtoClient } from '@logto/client';
const logto = new LogtoClient({
endpoint: 'https://logto.example.com',
resource: 'https://my-awesome-api.com/api',
});
async function getToken() {
const token = await logto.getAccessToken();
console.log('Access token for resource →', token);
}
The Default Management API
Logto automatically creates a default Management API resource during initialization via packages/schemas/src/seeds/management-api.ts. This special resource:
- Uses an indicator generated by
getManagementApiResourceIndicatorinpackages/schemas/src/utils/management-api.ts - Is automatically granted to machine-to-machine (M2M) clients
- Powers the Admin Console and tunnel CLI functionality
The database alteration in packages/schemas/alterations/1.9.2-1695198741-remove-m2m-app-admin-access-switch.ts enforces that only one resource can be marked as default, preventing configuration conflicts.
Summary
- API resources in Logto are defined by unique indicators (audiences) stored in the
resourcestable, implemented inpackages/schemas/src/types/resource.ts - Scopes (permissions) are resource-specific operations defined in
packages/schemas/src/types/scope.tsand managed via thePOST /api-resources/:id/permissionsendpoint - Management API endpoints in
packages/core/src/routes/api-resource.tsprovide programmatic CRUD operations for both resources and scopes - Token validation occurs in
packages/core/src/services/token-service.ts, which verifies resource indicators and filters scopes during JWT issuance - Default Management API is automatically seeded and restricted to one per tenant through database constraints
Frequently Asked Questions
What is the difference between an API resource indicator and a scope in Logto?
The indicator is a unique URL-like string (e.g., https://api.example.com) that identifies the API resource itself and becomes the aud claim in access tokens. A scope is a granular permission string (e.g., read:users) that defines specific operations allowed on that resource. While the indicator identifies what API is being accessed, scopes define how the client can interact with it.
How do I obtain a Management API token to manage API resources programmatically?
Create a machine-to-machine (M2M) application in the Logto Admin Console and grant it the appropriate Management API scopes (such as api:read and api:create). Then use the OIDC token endpoint with the Management API indicator—generated by getManagementApiResourceIndicator in packages/schemas/src/utils/management-api.ts—to request an access token. This token authorizes requests to the /api-resources endpoints.
Can I have multiple default API resources in Logto?
No. Logto enforces a unique constraint allowing only one default API resource per tenant. This restriction is implemented in the database alteration file packages/schemas/alterations/1.9.2-1695198741-remove-m2m-app-admin-access-switch.ts. The default resource is reserved for the Logto Management API, which is automatically seeded during installation via packages/schemas/src/seeds/management-api.ts.
How does Logto validate the resource parameter during token issuance?
When a client requests a token with a specific resource parameter, the token-service.ts in packages/core/src/services/ validates that the indicator exists in the resources table. It then filters the requested scopes against the permissions stored in that resource's permissions array, ensuring only authorized scopes are included in the final JWT's scope claim.
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 →