机器对机器:使用 Logto 进行认证 (Auth)
本指南假设你已经在管理控制台中创建了一个类型为“Machine-to-machine”的应用程序。
介绍
机器对机器 (M2M) 是一种常见的认证 (Authentication) 方式,如果你有一个应用程序(而不是用户)需要直接与资源通信(通常,使用 M2M 应用程序不需要用户交互,因此没有 UI),例如,一个更新 Logto 中用户自定义数据的 API 服务,一个提取每日订单的统计服务等。
由于 Logto 使用基于角色的访问控制 (RBAC) 作为其访问控制策略,因此为 M2M 应用程序分配 M2M 角色 (Role) 是保护需要直接服务通信的 API 的必要措施。
要了解我们当前的基于角色的访问控制 (RBAC) 以及用户角色 (Role) 和 M2M 角色的区别,请参阅 配置角色 以了解更多信息。
在 Logto 中使用机器对机器应用程序有两个常见的用例:
- 访问 Logto Management API:在这种情况下,你需要为 M2M 应用程序分配一个包含内置 Logto Management API 的
all
权限的 M2M 角色 (Role)。 - 访问你的 API 资源:在这种情况下,你需要为 M2M 应用程序分配包含你的 API 资源权限的 M2M 角色 (Role)。
在创建 M2M 应用的过程中,你将被引导到一个页面,在那里你可以为你的应用分配 M2M 角色 (Roles):
或者,当你已经创建了一个 M2M 应用时,你也可以在 M2M 应用详情页面上分配这些角色 (Roles):
现在,让我们逐步完成整个过程。为了清晰起见,我们将分开访问 Logto Management API 和其他 API 资源的步骤。我们假设你已经在 Logto 中创建了一个 M2M 应用程序。
获取访问令牌
关于访问令牌请求的基础知识
M2M 应用通过向令牌请求端点发送 POST
请求来获取访问令牌 (Access token),在 HTTP 请求实体主体中使用 application/x-www-form-urlencoded
格式添加以下参数:
- grant_type:必须设置为
client_credentials
- resource:你想要 访问的资源
- scope:访问请求的权限 (Scope)
你还需要在请求头中包含 M2M 应用的凭据,以便令牌请求端点认证 (Authentication) 你的 M2M 应用。
这是通过在请求的 Authorization
头中以 基本认证 (Basic authentication) 形式包含应用的凭据来实现的,其中用户名是 App ID,密码是 App Secret。
你可以在 M2M 应用的详细信息页面找到 App ID 和 App Secret:
访问令牌请求的示例如下:
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
请求访问令牌
在以下演示中,将 https://your.logto.endpoint
替换为你所针对的 Logto 端点。对于 Logto Cloud,它将是 https://{your-tenant-id}.logto.app
。
- For Logto Management API
- For your API resource
Logto 提供了一个内置的 “日志管理 API (Logto Management API)” 资源,它是一个只读资源,具有访问日志管理 API (Logto Management API) 的 all
权限,你可以在你的 API 资源列表中看到它。资源 API 指示器的模式是 https://{your-tenant-id}.logto.app/api
,这将是你在访问令牌请求正文中使用的资源值。
在访问日志管理 API (Logto Management API) 之前,确保你的 M2M 应用已被分配了包含此内置 “日志管理 API (Logto Management API)” 资源的 all
权限的 M2M 角色。
Logto 还为新创建的租户提供了一个预配置的 “日志管理 API (Logto Management API) 访问” M2M 角色,该角色已经分配了日志管理 API (Logto Management API) 资源的所有权限。你可以直接使用它,而无需手动设置权限。这个预配置的角色也可以根据需要进行编辑和删除。
现在,组合我们所有的内容并发送请求:
- Node.js
- cURL
const logtoEndpoint = 'https://your.logto.endpoint'; // 用你的 Logto 端点替换
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'
记得用你自己的实际值替换。
对于日志云 (Logto Cloud) 用户:当你与日志管理 API (Logto Management API) 交互时,不能使用自定义域名,请使用默认的 Logto 端点 https://{your_tenant_id}.logto.app/oidc/token
来授予访问令牌。
在你的 API 资源列表中,找到应用程序需要访问的 API 标识符。如果你还没有在 Logto 中添加 API 资源或不知道什么是 API 资源,请参阅 API 资源。
假设我们在这个“在线购物” API 资源下有 read:products
和 write:products
权限。
在访问你的 API 资源之前,确保你的 M2M 应用程序已被分配包含你的 API 资源权限的 M2M 角色 (Role)。
现在,组合我们所有的内容并发送请求:
- 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": "eyJhbG...2g", // 使用此令牌访问 Logto Management API
"expires_in": 3600, // 令牌过期时间(秒)
"token_type": "Bearer", // 使用访问令牌时请求的认证 (Auth) 类型
"scope": "all" // Logto Management API 的权限 `all`
}
Logto 目前不支持 M2M 应用表示用户。访问令牌 (Access token) 负载中的 sub
将是应用 ID。
使用访问令牌访问资源
你可能会注意到令牌响应中有一个 token_type
字段,它固定为 Bearer
。
因此,当你与 API 资源 (API resource) 服务器交互时,应该将访问令牌 (access token) 以 Bearer 格式(Bearer YOUR_TOKEN
)放在 HTTP 头的 Authorization
字段中。
- 与 Logto Management API 交互
- 与你的 API 资源交互
使用请求的 访问令牌 (Access token) 和内置的 Logto Management API 资源 https://[your-tenant-id].logto.app/api
来获取 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'
记得用你自己的实际值替换。Bearer
后的值应该是你收到的访问令牌 (JWT)。
使用请求的访问令牌与 API 资源 https://shopping.api
交互以获取购物 API 中的所有产品:
- Node.js
- cURL
const apiEndpoint = 'https://your.api.endpoint';
const accessToken = 'eyJhb...2g'; // 访问令牌
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 # 访问令牌
认证 (Authentication)
如果你正在保护 Logto Management API 以外的 API 资源,请记得为你的资源实现认证 (Authentication)。请参阅 保护你的 API 了解详细信息。