Machine-to-machine: Auth with Logto
This guide assumes you have created an Application of type "Machine-to-machine" in Admin Console.
Intro
Machine-to-machine (M2M) is a common practice to authenticate if you have an app (not user) that needs to directly talk to resources (usually, using M2M app doesn't need user interactions, so it has no UI). E.g., an API service that updates users' custom data in Logto, a statistic service that pulls daily orders, etc.
Since Logto uses RBAC as its access control policy, assigning M2M roles to M2M apps is necessary for protecting your API which needs direct service talk.
To learn our current RBAC and the difference between user role and M2M role, see Configure roles to learn more.
There are two common use cases of using machine-to-machine apps in Logto:
- Accessing Logto Management API: In this case, you need to assign a M2M role that include the
all
permission from the built-in Logto Management API to your M2M app. - Accessing your API resource: In this case, you need to assign M2M roles that include permissions from your API resources to your M2M app.
During the M2M app creation process, you’ll be directed to a page where you can assign M2M roles to your applications:
Or you can also assign these roles on the M2M app detail page when you already have an M2M app created:
Now, let’s walk through the end-to-end process. For clarity, we will separate the steps for accessing Logto Management API and other API resources. And we assume you have already created an M2M app in Logto.
Fetch an access token
Basics about access token request
M2M app makes a POST
request to the token endpoint to fetch an access token by adding the following parameters using the application/x-www-form-urlencoded
format in the HTTP request entity-body:
- grant_type: Must be set to
client_credentials
- resource: The resource you want to access
- scope: The scope of the access request
And you also need to include your M2M app's credentials in the request header for the token endpoint to authenticate your M2M app.
This is achieved by including the app's credentials in the Basic authentication form in the request Authorization
header, where username is the App ID, and password is the App Secret.
You can find the App ID and App Secret from your M2M app's details page:
An example of the access token request is:
POST /oidc/token HTTP/1.1
Host: your.logto.endpoint
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials
&resource=https://shopping.api
&scope=read:products write:products
Request an access token
In the following demonstration, replace https://your.logto.endpoint
with the Logto endpoint you are targeting. For Logto Cloud, it will be https://{your-tenant-id}.logto.app
.
- For Logto Management API
- For your API resource
Logto provides a built-in “Logto Management API” resource, it’s a readonly resource with the all
permission to access Logto Management API, you can see it from your API resource list.
The resource API indicator is in the pattern of https://{your-tenant-id}.logto.app/api
, and this will be your resource value used in the access token request body.
Before accessing Logto Management API, make sure your M2M app has been assigned with M2M roles that include the all
permission from this built-in “Logto Management API” resource.
Logto also provides a pre-configured “Logto Management API access” M2M role for new created tenants, which the Logto Management API resource’s all permission has already assigned to. You can use it directly without manually setting permissions. This pre-configured role can also be edited and deleted as needed.
Now, compose all we have and send the request:
- Node.js
- cURL
const logtoEndpoint = 'https://your.logto.endpoint'; // Replace with your Logto endpoint
const tokenEndpoint = `${logtoEndpoint}/oidc/token`;
const applicationId = 'your-application-id';
const applicationSecret = 'your-application-secret';
const tenantId = 'your-tenant-id';
const fetchAccessToken = async () => {
return await fetch(tokenEndpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
Authorization: `Basic ${Buffer.from(`${applicationId}:${applicationSecret}`).toString(
'base64'
)}`,
},
body: new URLSearchParams({
grant_type: 'client_credentials',
resource: `https://${tenantId}.logto.app/api`,
scope: 'all',
}).toString(),
});
};
curl --location \
--request POST 'https://your.logto.endpoint' \
--header 'Authorization: Basic ${your_auth_string}' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=client_credentials' \
--data-urlencode 'resource=https://${tenantId}.logto.app/api' \
--data-urlencode 'scope=all'
Remember to replace the actual values with your own.
For Logto Cloud users: when you’re interacting with Logto Management API, you can not use custom domain, use the default Logto endpoint https://{your_tenant_id}.logto.app/oidc/token
to grant access tokens.
In your API Resource list, find the API identifier that the app needs to access. If you haven't added the API Resource in Logto or don't know what API Resource is, see API Resource.
Assume that we have read:products
and write:products
permissions under this “Online Shopping” API resource.
Before accessing your API resource, make sure your M2M app has been assigned with M2M roles that include permissions from your API resource.
Now, compose all we have and send the request:
- Node.js
- cURL
const logtoEndpoint = 'https://your.logto.endpoint';
const tokenEndpoint = `${logtoEndpoint}/oidc/token`;
const applicationId = 'your-application-id';
const applicationSecret = 'your-application-secret';
const fetchAccessToken = async () => {
return await fetch(tokenEndpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
Authorization: `Basic ${Buffer.from(`${applicationId}:${applicationSecret}`).toString(
'base64'
)}`,
},
body: new URLSearchParams({
grant_type: 'client_credentials',
resource: 'https://shopping.api',
scope: 'read:products write:products',
}).toString(),
});
};
curl --location \
--request POST 'https://your.logto.endpoint/oidc/token' \
--header 'Authorization: Basic ${your_auth_string}' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=client_credentials' \
--data-urlencode 'resource=https://shopping.api' \
--data-urlencode 'scope=read:products write:products'
Access token response
A successful access response body would be like:
{
"access_token": "eyJhbG...2g", // Use this token for accessing the Logto Management API
"expires_in": 3600, // Token expiration in seconds
"token_type": "Bearer", // Auth type for your request when using the access token
"scope": "all" // scope `all` for Logto Management API
}
Logto does not currently support the M2M app to represent a user. The sub
in the access token payload will be the App ID.
Access resource using access token
You may notice the token response has a token_type
field, which it's fixed to Bearer
.
Thus you should put the access token in the Authorization
field of HTTP headers with the Bearer format (Bearer YOUR_TOKEN
) when you're interacting with your API resource server.
- Interact with Logto Management API
- Interact with your API resource
Using the requested access token with the built-in Logto Management API resource https://[your-tenant-id].logto.app/api
to get all applications in Logto:
- Node.js
- cURL
const logtoEndpoint = 'https://your.logto.endpoint'; // Replace with your Logto endpoint
const accessToken = 'eyJhb...2g'; // Access Token
const fetchLogtoApplications = async () => {
return await fetch(`${logtoEndpoint}/api/applications`, {
method: 'GET',
headers: {
Authorization: `Bearer ${accessToken}`,
},
});
};
curl --location \
--request GET 'https://your.logto.endpoint/api/applications' \
--header 'Authorization: Bearer eyJhbG...2g'
Remember to replace the actual values with your own. The value after Bearer
should be the access token (JWT) you received.
Using the requested access token with the API resource https://shopping.api
to get all products in the shopping API:
- Node.js
- cURL
const apiEndpoint = 'https://your.api.endpoint';
const accessToken = 'eyJhb...2g'; // Access Token
const fetchProducts = async () => {
return await fetch(`${apiEndpoint}/products`, {
method: 'GET',
headers: {
Authorization: `Bearer ${accessToken}`,
},
});
};
curl --location \
--request GET 'https://your.api.endpoint/products' \
--header 'Authorization: Bearer eyJhbG...2 # Access Token
Authentication
If you are protecting your own API Resources other than Logto Management API, remember to implement the authentication for your resource. See Protect your API for details.