@mcp-abap-adt/connection 1.8.1 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,5 @@
1
+ import type { ICertificateMaterial, ICertificateMaterialLoader, ISapConfig } from '@mcp-abap-adt/interfaces';
2
+ export declare class FileCertificateMaterialLoader implements ICertificateMaterialLoader {
3
+ load(config: ISapConfig): Promise<ICertificateMaterial>;
4
+ }
5
+ //# sourceMappingURL=FileCertificateMaterialLoader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FileCertificateMaterialLoader.d.ts","sourceRoot":"","sources":["../../src/auth/FileCertificateMaterialLoader.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,oBAAoB,EACpB,0BAA0B,EAC1B,UAAU,EACX,MAAM,0BAA0B,CAAC;AAElC,qBAAa,6BACX,YAAW,0BAA0B;IAE/B,IAAI,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,oBAAoB,CAAC;CAyB9D"}
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FileCertificateMaterialLoader = void 0;
4
+ const promises_1 = require("node:fs/promises");
5
+ class FileCertificateMaterialLoader {
6
+ async load(config) {
7
+ const hasPem = !!(config.certPath || config.certKeyPath); // any PEM-style field present
8
+ const hasPfx = !!config.certPfxPath;
9
+ if (hasPem && hasPfx) {
10
+ throw new Error('Certificate auth: provide either PEM (certPath+certKeyPath) OR certPfxPath, not both.');
11
+ }
12
+ if (hasPfx) {
13
+ return {
14
+ pfx: await (0, promises_1.readFile)(config.certPfxPath),
15
+ passphrase: config.certPassphrase,
16
+ };
17
+ }
18
+ if (config.certPath && config.certKeyPath) {
19
+ return {
20
+ cert: await (0, promises_1.readFile)(config.certPath),
21
+ key: await (0, promises_1.readFile)(config.certKeyPath),
22
+ passphrase: config.certPassphrase,
23
+ };
24
+ }
25
+ throw new Error('Certificate auth requires certPfxPath OR (certPath AND certKeyPath).');
26
+ }
27
+ }
28
+ exports.FileCertificateMaterialLoader = FileCertificateMaterialLoader;
@@ -0,0 +1,3 @@
1
+ /** Thin, lazily-loaded wrapper over the optional `kerberos` native package. */
2
+ export declare function generateSpnegoToken(spn: string): Promise<string>;
3
+ //# sourceMappingURL=kerberosSpnego.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kerberosSpnego.d.ts","sourceRoot":"","sources":["../../src/auth/kerberosSpnego.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,wBAAsB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAqBtE"}
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.generateSpnegoToken = generateSpnegoToken;
37
+ /** Thin, lazily-loaded wrapper over the optional `kerberos` native package. */
38
+ async function generateSpnegoToken(spn) {
39
+ // optional peer dep; typed as any because the module/types may be absent
40
+ let kerberos;
41
+ try {
42
+ // @ts-expect-error optional peer dependency — module may be absent at build time (TS2307)
43
+ kerberos = await Promise.resolve().then(() => __importStar(require('kerberos')));
44
+ }
45
+ catch {
46
+ throw new Error('Kerberos authentication requires the optional "kerberos" package. ' +
47
+ 'Install it (needs GSSAPI dev libs on Linux / build tools on Windows): npm i kerberos');
48
+ }
49
+ const client = await kerberos.initializeClient(spn, {});
50
+ await client.step('');
51
+ const token = client.response;
52
+ if (!token) {
53
+ throw new Error('Kerberos: no SPNEGO token produced (no TGT? run kinit, or check SPN).');
54
+ }
55
+ return token;
56
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"sapConfig.d.ts","sourceRoot":"","sources":["../../src/config/sapConfig.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,UAAU,EACV,WAAW,EACX,iBAAiB,EAClB,MAAM,0BAA0B,CAAC;AAGlC,YAAY,EAAE,WAAW,EAAE,iBAAiB,EAAE,CAAC;AAC/C,MAAM,MAAM,SAAS,GAAG,UAAU,CAAC;AAEnC,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CAuB7D"}
1
+ {"version":3,"file":"sapConfig.d.ts","sourceRoot":"","sources":["../../src/config/sapConfig.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,UAAU,EACV,WAAW,EACX,iBAAiB,EAClB,MAAM,0BAA0B,CAAC;AAGlC,YAAY,EAAE,WAAW,EAAE,iBAAiB,EAAE,CAAC;AAC/C,MAAM,MAAM,SAAS,GAAG,UAAU,CAAC;AAEnC,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CA4B7D"}
@@ -22,5 +22,10 @@ function sapConfigSignature(config) {
22
22
  jwtToken: jwtTokenPreview,
23
23
  refreshToken: refreshTokenPreview,
24
24
  sessionCookies: sessionCookiesPreview,
25
+ certPath: config.certPath ?? null,
26
+ certKeyPath: config.certKeyPath ?? null,
27
+ certPfxPath: config.certPfxPath ?? null,
28
+ certPassphrase: config.certPassphrase ? 'set' : null,
29
+ kerberosSpn: config.kerberosSpn ?? null,
25
30
  });
26
31
  }
