import Tabs from '@/mdx-components/Tabs';
import TabItem from '@/mdx-components/TabItem';
import InlineNotification from '@/ds-components/InlineNotification';
import Steps from '@/mdx-components/Steps';
import Step from '@/mdx-components/Step';
import ApplicationCredentials from '@/mdx-components/ApplicationCredentials';
import AppIdentifierSrc from './assets/api-identifier.png';
import AssignM2mRolesModalSrc from './assets/assign-m2m-roles-modal.png';
import AssignM2mRolesPageSrc from './assets/assign-m2m-roles-page.png';
import LogtoManagementApiSrc from './assets/logto-management-api.png';

<Steps>
<Step title="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.

There are two common use cases of using machine-to-machine apps in Logto:

1. **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.
2. **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 machine-to-machine (M2M) roles to your applications:

<img alt="Assign M2M roles modal" src={AssignM2mRolesModalSrc} width="600px" style={{ borderRadius: '6px' }} />

Or you can also assign these roles on the M2M app detail page.

<img alt="M2M app details page" src={AssignM2mRolesPageSrc} width="600px" style={{ borderRadius: '6px' }} />

</Step>
<Step title="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](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Authorization#basic_authentication) form in the request `Authorization` header, where username is the App ID, and password is the App Secret.

The App ID and App Secret are as follows, and you can also find them in your M2M app details page:

<ApplicationCredentials />

And the authentication string can be generated by encoding the app ID and app secret to Base64 format, with the fake code below:

<Code className="language-js">
{`base64_encode("${props.app.id}:${props.showAppSecret ? props.secrets[0].value : '*'.repeat(props.secrets[0].value.length)}")
// Result: ${props.showAppSecret ? btoa(`${props.app.id}:${props.secrets[0].value}`) : '*'.repeat(btoa(`${props.app.id}:${props.secrets[0].value}`).length)}`}
</Code>

An example of fetching the M2M access token request:

<Code className="language-bash">
{`POST /oidc/token HTTP/1.1
Host: ${props.endpoint}
Authorization: Basic ${props.showAppSecret ? btoa(`${props.app.id}:${props.secrets[0].value}`) : '*'.repeat(btoa(`${props.app.id}:${props.secrets[0].value}`).length)}
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials
resource=https://${props.app.tenantId}.logto.app/api
scope=all`}
</Code>

</Step>
<Step title="Request an access token">

<Tabs>

<TabItem value="Logto Management API" label="For Logto Management API">

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.

<img alt="Logto Management API details" src={LogtoManagementApiSrc} width="600px" style={{ borderRadius: '6px' }}/>

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.

<InlineNotification>
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.
</InlineNotification>

Now, compose all we have and send the request:

<Tabs>
<TabItem value="Node.js" label="Node.js">

<Code className="language-js">
    {`const logtoEndpoint = '${props.endpoint}';
const tokenEndpoint = '${new URL('/oidc/token', props.endpoint).href}';
const appId = '${props.app.id}';
const appSecret = '${props.showAppSecret ? props.secrets[0].value : '*'.repeat(props.secrets[0].value.length)}';

const fetchAccessToken = async () => {
  return await fetch(tokenEndpoint, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded',
      Authorization: \`Basic \${Buffer.from(\`\${appId}:\${appSecret}\`).toString('base64')}\`,
      )}\`,
    },
    body: new URLSearchParams({
      grant_type: 'client_credentials',
      resource: 'https://${props.app.tenantId}.logto.app/api',
      scope: 'all',
    }).toString(),
  });
};`}
</Code>

</TabItem>

<TabItem value="cURL" label="cURL">

<Code className="language-bash">
{`curl --location \\
  --request POST '${new URL('/oidc/token', props.endpoint).href}' \\
  --header 'Authorization: Basic ${props.showAppSecret ? btoa(`${props.app.id}:${props.secrets[0].value}`) : '*'.repeat(btoa(`${props.app.id}:${props.secrets[0].value}`).length)}' \\
  --header 'Content-Type: application/x-www-form-urlencoded' \\
  --data-urlencode 'grant_type=client_credentials' \\
  --data-urlencode 'resource=https://${props.app.tenantId}.logto.app/api' \\
  --data-urlencode 'scope=all'`}
