跳到主要内容

创建自定义访问令牌 (Access token) 脚本

添加自定义声明 (Claims)访问令牌 (Access token),你需要提供一个返回包含这些声明 (Claims) 的对象的脚本。该脚本应编写为一个返回自定义声明 (Claims) 对象的 JavaScript 函数。

  1. 进入 控制台 > 自定义 JWT

  2. 你可以为两种不同类型的访问令牌 (Access token) 自定义访问令牌 (Access token) 声明 (Claims):

    • 用户访问令牌 (User access token):为终端用户签发的访问令牌 (Access token)。例如,Web 应用或移动应用。
    • 机器对机器 (Machine-to-Machine) 访问令牌 (Access token):为服务或应用程序签发的访问令牌 (Access token)。例如,机器对机器应用

    不同类型的访问令牌 (Access token) 可能具有不同的令牌负载上下文。你可以分别为每种类型的访问令牌 (Access token) 自定义令牌声明 (Claims)。

    选择你想自定义令牌声明 (Claims) 的访问令牌 (Access token) 类型,并点击 添加自定义声明 (Claims) 按钮创建新脚本。

备注:

自定义令牌声明 (Claims) 功能仅对以下用户开放:

实现 getCustomJwtClaims() 函数

自定义 JWT 详情页,你可以找到脚本编辑器来编写你的自定义令牌声明 (Claims) 脚本。脚本应为一个返回自定义声明 (Claims) 对象的 JavaScript 函数。

自定义令牌声明 (Claims) 详情页

步骤 1:编辑脚本

使用左侧的代码编辑器修改脚本。系统为你提供了一个默认返回空对象的 getCustomJwtClaims,你可以在此基础上修改函数以返回你自己的自定义声明 (Claims) 对象。

const getCustomJwtClaims = async ({ token, context, environmentVariables }) => {
return {};
};

该编辑器使用 JavaScript 语言服务器,提供基础语法高亮、代码补全和错误检查。输入参数类型良好,并采用 jsDoc 风格文档。你可以利用编辑器的 IntelliSense 正确访问输入对象的属性。你可以在页面右侧找到详细的参数定义。

备注:

该函数将作为模块导出。请确保函数名保持为 getCustomJwtClaims,以便模块能正确导出该函数。

步骤 2:输入参数

getCustomJwtClaims 函数接收一个对象作为输入参数。该输入对象包含以下属性:

token

令牌负载对象。此对象包含原始令牌声明 (Claims) 和你可能需要在脚本中访问的元数据。

你可以在页面右侧找到令牌负载对象和用户数据对象的详细类型定义。编辑器的 IntelliSense 也会帮助你正确访问输入对象的这些属性。

  • 用户访问令牌 (User access token) 数据对象
    属性描述类型
    jti唯一 JWT idstring
    aud令牌的受众 (Audience)string
    scope令牌的权限 (Scopes)string
    clientId令牌的客户端 idstring
    accountId令牌的用户 idstring
    expiresWithSession令牌是否随会话过期boolean
    grantId令牌当前认证 (Authentication) 授权 (Authorization) idstring
    gty令牌的授权类型string
    kind令牌类型AccessToken
  • 机器对机器访问令牌 (Machine-to-machine access token) 数据对象
    属性描述类型
    jti唯一 JWT idstring
    aud令牌的受众 (Audience)string
    scope令牌的权限 (Scopes)string
    clientId令牌的客户端 idstring
    kind令牌类型ClientCredentials

context(仅对用户访问令牌 (User access token) 可用)

