@mcp-abap-adt/auth代理

MCP ABAP ADT服务器的JWT身份验证代理。基于目标标头管理身份验证令牌,自动从以下位置加载令牌 .env 文件,并在需要时使用服务密钥刷新它们。
特性
- 🔐 基于目标的身份验证:基于以下内容加载令牌
x-mcp-destination头球 - 📁 环境文件支持:自动从以下位置加载令牌
{destination}.env文件 - 🔄 自动令牌刷新:使用来自的服务密钥刷新过期的令牌
{destination}.json文件 - ✅ 令牌验证:通过提供者验证令牌(如果
validateToken已实施) - 💾 令牌缓存:内存缓存可提高性能
- 🔧 可配置的基本路径:自定义位置
.env和.json文件已存储
安装
npm install @mcp-abap-adt/auth-broker用法
基本用法(需要提供程序)
AuthBroker需要为目标配置令牌提供程序:
import { AuthBroker, AbapSessionStore } from '@mcp-abap-adt/auth-broker';
import { AuthorizationCodeProvider } from '@mcp-abap-adt/auth-providers';
const tokenProvider = new AuthorizationCodeProvider({
uaaUrl: 'https://...authentication...hana.ondemand.com',
clientId: '...',
clientSecret: '...',
browser: 'system',
});
const broker = new AuthBroker({
sessionStore: new AbapSessionStore('/path/to/destinations'),
tokenProvider,
});
const token = await broker.getToken('TRIAL');完整配置(所有依赖项)
为了获得最大的灵活性,请提供所有三个依赖关系:
import {
AuthBroker,
AbapServiceKeyStore,
AbapSessionStore,
} from '@mcp-abap-adt/auth-broker';
import { AuthorizationCodeProvider } from '@mcp-abap-adt/auth-providers';
const broker = new AuthBroker({
sessionStore: new AbapSessionStore('/path/to/destinations'),
serviceKeyStore: new AbapServiceKeyStore('/path/to/destinations'), // optional
tokenProvider: new AuthorizationCodeProvider({
uaaUrl: 'https://...authentication...hana.ondemand.com',
clientId: '...',
clientSecret: '...',
browser: 'system',
}),
}, 'chrome', logger);
// Disable browser authentication for headless/stdio environments (e.g., MCP with Cline)
const brokerNoBrowser = new AuthBroker({
sessionStore: new AbapSessionStore('/path/to/destinations'),
serviceKeyStore: new AbapServiceKeyStore('/path/to/destinations'),
tokenProvider: new AuthorizationCodeProvider({
uaaUrl: 'https://...authentication...hana.ondemand.com',
clientId: '...',
clientSecret: '...',
browser: 'none',
}),
allowBrowserAuth: false, // Throws BROWSER_AUTH_REQUIRED if browser auth needed
}, 'chrome', logger);会话+服务密钥(用于初始化)
如果需要从服务密钥初始化会话,请从服务密钥auth-config创建提供程序:
const broker = new AuthBroker({
sessionStore: new AbapSessionStore('/path/to/destinations'),
serviceKeyStore: new AbapServiceKeyStore('/path/to/destinations'),
tokenProvider: new AuthorizationCodeProvider({
uaaUrl: 'https://...authentication...hana.ondemand.com',
clientId: '...',
clientSecret: '...',
browser: 'system',
}),
});内存会话存储
对于测试或临时会话:
import { AuthBroker, SafeAbapSessionStore } from '@mcp-abap-adt/auth-broker';
const broker = new AuthBroker({
sessionStore: new SafeAbapSessionStore(), // In-memory, data lost after restart
});自定义浏览器身份验证端口
为了避免与浏览器身份验证的端口冲突:
const broker = new AuthBroker({
sessionStore: new AbapSessionStore('/path/to/destinations'),
serviceKeyStore: new AbapServiceKeyStore('/path/to/destinations'),
tokenProvider: new AuthorizationCodeProvider({
uaaUrl: 'https://...authentication...hana.ondemand.com',
clientId: '...',
clientSecret: '...',
browser: 'system',
redirectPort: 4001,
}),
}, 'chrome');获取代币
const token = await broker.getToken('TRIAL');
// Force refresh token
const newToken = await broker.refreshToken('TRIAL');为DI创建令牌刷新器
这 createTokenRefresher() 方法创建一个 ITokenRefresher 可以注入到连接中的实现。这使得连接能够透明地处理令牌刷新,而无需了解身份验证内部。
import { AuthBroker } from '@mcp-abap-adt/auth-broker';
import { JwtAbapConnection } from '@mcp-abap-adt/connection';
// Create broker
const broker = new AuthBroker({
sessionStore: mySessionStore,
serviceKeyStore: myServiceKeyStore,
tokenProvider: myTokenProvider,
});
// Create token refresher for specific destination
const tokenRefresher = broker.createTokenRefresher('TRIAL');
// Inject into connection (connection can handle 401/403 automatically)
const connection = new JwtAbapConnection(config, tokenRefresher);
// Token refresher methods:
// - getToken(): Returns cached token if valid, otherwise refreshes
// - refreshToken(): Forces token refresh and saves to session store代币更新的好处:
- 🔄 透明刷新:连接自动处理401/403错误
- 🧩 依赖注入:明确区分关注点
- 💾 自动持久化:刷新后保存到会话存储的令牌
- 🎯 目的地范围:每次复习都必须前往特定目的地
配置
环境变量
配置变量
AUTH_BROKER_PATH-用于搜索的冒号/分号分隔路径.env和.json文件(默认:当前工作目录)
调试变量
DEBUG_BROKER-启用调试日志记录auth-broker包(简称)
- 吃起来 true 启用日志记录(默认值: false) - 启用后,记录身份验证步骤、令牌操作和错误详细信息 - 可以通过设置明确禁用 false - 例子: DEBUG_BROKER=true npm test
DEBUG_AUTH_BROKER-长名称(向后兼容)
- 同 DEBUG_BROKER,但名称较长 - 例子: DEBUG_AUTH_BROKER=true npm test
LOG_LEVEL-控制日志详细程度
- 价值观: debug, info, warn, error (默认值: info) - debug -所有消息,包括详细的调试信息 - info -信息性消息、警告和错误 - warn -仅警告和错误 - error -仅错误 - 例子: LOG_LEVEL=debug DEBUG_BROKER=true npm test
DEBUG-启用调试的替代方法
- 吃起来 true 启用所有调试日志记录 - 或设置为包含以下内容的字符串 broker 或 auth-broker 仅启用此程序包 - 例子: DEBUG=true npm test 或 DEBUG=broker npm test 或 DEBUG=auth-broker npm test
备注:对于调试相关包:
DEBUG_STORES(短)或DEBUG_AUTH_STORES(long)-启用日志记录@mcp-abap-adt/auth-stores包裹DEBUG_PROVIDER(短)或DEBUG_AUTH_PROVIDERS(long)-启用日志记录@mcp-abap-adt/auth-providers包裹
传统支持: DEBUG_AUTH_LOG 仍然支持向后兼容性(相当于 DEBUG_BROKER=true LOG_LEVEL=debug)
日志记录功能
启用日志记录时(通过 DEBUG_BROKER=true 或 DEBUG_AUTH_BROKER=true),代理提供详细的结构化日志记录:
记录的内容:
- 代理初始化:配置详细信息、存储、令牌提供程序、浏览器设置
- 令牌检索:会话状态检查、令牌存在、刷新令牌可用性
- 代币操作:通过提供者请求令牌,收到带有过期信息的令牌
- 令牌持久性:使用格式化的令牌值和到期日期将令牌保存到会话
- 错误上下文:详细的错误信息,包括文件路径、错误代码、缺少的字段
日志记录功能:
- 令牌格式:为了安全性和可读性,令牌以截断格式记录(前25个字符和后25个字符,跳过中间)
- 日期格式:过期日期以可读格式记录(例如,“2025-12-25 19:21:27 UTC”),而不是原始时间戳
- 结构化日志:用途
DefaultLogger从@mcp-abap-adt/logger使用图标和级别前缀进行正确格式化 - 日志级别:通过控制
LOG_LEVEL或AUTH_LOG_LEVEL环境变量(错误、警告、信息、调试)
输出示例 DEBUG_BROKER=true LOG_LEVEL=info:
[INFO] ℹ️ [AUTH-BROKER] Broker initialized: hasServiceKeyStore(true), hasSessionStore(true), hasTokenProvider(true), browser(system), allowBrowserAuth(true)
[INFO] ℹ️ [AUTH-BROKER] Getting token for destination: TRIAL
[INFO] ℹ️ [AUTH-BROKER] Session check for TRIAL: hasToken(true), hasAuthConfig(true), hasServiceUrl(true), serviceUrl(https://...abap...), authorizationToken(eyJ0eXAiOiJKV1QiLCJqaWQiO...Q5ti7aYmEzItIDuLp7axNYo6w), hasRefreshToken(true)
[INFO] ℹ️ [AUTH-BROKER] Requesting tokens for TRIAL via session
[INFO] ℹ️ [AUTH-BROKER] Tokens received for TRIAL: authorizationToken(eyJ0eXAiOiJKV1QiLCJqaWQiO...Q5ti7aYmEzItIDuLp7axNYo6w), hasRefreshToken(true), authType(authorization_code), expiresIn(43199), expiresAt(2025-12-26 20:15:30 UTC)
[INFO] ℹ️ [AUTH-BROKER] Saving tokens to session for TRIAL: serviceUrl(https://...abap...), authorizationToken(eyJ0eXAiOiJKV1QiLCJqaWQiO...Q5ti7aYmEzItIDuLp7axNYo6w), hasRefreshToken(true), expiresAt(2025-12-26 20:15:30 UTC)
[INFO] ℹ️ [AUTH-BROKER] Token retrieved for TRIAL (via session): authorizationToken(eyJ0eXAiOiJKV1QiLCJqaWQiO...Q5ti7aYmEzItIDuLp7axNYo6w)备注:只有当向代理构造函数显式提供记录器时,日志记录才有效。如果没有传递记录器,代理将不会向控制台输出任何内容。
文件结构
ABAP环境文件({destination}.env)
对于ABAP连接,请使用 SAP_* 环境变量:
SAP_URL=https://your-system.abap.us10.hana.ondemand.com
SAP_CLIENT=100
SAP_JWT_TOKEN=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...
SAP_REFRESH_TOKEN=refresh_token_string
SAP_UAA_URL=https://your-account.authentication.us10.hana.ondemand.com
SAP_UAA_CLIENT_ID=client_id
SAP_UAA_CLIENT_SECRET=client_secretXSUAA环境文件({destination}.env)
对于XSUAA连接(范围缩小),使用 XSUAA_* 环境变量:
XSUAA_MCP_URL=https://your-mcp-server.cfapps.eu10.hana.ondemand.com
XSUAA_JWT_TOKEN=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...
XSUAA_REFRESH_TOKEN=refresh_token_string
XSUAA_UAA_URL=https://your-account.authentication.eu10.hana.ondemand.com
XSUAA_UAA_CLIENT_ID=client_id
XSUAA_UAA_CLIENT_SECRET=client_secret备注: XSUAA_MCP_URL 是可选的-它不是身份验证的一部分,只需要发出请求。令牌和UAA凭据足以进行身份验证。
BTP环境文件({destination}.env)
对于BTP连接(ABAP系统的完整范围),请使用 BTP_* 环境变量:
BTP_ABAP_URL=https://your-system.abap.us10.hana.ondemand.com
BTP_JWT_TOKEN=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...
BTP_REFRESH_TOKEN=refresh_token_string
BTP_UAA_URL=https://your-account.authentication.eu10.hana.ondemand.com
BTP_UAA_CLIENT_ID=client_id
BTP_UAA_CLIENT_SECRET=client_secret
BTP_SAP_CLIENT=100
BTP_LANGUAGE=EN备注: BTP_ABAP_URL 是必需的-它是ABAP系统URL。所有参数(令牌除外)都来自服务密钥。
ABAP的服务密钥文件({destination}.json)
标准ABAP服务密钥格式:
{
"url": "https://your-system.abap.us10.hana.ondemand.com",
"uaa": {
"url": "https://your-account.authentication.us10.hana.ondemand.com",
"clientid": "your_client_id",
"clientsecret": "your_client_secret"
}
}XSUAA的服务密钥文件({destination}.json)
直接XSUAA服务密钥格式(来自BTP):
{
"url": "https://your-account.authentication.eu10.hana.ondemand.com",
"apiurl": "https://api.authentication.eu10.hana.ondemand.com",
"clientid": "your_client_id",
"clientsecret": "your_client_secret"
}备注:对于XSUAA服务密钥, apiurl 优先于 url 用于UAA授权(如果存在)。
XSUAA与BTP身份验证
此软件包支持两种类型的BTP身份验证:
XSUAA(缩小范围)
- 目的:访问范围有限的BTP服务
- 服务密钥:仅包含UAA凭据(无ABAP URL)
- 会话存储:
XsuaaSessionStore(使用XSUAA_*环境变量) - 认证:客户端凭据授予类型(无需浏览器)
- MCP网址:可选,单独提供(来自YAML配置
mcp_url、参数或请求标头) - 用例:以减少的权限访问MCP服务器等BTP服务
BTP(ABAP的完整范围)
- 目的:以完整角色和范围访问ABAP系统
- 服务密钥:包含UAA凭据和ABAP URL
- 会话存储:
BtpSessionStore(使用BTP_*环境变量) - 认证:基于浏览器的OAuth2(如ABAP)或刷新令牌
- ABAP网址:必填,来自服务密钥或YAML配置
- 用例:以完全权限访问BTP中的ABAP系统
责任和设计原则
核心开发原则
仅接口通信该方案遵循一个基本的发展原则: 所有与外部依赖关系的交互都只能通过接口进行代码知道 没有超出接口中定义的内容.
这意味着:
- 不知道具体的实现类(例如。,
AbapSessionStore,AuthorizationCodeProvider) - 不了解接口中未定义的内部数据结构或方法
- 不假设接口契约之外的实现行为
- 不访问接口中未明确定义的属性或方法
这一原则确保:
- 松散结合:
AuthBroker与具体实现解耦 - 灵活性:无需修改即可添加新实现
AuthBroker - 可测试性:易于模拟测试依赖关系
- 可维护性:对实现的更改不会影响
AuthBroker
包装责任
这 @mcp-abap-adt/auth-broker 包定义 接口 并提供 编排逻辑 用于身份验证。确实如此 不 实现具体的存储或令牌获取机制-这些由单独的包提供(@mcp-abap-adt/auth-stores, @mcp-abap-adt/auth-providers).
AuthBroker做什么
- 协调身份验证流程:使用提供的存储和提供程序协调令牌检索、验证和刷新
- 管理令牌生命周期:处理令牌缓存、验证和自动刷新
- 仅适用于接口:用途
IServiceKeyStore,ISessionStore,以及ITokenProvider不知道具体实现的接口 - 供应商代表:通话
tokenProvider.getTokens()获取代币 - 代表到商店:将令牌和连接配置保存到
sessionStore
AuthBroker不做什么
- 不实施存储:文件I/O、解析和存储逻辑由以下具体存储实现处理
@mcp-abap-adt/auth-stores - 不实施代币获取:OAuth2流、刷新令牌逻辑和客户端凭据由来自的具体提供者实现处理
@mcp-abap-adt/auth-providers
消费者责任
这 消费者 (应用程序使用 AuthBroker)负责:
- 选择合适的实施方式:选择正确的
IServiceKeyStore,ISessionStore,以及ITokenProvider基于用例的实现:
- ABAP系统:使用 AbapServiceKeyStore, AbapSessionStore (或 SafeAbapSessionStore),以及 AuthorizationCodeProvider - BTP系统:使用 AbapServiceKeyStore, BtpSessionStore (或 SafeBtpSessionStore),以及 AuthorizationCodeProvider - XSUAA服务:使用 XsuaaServiceKeyStore, XsuaaSessionStore (或 SafeXsuaaSessionStore),以及 ClientCredentialsProvider
- 确保完整配置:如果会话存储需要
serviceUrl(例如。,AbapSessionStore需要sapUrl),消费者必须确保:
- 会话是通过以下方式创建的 serviceUrl 打电话之前 AuthBroker.getToken(),或 - 会话存储实现处理 serviceUrl 内部检索(例如,从 serviceKeyStore)
- 了解店铺要求:不同的会话存储实现有不同的要求:
- AbapSessionStore:需要 sapUrl (地图到 serviceUrl 在 IConnectionConfig) - BtpSessionStore:不需要 serviceUrl (使用 mcpUrl 相反) - XsuaaSessionStore:不需要 serviceUrl (MCP URL是可选的)
门店职责
混凝土 ISessionStore 实施负责:
- 处理自己的数据格式:每个商店都知道其内部数据格式(例如。,
AbapSessionData,BtpBaseSessionData) - 格式之间的转换:转换
IConfig/IConnectionConfig以及内部存储格式 - 管理必填字段:如果商店需要
serviceUrl(例如。,AbapSessionStore),它应该:
- 从以下位置检索 serviceKeyStore 如果未提供 IConnectionConfig,或 - 使用当前会话中的现有值(如果可用),或者 - 如果两者都不可用,则抛出错误(取决于实现)
供应商责任
混凝土 ITokenProvider 实施负责:
- 获取代币:使用OAuth2流、刷新令牌或客户端凭据来获取JWT令牌
- 管理令牌生命周期:根据需要进行缓存、验证、刷新和重新身份验证
设计原则
- 仅接口通信 (核心原则):所有与外部依赖的交互都会发生 仅通过接口代码知道 没有超出接口中定义的内容 (参见 核心开发原则 以上)
- 依赖倒置原理(DIP):
AuthBroker取决于抽象(IServiceKeyStore,ISessionStore,ITokenProvider),而非具体实现 - 单一责任:每个组成部分都有一个明确的责任:
- AuthBroker:编排和令牌生命周期管理 - ISessionStore:会话数据存储和检索 - ITokenProvider:代币获取 - IServiceKeyStore:服务密钥存储和检索
- 接口隔离:接口集中且最小化,只包含其特定目的所需的内容
- 开闭原则:可以添加新的存储和提供程序实现,而无需修改
AuthBroker
API
AuthBroker
构造函数
new AuthBroker(
config: {
sessionStore: ISessionStore; // required
serviceKeyStore?: IServiceKeyStore; // optional
tokenProvider: ITokenProvider; // required
allowBrowserAuth?: boolean; // optional
},
browser?: string,
logger?: ILogger
)参数:
config-配置对象:
- sessionStore - 必需 -存储会话数据。必须包含初始会话 serviceUrl - serviceKeyStore - 可选的 -存放维修钥匙。仅需要从服务密钥初始化会话 - tokenProvider - 必需 -用于令牌获取和刷新的令牌提供者 - allowBrowserAuth - 可选的 -何时 false,投掷 BROWSER_AUTH_REQUIRED 而不是启动浏览器身份验证
browser-用于身份验证的可选浏览器名称(chrome,edge,firefox,system,headless,none).违约:system
- 使用 'headless' 对于SSH/远程会话-记录URL并等待手动回调 - 使用 'none' 用于自动测试-记录URL并立即拒绝 - 对于XSUAA,不使用浏览器(client_credentials授权类型)-使用 'none'
logger-可选记录器实例。如果没有提供,则不使用操作记录器
何时提供每种依赖关系:
sessionStore(必填):总是需要的。必须包含初始会话serviceUrlserviceKeyStore(可选):
- 如果需要从服务密钥初始化会话(步骤0),则需要此项 - 如果会话已包含授权配置和令牌,则不需要
tokenProvider(必填):
- 用于所有令牌获取和刷新流程 - 必须配置目标的身份验证参数(例如,UAA凭据)
可用实现:
- ABAP:
AbapServiceKeyStore(directory, defaultServiceUrl?, logger?),AbapSessionStore(directory, defaultServiceUrl?, logger?),SafeAbapSessionStore(defaultServiceUrl?, logger?),AuthorizationCodeProvider(...) - XSUAA (缩小范围):
XsuaaServiceKeyStore(directory, logger?),XsuaaSessionStore(directory, defaultServiceUrl, logger?),SafeXsuaaSessionStore(defaultServiceUrl, logger?),ClientCredentialsProvider(...) - 业务流程平台 (ABAP的全部范围):
AbapServiceKeyStore(directory, defaultServiceUrl?, logger?),BtpSessionStore(directory, defaultServiceUrl, logger?),SafeBtpSessionStore(defaultServiceUrl, logger?),AuthorizationCodeProvider(...)
方法
getToken(destination: string): Promise
获取目标的身份验证令牌。实现一个三步流程:
步骤0:使用令牌初始化会话(如果需要)
- 检查会话是否具有
authorizationToken和授权配置 - 如果两者都缺失
serviceKeyStore可用:
- 从服务密钥加载授权配置 - 用途 tokenProvider.getTokens() 获取代币 - 将令牌持久化到会话
- 否则→ 进入步骤1
步骤1:令牌刷新/重新认证
- 如果会话具有授权配置:
- 用途 tokenProvider.getTokens() 刷新或重新验证 - 将令牌持久化到会话 - 返回新令牌
- 如果失败(或没有会话身份验证配置)
serviceKeyStore可用:
- 从服务密钥加载授权配置 - 用途 tokenProvider.getTokens() 获取代币 - 将令牌持久化到会话
- 如果全部失败→ 抛出错误
重要提示:
- 所有身份验证都由注入的提供者(authorization_code或client_credentials)处理。
tokenProvider所有令牌获取和刷新流程都需要。- 经纪人总是打电话
provider.getTokens()-提供者在内部处理令牌生命周期(验证、刷新、登录)。消费者不需要知道代币问题。 - 提供者根据令牌状态决定是返回缓存令牌、刷新还是执行登录。
- 存储错误得到妥善处理:如果服务密钥文件丢失或格式错误,代理会记录错误并继续使用回退机制(会话存储数据或基于提供者的身份验证)
错误处理
代理为所有外部操作实现了全面的错误处理,将所有注入的依赖关系视为不受信任的:
import { STORE_ERROR_CODES } from '@mcp-abap-adt/interfaces';
try {
const token = await broker.getToken('TRIAL');
} catch (error: any) {
// Broker handles errors internally where possible, but critical errors propagate
console.error('Failed to get token:', error.message);
}错误类别 (由代理以优雅的降级方式处理):
1.会话存储错误 (读取会话文件):
STORE_ERROR_CODES.FILE_NOT_FOUND-会话文件丢失(已记录,尝试serviceKeyStore回退)STORE_ERROR_CODES.PARSE_ERROR-会话文件已损坏(使用文件路径记录,尝试回退)- 保存令牌时写入失败(记录和抛出-严重)
2.ServiceKeyStore错误 (读取服务密钥文件):
STORE_ERROR_CODES.FILE_NOT_FOUND-服务密钥文件丢失(已记录,继续会话数据)STORE_ERROR_CODES.PARSE_ERROR-服务密钥中的JSON无效(记录文件路径和原因)STORE_ERROR_CODES.INVALID_CONFIG-缺少必填字段(记录时缺少字段名)STORE_ERROR_CODES.STORAGE_ERROR-权限/写入错误(已记录)
3.令牌提供者错误 (网络操作):
- 网络错误:
ECONNREFUSED,ETIMEDOUT,ENOTFOUND(记录,抛出描述性消息) VALIDATION_ERROR-缺少必需的身份验证字段(用字段名记录,throws)BROWSER_AUTH_ERROR-浏览器身份验证失败或取消(记录、抛出)REFRESH_ERROR-UAA服务器上的令牌刷新失败(已记录,抛出)
4.浏览器身份验证禁用错误 (当 allowBrowserAuth: false):
BROWSER_AUTH_REQUIRED-浏览器身份验证是必需的,但已禁用。投掷时间:
- 步骤0:会话中没有令牌和UAA凭据,服务密钥存在,但需要浏览器身份验证 - 步骤2b:刷新令牌已过期/无效,新令牌需要浏览器身份验证 - 错误包括 destination 上下文属性 - 用例:浏览器无法打开的非交互式环境(MCP stdio、Cline)
防御性设计原则:
- 所有外部操作都包含在try-catch中:文件可能丢失/损坏,网络可能出现故障
- 优雅降级:存储错误触发回退机制(serviceKey→ 会话→ 供应商)
- 详细的错误上下文:日志包括文件路径、错误代码、缺少调试字段
- 严重错误快速失败:写入失败和提供程序错误立即抛出(无法恢复)
- 没有关于注入依赖关系的假设:所有被视为可能不可靠的商店/供应商
处理的错误场景示例:
- 会话文件在操作过程中被删除→ 使用服务密钥
- 服务密钥的JSON无效→ 日志解析错误,使用会话数据
- 令牌刷新期间网络超时→ 记录超时,抛出描述性错误
- 文件权限被拒绝→ 记录文件路径错误,抛出
refreshToken(destination: string): Promise
强制刷新目标令牌。呼叫 getToken() 运行完整的刷新流并保存更新的令牌。
clearCache(destination: string): void
清除特定目标的缓存令牌。
clearAllCache(): void
清除所有缓存的令牌。
令牌提供商
该软件包使用 ITokenProvider 令牌获取接口。提供程序实现在 @mcp-abap-adt/auth-providers:
ClientCredentialsProvider-用于XSUAA身份验证(范围缩小)
- 使用client_credentials授权类型 - 无需浏览器交互 - 未提供刷新令牌
AuthorizationCodeProvider-用于BTP/ABAP身份验证(全范围)
- 构造函数接受可选 browserAuthPort?: number 参数(默认值:3001) - 如果请求的端口正在使用中,则自动查找可用端口(防止 EADDRINUSE 错误) - 身份验证完成后,服务器正确关闭所有连接并释放端口 - 在与其他服务(例如代理服务器)一起运行时,使用自定义端口以避免冲突 - 使用基于浏览器的OAuth2流(如果没有刷新令牌) - 使用刷新令牌(如果可用) - 提供刷新令牌以供将来使用
示例用法:
import {
AuthBroker,
XsuaaServiceKeyStore,
XsuaaSessionStore,
AbapServiceKeyStore,
BtpSessionStore
} from '@mcp-abap-adt/auth-broker';
import {
ClientCredentialsProvider,
AuthorizationCodeProvider,
} from '@mcp-abap-adt/auth-providers';
// XSUAA authentication
const xsuaaBroker = new AuthBroker({
sessionStore: new XsuaaSessionStore('/path/to/sessions', 'https://mcp.example.com'),
tokenProvider: new ClientCredentialsProvider({
uaaUrl: 'https://auth.example.com',
clientId: '...',
clientSecret: '...',
}),
});
// XSUAA authentication - with service key initialization
const xsuaaBrokerWithServiceKey = new AuthBroker({
sessionStore: new XsuaaSessionStore('/path/to/sessions', 'https://mcp.example.com'),
serviceKeyStore: new XsuaaServiceKeyStore('/path/to/keys'),
tokenProvider: new ClientCredentialsProvider({
uaaUrl: 'https://auth.example.com',
clientId: '...',
clientSecret: '...',
}),
}, 'none');
// BTP authentication
const btpBroker = new AuthBroker({
sessionStore: new BtpSessionStore('/path/to/sessions', 'https://abap.example.com'),
tokenProvider: new AuthorizationCodeProvider({
uaaUrl: 'https://auth.example.com',
clientId: '...',
clientSecret: '...',
browser: 'system',
}),
});
// BTP authentication - with service key and provider (for browser auth)
const btpBrokerFull = new AuthBroker({
sessionStore: new BtpSessionStore('/path/to/sessions', 'https://abap.example.com'),
serviceKeyStore: new AbapServiceKeyStore('/path/to/keys'),
tokenProvider: new AuthorizationCodeProvider({
uaaUrl: 'https://auth.example.com',
clientId: '...',
clientSecret: '...',
browser: 'system',
}),
});CLI:mcp身份验证
生成或刷新 .env/使用AuthBroker+存储的JSON输出:
mcp-auth [options]
mcp-auth --service-key
--output
[--env
] [--type abap|xsuaa] [--credential] [--browser auto|none|system|chrome|edge|firefox] [--format json|env]备注:已发布的CLI编译为 dist/bin 并且不需要 tsx 在运行时。要使用repo,请运行 npm install 和 npm run build.
身份验证流程:
- 违约:
authorization_code(基于浏览器的OAuth2) --credential:client_credentials(clientId/clientSecret,无浏览器)
浏览器选项(用于authorization_code):
auto(默认):尝试打开浏览器,回退到显示URLnone:在控制台中显示URL并等待回调(无浏览器)system/chrome/edge/firefox:打开特定浏览器
示例:
# Auth code (default via service key)
mcp-auth auth-code --service-key ./abap.json --output ./abap.env --type abap
# OIDC SSO (device flow example)
mcp-auth oidc --flow device --issuer https://issuer --client-id my-client --output ./sso.env --type xsuaa
# SAML2 pure (cookie)
mcp-auth saml2-pure --idp-sso-url https://idp/sso --sp-entity-id my-sp --output ./saml.env --type abap
# SAML2 bearer (in progress, requires --dev)
mcp-auth saml2-bearer --dev --service-key ./mcp.json --assertion --output ./sso.env --type xsuaa
# ABAP: authorization_code (default, opens browser)
mcp-auth --service-key ./abap.json --output ./abap.env --type abap
# ABAP: authorization_code (show URL in console, no browser)
mcp-auth --service-key ./abap.json --output ./abap.env --type abap --browser none
# XSUAA: authorization_code (default)
mcp-auth --service-key ./mcp.json --output ./mcp.env --type xsuaa
# XSUAA: client_credentials (special cases)
mcp-auth --service-key ./mcp.json --output ./mcp.env --type xsuaa --credential
# Using existing .env for refresh token
mcp-auth --env ./mcp.env --service-key ./mcp.json --output ./mcp.env --type xsuaaCLI:mcp-sso
通过SSO提供程序(OIDC/SAML)获取令牌并生成 .env/JSON输出:
mcp-sso [options]
mcp-sso --protocol --flow --output
[--type abap|xsuaa] [--format env|json] [--env
] [--config
]支持的流量:
- OIDC:
browser,device,password,token_exchange - SAML2:
bearer,pure
示例:
# OIDC browser flow
mcp-sso oidc --flow browser --issuer https://issuer --client-id my-client --output ./sso.env --type xsuaa
# OIDC browser flow (manual code / OOB)
mcp-sso oidc --flow browser --token-endpoint https://issuer/token --client-id my-client --code --redirect-uri urn:ietf:wg:oauth:2.0:oob --output ./sso.env --type xsuaa
# OIDC device flow
mcp-sso oidc --flow device --issuer https://issuer --client-id my-client --output ./sso.env --type xsuaa
# OIDC password flow
mcp-sso oidc --flow password --token-endpoint https://issuer/oauth/token --client-id my-client --username user --password pass --output ./sso.env --type xsuaa
# OIDC token exchange
mcp-sso oidc --flow token_exchange --issuer https://issuer --client-id my-client --subject-token --output ./sso.env --type xsuaa
# SAML bearer flow (assertion -> token)
mcp-sso bearer --idp-sso-url https://idp/sso --sp-entity-id my-sp --token-endpoint https://uaa.example/oauth/token --assertion --output ./sso.env --type xsuaa
# SAML pure flow (cookie)
mcp-sso saml2 --flow pure --idp-sso-url https://idp/sso --sp-entity-id my-sp --assertion --cookie "SAP_SESSION=..." --output ./sso.env --type abapSAML令牌别名(XSUAA): 如果您的IdP需要令牌别名端点,请传递SAML元数据XML:
mcp-sso bearer --saml-metadata ./saml-sp.xml --assertion --service-key ./service-key.json --output ./sso.env --type xsuaa本地密钥斗篷(OIDC+SAML测试)
用于本地测试 mcp-sso,包括一个可运行的Keycloak设置 (OIDC浏览器/密码/设备+SAML断言捕获)。
cd tests/keycloak
docker compose up -d然后使用:
node dist/bin/mcp-sso.js \
oidc \
--flow browser \
--issuer http://localhost:8080/realms/mcp-sso \
--client-id mcp-sso-cli \
--scopes openid,profile,email \
--output /tmp/keycloak.env \
--type xsuaa看 tests/keycloak/README.md 用于设备流和SAML示例。
XSUAA演示(CAP)
用于测试XSUAA流的最小CAP应用程序包含在 tests/sso-demo. 它使 authorization_code 和 saml2-bearer 赠款类型和提供 简单 CatalogService。参见 tests/sso-demo/readme.md 用于部署步骤。
配置文件: 您可以通过提供程序配置传递JSON文件:
{
"protocol": "oidc",
"flow": "device",
"issuerUrl": "https://issuer",
"clientId": "my-client",
"scopes": ["openid", "profile"]
}实用程序脚本
生成 .env 服务密钥中的文件:
npm run generate-env [service-key-path] [session-path]测试
测试位于 src/__tests__/ 并使用Jest作为测试运行器。
运行测试
# Run all tests
npm test
# Run specific test file (all tests in that file)
npm test -- getToken.test.ts
npm test -- refreshToken.test.ts
# Run specific test by name/pattern
npm test -- getToken.test.ts -t "Test 1"
npm test -- getToken.test.ts -t "Test 2"
npm test -- getToken.test.ts -t "Test 3"
# Run test group (e.g., all getToken tests)
npm test -- getToken.test.ts
# Note: Test 2 requires Test 1 to pass first (test1Passed flag)
# To run Test 2 alone, you may need to run all tests in the file:
npm test -- getToken.test.ts测试结构
测试设计为按顺序运行(保证 maxWorkers: 1 和 maxConcurrency: 1 在 jest.config.js):
- 测试1:验证不存在的目标的错误处理(
NO_EXISTS)
- 要求: NO_EXISTS.json 不应该存在
- 测试2:当服务密钥存在但
.env文件没有
- 要求: TRIAL.json 必须存在, TRIAL.env 不应该存在 - 将打开浏览器进行OAuth身份验证
- 测试3:使用现有测试令牌刷新
.env文件
- 要求: TRIAL.json 和 TRIAL.env 必须存在 - 如果满足以下条件,可以独立运行 .env 文件存在(手动创建或由测试2创建)
测试设置
- 复制
tests/test-config.yaml.template到tests/test-config.yaml - 填写配置值(路径、目的地、XSUAA的MCP URL)
- 将服务密钥文件放入已配置的
service_keys_dir:
- {destination}.json 对于ABAP测试(例如。, trial.json) - {btp_destination}.json 对于XSUAA测试(例如。, btp.json)
如果缺少所需文件或配置包含占位符,测试将自动跳过。
文档
完整的文档可在 docs/ 目录:
看 docs/README.md 查看完整的文档索引。
贡献者
感谢所有贡献者!看 贡献者.md 查看完整列表。
许可证
麻省理工学院
