@mcp-abap-adt/auth-broker 2.2.0 → 3.0.1
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.
- package/CHANGELOG.md +235 -0
- package/README.md +324 -284
- package/dist/AuthBroker.d.ts +81 -106
- package/dist/AuthBroker.d.ts.map +1 -1
- package/dist/AuthBroker.js +209 -567
- package/dist/bin/mcp-auth.js +54 -31
- package/dist/bin/mcp-sso.js +52 -44
- package/dist/bin/mcpSsoConfig.js +169 -1
- package/dist/bin/samlMetadata.js +175 -0
- package/dist/bin/workDir.js +79 -0
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/providers/ITokenProvider.d.ts +2 -2
- package/dist/providers/ITokenProvider.d.ts.map +1 -1
- package/dist/providers/index.d.ts +1 -1
- package/dist/providers/index.d.ts.map +1 -1
- package/package.json +12 -8
- package/dist/utils/formatting.d.ts +0 -16
- package/dist/utils/formatting.d.ts.map +0 -1
- package/dist/utils/formatting.js +0 -34
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* SAML metadata, read for what a SAML run would otherwise be told by hand.
|
|
4
|
+
*
|
|
5
|
+
* Two documents, two sides:
|
|
6
|
+
* - the identity provider's (`--idp-metadata`): its entityID, its signing
|
|
7
|
+
* certificates and its SSO URL — what the assertion is validated against;
|
|
8
|
+
* - the service provider's, which for XSUAA is `<uaa.url>/saml/metadata`: its
|
|
9
|
+
* entityID (the assertion's Audience) and the bearer endpoint
|
|
10
|
+
* `/oauth/token/alias/<alias>`, which is both the assertion's Recipient and
|
|
11
|
+
* the token endpoint of the saml2-bearer grant.
|
|
12
|
+
*
|
|
13
|
+
* What is read here is trust material. A certificate taken from metadata is
|
|
14
|
+
* only as trustworthy as the channel that delivered it, so a URL must be
|
|
15
|
+
* https; plain http is accepted for loopback hosts only, which is a local test
|
|
16
|
+
* identity provider and nothing else.
|
|
17
|
+
*/
|
|
18
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
+
exports.loadMetadata = loadMetadata;
|
|
20
|
+
exports.readIdpMetadata = readIdpMetadata;
|
|
21
|
+
exports.readSpMetadata = readSpMetadata;
|
|
22
|
+
exports.applySamlMetadata = applySamlMetadata;
|
|
23
|
+
const node_fs_1 = require("node:fs");
|
|
24
|
+
const node_path_1 = require("node:path");
|
|
25
|
+
const LOOPBACK = new Set(['localhost', '127.0.0.1', '[::1]']);
|
|
26
|
+
/** A metadata document from an https URL, a loopback http URL, or a file. */
|
|
27
|
+
async function loadMetadata(source) {
|
|
28
|
+
if (/^https?:\/\//i.test(source)) {
|
|
29
|
+
const url = new URL(source);
|
|
30
|
+
if (url.protocol !== 'https:' && !LOOPBACK.has(url.hostname)) {
|
|
31
|
+
throw new Error(`refusing SAML metadata over ${url.protocol} from ${url.hostname}: it carries the certificates assertions are verified against, so it must come over https`);
|
|
32
|
+
}
|
|
33
|
+
const response = await fetch(url);
|
|
34
|
+
if (!response.ok) {
|
|
35
|
+
throw new Error(`SAML metadata ${source} answered ${response.status}`);
|
|
36
|
+
}
|
|
37
|
+
return response.text();
|
|
38
|
+
}
|
|
39
|
+
return (0, node_fs_1.readFileSync)((0, node_path_1.resolve)(source), 'utf8');
|
|
40
|
+
}
|
|
41
|
+
function attribute(tag, name) {
|
|
42
|
+
return tag.match(new RegExp(`\\s${name}="([^"]*)"`))?.[1];
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Every `EntityDescriptor` in the document, whether it is the root or one of
|
|
46
|
+
* many inside an `EntitiesDescriptor` (federation metadata). An entity's
|
|
47
|
+
* entityID, keys and endpoints are read from the same element — taking the
|
|
48
|
+
* first entityID in the document paired one entity's ID with another's
|
|
49
|
+
* certificate.
|
|
50
|
+
*/
|
|
51
|
+
function entitiesOf(xml) {
|
|
52
|
+
return [
|
|
53
|
+
...xml.matchAll(/<(?:[\w-]+:)?EntityDescriptor\b([^>]*)>([\s\S]*?)<\/(?:[\w-]+:)?EntityDescriptor>/g),
|
|
54
|
+
].map(([, attributes, body]) => ({
|
|
55
|
+
entityId: attribute(attributes, 'entityID'),
|
|
56
|
+
body,
|
|
57
|
+
}));
|
|
58
|
+
}
|
|
59
|
+
/** The inner XML of the first `<prefix:name>` element, whatever the prefix. */
|
|
60
|
+
function section(xml, name) {
|
|
61
|
+
return xml.match(new RegExp(`<(?:[\\w-]+:)?${name}\\b[^>]*>([\\s\\S]*?)</(?:[\\w-]+:)?${name}>`))?.[1];
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* The one entity the caller means. With a wanted entityID, the entity with
|
|
65
|
+
* it; without one, the only candidate — several are a question only the
|
|
66
|
+
* caller can answer, and picking the first would trust an identity provider
|
|
67
|
+
* nobody named.
|
|
68
|
+
*/
|
|
69
|
+
function chooseEntity(candidates, wanted, role, flag) {
|
|
70
|
+
if (wanted !== undefined) {
|
|
71
|
+
const match = candidates.find((entity) => entity.entityId === wanted);
|
|
72
|
+
if (!match) {
|
|
73
|
+
throw new Error(`the metadata has no ${role} with entityID ${JSON.stringify(wanted)}; it has: ${candidates
|
|
74
|
+
.map((entity) => JSON.stringify(entity.entityId))
|
|
75
|
+
.join(', ') || 'none'}`);
|
|
76
|
+
}
|
|
77
|
+
return match;
|
|
78
|
+
}
|
|
79
|
+
if (candidates.length === 0) {
|
|
80
|
+
throw new Error(`the metadata describes no ${role}`);
|
|
81
|
+
}
|
|
82
|
+
if (candidates.length > 1) {
|
|
83
|
+
throw new Error(`the metadata describes ${candidates.length} ${role}s; name the one to use with ${flag}: ${candidates
|
|
84
|
+
.map((entity) => JSON.stringify(entity.entityId))
|
|
85
|
+
.join(', ')}`);
|
|
86
|
+
}
|
|
87
|
+
return candidates[0];
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* The identity provider in `xml`: the one whose entityID is `entityId`, or the
|
|
91
|
+
* only one there is.
|
|
92
|
+
*/
|
|
93
|
+
function readIdpMetadata(xml, entityId) {
|
|
94
|
+
const candidates = entitiesOf(xml).filter((entity) => section(entity.body, 'IDPSSODescriptor') !== undefined);
|
|
95
|
+
if (candidates.length === 0) {
|
|
96
|
+
throw new Error('the metadata has no IDPSSODescriptor: not an identity provider');
|
|
97
|
+
}
|
|
98
|
+
const entity = chooseEntity(candidates, entityId, 'identity provider', '--idp-entity-id');
|
|
99
|
+
const descriptor = section(entity.body, 'IDPSSODescriptor') ?? '';
|
|
100
|
+
const certificates = [];
|
|
101
|
+
const keyDescriptors = descriptor.matchAll(/<(?:[\w-]+:)?KeyDescriptor\b([^>]*)>([\s\S]*?)<\/(?:[\w-]+:)?KeyDescriptor>/g);
|
|
102
|
+
for (const [, attributes, body] of keyDescriptors) {
|
|
103
|
+
// No `use` means the key serves both purposes (SAML metadata 2.4.1.1).
|
|
104
|
+
if (attribute(attributes, 'use') === 'encryption')
|
|
105
|
+
continue;
|
|
106
|
+
for (const [, certificate] of body.matchAll(/<(?:[\w-]+:)?X509Certificate>([^<]+)</g)) {
|
|
107
|
+
certificates.push(certificate.replace(/\s+/g, ''));
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
const ssoServices = [
|
|
111
|
+
...descriptor.matchAll(/<(?:[\w-]+:)?SingleSignOnService\b[^>]*>/g),
|
|
112
|
+
].map(([tag]) => ({
|
|
113
|
+
binding: attribute(tag, 'Binding') ?? '',
|
|
114
|
+
location: attribute(tag, 'Location'),
|
|
115
|
+
}));
|
|
116
|
+
const ssoUrl = ssoServices.find((s) => s.binding.endsWith(':HTTP-Redirect'))?.location ??
|
|
117
|
+
ssoServices.find((s) => s.binding.endsWith(':HTTP-POST'))?.location;
|
|
118
|
+
return { entityId: entity.entityId, certificates, ssoUrl };
|
|
119
|
+
}
|
|
120
|
+
/** The bearer ACS of an entity: the one at `/oauth/token/alias/…`. */
|
|
121
|
+
function bearerAcsOf(body) {
|
|
122
|
+
return [...body.matchAll(/<(?:[\w-]+:)?AssertionConsumerService\b[^>]*>/g)]
|
|
123
|
+
.map(([tag]) => attribute(tag, 'Location'))
|
|
124
|
+
.find((location) => location?.includes('/oauth/token/alias/'));
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* The XSUAA service provider in `xml`: the entity publishing a bearer ACS,
|
|
128
|
+
* `spEntityId`'s when several do.
|
|
129
|
+
*/
|
|
130
|
+
function readSpMetadata(xml, spEntityId) {
|
|
131
|
+
const candidates = entitiesOf(xml).filter((entity) => bearerAcsOf(entity.body));
|
|
132
|
+
if (candidates.length === 0)
|
|
133
|
+
return {};
|
|
134
|
+
const entity = chooseEntity(candidates, spEntityId, 'XSUAA service provider', '--sp-entity-id');
|
|
135
|
+
return { entityId: entity.entityId, bearerAcsUrl: bearerAcsOf(entity.body) };
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Fills what the metadata states and the caller did not. An explicit option
|
|
139
|
+
* always wins, and trust is replaced rather than widened: a `--idp-cert`
|
|
140
|
+
* means no certificate from the metadata is trusted alongside it.
|
|
141
|
+
*
|
|
142
|
+
* `explicitTokenEndpoint` is the `--token-endpoint` the user gave, if any —
|
|
143
|
+
* distinct from `options.tokenEndpoint`, which a service key may already have
|
|
144
|
+
* set to the plain `/oauth/token` that a bearer grant must not use.
|
|
145
|
+
*/
|
|
146
|
+
async function applySamlMetadata(options, explicitTokenEndpoint, load = loadMetadata) {
|
|
147
|
+
if (options.protocol !== 'saml2')
|
|
148
|
+
return;
|
|
149
|
+
if (options.idpMetadata) {
|
|
150
|
+
const idp = readIdpMetadata(await load(options.idpMetadata), options.idpEntityId);
|
|
151
|
+
options.idpEntityId ??= idp.entityId;
|
|
152
|
+
options.idpSsoUrl ??= idp.ssoUrl;
|
|
153
|
+
if (options.idpCertificates === undefined &&
|
|
154
|
+
options.idpCertificateFiles === undefined &&
|
|
155
|
+
idp.certificates.length > 0) {
|
|
156
|
+
options.idpCertificates = idp.certificates;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
if (options.flow !== 'bearer')
|
|
160
|
+
return;
|
|
161
|
+
// The file wins over the service key's UAA, as an explicit source does.
|
|
162
|
+
const spSource = options.samlMetadataPath ??
|
|
163
|
+
(options.uaaUrl
|
|
164
|
+
? `${options.uaaUrl.replace(/\/+$/, '')}/saml/metadata`
|
|
165
|
+
: undefined);
|
|
166
|
+
if (!spSource)
|
|
167
|
+
return;
|
|
168
|
+
const sp = readSpMetadata(await load(spSource), options.spEntityId);
|
|
169
|
+
if (!sp.bearerAcsUrl) {
|
|
170
|
+
throw new Error(`${spSource} names no /oauth/token/alias/ endpoint: not an XSUAA service provider's metadata`);
|
|
171
|
+
}
|
|
172
|
+
options.spEntityId ??= sp.entityId;
|
|
173
|
+
options.acsUrl ??= sp.bearerAcsUrl;
|
|
174
|
+
options.tokenEndpoint = explicitTokenEndpoint ?? sp.bearerAcsUrl;
|
|
175
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* A private working directory for one CLI run, removed however the run ends.
|
|
4
|
+
*
|
|
5
|
+
* The CLIs keep a temporary session store (and, for a `credentials`-wrapped
|
|
6
|
+
* service key, an unwrapped copy of the key) while they log in. Both hold the
|
|
7
|
+
* client secret. They used to live in `.tmp` beside the output file — in the
|
|
8
|
+
* user's sessions folder, shared by every run — and were removed only on
|
|
9
|
+
* success: a failed or interrupted login left the secret there, and two runs
|
|
10
|
+
* at once shared one directory that each removed on exit.
|
|
11
|
+
*
|
|
12
|
+
* Now each run gets its own directory under the OS temp dir, readable by the
|
|
13
|
+
* user alone, and it is removed on any exit: success, error, or a signal.
|
|
14
|
+
*/
|
|
15
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
16
|
+
if (k2 === undefined) k2 = k;
|
|
17
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
18
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
19
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
20
|
+
}
|
|
21
|
+
Object.defineProperty(o, k2, desc);
|
|
22
|
+
}) : (function(o, m, k, k2) {
|
|
23
|
+
if (k2 === undefined) k2 = k;
|
|
24
|
+
o[k2] = m[k];
|
|
25
|
+
}));
|
|
26
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
27
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
28
|
+
}) : function(o, v) {
|
|
29
|
+
o["default"] = v;
|
|
30
|
+
});
|
|
31
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
32
|
+
var ownKeys = function(o) {
|
|
33
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
34
|
+
var ar = [];
|
|
35
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
36
|
+
return ar;
|
|
37
|
+
};
|
|
38
|
+
return ownKeys(o);
|
|
39
|
+
};
|
|
40
|
+
return function (mod) {
|
|
41
|
+
if (mod && mod.__esModule) return mod;
|
|
42
|
+
var result = {};
|
|
43
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
44
|
+
__setModuleDefault(result, mod);
|
|
45
|
+
return result;
|
|
46
|
+
};
|
|
47
|
+
})();
|
|
48
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
49
|
+
exports.createWorkDir = createWorkDir;
|
|
50
|
+
const fs = __importStar(require("node:fs"));
|
|
51
|
+
const os = __importStar(require("node:os"));
|
|
52
|
+
const path = __importStar(require("node:path"));
|
|
53
|
+
const SIGNAL_EXIT_CODES = {
|
|
54
|
+
SIGINT: 130,
|
|
55
|
+
SIGTERM: 143,
|
|
56
|
+
SIGHUP: 129,
|
|
57
|
+
};
|
|
58
|
+
function createWorkDir(prefix) {
|
|
59
|
+
// mkdtemp creates the directory with mode 0700: the secret is the user's alone.
|
|
60
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), `${prefix}-`));
|
|
61
|
+
const remove = () => {
|
|
62
|
+
try {
|
|
63
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
// Nothing left to do on the way out.
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
// `exit` covers process.exit() and a normal end; it does not fire for a
|
|
70
|
+
// signal, so each is handled too, and exits with the conventional code.
|
|
71
|
+
process.once('exit', remove);
|
|
72
|
+
for (const [signal, code] of Object.entries(SIGNAL_EXIT_CODES)) {
|
|
73
|
+
process.once(signal, () => {
|
|
74
|
+
remove();
|
|
75
|
+
process.exit(code);
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
return dir;
|
|
79
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @mcp-abap-adt/auth-broker
|
|
3
|
-
*
|
|
3
|
+
* Per-destination token broker: sessions and service keys from stores,
|
|
4
|
+
* tokens from an injected provider, results persisted.
|
|
4
5
|
*/
|
|
5
6
|
export type { ITokenRefresher } from '@mcp-abap-adt/interfaces-auth';
|
|
6
7
|
export type { AuthType } from '@mcp-abap-adt/interfaces-auth-sap';
|
|
7
8
|
export type { ILogger } from '@mcp-abap-adt/interfaces-utils';
|
|
8
|
-
export { AuthBroker, type AuthBrokerConfig } from './AuthBroker';
|
|
9
|
-
export type { ITokenProvider, ITokenResult, TokenProviderOptions, } from './providers';
|
|
9
|
+
export { AuthBroker, type AuthBrokerConfig, type TokenProviderFactory, } from './AuthBroker';
|
|
10
|
+
export type { IRefreshableTokenProvider, ITokenProvider, ITokenResult, TokenProviderOptions, } from './providers';
|
|
10
11
|
export type { IAuthorizationConfig, IConnectionConfig, IServiceKeyStore, ISessionStore, } from './stores/interfaces';
|
|
11
12
|
export type { IConfig } from './types';
|
|
12
13
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,YAAY,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AACrE,YAAY,EAAE,QAAQ,EAAE,MAAM,mCAAmC,CAAC;AAClE,YAAY,EAAE,OAAO,EAAE,MAAM,gCAAgC,CAAC;AAC9D,OAAO,EACL,UAAU,EACV,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,GAC1B,MAAM,cAAc,CAAC;AAEtB,YAAY,EACV,yBAAyB,EACzB,cAAc,EACd,YAAY,EACZ,oBAAoB,GACrB,MAAM,aAAa,CAAC;AAGrB,YAAY,EACV,oBAAoB,EACpB,iBAAiB,EACjB,gBAAgB,EAChB,aAAa,GACd,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
3
|
* @mcp-abap-adt/auth-broker
|
|
4
|
-
*
|
|
4
|
+
* Per-destination token broker: sessions and service keys from stores,
|
|
5
|
+
* tokens from an injected provider, results persisted.
|
|
5
6
|
*/
|
|
6
7
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
8
|
exports.AuthBroker = void 0;
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Stateful providers handle token lifecycle internally (refresh/relogin).
|
|
5
5
|
*/
|
|
6
|
-
import type { ITokenProvider, ITokenProviderOptions, ITokenResult } from '@mcp-abap-adt/interfaces-auth';
|
|
6
|
+
import type { IRefreshableTokenProvider, ITokenProvider, ITokenProviderOptions, ITokenResult } from '@mcp-abap-adt/interfaces-auth';
|
|
7
7
|
import type { IAuthorizationConfig, IConnectionConfig } from '@mcp-abap-adt/interfaces-auth-sap';
|
|
8
|
-
export type { IAuthorizationConfig, IConnectionConfig, ITokenProvider, ITokenResult, };
|
|
8
|
+
export type { IAuthorizationConfig, IConnectionConfig, IRefreshableTokenProvider, ITokenProvider, ITokenResult, };
|
|
9
9
|
export type TokenProviderOptions = ITokenProviderOptions;
|
|
10
10
|
//# sourceMappingURL=ITokenProvider.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ITokenProvider.d.ts","sourceRoot":"","sources":["../../src/providers/ITokenProvider.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EACV,cAAc,EACd,qBAAqB,EACrB,YAAY,EACb,MAAM,+BAA+B,CAAC;AACvC,OAAO,KAAK,EACV,oBAAoB,EACpB,iBAAiB,EAClB,MAAM,mCAAmC,CAAC;AAG3C,YAAY,EACV,oBAAoB,EACpB,iBAAiB,EACjB,cAAc,EACd,YAAY,GACb,CAAC;AACF,MAAM,MAAM,oBAAoB,GAAG,qBAAqB,CAAC"}
|
|
1
|
+
{"version":3,"file":"ITokenProvider.d.ts","sourceRoot":"","sources":["../../src/providers/ITokenProvider.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EACV,yBAAyB,EACzB,cAAc,EACd,qBAAqB,EACrB,YAAY,EACb,MAAM,+BAA+B,CAAC;AACvC,OAAO,KAAK,EACV,oBAAoB,EACpB,iBAAiB,EAClB,MAAM,mCAAmC,CAAC;AAG3C,YAAY,EACV,oBAAoB,EACpB,iBAAiB,EACjB,yBAAyB,EACzB,cAAc,EACd,YAAY,GACb,CAAC;AACF,MAAM,MAAM,oBAAoB,GAAG,qBAAqB,CAAC"}
|
|
@@ -4,5 +4,5 @@
|
|
|
4
4
|
* Provider implementations are in separate packages:
|
|
5
5
|
* - @mcp-abap-adt/auth-providers - XSUAA and BTP providers
|
|
6
6
|
*/
|
|
7
|
-
export type { ITokenProvider, ITokenResult, TokenProviderOptions, } from './ITokenProvider';
|
|
7
|
+
export type { IRefreshableTokenProvider, ITokenProvider, ITokenResult, TokenProviderOptions, } from './ITokenProvider';
|
|
8
8
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/providers/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,YAAY,EACV,cAAc,EACd,YAAY,EACZ,oBAAoB,GACrB,MAAM,kBAAkB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/providers/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,YAAY,EACV,yBAAyB,EACzB,cAAc,EACd,YAAY,EACZ,oBAAoB,GACrB,MAAM,kBAAkB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mcp-abap-adt/auth-broker",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "3.0.1",
|
|
4
|
+
"description": "Per-destination token broker for SAP BTP and ABAP: sessions and service keys from stores, tokens from an injected provider, results persisted - plus the mcp-auth and mcp-sso CLIs",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"files": [
|
|
@@ -21,7 +21,11 @@
|
|
|
21
21
|
"token",
|
|
22
22
|
"broker",
|
|
23
23
|
"mcp",
|
|
24
|
-
"abap-adt"
|
|
24
|
+
"abap-adt",
|
|
25
|
+
"xsuaa",
|
|
26
|
+
"oauth2",
|
|
27
|
+
"saml",
|
|
28
|
+
"sso"
|
|
25
29
|
],
|
|
26
30
|
"author": "Oleksii Kyslytsia <oleksij.kyslytsja@gmail.com>",
|
|
27
31
|
"license": "LGPL-3.0-only",
|
|
@@ -60,13 +64,13 @@
|
|
|
60
64
|
"mcp-sso": "./dist/bin/mcp-sso.js"
|
|
61
65
|
},
|
|
62
66
|
"engines": {
|
|
63
|
-
"node": "
|
|
67
|
+
"node": "^22 || ^24"
|
|
64
68
|
},
|
|
65
69
|
"dependencies": {
|
|
66
|
-
"@mcp-abap-adt/auth-providers": "^
|
|
67
|
-
"@mcp-abap-adt/auth-stores": "^1.2.
|
|
68
|
-
"@mcp-abap-adt/interfaces-auth": "^1.
|
|
69
|
-
"@mcp-abap-adt/interfaces-auth-sap": "^1.0.
|
|
70
|
+
"@mcp-abap-adt/auth-providers": "^4.2.0",
|
|
71
|
+
"@mcp-abap-adt/auth-stores": "^1.2.3",
|
|
72
|
+
"@mcp-abap-adt/interfaces-auth": "^2.1.0",
|
|
73
|
+
"@mcp-abap-adt/interfaces-auth-sap": "^1.0.1",
|
|
70
74
|
"@mcp-abap-adt/interfaces-utils": "^1.1.0",
|
|
71
75
|
"axios": "^1.13.5"
|
|
72
76
|
},
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Formatting utilities for logging
|
|
3
|
-
*/
|
|
4
|
-
/**
|
|
5
|
-
* Format token for logging (start...end)
|
|
6
|
-
* @param token Token string to format
|
|
7
|
-
* @returns Formatted token string or undefined if token is empty
|
|
8
|
-
*/
|
|
9
|
-
export declare function formatToken(token?: string): string | undefined;
|
|
10
|
-
/**
|
|
11
|
-
* Format timestamp to readable date/time string
|
|
12
|
-
* @param timestamp Timestamp in milliseconds
|
|
13
|
-
* @returns Formatted date string (e.g., "2025-12-25 19:21:27 UTC")
|
|
14
|
-
*/
|
|
15
|
-
export declare function formatExpirationDate(timestamp: number): string;
|
|
16
|
-
//# sourceMappingURL=formatting.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"formatting.d.ts","sourceRoot":"","sources":["../../src/utils/formatting.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAI9D;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAS9D"}
|
package/dist/utils/formatting.js
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Formatting utilities for logging
|
|
4
|
-
*/
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.formatToken = formatToken;
|
|
7
|
-
exports.formatExpirationDate = formatExpirationDate;
|
|
8
|
-
/**
|
|
9
|
-
* Format token for logging (start...end)
|
|
10
|
-
* @param token Token string to format
|
|
11
|
-
* @returns Formatted token string or undefined if token is empty
|
|
12
|
-
*/
|
|
13
|
-
function formatToken(token) {
|
|
14
|
-
if (!token)
|
|
15
|
-
return undefined;
|
|
16
|
-
if (token.length <= 50)
|
|
17
|
-
return token;
|
|
18
|
-
return `${token.substring(0, 25)}...${token.substring(token.length - 25)}`;
|
|
19
|
-
}
|
|
20
|
-
/**
|
|
21
|
-
* Format timestamp to readable date/time string
|
|
22
|
-
* @param timestamp Timestamp in milliseconds
|
|
23
|
-
* @returns Formatted date string (e.g., "2025-12-25 19:21:27 UTC")
|
|
24
|
-
*/
|
|
25
|
-
function formatExpirationDate(timestamp) {
|
|
26
|
-
const date = new Date(timestamp);
|
|
27
|
-
const year = date.getUTCFullYear();
|
|
28
|
-
const month = String(date.getUTCMonth() + 1).padStart(2, '0');
|
|
29
|
-
const day = String(date.getUTCDate()).padStart(2, '0');
|
|
30
|
-
const hours = String(date.getUTCHours()).padStart(2, '0');
|
|
31
|
-
const minutes = String(date.getUTCMinutes()).padStart(2, '0');
|
|
32
|
-
const seconds = String(date.getUTCSeconds()).padStart(2, '0');
|
|
33
|
-
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds} UTC`;
|
|
34
|
-
}
|