context 对象包含与当前授权 (Authorization) 过程相关的用户数据和授权数据。

  • 用户数据对象 对于用户访问令牌 (User access token),Logto 提供了额外的用户数据上下文供你访问。用户数据对象包含你设置自定义声明 (Claims) 可能需要的所有用户资料数据和组织 (Organization) 成员数据。请参阅 用户组织 (Organizations) 获取更多详情。

  • 组织 (Organization) 数据对象 对于组织访问令牌 (Organization tokens)(为特定组织请求的访问令牌 (Access token)),Logto 通过 context.organization 提供目标组织,你可以为每个组织附加声明 (Claims)——例如,将 Logto 组织 ID 映射到组织自定义数据中存储的内部 ID。此数据对象仅在为特定组织签发令牌时存在,包含:

    属性描述类型
    id唯一组织 (Organization) idstring
    name组织 (Organization) 名称string
    description组织 (Organization) 描述string | null
    customData组织 (Organization) 的自定义数据Record<string, unknown>

    详情请参阅 组织 (Organizations)

  • 授权 (Grant) 数据对象 对于通过模拟 (Impersonation) 令牌交换获得的用户访问令牌 (User access token),Logto 提供了额外的授权 (Grant) 数据上下文供你访问。授权 (Grant) 数据对象包含来自主体 (Subject) 令牌的自定义上下文。详情请参阅 用户模拟 (User impersonation)

  • 用户交互数据对象 对于给定的用户访问令牌 (User access token),你可能需要访问用户在当前授权 (Authorization) 会话中的交互详情。例如,你可能需要获取用户用于登录的企业单点登录 (Enterprise SSO) 身份。该用户交互数据对象包含最近用户提交的交互数据,包括:

    属性描述类型
    interactionEvent当前用户交互的事件SignInRegister
    userId当前用户交互的用户 idstring
    verificationRecords用户在交互过程中提交的用于识别和验证身份的验证记录列表VerificationRecord[]

    验证记录类型:

    // VerificationType.Password
    {
    id: string;
    type: 'Password';
    identifier: {
    type: 'username' | 'email' | 'phone' | 'userId';
    value: string;
    }
    verified: boolean;
    }
    // VerificationType.EmailVerificationCode
    {
    id: string;
    templateType: 'SignIn' | 'Register' | 'ForgotPassword' | 'Generic';
    verified: boolean;
    type: 'EmailVerificationCode';
    identifier: {
    type: 'email';
    value: string;
    }
    }
    // VerificationType.PhoneVerificationCode
    {
    id: string;
    templateType: 'SignIn' | 'Register' | 'ForgotPassword' | 'Generic';
    verified: boolean;
    type: 'PhoneVerificationCode';
    identifier: {
    type: 'phone';
    value: string;
    }
    }
    // VerificationType.Social
    {
    id: string;
    type: 'Social';
    connectorId: string;
    socialUserInfo?: {
    id: string;
    email?: string | undefined;
    phone?: string | undefined;
    name?: string | undefined;
    avatar?: string | undefined;
    rawData?: Record<string, unknown> | undefined;
    } | undefined;
    }
    // VerificationType.EnterpriseSso
    {
    id: string;
    type: 'EnterpriseSso';
    connectorId: string;
    enterpriseUserInfo?: {
    id: string;
    email?: string | undefined;
    phone?: string | undefined;
    name?: string | undefined;
    avatar?: string | undefined;
    [key: string]?: unknown;
    } | undefined;
    issuer?: string | undefined;
    }
    // VerificationType.Totp (MFA)
    {
    id: string;
    type: 'Totp';
    userId: string;
    verified: boolean;
    }
    // VerificationType.WebAuthn (MFA)
    {
    id: string;
    type: 'WebAuthn';
    userId: string;
    verified: boolean;
    }
    // VerificationType.BackupCode (MFA)
    {
    id: string;
    type: "BackupCode";
    userId: string;
    code?: string | undefined;
    }
    // VerificationType.OneTimeToken
    {
    id: string;
    type: "OneTimeToken";
    verified: boolean;
    identifier: {
    type: "email";
    value: string;
    };
    oneTimeTokenContext?: {
    jitOrganizationIds?: string[] | undefined;
    } | undefined;
    }
    备注:

    用户交互数据对象中可能存在多条验证记录,尤其是在用户经历了多次登录或注册流程时。

    例如,用户先通过 Social 验证记录登录,然后通过 EmailVerificationCode 验证记录绑定新邮箱,再通过 Totp 验证记录验证 MFA 状态。在这种情况下,你可能需要在脚本中分别处理所有验证记录。

    每种类型的验证记录在用户交互数据对象中只会出现一次。

