Tableau 与 DocuSign Integration 的 OAuth Token 认证
结论
不建议把 DocuSign OAuth Token 直接保存在 Tableau 工作簿中。更稳妥的做法是增加中间数据服务或定时 ETL 任务:
- 后台任务通过 DocuSign OAuth 获取 Access Token,并在需要时自动更新。
- 调用
/oauth/userinfo,查询当前用户有权访问的 DocuSign Accounts。 - 根据各账户返回的
account_id和base_uri,分别调用 Billing API。 - 整理结果并写入数据库、数据仓库或 Tableau 可以读取的数据文件。
- Tableau 连接处理后的数据源,并按计划刷新。
如果必须让 Tableau 直接访问 DocuSign API,就需要开发 Tableau Web Data Connector 或自定义连接器,并在连接器中实现完整的 OAuth 流程。只在 Tableau 中填写 Access Token 通常无法长期运行,因为 Token 会过期。
推荐架构
DocuSign OAuth
│
▼
定时 ETL / 中间 API
│
├── 获取可访问的账户列表
├── 逐账户调用 Billing API
├── 处理分页、限流和错误
└── 统一账户、发票及用量字段
│
▼
数据库 / 数据仓库 / Hyper Extract
│
▼
Tableau Dashboard
采用这种架构,可以避免把 Integration Key、Client Secret、RSA 私钥或 Refresh Token 暴露在 Tableau 工作簿中。多个账户、Token 续期和定时刷新也更容易集中处理。
选择 OAuth 授权方式
DocuSign 后台集成通常有两种授权方式。
Authorization Code Grant
适用于需要管理员或用户通过浏览器登录并授权的场景。完成首次授权后,后台保存 Refresh Token,再用它换取新的 Access Token。
使用时需要注意:
- Refresh Token 必须保存在服务端的密钥管理系统中。
- 后台任务需要在 Refresh Token 仍然有效时完成轮换。
- 不要把 Client Secret 或 Refresh Token 放入 Tableau 计算字段、参数或工作簿。
- 生产环境和演示环境使用不同的 OAuth 地址。
生产环境的 Token Endpoint:
https://account.docusign.com/oauth/token
演示环境的 Token Endpoint:
https://account-d.docusign.com/oauth/token
使用 Refresh Token 获取 Access Token 的示例:
curl --request POST \
--url https://account.docusign.com/oauth/token \
--user "${DOCUSIGN_INTEGRATION_KEY}:${DOCUSIGN_CLIENT_SECRET}" \
--header "Content-Type: application/x-www-form-urlencoded" \
--data-urlencode "grant_type=refresh_token" \
--data-urlencode "refresh_token=${DOCUSIGN_REFRESH_TOKEN}"
JWT Grant
适用于无人值守的服务端定时任务。程序使用 Integration Key 和 RSA 私钥,代表指定用户申请短期 Access Token。
使用 JWT Grant 通常需要满足以下条件:
- 已创建 DocuSign Integration Key。
- 已配置 RSA 公钥。
- 被模拟用户已授予所需的 OAuth consent。
- 该用户有权访问目标账户及 Billing API。
- 后台服务能够安全保存 RSA 私钥。
通过 JWT Grant 获得的 Access Token 仍然是短期令牌。任务必须在 Token 过期前重新申请,不能获取一次后长期写死在 Tableau 中。
获取所有可访问账户
获得 Access Token 后,先调用 DocuSign UserInfo Endpoint:
curl --request GET \
--url https://account.docusign.com/oauth/userinfo \
--header "Authorization: Bearer ${DOCUSIGN_ACCESS_TOKEN}"
响应中的 accounts 数组通常包含以下信息:
{
"accounts": [
{
"account_id": "11111111-2222-3333-4444-555555555555",
"account_name": "Account A",
"base_uri": "https://na4.docusign.net",
"is_default": true
},
{
"account_id": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee",
"account_name": "Account B",
"base_uri": "https://eu.docusign.net",
"is_default": false
}
]
}
不能假设所有账户都使用同一个 API 主机。构造请求地址时,应使用每个账户对应的 base_uri:
{base_uri}/restapi/v2.1/accounts/{account_id}/...
账户出现在 accounts 数组中,只能说明该 OAuth 用户与这个账户有关联,不代表用户有权调用所有 Billing Endpoint。账单接口还可能受到用户权限、账户类型和合同方式的限制。
调用 Billing API
下面以 Invoices API 为例。请求应使用 UserInfo 返回的 base_uri 和对应的 account_id。具体路径、必填参数和返回字段,需要以当前 DocuSign API Reference 为准。
调用形式如下:
curl --request GET \
--url "${BASE_URI}/restapi/v2.1/accounts/${ACCOUNT_ID}/billing_invoices/${INVOICE_ID}" \
--header "Authorization: Bearer ${DOCUSIGN_ACCESS_TOKEN}" \
--header "Accept: application/json"
如果要构建历史账单或用量看板,通常需要先调用相应的列表接口取得 Invoice ID,再逐条获取详细信息。还要确认接口是否支持日期过滤和分页,不能默认一次请求会返回全部历史数据。
Python ETL 示例
下面的示例包含几个主要步骤:使用 Refresh Token 获取 Access Token,查询账户列表,再按账户发起请求。billing_path 需要根据实际使用的 Billing Endpoint 调整。
import os
import requests
from requests.auth import HTTPBasicAuth
OAUTH_BASE_URL = os.environ.get(
"DOCUSIGN_OAUTH_BASE_URL",
"https://account.docusign.com"
)
INTEGRATION_KEY = os.environ["DOCUSIGN_INTEGRATION_KEY"]
CLIENT_SECRET = os.environ["DOCUSIGN_CLIENT_SECRET"]
REFRESH_TOKEN = os.environ["DOCUSIGN_REFRESH_TOKEN"]
def refresh_access_token():
response = requests.post(
f"{OAUTH_BASE_URL}/oauth/token",
auth=HTTPBasicAuth(INTEGRATION_KEY, CLIENT_SECRET),
headers={
"Content-Type": "application/x-www-form-urlencoded"
},
data={
"grant_type": "refresh_token",
"refresh_token": REFRESH_TOKEN
},
timeout=30
)
response.raise_for_status()
return response.json()
def get_accounts(access_token):
response = requests.get(
f"{OAUTH_BASE_URL}/oauth/userinfo",
headers={
"Authorization": f"Bearer {access_token}",
"Accept": "application/json"
},
timeout=30
)
response.raise_for_status()
return response.json().get("accounts", [])
def call_account_api(access_token, base_uri, account_id, billing_path):
url = (
f"{base_uri}/restapi/v2.1/accounts/"
f"{account_id}/{billing_path.lstrip('/')}"
)
response = requests.get(
url,
headers={
"Authorization": f"Bearer {access_token}",
"Accept": "application/json"
},
timeout=60
)
response.raise_for_status()
return response.json()
def main():
token_result = refresh_access_token()
access_token = token_result["access_token"]
accounts = get_accounts(access_token)
rows = []
for account in accounts:
account_id = account["account_id"]
base_uri = account["base_uri"].rstrip("/")
# 替换为实际需要调用的 Billing Endpoint。
billing_path = "billing_invoices/{invoice_id}"
# 只有取得真实 invoice_id 后才能执行该请求。
# data = call_account_api(
# access_token,
# base_uri,
# account_id,
# billing_path.format(invoice_id=invoice_id)
# )
rows.append({
"account_id": account_id,
"account_name": account.get("account_name"),
"base_uri": base_uri
})
return rows
if __name__ == "__main__":
print(main())
生产环境还需要处理以下事项:
- 循环读取分页数据;
- 遇到 HTTP
429时进行退避重试; - 遇到
401时更新 Token; - 遇到
403时记录对应的账户权限问题; - 设置请求超时和有限次数的重试;
- 支持增量同步并处理重复数据;
- 使用 Secret Manager 或同类服务存储凭据;
- 对日志进行脱敏,不得记录 Access Token 和 Refresh Token。
Tableau 接入方式
方式一:连接数据库或数据仓库
这是优先考虑的接入方式。ETL 可以把数据写入以下表:
docusign_account
docusign_invoice
docusign_usage_daily
docusign_sync_status
每张业务表至少保留这些字段:
account_id
account_name
source_base_uri
extracted_at
Tableau 直接连接数据库,再通过 Tableau Server 或 Tableau Cloud 的刷新计划更新 Extract。
方式二:生成 Tableau Hyper Extract
后台任务可以把统一处理后的数据写入 .hyper 文件,再发布或替换 Tableau 数据源。OAuth 仍由后台任务管理,Tableau 只负责读取结果。
方式三:开发 Web Data Connector 或自定义连接器
如果业务要求 Tableau 直接访问 DocuSign,连接器需要完成:
- OAuth 登录跳转;
- Redirect URI 处理;
- Token 安全存储;
- Access Token 更新;
- 账户选择或多账户循环;
- API 分页和错误处理;
- Tableau Server 或 Tableau Cloud 上的连接器部署与刷新配置。
这种方式的开发和运维成本较高,还需要确认 Tableau 的版本、部署形态,以及环境是否允许使用自定义连接器。
多账户场景的限制
OAuth 用户必须已加入目标账户,并且具备相应权限,才能以该用户身份访问这些账户。如果组织内的账户彼此独立,可能需要:
- 创建或指定一个集成用户,并把它加入所有目标账户;
- 为该用户授予所需的账单权限;
- 或者分别维护各账户的授权,再由 ETL 合并数据。
DocuSign Organization 管理权限不一定等同于具体账户中的 API 和 Billing 权限。即使用户是组织管理员,也要逐个账户验证接口是否返回 200,并妥善记录 403 Forbidden 等结果。
安全与上线检查
上线前至少要确认:
- OAuth 应用已经从演示环境迁移,并满足生产环境要求。
- Redirect URI 与实际回调地址完全一致。
- Scope 只包含业务需要的权限。
- Integration Key、Client Secret、私钥和 Refresh Token 均未写入工作簿。
- 每个账户都使用 UserInfo 返回的
base_uri。 - 集成用户对所有目标账户都有所需权限。
- API 调用能够处理分页、限流和 Token 过期。
- Tableau 中的数据按
account_id区分,避免混合不同账户的数据。 - 日志和错误信息不会输出任何 OAuth 凭据。
配置工作的重点不是在 Tableau 中粘贴 OAuth Token,而是建立一个集成层。这个集成层负责安全维护 DocuSign OAuth 会话、遍历账户,并为 Tableau 提供稳定的数据源。
备注:内容仅供参考。
