调用DocuSign createEnvelope时JWT令牌返回401 AUTHORIZATION_INVALID_TOKEN
我通过以下代码获取DocuSign访问令牌:
import * as docusign from 'docusign-esign'; const expiresIn = 28800; export const createClient = async (): Promise<docusign.ApiClient> => { const apiClient = new docusign.ApiClient({ basePath: process.env.BASE_PATH, oAuthBasePath: process.env.O_AUTH_BASE_PATH }); try { const response = await apiClient.requestJWTApplicationToken( process.env.CLIENT_ID, ["signature", "impersonation"], Buffer.from(process.env.RSA_PRIVATE_KEY.replace(/\\n/g, '\n')), expiresIn ); console.log(response.body.access_token) // -> 成功返回访问令牌 apiClient.addDefaultHeader("Authorization", "Bearer " + response.body.access_token); } catch (error){ throw AppError("自定义错误") } return apiClient; }
随后在另一处编写了创建信封的函数:
import { EnvelopeDefinition, EnvelopeRecipients, EnvelopesApi, Signer } from 'docusign-esign'; import { createClient } from "./above-file-path.ts"; export const someFunction = async () => { const signer1: Signer = { email: 'my-personal-email-for-testing@gmail.com', name: '我的名字', roleName: '我的名字' }; const signer2: Signer = { email: 'client@gmail.com', name: '客户名字', roleName: 'Client' }; const recipients: EnvelopeRecipients = { signers: [signer1, signer2] }; const dsApiClient = await createClient(); const envelope: EnvelopeDefinition = { emailSubject: '请签署这份协议', templateId: '我的模板UUID', status: 'created', recipients, }; const envelopesApi = new EnvelopesApi(dsApiClient); try { const { envelopeId, errorDetails } = await envelopesApi.createEnvelope(process.env.ACCOUNT_ID, { envelopeDefinition: envelope, }); console.log("是否成功?: ", envelopeId) } catch (error) { console.log(error) // -> 返回错误:Unauthorized...{"errorCode":"AUTHORIZATION_INVALID_TOKEN","message":"提供的访问令牌已过期、被吊销或格式错误。系统应用程序认证失败。"} } }
调用该函数时持续收到AUTHORIZATION_INVALID_TOKEN错误。
补充信息:
- 获取令牌后立即调用函数,确认令牌未过期;
- O_AUTH_BASE_PATH使用
account-d.docusign.com,BASE_PATH使用https://demo.docusign.net/restapi。
可能的原因及解决方案
1. 缺少用户模拟(Impersonation)步骤
你当前用requestJWTApplicationToken生成的是应用级令牌,无法直接代表特定用户执行创建信封这类需要用户上下文的操作,必须完成用户模拟:
修改createClient函数,添加用户信息获取与账户配置步骤:
export const createClient = async (): Promise<docusign.ApiClient> => { const apiClient = new docusign.ApiClient({ basePath: process.env.BASE_PATH, oAuthBasePath: process.env.O_AUTH_BASE_PATH }); try { const response = await apiClient.requestJWTApplicationToken( process.env.CLIENT_ID, ["signature", "impersonation"], Buffer.from(process.env.RSA_PRIVATE_KEY.replace(/\\n/g, '\n')), expiresIn ); const accessToken = response.body.access_token; console.log(accessToken) // 新增:获取用户信息,完成模拟 const userInfo = await apiClient.getUserInfo(accessToken); // 选择默认账户(多账户场景可按需筛选) const targetAccount = userInfo.accounts.find(acc => acc.isDefault); if (!targetAccount) throw new Error("未找到目标账户"); // 更新apiClient的basePath为当前账户的专属路径 apiClient.setBasePath(`${targetAccount.baseUri}/restapi`); // 重新设置授权头 apiClient.addDefaultHeader("Authorization", `Bearer ${accessToken}`); // 同步正确的账户ID到环境变量 process.env.ACCOUNT_ID = targetAccount.accountId; } catch (error){ throw new Error(`自定义错误: ${(error as Error).message}`) } return apiClient; }
2. 账户ID与模板不匹配
- 确保
process.env.ACCOUNT_ID是模拟后获取的正确账户ID,而非硬编码的错误值; - 验证
templateId对应的模板确实存在于当前使用的演示环境账户中。
3. RSA密钥格式验证
检查RSA_PRIVATE_KEY环境变量的换行符处理是否正确,转换后的私钥需完整无缺失,可在代码中打印转换后的密钥内容确认格式。
4. 应用权限确认
登录DocuSign开发者后台,确认你的应用已启用signature和impersonation权限,且JWT请求中正确传递了这两个权限范围。
内容的提问来源于stack exchange,提问作者Rubek Joshi
相关产品推荐
相关产品推荐