environmentVariables

使用右侧的 设置环境变量 区域为你的脚本设置环境变量。你可以使用这些变量存储敏感信息或配置数据,而无需在脚本中硬编码。例如 API 密钥、密钥或 URL。

你在此处设置的所有环境变量都可以在脚本中使用。通过输入参数中的 environmentVariables 对象访问这些变量。

api

api 对象为你的脚本提供了一组实用函数,用于对令牌签发过程进行额外访问控制。api 对象包含以下函数:

api.denyAccess(message?: string): void

api.denyAccess() 函数允许你通过自定义消息拒绝令牌签发过程。你可以使用此函数在令牌签发过程中执行额外的访问校验。

运行时 API

自定义访问令牌 (Access token) 脚本在 Logto Cloud 和自托管 Logto 中均可使用以下标准 Web API:

  • fetchRequestResponseHeaders
  • 通过 cryptocrypto.subtle 使用 Web Crypto
  • TextEncoderTextDecoder
  • URLURLSearchParams

例如,你可以使用 Web Crypto 计算 SHA-256 和 HMAC-SHA-256 值:

const toHex = (buffer) =>
[...new Uint8Array(buffer)].map((byte) => byte.toString(16).padStart(2, '0')).join('');

const sha256 = async (input) =>
toHex(await crypto.subtle.digest('SHA-256', new TextEncoder().encode(input)));

const hmacSha256 = async (key, input) => {
const encoder = new TextEncoder();
const cryptoKey = await crypto.subtle.importKey(
'raw',
encoder.encode(key),
{ name: 'HMAC', hash: 'SHA-256' },
false,
['sign']
);

return toHex(await crypto.subtle.sign('HMAC', cryptoKey, encoder.encode(input)));
};

脚本不能导入包。请避免使用 Node.js 特有的全局变量和模块,因为它们在自托管 Logto 和 Logto Cloud 之间不可移植,也不属于支持的脚本约定。

步骤 3:获取外部数据

你可以在脚本中使用标准的 fetch 函数获取外部数据。fetch 是一个基于 Promise 的函数,允许你向外部 API 发起 HTTP 请求。

const getCustomJwtClaims = async ({ environmentVariables }) => {
const response = await fetch('https://api.example.com/data', {
headers: {
Authorization: `Bearer ${environmentVariables.API_KEY}`,
},
});

const data = await response.json();

return {
data,
};
};
备注:

请注意,任何外部数据获取都可能为令牌签发过程引入延迟。请确保外部 API 可靠且足够快速以满足你的需求。

此外:

  • 在脚本中妥善处理错误和超时,避免令牌签发过程被阻塞。
  • 使用合适的授权 (Authorization) 头保护你的外部 API,防止未授权访问。

步骤 4:测试脚本

在保存脚本前务必进行测试。点击页面右侧的 测试上下文 标签,修改用于测试的模拟令牌负载和用户数据上下文。

点击编辑器右上角的 运行测试,即可使用模拟数据运行脚本。脚本输出将在 测试结果 抽屉中显示。

测试自定义 JWT 脚本
备注:

测试结果是你设置的模拟数据下 getCustomJwtClaims 函数的输出(即在时序图第 3 步获得的“额外令牌声明 (Claims)”)。实际令牌负载和用户数据上下文在令牌签发过程中会有所不同。

点击 创建 按钮保存脚本。自定义令牌声明 (Claims) 脚本将被保存并应用于访问令牌 (Access token) 签发流程。