保护组织资源
除了将 API 作为资源的 保护你的 API 外,组织也可以作为资源,并以相同的方式保护你的组织资源。在本文中,我们将重点介绍如何以类似的方式保护你的组织资源。
步骤 1:从 OIDC 流程获取组织 ID
Logto 扩展了标准的 OpenID Connect 协议,允许你的应用从用户那里获取组织信息。有两种方法可以做到这一点:
- 如果你使用支持组织的 Logto SDK,可以将
urn:logto:scope:organizations权限添加到配置对象的scopes参数中。通常,SDK 会为此权限提供一个枚举,例如 Logto JS SDKs 中的UserScope.Organizations。
- JavaScript
- React
- Python
- PHP
- Swift
- Others
import { LogtoClient, UserScope } from '@logto/browser'; // 或 @logto/node, @logto/client
const logto = new LogtoClient({
// ...
scopes: [UserScope.Organizations],
});
import { LogtoProvider, UserScope } from '@logto/react';
const App = () => (
<LogtoProvider
config={{
// ...
scopes: [UserScope.Organizations],
}}
>
{/* ... */}
</LogtoProvider>
);
from logto import UserInfoScope
client = LogtoClient(
LogtoConfig(
# ...
scopes=[UserInfoScope.organizations],
)
)
use Logto\Sdk\Constants\UserScope;
$client = new LogtoClient(
new LogtoConfig(
// ...
scopes: [UserScope::organizations],
)
);
import Logto
import LogtoClient
let config = try? LogtoConfig(
// ...
scopes: [
UserScope.organizations.rawValue,
],
// ...
)
let client = LogtoClient(useConfig: config)
const config = {
// ...
scope: 'openid offline_access urn:logto:scope:organizations',
};
- 对于其他情况,你需要将
urn:logto:scope:organizations权限添加到 SDK 配置(或认证请求)的scope参数中。
一旦用户完成认证流程,你可以从 idToken 中获取组织信息:
// 以 JavaScript 为例
const idToken = await logto.getIdTokenClaims();
console.log(idToken.organizations); // 一个包含组织 ID 的字符串数组
organizations 字段(声明)也会包含在 UserInfo 端点 的响应中。
可选:获取组织角色
如果你还没有设置组织角色,请参考 此部分。
要获取当前用户的所有组织角色:
- 如果你使用支持组织的 Logto SDK,可以将
urn:logto:scope:organization_roles权限添加到配置对象的scopes参数中。通常,SDK 会为此权限提供一个枚举,例如 Logto JS SDKs 中的UserScope.OrganizationRoles。 - 对于其他情况,你需要将
urn:logto:scope:organization_roles权限添加到 SDK 配置(或认证请求)的scope参数中。
然后你可以从 idToken 中获取组织角色:
// 以 JavaScript 为例
const idToken = await logto.getIdTokenClaims();
console.log(idToken.organization_roles); // 一个包含组织角色的字符串数组
数组中的每个字符串格式为 organization_id:role_id,例如 org_123:admin 表示用户在 ID 为 org_123 的组织中具有 admin 角色。
organization_roles 字段(声明)也会包含在 UserInfo 端点 的响应中。
步骤 2:获取组织令牌
要在组织的上下文中执行操作,用户需要为该组织授予访问令牌(组织令牌)。组织令牌是一个 JWT 令牌,包含组织 ID 和用户在组织中的权限(权限)。
向认证请求添加参数
- 如果你使用支持组织的 Logto SDK,可以将
urn:logto:scope:organizations权限添加到配置对象的scopes参数中,与 获取当前用户的组织 ID 相同。- 支持组织的 Logto SDK 将自动处理其余的配置。
- 对于其他情况,你需要将
offline_access和urn:logto:scope:organizations权限添加到scope参数中,并将urn:logto:resource:organizations资源添加到 SDK 配置(或认证请求)的resource参数中。- 注意:
offline_access是获取可用于获取组织令牌的refresh_token所必需的。
- 注意:
// 仅适用于其他情况。对于 Logto SDK,请参见上文。
const config = {
// ...
scope: 'openid offline_access urn:logto:scope:organizations',
resource: 'urn:logto:resource:organizations',
};
urn:logto:resource:organizations 资源是一个特殊资源,代表组织模板。
获取组织令牌
Logto 扩展了标准的 refresh_token 授权类型,允许你的应用获取组织令牌。
- 如果你使用支持组织的 Logto SDK,可以调用 SDK 的
getOrganizationToken()方法(或getOrganizationTokenClaims()方法)。 - 对于其他情况,你需要使用以下参数调用令牌端点:
grant_type:refresh_token。client_id: 用户用于认证的应用 ID。refresh_token: 你从认证流程中获得的refresh_token。organization_id: 你想要获取令牌的组织 ID。scope(可选):你想要授予用户在组织中的权限。如果未指定,授权服务器将尝试授予与认证流程相同的权限。
- JavaScript
- React
- Python
- PHP
- Swift
- Others
const token = await logto.getOrganizationToken('<organization-id>');
const App = () => {
const { getOrganizationToken } = useLogto();
const getToken = async () => {
const token = await getOrganizationToken('<organization-id>');
};
return <button onClick={getToken}>Get organization token</button>;
};
token = await client.getOrganizationToken("<organization-id>")
# 或
claims = await client.getOrganizationTokenClaims("<organization-id>")
$token = $client->getOrganizationToken('<organization-id>');
// 或
$claims = $client->getOrganizationTokenClaims('<organization-id>');
let token = try await client.getOrganizationToken(forId: "<organization-id>")
// 以 JavaScript 为例
const params = new URLSearchParams();
params.append('grant_type', 'refresh_token');
params.append('client_id', 'YOUR_CLIENT_ID');
params.append('refresh_token', 'REFRESH_TOKEN');
params.append('organization_id', 'org_123');
const response = await fetch('https://YOUR_LOGTO_ENDPOINT/oidc/token', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: params,
});
响应将与 标准令牌端点 的格式相同,access_token 是 JWT 格式的组织令牌。
除了访问令牌的常规声明外,组织令牌还包含以下声明:
aud: 组织令牌的受众是urn:logto:organization:{organization_id}。scope: 授予用户在组织中的权限,以空格为分隔符。
示例
一个好的示例胜过千言万语。假设我们的组织模板有以下设置:
- 权限:
read:logs,write:logs,read:users,write:users。 - 角色:
admin,member。admin角色拥有所有权限。member角色拥有read:logs和read:users权限。
用户有以下设置:
- 组织 ID:
org_1,org_2。 - 组织角色:
org_1:admin,org_2:member。
在 Logto SDK 配置(或认证请求)中,我们正确设置了其他内容,并添加了以下权限:
urn:logto:scope:organizationsopenidoffline_accessread:logswrite:logs
现在,当用户完成认证流程时,我们可以从 idToken 中获取组织 ID:
// 以 JavaScript 为例
const idToken = await logto.getIdTokenClaims();
console.log(idToken.organizations); // ['org_1', 'org_2']
如果我们想获取组织令牌:
// 以 JavaScript 为例
const org1Token = await logto.getOrganizationTokenClaims('org_1');
const org2Token = await logto.getOrganizationTokenClaims('org_2');
console.log(org1Token.aud); // 'urn:logto:organization:org_1'
console.log(org1Token.scope); // 'read:logs write:logs'
console.log(org2Token.aud); // 'urn:logto:organization:org_2'
console.log(org2Token.scope); // 'read:logs'
const org3Token = await logto.getOrganizationTokenClaims('org_3'); // 错误:用户不是该组织的成员
解释:
- 对于
org_1,用户具有admin角色,因此组织令牌应具有所有可用权限(权限)。 - 对于
org_2,用户具有member角色,因此组织令牌应具有read:logs和read:users权限(权限)。
由于我们在认证流程中仅请求了 read:logs 和 write:logs 权限,因此组织令牌已相应地“降级”,导致请求权限和可用权限的交集。
为机器对机器应用获取组织令牌
与为用户获取组织令牌类似,你也可以为机器对机器应用获取组织令牌。唯一的区别是你需要使用 client_credentials 授权类型,而不是 refresh_token 授权类型。
要了解有关机器对机器应用的更多信息,请参阅 机器对机器:使用 Logto 进行认证。
步骤 3:验证组织令牌
一旦应用获得组织令牌,它可以像常规访问令牌一样使用该令牌,例如在 Authorization 头中以 Bearer {token} 格式调用 API。
在你的 API 中,验证组织令牌的方式与 保护你的 API 非常相似。主要区别:
- 与 API 资源的访问令牌不同,如果用户不是组织的成员,则无法获得组织令牌。
- 组织令牌的受众是
urn:logto:organization:{organization_id}。 - 对于某些权限(权限),你需要通过以空格为分隔符拆分字符串来检查组织令牌的
scope声明。