@mcp abap adt/auth商店

存储MCP ABAP ADT身份验证代理-BTP、ABAP和XSUAA实现。
此软件包为服务密钥和会话提供基于文件的内存存储 @mcp-abap-adt/auth-broker 包裹。
安装
npm install @mcp-abap-adt/auth-stores概述
此包实现了 IServiceKeyStore 和 ISessionStore 接口来自 @mcp-abap-adt/interfaces:
- 服务密钥存储:从指定目录读取服务密钥JSON文件
- 会话存储:从/向读取/写入会话数据
.env文件或内存存储 - 文件处理程序:用于处理JSON和ENV文件的实用程序类
责任和设计原则
核心开发原则
仅接口通信该方案遵循一个基本的发展原则: 所有与外部依赖关系的交互都只能通过接口进行代码知道 没有超出接口中定义的内容.
这意味着:
- 不知道其他包中的具体实现类
- 不了解接口中未定义的内部数据结构或方法
- 不假设接口契约之外的实现行为
- 不访问接口中未明确定义的属性或方法
这一原则确保:
- 松散结合:存储与其他包中的具体实现解耦
- 灵活性:可以添加新的实现,而无需修改存储
- 可测试性:易于模拟测试依赖关系
- 可维护性:对实现的更改不会影响商店
包装责任
该包负责:
- 实施存储接口:提供以下内容的具体实施
IServiceKeyStore和ISessionStore中定义的接口@mcp-abap-adt/interfaces - 文件I/O操作:处理读取和写入服务密钥JSON文件和会话
.env文件 - 数据格式转换:在接口类型之间转换(
IConfig,IConnectionConfig,IAuthorizationConfig)以及内部存储格式 - 平台特定处理:为ABAP、BTP和XSUAA提供不同的存储实现及其特定的数据格式
这个包有什么作用
- 实现接口:提供以下内容的具体实施
IServiceKeyStore和ISessionStore - 处理文件操作:读取/写入JSON和
.env使用原子操作的文件 - 管理数据格式:在接口类型和内部存储格式之间转换(例如。,
AbapSessionData,BtpBaseSessionData) - 提供实用程序:文件处理程序(
JsonFileHandler,EnvFileHandler)用于安全的文件操作
此软件包不做什么
- 不实现身份验证逻辑:令牌获取和OAuth2流由以下人员处理
@mcp-abap-adt/auth-providers - 不编排身份验证:令牌生命周期管理由以下人员处理
@mcp-abap-adt/auth-broker - 不知道令牌验证:令牌验证逻辑不是此包的一部分
- 不与外部服务交互:所有HTTP请求和OAuth流都由其他包处理
外部依赖
此包与外部包交互 仅通过接口:
@mcp-abap-adt/interfaces:使用接口(IServiceKeyStore,ISessionStore,IConfig,IConnectionConfig,IAuthorizationConfig,ILogger)-不知道其他包中的具体实现- 不直接依赖于其他包:所有交互都是通过定义良好的接口进行的
店铺类型
服务密钥存储
服务密钥存储读取包含UAA凭据和连接信息的JSON文件:
BtpServiceKeyStore-读取基本BTP的XSUAA服务密钥(直接XSUAA格式)AbapServiceKeyStore-读取ABAP服务密钥(嵌套uaa对象)XsuaaServiceKeyStore-读取XSUAA服务密钥(BtpServiceKeyStore的别名)
会话存储
会话存储管理身份验证令牌和配置:
基于文件的存储 (坚持 .env 文件):
BtpSessionStore-使用以下方式存储基础BTP会话XSUAA_*环境变量AbapSessionStore-使用存储ABAP会话SAP_*环境变量XsuaaSessionStore-使用以下命令存储XSUAA会话XSUAA_*环境变量
内存存储 (非持久、安全):
SafeBtpSessionStore-基本BTP会话的内存存储SafeAbapSessionStore-ABAP会话的内存存储SafeXsuaaSessionStore-XSUAA会话的内存存储
基于文件的单文件存储:
EnvFileSessionStore-从特定位置读取.env文件路径(例如。,--env /path/to/.env)
用法
BTP商店(不带sapUrl的基本BTP)
import { BtpServiceKeyStore, BtpSessionStore, SafeBtpSessionStore } from '@mcp-abap-adt/auth-stores';
// Service key store - reads {destination}.json files from directory
const serviceKeyStore = new BtpServiceKeyStore('/path/to/service-keys');
// File-based session store - reads/writes {destination}.env files
// defaultServiceUrl is REQUIRED (cannot be obtained from service key)
const sessionStore = new BtpSessionStore('/path/to/sessions', 'https://default.mcp.com', logger);
// In-memory session store (non-persistent)
// defaultServiceUrl is REQUIRED (cannot be obtained from service key)
const safeSessionStore = new SafeBtpSessionStore('https://default.mcp.com', logger);ABAP存储区(带sapUrl)
import {
AbapServiceKeyStore,
AbapSessionStore,
SafeAbapSessionStore,
SamlSessionStore,
SafeSamlSessionStore,
} from '@mcp-abap-adt/auth-stores';
// Service key store - reads ABAP service keys with nested uaa object
const serviceKeyStore = new AbapServiceKeyStore('/path/to/service-keys');
// File-based session store - stores ABAP sessions with SAP_* env vars
const sessionStore = new AbapSessionStore('/path/to/sessions');
// In-memory session store
const safeSessionStore = new SafeAbapSessionStore();
// SAML aliases (same behavior as ABAP stores)
const samlSessionStore = new SamlSessionStore('/path/to/sessions');
const safeSamlSessionStore = new SafeSamlSessionStore();XSUAA商店
import { XsuaaServiceKeyStore, XsuaaSessionStore, SafeXsuaaSessionStore } from '@mcp-abap-adt/auth-stores';
// Service key store - reads XSUAA service keys
const serviceKeyStore = new XsuaaServiceKeyStore('/path/to/service-keys');
// File-based session store - stores XSUAA sessions
// defaultServiceUrl is REQUIRED (cannot be obtained from service key)
const sessionStore = new XsuaaSessionStore('/path/to/sessions', 'https://default.mcp.com', logger);
// In-memory session store
// defaultServiceUrl is REQUIRED (cannot be obtained from service key)
const safeSessionStore = new SafeXsuaaSessionStore('https://default.mcp.com', logger);EnvFileSessionStore(单个文件)
EnvFileSessionStore 从特定位置读取连接配置 .env 文件路径而不是目录。这对 --env CLI选项。
import { EnvFileSessionStore } from '@mcp-abap-adt/auth-stores';
// Create store pointing to specific .env file
const store = new EnvFileSessionStore('/path/to/.env', logger);
// Check the auth type from the file
const authType = store.getAuthType(); // 'basic' | 'jwt' | 'saml' | null
// Load session (works like other session stores)
const config = await store.loadSession('default');
console.log(config?.serviceUrl, config?.authType);
// For basic auth
console.log(config?.username, config?.password);
// For JWT auth
console.log(config?.authorizationToken, config?.refreshToken);环境文件格式:
# Connection
SAP_URL=https://your-sap-system.com
SAP_CLIENT=100
# Auth type: 'basic', 'jwt', or 'saml' (defaults to 'basic')
SAP_AUTH_TYPE=basic
# Basic auth credentials
SAP_USERNAME=your-username
SAP_PASSWORD=your-password
# OR JWT auth
# SAP_AUTH_TYPE=jwt
# SAP_JWT_TOKEN=your-jwt-token
# SAP_REFRESH_TOKEN=your-refresh-token
# SAP_UAA_URL=https://uaa.example.com
# SAP_UAA_CLIENT_ID=client-id
# SAP_UAA_CLIENT_SECRET=client-secret
# OR SAML auth (session cookies, base64-encoded)
# SAP_AUTH_TYPE=saml
# SAP_SESSION_COOKIES_B64=base64-encoded-cookie-string重要:这家店是 只读 对于文件。令牌更新(例如,刷新的JWT令牌)仅存储在内存中,不会修改原始令牌 .env 文件。
目录配置
所有存储在构造函数中都接受单个目录路径:
// Single directory path
const store = new BtpServiceKeyStore('/path/to/service-keys');
// File-based session stores automatically create directory in constructor if it doesn't exist
const sessionStore = new AbapSessionStore('/path/to/sessions'); // Directory created automatically备注:基于文件的会话存储(AbapSessionStore, BtpSessionStore, XsuaaSessionStore)如果目录不存在,则自动在构造函数中创建该目录。施工完成后,商店即可立即使用。
默认服务URL配置
适用于XSUAA和BTP商店: defaultServiceUrl 是 必需的 在构造函数中,因为 serviceUrl 无法从服务密钥中获取:
XsuaaSessionStore(directory, defaultServiceUrl, log?)-defaultServiceUrl是必需的SafeXsuaaSessionStore(defaultServiceUrl, log?)-defaultServiceUrl是必需的BtpSessionStore(directory, defaultServiceUrl, log?)-defaultServiceUrl是必需的SafeBtpSessionStore(defaultServiceUrl, log?)-defaultServiceUrl是必需的
ABAP商店: defaultServiceUrl 是 可选的 因为 serviceUrl 可以从ABAP服务密钥中获得:
AbapSessionStore(directory, log?, defaultServiceUrl?)-defaultServiceUrl是可选的SafeAbapSessionStore(log?, defaultServiceUrl?)-defaultServiceUrl是可选的
这 defaultServiceUrl 通过创建新会话时使用 setConnectionConfig 或 setAuthorizationConfig 如果 config.serviceUrl 未提供。它从不用于修改现有会话。
服务密钥格式
ABAP服务密钥 (带嵌套 uaa 对象):
{
"uaa": {
"url": "https://...authentication...hana.ondemand.com",
"clientid": "...",
"clientsecret": "..."
},
"abap": {
"url": "https://...abap...hana.ondemand.com",
"client": "001"
}
}XSUAA服务密钥 (直接格式):
{
"url": "https://...authentication...hana.ondemand.com",
"clientid": "...",
"clientsecret": "...",
"apiurl": "https://...api...hana.ondemand.com"
}文件处理程序
此包提供了用于安全文件操作的实用程序类:
错误处理
所有服务密钥存储都会抛出键入错误,以更好地处理错误:
import {
BtpServiceKeyStore,
FileNotFoundError,
ParseError,
InvalidConfigError
} from '@mcp-abap-adt/auth-stores';
import { STORE_ERROR_CODES } from '@mcp-abap-adt/interfaces';
const serviceKeyStore = new BtpServiceKeyStore('/path/to/keys');
try {
const authConfig = await serviceKeyStore.getAuthorizationConfig('TRIAL');
console.log('Auth config loaded:', authConfig);
} catch (error: any) {
if (error.code === STORE_ERROR_CODES.FILE_NOT_FOUND) {
// File not found - returns null instead of throwing
console.error('Service key file not found:', error.filePath);
} else if (error.code === STORE_ERROR_CODES.PARSE_ERROR) {
// JSON parsing failed or invalid format
console.error('Failed to parse service key:', error.filePath);
console.error('Cause:', error.cause);
} else if (error.code === STORE_ERROR_CODES.INVALID_CONFIG) {
// Required UAA fields missing - returns null instead of throwing
console.error('Invalid config:', error.missingFields);
} else if (error.code === STORE_ERROR_CODES.STORAGE_ERROR) {
// File write/permission error
console.error('Storage operation failed:', error.operation);
console.error('Cause:', error.cause);
} else {
// Generic error
console.error('Unexpected error:', error.message);
}
}错误类型:
FileNotFoundError-找不到服务密钥文件(包括filePath)ParseError-JSON解析失败或格式无效(包括filePath和cause)InvalidConfigError-缺少必需的配置字段(包括missingFields阵列)StorageError-文件写入或权限错误(包括operation和cause)
备注:大多数错误会导致 null 返回值而不是异常。只有致命错误(如JSON解析失败)才会引发异常。
文件处理程序
用于处理文件的实用程序类:
Jsonfilehandler
import { JsonFileHandler } from '@mcp-abap-adt/auth-stores';
// Load JSON file
const data = await JsonFileHandler.load('TRIAL.json', '/path/to/directory');
// Save JSON file (atomic write)
await JsonFileHandler.save('/path/to/file.json', { key: 'value' });EnvFileHandler
import { EnvFileHandler } from '@mcp-abap-adt/auth-stores';
// Load .env file
const vars = await EnvFileHandler.load('TRIAL.env', '/path/to/directory');
// Save .env file (atomic write, preserves existing variables)
await EnvFileHandler.save('/path/to/file.env', {
KEY1: 'value1',
KEY2: 'value2'
}, true); // preserveExisting = true公用事业
常量
import {
ABAP_AUTHORIZATION_VARS,
ABAP_CONNECTION_VARS,
BTP_AUTHORIZATION_VARS,
BTP_CONNECTION_VARS,
XSUAA_AUTHORIZATION_VARS,
XSUAA_CONNECTION_VARS
} from '@mcp-abap-adt/auth-stores';服务密钥加载器
import { loadServiceKey, loadXSUAAServiceKey } from '@mcp-abap-adt/auth-stores';
// Load ABAP service key (auto-detects format)
const abapKey = await loadServiceKey('TRIAL', '/path/to/service-keys');
// Load XSUAA service key
const xsuaaKey = await loadXSUAAServiceKey('mcp', '/path/to/service-keys');调试日志记录
商店支持通过以下方式进行可选日志记录 ILogger 界面。要启用详细日志记录,请执行以下操作:
在代码中使用记录器
import { AbapServiceKeyStore } from '@mcp-abap-adt/auth-stores';
import type { ILogger } from '@mcp-abap-adt/interfaces';
// Create logger (or use your own implementation)
const logger: ILogger = {
debug: (msg) => console.debug(msg),
info: (msg) => console.info(msg),
warn: (msg) => console.warn(msg),
error: (msg) => console.error(msg),
};
// Pass logger to store constructor
const store = new AbapServiceKeyStore('/path/to/service-keys', logger);
const sessionStore = new AbapSessionStore('/path/to/sessions', logger);在测试中使用测试记录器
对于测试,请使用 createTestLogger 尊重环境变量的助手:
import { createTestLogger } from './__tests__/helpers/testLogger';
// Logger will output only if DEBUG_AUTH_STORES=true is set
const logger = createTestLogger('MY-TEST');
const store = new AbapServiceKeyStore('/path/to/service-keys', logger);环境变量
启用测试登录或使用时 createTestLogger:
# Enable logging for auth stores (short name)
DEBUG_STORES=true npm test
# Or use long name (backward compatibility)
DEBUG_AUTH_STORES=true npm test
# Or enable via general DEBUG variable
DEBUG=true npm test
# Or include in DEBUG list
DEBUG=stores npm test
# Or
DEBUG=auth-stores npm test
# Set log level (debug, info, warn, error)
LOG_LEVEL=debug npm test备注:日志记录需要通过环境变量显式启用。两者 DEBUG_STORES (简短)和 DEBUG_AUTH_STORES (long)支持向后兼容性。
日志显示:
- 文件 操作:读取/写入哪些文件、文件大小、文件路径
- 解析操作:解析数据的结构、验证结果、找到的键
- 存储操作:保存/加载哪些数据、令牌长度、刷新令牌存在、URL
- 令牌格式化:为了安全性和可读性,令牌以截断格式(开始…结束)记录
- 错误:带有上下文的详细错误信息
日志记录功能:
- 令牌格式:为了安全起见,令牌以截断格式(开始…结束)记录
- 结构化日志:用途
DefaultLogger从@mcp-abap-adt/logger使用图标和级别前缀进行正确格式化 - 日志级别:通过控制
LOG_LEVEL或AUTH_LOG_LEVEL环境变量(错误、警告、信息、调试)
输出示例 DEBUG_STORES=true LOG_LEVEL=debug:
[INFO] ℹ️ [TEST-STORE] Reading service key file: /path/to/TRIAL.json
[DEBUG] 🐛 [TEST-STORE] File read successfully, size: 121 bytes, keys: uaa
[DEBUG] 🐛 [TEST-STORE] Parsed service key structure: hasUaa(true), uaaKeys(url, clientid, clientsecret)
[INFO] ℹ️ [TEST-STORE] Authorization config loaded from /path/to/TRIAL.json: uaaUrl(https://...authentication...), clientId(test-client...)
[DEBUG] 🐛 [TEST-STORE] Reading env file: /path/to/TRIAL.env
[DEBUG] 🐛 [TEST-STORE] Env file read successfully, size: 245 bytes
[INFO] ℹ️ [TEST-STORE] Session loaded for TRIAL: token(2263 chars, eyJ0eXAiOiJKV1QiLCJqaWQiO...Q5ti7aYmEzItIDuLp7axNYo6w), refreshToken(fcc971e1cf1548629216a96b0680eb85-r), sapUrl(https://...abap...)备注:日志记录仅在明确提供日志记录程序时才有效。如果没有传递记录器,商店将不会向控制台输出任何内容。
测试
该包包括单元测试(使用模拟文件系统)和集成测试(使用真实文件)。
单元测试
单元测试使用Jest模拟文件系统操作:
npm test集成测试
集成测试使用来自的真实文件 tests/test-config.yaml:
- 复制
tests/test-config.yaml.template到tests/test-config.yaml - 填写真实的路径和目的地
- 运行测试-如果已配置,集成测试将使用真实文件
auth_broker:
paths:
service_keys_dir: ~/.config/mcp-abap-adt/service-keys/
sessions_dir: ~/.config/mcp-abap-adt/sessions/
abap:
destination: "TRIAL"
xsuaa:
btp_destination: "mcp"
mcp_url: "https://..."如果出现以下情况,集成测试将跳过 test-config.yaml 未配置或包含占位符值。
建筑
文件操作
- 服务密钥存储 使用
JsonFileHandler读取JSON文件 - 会话存储 使用
EnvFileHandler读/写.env文件 - 所有文件写入都是原子性的(写入临时文件,然后重命名)
门店实施
- 所有商店实施
IServiceKeyStore或ISessionStore接口来自@mcp-abap-adt/interfaces - 存储在构造函数中接受单个目录路径
- 如果目录不存在,基于文件的会话存储会自动在构造函数中创建目录
- 会话存储在调用时自动创建会话
setConnectionConfig或setAuthorizationConfig(无需致电saveSession第一) - 内存存储(
Safe*SessionStore)不将数据持久化到磁盘
会话存储行为
会话商店旨在与 AuthBroker:
- 施工后准备就绪:基于文件的存储会自动创建目录,存储可以立即使用
- 自动创建会话:呼叫
setConnectionConfig或setAuthorizationConfig在空存储上创建新会话 - ABAP商店:需要
serviceUrl创建新会话时(从配置或defaultServiceUrl参数) - BTP/XSUAA商店:需要
defaultServiceUrl在构造函数中(无法从服务密钥获得),在以下情况下创建新会话时使用config.serviceUrl未提供 - 令牌更新:
setConnectionConfig更新令牌(如果提供),保留现有令牌(如果不提供) - 会话更新:更新现有会话时,仅
config.serviceUrl如果明确提供,则使用;defaultServiceUrl从不用于修改现有会话
依赖项
@mcp-abap-adt/interfaces(^0.1.4)-接口定义(IServiceKeyStore,ISessionStore,IConfig,IConnectionConfig,IAuthorizationConfig,ILogger)dotenv-环境变量解析
许可证
麻省理工学院