@@ -81,6 +81,11 @@ declare abstract class AbstractAbapConnection implements AbapConnection {
81
81
  protected getCookies(): string | null;
82
82
  protected setInitialCookies(cookies: string): void;
83
83
  private updateCookiesFromResponse;
84
+ /**
85
+ * Subclasses override to inject extra https.Agent options (e.g. mTLS cert/key/pfx).
86
+ * The returned options are merged with the base options (rejectUnauthorized).
87
+ */
88
+ protected getHttpsAgentOptions(): import('node:https').AgentOptions;
84
89
  private getAxiosInstance;
85
90
  private ensureFreshCsrfToken;
86
91
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"AbstractAbapConnection.d.ts","sourceRoot":"","sources":["../../src/connection/AbstractAbapConnection.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,YAAY,EAAkB,MAAM,0BAA0B,CAAC;AAM7E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAE5C,OAAO,KAAK,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAG9E,uBAAe,sBAAuB,YAAW,cAAc;IAW3D,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI;IAX3C,OAAO,CAAC,aAAa,CAA8B;IACnD,OAAO,CAAC,SAAS,CAAuB;IACxC,OAAO,CAAC,OAAO,CAAuB;IACtC,OAAO,CAAC,WAAW,CAAkC;IACrD,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,SAAS,CAAuB;IACxC,OAAO,CAAC,WAAW,CAAyC;IAC5D,OAAO,CAAC,eAAe,CAAU;IAEjC,SAAS,aACU,MAAM,EAAE,SAAS,EACf,MAAM,EAAE,OAAO,GAAG,IAAI,EACzC,SAAS,CAAC,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE;QAAE,eAAe,CAAC,EAAE,OAAO,CAAA;KAAE;IAqBzC;;;;;;;;;;;OAWG;IACH,cAAc,CAAC,IAAI,EAAE,UAAU,GAAG,WAAW,GAAG,IAAI;IAUpD;;OAEG;IACH,cAAc,IAAI,WAAW,GAAG,UAAU;IAI1C;;;OAGG;IACH,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAKrC;;OAEG;IACH,YAAY,IAAI,MAAM,GAAG,IAAI;IAI7B,SAAS,IAAI,SAAS;IAItB,KAAK,IAAI,IAAI;IAYP,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC;IAI7B,cAAc,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAevD;;;;;;;;OAQG;IACH,QAAQ,CAAC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAE3B,cAAc,CAAC,CAAC,GAAG,GAAG,EAAE,CAAC,GAAG,GAAG,EACnC,OAAO,EAAE,kBAAkB,GAC1B,OAAO,CAAC,YAAY,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IA+R9B,SAAS,CAAC,QAAQ,CAAC,wBAAwB,IAAI,MAAM;IAErD;;;OAGG;cACa,cAAc,CAC5B,GAAG,EAAE,MAAM,EACX,UAAU,GAAE,MAAgC,EAC5C,UAAU,GAAE,MAAgC,GAC3C,OAAO,CAAC,MAAM,CAAC;IA2ClB;;OAEG;YACW,0BAA0B;IAsKxC;;OAEG;IACH,SAAS,CAAC,YAAY,IAAI,MAAM,GAAG,IAAI;IAIvC;;OAEG;IACH,SAAS,CAAC,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAIlD;;OAEG;IACH,SAAS,CAAC,UAAU,IAAI,MAAM,GAAG,IAAI;IAIrC,SAAS,CAAC,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAIlD,OAAO,CAAC,yBAAyB;IAkEjC,OAAO,CAAC,gBAAgB;YAqBV,oBAAoB;IAiClC;;;;;;OAMG;IACH,OAAO,CAAC,iBAAiB;IAMzB,OAAO,CAAC,eAAe;CA+BxB;AAGD,OAAO,EAAE,sBAAsB,EAAE,CAAC"}
1
+ {"version":3,"file":"AbstractAbapConnection.d.ts","sourceRoot":"","sources":["../../src/connection/AbstractAbapConnection.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,YAAY,EAAkB,MAAM,0BAA0B,CAAC;AAM7E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAE5C,OAAO,KAAK,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAG9E,uBAAe,sBAAuB,YAAW,cAAc;IAW3D,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI;IAX3C,OAAO,CAAC,aAAa,CAA8B;IACnD,OAAO,CAAC,SAAS,CAAuB;IACxC,OAAO,CAAC,OAAO,CAAuB;IACtC,OAAO,CAAC,WAAW,CAAkC;IACrD,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,SAAS,CAAuB;IACxC,OAAO,CAAC,WAAW,CAAyC;IAC5D,OAAO,CAAC,eAAe,CAAU;IAEjC,SAAS,aACU,MAAM,EAAE,SAAS,EACf,MAAM,EAAE,OAAO,GAAG,IAAI,EACzC,SAAS,CAAC,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE;QAAE,eAAe,CAAC,EAAE,OAAO,CAAA;KAAE;IAqBzC;;;;;;;;;;;OAWG;IACH,cAAc,CAAC,IAAI,EAAE,UAAU,GAAG,WAAW,GAAG,IAAI;IAUpD;;OAEG;IACH,cAAc,IAAI,WAAW,GAAG,UAAU;IAI1C;;;OAGG;IACH,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAKrC;;OAEG;IACH,YAAY,IAAI,MAAM,GAAG,IAAI;IAI7B,SAAS,IAAI,SAAS;IAItB,KAAK,IAAI,IAAI;IAYP,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC;IAI7B,cAAc,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAevD;;;;;;;;OAQG;IACH,QAAQ,CAAC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAE3B,cAAc,CAAC,CAAC,GAAG,GAAG,EAAE,CAAC,GAAG,GAAG,EACnC,OAAO,EAAE,kBAAkB,GAC1B,OAAO,CAAC,YAAY,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IA+R9B,SAAS,CAAC,QAAQ,CAAC,wBAAwB,IAAI,MAAM;IAErD;;;OAGG;cACa,cAAc,CAC5B,GAAG,EAAE,MAAM,EACX,UAAU,GAAE,MAAgC,EAC5C,UAAU,GAAE,MAAgC,GAC3C,OAAO,CAAC,MAAM,CAAC;IA2ClB;;OAEG;YACW,0BAA0B;IAsKxC;;OAEG;IACH,SAAS,CAAC,YAAY,IAAI,MAAM,GAAG,IAAI;IAIvC;;OAEG;IACH,SAAS,CAAC,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAIlD;;OAEG;IACH,SAAS,CAAC,UAAU,IAAI,MAAM,GAAG,IAAI;IAIrC,SAAS,CAAC,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAIlD,OAAO,CAAC,yBAAyB;IAkEjC;;;OAGG;IACH,SAAS,CAAC,oBAAoB,IAAI,OAAO,YAAY,EAAE,YAAY;IAInE,OAAO,CAAC,gBAAgB;YAsBV,oBAAoB;IAiClC;;;;;;OAMG;IACH,OAAO,CAAC,iBAAiB;IAMzB,OAAO,CAAC,eAAe;CA+BxB;AAGD,OAAO,EAAE,sBAAsB,EAAE,CAAC"}
@@ -577,6 +577,13 @@ class AbstractAbapConnection {
577
577
  this.cookies = combined;
578
578
  this.logger?.debug(`[DEBUG] BaseAbapConnection - Updated cookies from response (first 100 chars): ${this.cookies.substring(0, 100)}...`);
579
579
  }
580
+ /**
581
+ * Subclasses override to inject extra https.Agent options (e.g. mTLS cert/key/pfx).
582
+ * The returned options are merged with the base options (rejectUnauthorized).
583
+ */
584
+ getHttpsAgentOptions() {
585
+ return {};
586
+ }
580
587
  getAxiosInstance() {
581
588
  if (!this.axiosInstance) {
582
589
  const rejectUnauthorized = process.env.NODE_TLS_REJECT_UNAUTHORIZED === '1' ||
@@ -586,6 +593,7 @@ class AbstractAbapConnection {
586
593
  this.axiosInstance = axios_1.default.create({
587
594
  httpsAgent: new node_https_1.Agent({
588
595
  rejectUnauthorized,
596
+ ...this.getHttpsAgentOptions(),
589
597
  }),
590
598
  });
591
599
  }
@@ -0,0 +1,21 @@
1
+ import type { AgentOptions } from 'node:https';
2
+ import type { ICertificateMaterialLoader } from '@mcp-abap-adt/interfaces';
3
+ import type { SapConfig } from '../config/sapConfig.js';
4
+ import type { ILogger } from '../logger.js';
5
+ import { AbstractAbapConnection } from './AbstractAbapConnection.js';
6
+ /** Client-certificate (mTLS) authentication. Credential lives on the TLS agent. */
7
+ export declare class CertificateAbapConnection extends AbstractAbapConnection {
8
+ private loader;
9
+ private material;
10
+ constructor(config: SapConfig, logger?: ILogger | null, sessionId?: string, loader?: ICertificateMaterialLoader);
11
+ private static validateConfig;
12
+ protected ensureMaterial(): Promise<void>;
13
+ /**
14
+ * Loads certificate material and primes the session. MUST be called before the first
15
+ * request — the TLS agent is built lazily and needs the client cert present.
16
+ */
17
+ connect(): Promise<void>;
18
+ protected getHttpsAgentOptions(): AgentOptions;
19
+ protected buildAuthorizationHeader(): string;
20
+ }
21
+ //# sourceMappingURL=CertificateAbapConnection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CertificateAbapConnection.d.ts","sourceRoot":"","sources":["../../src/connection/CertificateAbapConnection.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,KAAK,EAEV,0BAA0B,EAC3B,MAAM,0BAA0B,CAAC;AAGlC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AAErE,mFAAmF;AACnF,qBAAa,yBAA0B,SAAQ,sBAAsB;IACnE,OAAO,CAAC,MAAM,CAA6B;IAC3C,OAAO,CAAC,QAAQ,CAAqC;gBAGnD,MAAM,EAAE,SAAS,EACjB,MAAM,CAAC,EAAE,OAAO,GAAG,IAAI,EACvB,SAAS,CAAC,EAAE,MAAM,EAClB,MAAM,CAAC,EAAE,0BAA0B;IAOrC,OAAO,CAAC,MAAM,CAAC,cAAc;cAyBb,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC;IAM/C;;;OAGG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAsB9B,SAAS,CAAC,oBAAoB,IAAI,YAAY;IAU9C,SAAS,CAAC,wBAAwB,IAAI,MAAM;CAG7C"}
@@ -0,0 +1,69 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CertificateAbapConnection = void 0;
4
+ const axios_1 = require("axios");
5
+ const FileCertificateMaterialLoader_js_1 = require("../auth/FileCertificateMaterialLoader.js");
6
+ const AbstractAbapConnection_js_1 = require("./AbstractAbapConnection.js");
7
+ /** Client-certificate (mTLS) authentication. Credential lives on the TLS agent. */
8
+ class CertificateAbapConnection extends AbstractAbapConnection_js_1.AbstractAbapConnection {
9
+ loader;
10
+ material = null;
11
+ constructor(config, logger, sessionId, loader) {
12
+ CertificateAbapConnection.validateConfig(config);
13
+ super(config, logger || null, sessionId);
14
+ this.loader = loader ?? new FileCertificateMaterialLoader_js_1.FileCertificateMaterialLoader();
15
+ }
16
+ static validateConfig(config) {
17
+ if (config.authType !== 'certificate') {
18
+ throw new Error(`Certificate connection expects authType "certificate", got "${config.authType}"`);
19
+ }
20
+ if (config.connectionType === 'rfc') {
21
+ throw new Error('Certificate auth is not supported with connectionType "rfc".');
22
+ }
23
+ const hasPem = !!(config.certPath && config.certKeyPath); // complete PEM pair present
24
+ const hasPfx = !!config.certPfxPath;
25
+ if (!hasPem && !hasPfx) {
26
+ throw new Error('Certificate auth requires certPfxPath OR (certPath AND certKeyPath).');
27
+ }
28
+ if ((config.certPath || config.certKeyPath) && config.certPfxPath) {
29
+ throw new Error('Certificate auth: provide either PEM pair OR PFX, not both.');
30
+ }
31
+ }
32
+ async ensureMaterial() {
33
+ if (!this.material) {
34
+ this.material = await this.loader.load(this.getConfig());
35
+ }
36
+ }
37
+ /**
38
+ * Loads certificate material and primes the session. MUST be called before the first
39
+ * request — the TLS agent is built lazily and needs the client cert present.
40
+ */
41
+ async connect() {
42
+ await this.ensureMaterial();
43
+ const baseUrl = await this.getBaseUrl();
44
+ const discoveryUrl = `${baseUrl}/sap/bc/adt/discovery`;
45
+ try {
46
+ const token = await this.fetchCsrfToken(discoveryUrl, 3, 1000);
47
+ this.setCsrfToken(token);
48
+ }
49
+ catch (error) {
50
+ this.logger?.warn(`[WARN] CertificateAbapConnection - connect deferred: ${error instanceof Error ? error.message : String(error)}`);
51
+ if (error instanceof axios_1.AxiosError && error.response?.headers) {
52
+ if (this.getCookies()) {
53
+ this.logger?.debug(`[DEBUG] CertificateAbapConnection - Cookies extracted from error response during connect (first 100 chars): ${this.getCookies()?.substring(0, 100)}...`);
54
+ }
55
+ }
56
+ }
57
+ }
58
+ getHttpsAgentOptions() {
59
+ if (!this.material) {
60
+ throw new Error('CertificateAbapConnection: certificate material not loaded. Call connect() before making requests.');
61
+ }
62
+ const { cert, key, pfx, passphrase } = this.material;
63
+ return { cert, key, pfx, passphrase };
64
+ }
65
+ buildAuthorizationHeader() {
66
+ return '';
67
+ }
68
+ }
69
+ exports.CertificateAbapConnection = CertificateAbapConnection;
@@ -0,0 +1,20 @@
1
+ import type { SapConfig } from '../config/sapConfig.js';
2
+ import type { ILogger } from '../logger.js';
3
+ import { AbstractAbapConnection } from './AbstractAbapConnection.js';
4
+ /** Kerberos / SPNEGO single-leg auth: send Negotiate token, reuse the resulting SAP session cookie. */
5
+ export declare class KerberosAbapConnection extends AbstractAbapConnection {
6
+ private spn;
7
+ private currentToken;
8
+ constructor(config: SapConfig, logger?: ILogger | null, sessionId?: string);
9
+ private static validateConfig;
10
+ /** Generate the SPNEGO token once (single-leg). */
11
+ protected ensureToken(): Promise<void>;
12
+ /**
13
+ * Generates the SPNEGO token and primes the session. MUST be called before the first
14
+ * request — the first request carries the Negotiate header; SAP then issues a session
15
+ * cookie which is reused for subsequent requests.
16
+ */
17
+ connect(): Promise<void>;
18
+ protected buildAuthorizationHeader(): string;
19
+ }
20
+ //# sourceMappingURL=KerberosAbapConnection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"KerberosAbapConnection.d.ts","sourceRoot":"","sources":["../../src/connection/KerberosAbapConnection.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AAErE,uGAAuG;AACvG,qBAAa,sBAAuB,SAAQ,sBAAsB;IAChE,OAAO,CAAC,GAAG,CAAS;IACpB,OAAO,CAAC,YAAY,CAAM;gBAEd,MAAM,EAAE,SAAS,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,IAAI,EAAE,SAAS,CAAC,EAAE,MAAM;IAQ1E,OAAO,CAAC,MAAM,CAAC,cAAc;IAa7B,mDAAmD;cACnC,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;IAK5C;;;;OAIG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IA0B9B,SAAS,CAAC,wBAAwB,IAAI,MAAM;CAS7C"}
@@ -0,0 +1,63 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.KerberosAbapConnection = void 0;
4
+ const axios_1 = require("axios");
5
+ const kerberosSpnego_js_1 = require("../auth/kerberosSpnego.js");
6
+ const AbstractAbapConnection_js_1 = require("./AbstractAbapConnection.js");
7
+ /** Kerberos / SPNEGO single-leg auth: send Negotiate token, reuse the resulting SAP session cookie. */
8
+ class KerberosAbapConnection extends AbstractAbapConnection_js_1.AbstractAbapConnection {
9
+ spn;
10
+ currentToken = '';
11
+ constructor(config, logger, sessionId) {
12
+ KerberosAbapConnection.validateConfig(config);
13
+ super(config, logger || null, sessionId);
14
+ this.spn =
15
+ config.kerberosSpn ??
16
+ `${config.kerberosService ?? 'HTTP'}@${new URL(config.url).hostname}`;
17
+ }
18
+ static validateConfig(config) {
19
+ if (config.authType !== 'kerberos') {
20
+ throw new Error(`Kerberos connection expects authType "kerberos", got "${config.authType}"`);
21
+ }
22
+ if (config.connectionType === 'rfc') {
23
+ throw new Error('Kerberos auth is not supported with connectionType "rfc".');
24
+ }
25
+ }
26
+ /** Generate the SPNEGO token once (single-leg). */
27
+ async ensureToken() {
28
+ if (!this.currentToken)
29
+ this.currentToken = await (0, kerberosSpnego_js_1.generateSpnegoToken)(this.spn);
30
+ }
31
+ /**
32
+ * Generates the SPNEGO token and primes the session. MUST be called before the first
33
+ * request — the first request carries the Negotiate header; SAP then issues a session
34
+ * cookie which is reused for subsequent requests.
35
+ */
36
+ async connect() {
37
+ await this.ensureToken();
38
+ const baseUrl = await this.getBaseUrl();
39
+ const discoveryUrl = `${baseUrl}/sap/bc/adt/discovery`;
40
+ this.logger?.debug(`[DEBUG] KerberosAbapConnection - Connecting to SAP system: ${discoveryUrl}`);
41
+ try {
42
+ const token = await this.fetchCsrfToken(discoveryUrl, 3, 1000);
43
+ this.setCsrfToken(token);
44
+ }
45
+ catch (error) {
46
+ this.logger?.warn(`[WARN] KerberosAbapConnection - connect deferred: ${error instanceof Error ? error.message : String(error)}`);
47
+ if (error instanceof axios_1.AxiosError &&
48
+ error.response?.headers &&
49
+ this.getCookies()) {
50
+ this.logger?.debug('[DEBUG] KerberosAbapConnection - cookies captured from error response during connect');
51
+ }
52
+ }
53
+ }
54
+ buildAuthorizationHeader() {
55
+ if (this.getCookies())
56
+ return ''; // cookie carries auth after first round-trip
57
+ if (!this.currentToken) {
58
+ throw new Error('KerberosAbapConnection: SPNEGO token not yet available. Call connect() before making requests.');
59
+ }
60
+ return `Negotiate ${this.currentToken}`;
61
+ }
62
+ }
63
+ exports.KerberosAbapConnection = KerberosAbapConnection;
@@ -1,8 +1,9 @@
1
- import type { ITokenRefresher } from '@mcp-abap-adt/interfaces';
1
+ import type { ICertificateMaterialLoader, ITokenRefresher } from '@mcp-abap-adt/interfaces';
2
2
  import type { SapConfig } from '../config/sapConfig.js';
3
3
  import type { ILogger } from '../logger.js';
4
4
  import type { AbapConnection } from './AbapConnection.js';
5
5
  export declare function createAbapConnection(config: SapConfig, logger?: ILogger | null, sessionId?: string, tokenRefresher?: ITokenRefresher, options?: {
6
6
  skipSessionType?: boolean;
7
+ certLoader?: ICertificateMaterialLoader;
7
8
  }): AbapConnection;
8
9
  //# sourceMappingURL=connectionFactory.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"connectionFactory.d.ts","sourceRoot":"","sources":["../../src/connection/connectionFactory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAChE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAM1D,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,SAAS,EACjB,MAAM,CAAC,EAAE,OAAO,GAAG,IAAI,EACvB,SAAS,CAAC,EAAE,MAAM,EAClB,cAAc,CAAC,EAAE,eAAe,EAChC,OAAO,CAAC,EAAE;IAAE,eAAe,CAAC,EAAE,OAAO,CAAA;CAAE,GACtC,cAAc,CAkBhB"}
1
+ {"version":3,"file":"connectionFactory.d.ts","sourceRoot":"","sources":["../../src/connection/connectionFactory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,0BAA0B,EAC1B,eAAe,EAChB,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAQ1D,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,SAAS,EACjB,MAAM,CAAC,EAAE,OAAO,GAAG,IAAI,EACvB,SAAS,CAAC,EAAE,MAAM,EAClB,cAAc,CAAC,EAAE,eAAe,EAChC,OAAO,CAAC,EAAE;IACR,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,UAAU,CAAC,EAAE,0BAA0B,CAAC;CACzC,GACA,cAAc,CAgChB"}
@@ -2,12 +2,17 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createAbapConnection = createAbapConnection;
4
4
  const BaseAbapConnection_js_1 = require("./BaseAbapConnection.js");
5
+ const CertificateAbapConnection_js_1 = require("./CertificateAbapConnection.js");
5
6
  const JwtAbapConnection_js_1 = require("./JwtAbapConnection.js");
7
+ const KerberosAbapConnection_js_1 = require("./KerberosAbapConnection.js");
6
8
  const RfcAbapConnection_js_1 = require("./RfcAbapConnection.js");
7
9
  const SamlAbapConnection_js_1 = require("./SamlAbapConnection.js");
8
10
  function createAbapConnection(config, logger, sessionId, tokenRefresher, options) {
9
11
  // RFC connection type takes priority over auth type
10
12
  if (config.connectionType === 'rfc') {
13
+ if (config.authType === 'certificate' || config.authType === 'kerberos') {
14
+ throw new Error(`authType "${config.authType}" is not supported with connectionType "rfc".`);
15
+ }
11
16
  return new RfcAbapConnection_js_1.RfcAbapConnection(config, logger);
12
17
  }
13
18
  switch (config.authType) {
@@ -17,6 +22,10 @@ function createAbapConnection(config, logger, sessionId, tokenRefresher, options
17
22
  return new JwtAbapConnection_js_1.JwtAbapConnection(config, logger, sessionId, tokenRefresher);
18
23
  case 'saml':
19
24
  return new SamlAbapConnection_js_1.SamlAbapConnection(config, logger, sessionId, options);
25
+ case 'certificate':
26
+ return new CertificateAbapConnection_js_1.CertificateAbapConnection(config, logger, sessionId, options?.certLoader);
27
+ case 'kerberos':
28
+ return new KerberosAbapConnection_js_1.KerberosAbapConnection(config, logger, sessionId);
20
29
  default:
21
30
  throw new Error(`Unsupported SAP authentication type: ${config.authType}`);
22
31
  }
package/dist/index.d.ts CHANGED
@@ -1,12 +1,15 @@
1
1
  export type { IWebSocketCloseInfo, IWebSocketConnectOptions, IWebSocketMessageEnvelope, IWebSocketMessageHandler, IWebSocketTransport, } from '@mcp-abap-adt/interfaces';
2
+ export { FileCertificateMaterialLoader } from './auth/FileCertificateMaterialLoader.js';
2
3
  export type { SapAuthType, SapConfig, SapConnectionType, } from './config/sapConfig.js';
3
4
  export { sapConfigSignature } from './config/sapConfig.js';
4
5
  export type { AbapConnection, AbapRequestOptions, } from './connection/AbapConnection.js';
5
6
  export { BaseAbapConnection, BaseAbapConnection as OnPremAbapConnection, } from './connection/BaseAbapConnection.js';
7
+ export { CertificateAbapConnection } from './connection/CertificateAbapConnection.js';
6
8
  export { createAbapConnection } from './connection/connectionFactory.js';
7
9
  export { CSRF_CONFIG, CSRF_ERROR_MESSAGES } from './connection/csrfConfig.js';
8
10
  export { GenericWebSocketTransport, type IWebSocketFactory, type IWebSocketLike, } from './connection/GenericWebSocketTransport.js';
9
11
  export { JwtAbapConnection, JwtAbapConnection as CloudAbapConnection, } from './connection/JwtAbapConnection.js';
12
+ export { KerberosAbapConnection } from './connection/KerberosAbapConnection.js';
10
13
  export { RfcAbapConnection } from './connection/RfcAbapConnection.js';
11
14
  export { SamlAbapConnection } from './connection/SamlAbapConnection.js';
12
15
  export type { ILogger } from './logger.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,YAAY,EACV,mBAAmB,EACnB,wBAAwB,EACxB,yBAAyB,EACzB,wBAAwB,EACxB,mBAAmB,GACpB,MAAM,0BAA0B,CAAC;AAClC,YAAY,EACV,WAAW,EACX,SAAS,EACT,iBAAiB,GAClB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAE3D,YAAY,EACV,cAAc,EACd,kBAAkB,GACnB,MAAM,gCAAgC,CAAC;AAGxC,OAAO,EACL,kBAAkB,EAClB,kBAAkB,IAAI,oBAAoB,GAC3C,MAAM,oCAAoC,CAAC;AAE5C,OAAO,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AAEzE,OAAO,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAC9E,OAAO,EACL,yBAAyB,EACzB,KAAK,iBAAiB,EACtB,KAAK,cAAc,GACpB,MAAM,2CAA2C,CAAC;AACnD,OAAO,EACL,iBAAiB,EACjB,iBAAiB,IAAI,mBAAmB,GACzC,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AACtE,OAAO,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAC;AACxE,YAAY,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,EACL,UAAU,EACV,gBAAgB,EAChB,KAAK,aAAa,GACnB,MAAM,qBAAqB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,YAAY,EACV,mBAAmB,EACnB,wBAAwB,EACxB,yBAAyB,EACzB,wBAAwB,EACxB,mBAAmB,GACpB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,6BAA6B,EAAE,MAAM,yCAAyC,CAAC;AACxF,YAAY,EACV,WAAW,EACX,SAAS,EACT,iBAAiB,GAClB,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAE3D,YAAY,EACV,cAAc,EACd,kBAAkB,GACnB,MAAM,gCAAgC,CAAC;AAGxC,OAAO,EACL,kBAAkB,EAClB,kBAAkB,IAAI,oBAAoB,GAC3C,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EAAE,yBAAyB,EAAE,MAAM,2CAA2C,CAAC;AAEtF,OAAO,EAAE,oBAAoB,EAAE,MAAM,mCAAmC,CAAC;AAEzE,OAAO,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAC9E,OAAO,EACL,yBAAyB,EACzB,KAAK,iBAAiB,EACtB,KAAK,cAAc,GACpB,MAAM,2CAA2C,CAAC;AACnD,OAAO,EACL,iBAAiB,EACjB,iBAAiB,IAAI,mBAAmB,GACzC,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EAAE,sBAAsB,EAAE,MAAM,wCAAwC,CAAC;AAChF,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AACtE,OAAO,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAC;AACxE,YAAY,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAE3C,OAAO,EACL,UAAU,EACV,gBAAgB,EAChB,KAAK,aAAa,GACnB,MAAM,qBAAqB,CAAC"}
package/dist/index.js CHANGED
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
2
  // Types - re-exported from interfaces package with backward compatibility aliases
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
- exports.getTimeoutConfig = exports.getTimeout = exports.SamlAbapConnection = exports.RfcAbapConnection = exports.CloudAbapConnection = exports.JwtAbapConnection = exports.GenericWebSocketTransport = exports.CSRF_ERROR_MESSAGES = exports.CSRF_CONFIG = exports.createAbapConnection = exports.OnPremAbapConnection = exports.BaseAbapConnection = exports.sapConfigSignature = void 0;
4
+ exports.getTimeoutConfig = exports.getTimeout = exports.SamlAbapConnection = exports.RfcAbapConnection = exports.KerberosAbapConnection = exports.CloudAbapConnection = exports.JwtAbapConnection = exports.GenericWebSocketTransport = exports.CSRF_ERROR_MESSAGES = exports.CSRF_CONFIG = exports.createAbapConnection = exports.CertificateAbapConnection = exports.OnPremAbapConnection = exports.BaseAbapConnection = exports.sapConfigSignature = exports.FileCertificateMaterialLoader = void 0;
5
+ var FileCertificateMaterialLoader_js_1 = require("./auth/FileCertificateMaterialLoader.js");
6
+ Object.defineProperty(exports, "FileCertificateMaterialLoader", { enumerable: true, get: function () { return FileCertificateMaterialLoader_js_1.FileCertificateMaterialLoader; } });
5
7
  // Config utilities
6
8
  var sapConfig_js_1 = require("./config/sapConfig.js");
7
9
  Object.defineProperty(exports, "sapConfigSignature", { enumerable: true, get: function () { return sapConfig_js_1.sapConfigSignature; } });
@@ -10,6 +12,8 @@ Object.defineProperty(exports, "sapConfigSignature", { enumerable: true, get: fu
10
12
  var BaseAbapConnection_js_1 = require("./connection/BaseAbapConnection.js");
11
13
  Object.defineProperty(exports, "BaseAbapConnection", { enumerable: true, get: function () { return BaseAbapConnection_js_1.BaseAbapConnection; } });
12
14
  Object.defineProperty(exports, "OnPremAbapConnection", { enumerable: true, get: function () { return BaseAbapConnection_js_1.BaseAbapConnection; } });
15
+ var CertificateAbapConnection_js_1 = require("./connection/CertificateAbapConnection.js");
16
+ Object.defineProperty(exports, "CertificateAbapConnection", { enumerable: true, get: function () { return CertificateAbapConnection_js_1.CertificateAbapConnection; } });
13
17
  // Factory
14
18
  var connectionFactory_js_1 = require("./connection/connectionFactory.js");
15
19
  Object.defineProperty(exports, "createAbapConnection", { enumerable: true, get: function () { return connectionFactory_js_1.createAbapConnection; } });
@@ -22,6 +26,8 @@ Object.defineProperty(exports, "GenericWebSocketTransport", { enumerable: true,
22
26
  var JwtAbapConnection_js_1 = require("./connection/JwtAbapConnection.js");
23
27
  Object.defineProperty(exports, "JwtAbapConnection", { enumerable: true, get: function () { return JwtAbapConnection_js_1.JwtAbapConnection; } });
24
28
  Object.defineProperty(exports, "CloudAbapConnection", { enumerable: true, get: function () { return JwtAbapConnection_js_1.JwtAbapConnection; } });
29
+ var KerberosAbapConnection_js_1 = require("./connection/KerberosAbapConnection.js");
30
+ Object.defineProperty(exports, "KerberosAbapConnection", { enumerable: true, get: function () { return KerberosAbapConnection_js_1.KerberosAbapConnection; } });
25
31
  var RfcAbapConnection_js_1 = require("./connection/RfcAbapConnection.js");
26
32
  Object.defineProperty(exports, "RfcAbapConnection", { enumerable: true, get: function () { return RfcAbapConnection_js_1.RfcAbapConnection; } });
27
33
  var SamlAbapConnection_js_1 = require("./connection/SamlAbapConnection.js");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mcp-abap-adt/connection",
3
- "version": "1.8.1",
3
+ "version": "1.9.0",
4
4
  "description": "ABAP connection layer for MCP ABAP ADT server",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -46,14 +46,15 @@
46
46
  "node": ">=18.0.0"
47
47
  },
48
48
  "dependencies": {
49
- "@mcp-abap-adt/interfaces": "^7.0.0",
49
+ "@mcp-abap-adt/interfaces": "^7.2.0",
50
50
  "axios": "^1.13.5",
51
51
  "commander": "^14.0.3",
52
52
  "express": "^5.1.0",
53
53
  "open": "^11.0.0"
54
54
  },
55
55
  "optionalDependencies": {
56
- "@mcp-abap-adt/sap-rfc-lite": "^0.1.0"
56
+ "@mcp-abap-adt/sap-rfc-lite": "^0.1.0",
57
+ "kerberos": "^2.1.0"
57
58
  },
58
59
  "devDependencies": {
59
60
  "@biomejs/biome": "^2.3.14",