</Code>

</TabItem>

</Tabs>

### Access token response

A successful access token response body would be like:

```json
{
  "access_token": "<granted-access-token>", // E.g. eyJhb...2g
  "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
}
```

<InlineNotification>
Logto does not currently support the M2M app to represent a user. The `sub` in the access token payload will be the App ID.
</InlineNotification>

</TabItem>

<TabItem value="Your API resource" label="For your API resource">

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](/docs/references/resources).

<img alt="API identifier" src={AppIdentifierSrc} width="600px" style={{ borderRadius: '6px' }} />

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:

<Tabs>

<TabItem value="Node.js" label="Node.js">

<Code className="language-js">
{`const logtoEndpoint = '${props.endpoint}';
const tokenEndpoint = '${new URL('/oidc/token', props.endpoint).href}';
const appId = '${props.app.id}';
const appSecret = '${props.showAppSecret ? props.secrets[0].value : '*'.repeat(props.secrets[0].value.length)}';

const fetchAccessToken = async () => {
  return await fetch(tokenEndpoint, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded',
      Authorization: \`Basic \${Buffer.from(\`\${appId}:\${appSecret}\`).toString('base64')}\`,
    },
    body: new URLSearchParams({
      grant_type: 'client_credentials',
      resource: 'https://shopping.api',
      scope: 'read:products write:products',
    }).toString(),
  });
};`}
</Code>

</TabItem>

<TabItem value="cURL" label="cURL">

<Code className="language-bash">
{`curl --location \\
  --request POST '${new URL('/oidc/token', props.endpoint).href}' \\
  --header 'Authorization: Basic ${props.showAppSecret ? btoa(`${props.app.id}:${props.secrets[0].value}`) : '*'.repeat(btoa(`${props.app.id}:${props.secrets[0].value}`).length)}' \\
  --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'`}
</Code>

</TabItem>

</Tabs>

### Access token response

A successful access token response body would be like:

```json
{
  "access_token": "<granted-access-token>", // Use this token to access the API resource
  "expires_in": 3600, // Token expiration in seconds
  "token_type": "Bearer", // Auth type for your request when using the access token
  "scope": "read:products write:products" // scopes for the access token
}
```

</TabItem>
  
</Tabs>

</Step>
<Step title="Access API 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-access-token>`).

<Tabs>
<TabItem value="Logto Management API" label="Interact with Logto Management API">

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 your Logto tenant:

<Tabs>
<TabItem value="Node.js" label="Node.js">

<Code className="language-js">
{`const logtoEndpoint = '${props.endpoint}';
const accessToken = 'eyJhb...2g'; // Your JWT access token

const fetchLogtoApplications = async () => {
  return await fetch('${new URL('/api/applications', props.endpoint).href}', {
    method: 'GET',
    headers: {
      Authorization: \`Bearer \${accessToken}\`,
    },
  });
};`}
</Code>

</TabItem>

<TabItem value="cURL" label="cURL">

<Code className="language-bash">
{`curl --location \\
  --request GET '${new URL('/api/applications', props.endpoint).href}' \\
  --header 'Authorization: Bearer eyJhbG...2g' # Access Token`}
</Code>

</TabItem>

</Tabs>

</TabItem>

<TabItem value="Your API resource" label="Interact with your API resource">

Using the requested access token with the API resource `https://shopping.api` to get all products in the shopping API:

<Tabs>
<TabItem value="Node.js" label="Node.js">

```js
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}`,
    },
  });
};
```

</TabItem>

<TabItem value="cURL" label="cURL">

```bash
curl --location \
  --request GET 'https://your.api.endpoint/products' \
  --header 'Authorization: Bearer eyJhbG...2 # Access token
```

</TabItem>
</Tabs>
</TabItem>
</Tabs>

</Step>
</Steps>
