@mcp-abap-adt/auth-providers 1.1.0 → 2.0.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.
- package/CHANGELOG.md +121 -0
- package/README.md +376 -61
- package/bin/auth-authorization-code.ts +5 -3
- package/dist/__tests__/helpers/netHelpers.d.ts +16 -0
- package/dist/__tests__/helpers/netHelpers.d.ts.map +1 -1
- package/dist/__tests__/helpers/netHelpers.js +29 -0
- package/dist/auth/announce.d.ts +11 -0
- package/dist/auth/announce.d.ts.map +1 -0
- package/dist/auth/announce.js +19 -0
- package/dist/auth/browserAuth.d.ts +22 -16
- package/dist/auth/browserAuth.d.ts.map +1 -1
- package/dist/auth/browserAuth.js +101 -536
- package/dist/auth/callbackServer.d.ts +46 -0
- package/dist/auth/callbackServer.d.ts.map +1 -0
- package/dist/auth/callbackServer.js +332 -0
- package/dist/auth/oidcBrowserAuth.d.ts +4 -3
- package/dist/auth/oidcBrowserAuth.d.ts.map +1 -1
- package/dist/auth/oidcBrowserAuth.js +25 -134
- package/dist/auth/saml2Auth.d.ts +2 -2
- package/dist/auth/saml2Auth.d.ts.map +1 -1
- package/dist/auth/saml2Auth.js +21 -141
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +21 -1
- package/dist/providers/AuthorizationCodeProvider.d.ts +7 -3
- package/dist/providers/AuthorizationCodeProvider.d.ts.map +1 -1
- package/dist/providers/AuthorizationCodeProvider.js +51 -41
- package/dist/providers/DeviceFlowProvider.d.ts +2 -1
- package/dist/providers/DeviceFlowProvider.d.ts.map +1 -1
- package/dist/providers/DeviceFlowProvider.js +13 -10
- package/dist/providers/OidcBrowserProvider.d.ts +4 -7
- package/dist/providers/OidcBrowserProvider.d.ts.map +1 -1
- package/dist/providers/OidcBrowserProvider.js +54 -57
- package/dist/providers/OidcDeviceFlowProvider.d.ts.map +1 -1
- package/dist/providers/OidcDeviceFlowProvider.js +9 -7
- package/dist/providers/Saml2BearerProvider.d.ts +2 -2
- package/dist/providers/Saml2BearerProvider.d.ts.map +1 -1
- package/dist/providers/Saml2BearerProvider.js +3 -0
- package/dist/providers/Saml2PureProvider.d.ts +2 -2
- package/dist/providers/Saml2PureProvider.d.ts.map +1 -1
- package/dist/providers/Saml2PureProvider.js +3 -0
- package/dist/providers/saml2Utils.d.ts +12 -12
- package/dist/providers/saml2Utils.d.ts.map +1 -1
- package/dist/providers/saml2Utils.js +46 -26
- package/dist/strategies/BrowserCallbackStrategy.d.ts +64 -0
- package/dist/strategies/BrowserCallbackStrategy.d.ts.map +1 -0
- package/dist/strategies/BrowserCallbackStrategy.js +210 -0
- package/dist/strategies/asOidcResult.d.ts +11 -0
- package/dist/strategies/asOidcResult.d.ts.map +1 -0
- package/dist/strategies/asOidcResult.js +30 -0
- package/dist/strategies/codeStrategies.d.ts +22 -0
- package/dist/strategies/codeStrategies.d.ts.map +1 -0
- package/dist/strategies/codeStrategies.js +39 -0
- package/dist/strategies/index.d.ts +8 -0
- package/dist/strategies/index.d.ts.map +1 -0
- package/dist/strategies/index.js +18 -0
- package/dist/strategies/manualStrategies.d.ts +20 -0
- package/dist/strategies/manualStrategies.d.ts.map +1 -0
- package/dist/strategies/manualStrategies.js +77 -0
- package/package.json +4 -4
- package/dist/auth/manualInput.d.ts +0 -5
- package/dist/auth/manualInput.d.ts.map +0 -1
- package/dist/auth/manualInput.js +0 -19
package/dist/auth/saml2Auth.js
CHANGED
|
@@ -2,70 +2,17 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* SAML 2.0 auth helpers
|
|
4
4
|
*/
|
|
5
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
6
|
-
if (k2 === undefined) k2 = k;
|
|
7
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
8
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
9
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
10
|
-
}
|
|
11
|
-
Object.defineProperty(o, k2, desc);
|
|
12
|
-
}) : (function(o, m, k, k2) {
|
|
13
|
-
if (k2 === undefined) k2 = k;
|
|
14
|
-
o[k2] = m[k];
|
|
15
|
-
}));
|
|
16
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
17
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
18
|
-
}) : function(o, v) {
|
|
19
|
-
o["default"] = v;
|
|
20
|
-
});
|
|
21
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
22
|
-
var ownKeys = function(o) {
|
|
23
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
24
|
-
var ar = [];
|
|
25
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
26
|
-
return ar;
|
|
27
|
-
};
|
|
28
|
-
return ownKeys(o);
|
|
29
|
-
};
|
|
30
|
-
return function (mod) {
|
|
31
|
-
if (mod && mod.__esModule) return mod;
|
|
32
|
-
var result = {};
|
|
33
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
34
|
-
__setModuleDefault(result, mod);
|
|
35
|
-
return result;
|
|
36
|
-
};
|
|
37
|
-
})();
|
|
38
5
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
39
6
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
40
7
|
};
|
|
41
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.withSamlCallbackServer = void 0;
|
|
42
10
|
exports.buildSamlAuthorizationUrl = buildSamlAuthorizationUrl;
|
|
43
|
-
exports.startSamlBrowserAuth = startSamlBrowserAuth;
|
|
44
11
|
exports.parseSamlNotOnOrAfter = parseSamlNotOnOrAfter;
|
|
45
12
|
const node_crypto_1 = require("node:crypto");
|
|
46
|
-
const http = __importStar(require("node:http"));
|
|
47
|
-
const net = __importStar(require("node:net"));
|
|
48
13
|
const node_zlib_1 = require("node:zlib");
|
|
49
14
|
const express_1 = __importDefault(require("express"));
|
|
50
|
-
const
|
|
51
|
-
chrome: 'chrome',
|
|
52
|
-
edge: 'msedge',
|
|
53
|
-
firefox: 'firefox',
|
|
54
|
-
system: undefined,
|
|
55
|
-
auto: undefined,
|
|
56
|
-
headless: null,
|
|
57
|
-
none: null,
|
|
58
|
-
};
|
|
59
|
-
function isPortAvailable(port) {
|
|
60
|
-
return new Promise((resolve) => {
|
|
61
|
-
const server = net.createServer();
|
|
62
|
-
server.listen(port, () => {
|
|
63
|
-
server.once('close', () => resolve(true));
|
|
64
|
-
server.close();
|
|
65
|
-
});
|
|
66
|
-
server.on('error', () => resolve(false));
|
|
67
|
-
});
|
|
68
|
-
}
|
|
15
|
+
const callbackServer_1 = require("./callbackServer");
|
|
69
16
|
function base64Encode(input) {
|
|
70
17
|
return Buffer.isBuffer(input)
|
|
71
18
|
? input.toString('base64')
|
|
@@ -99,96 +46,29 @@ function buildSamlAuthorizationUrl(config) {
|
|
|
99
46
|
: '';
|
|
100
47
|
return `${config.idpSsoUrl}?SAMLRequest=${samlRequest}${relayState}`;
|
|
101
48
|
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
return;
|
|
109
|
-
}
|
|
110
|
-
if (browser === 'auto') {
|
|
111
|
-
try {
|
|
112
|
-
const openModule = await Promise.resolve().then(() => __importStar(require('open')));
|
|
113
|
-
const open = openModule.default;
|
|
114
|
-
await open(authorizationUrl);
|
|
115
|
-
logger?.info('[SAML] Browser opened');
|
|
116
|
-
return;
|
|
117
|
-
}
|
|
118
|
-
catch (error) {
|
|
119
|
-
logger?.warn('[SAML] Failed to open browser automatically', {
|
|
120
|
-
error: error instanceof Error ? error.message : String(error),
|
|
121
|
-
});
|
|
122
|
-
logger?.info('[SAML] Open URL manually', { authorizationUrl });
|
|
123
|
-
return;
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
try {
|
|
127
|
-
const openModule = await Promise.resolve().then(() => __importStar(require('open')));
|
|
128
|
-
const open = openModule.default;
|
|
129
|
-
if (browserApp) {
|
|
130
|
-
await open(authorizationUrl, { app: { name: browserApp } });
|
|
131
|
-
}
|
|
132
|
-
else {
|
|
133
|
-
await open(authorizationUrl);
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
catch (error) {
|
|
137
|
-
logger?.warn('[SAML] Failed to open browser', {
|
|
138
|
-
error: error instanceof Error ? error.message : String(error),
|
|
139
|
-
});
|
|
140
|
-
logger?.info('[SAML] Open URL manually', { authorizationUrl });
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
async function startSamlBrowserAuth(config, browser, logger, port = 3001) {
|
|
144
|
-
const portAvailable = await isPortAvailable(port);
|
|
145
|
-
if (!portAvailable) {
|
|
146
|
-
throw new Error(`Port ${port} is already in use. Please specify a different port or free the port.`);
|
|
147
|
-
}
|
|
148
|
-
const authorizationUrl = buildSamlAuthorizationUrl(config);
|
|
149
|
-
return new Promise((resolve, reject) => {
|
|
150
|
-
const app = (0, express_1.default)();
|
|
151
|
-
app.use(express_1.default.urlencoded({ extended: false, limit: '5mb' }));
|
|
152
|
-
const server = http.createServer(app);
|
|
153
|
-
server.keepAliveTimeout = 0;
|
|
154
|
-
server.headersTimeout = 0;
|
|
155
|
-
const PORT = port;
|
|
156
|
-
let resolved = false;
|
|
157
|
-
const cleanup = () => {
|
|
158
|
-
if (resolved)
|
|
159
|
-
return;
|
|
160
|
-
resolved = true;
|
|
161
|
-
server.close();
|
|
162
|
-
};
|
|
163
|
-
const handleResponse = (samlResponse) => {
|
|
164
|
-
if (!samlResponse) {
|
|
165
|
-
cleanup();
|
|
166
|
-
reject(new Error('Missing SAMLResponse'));
|
|
167
|
-
return;
|
|
168
|
-
}
|
|
169
|
-
cleanup();
|
|
170
|
-
resolve(samlResponse);
|
|
171
|
-
};
|
|
172
|
-
app.post('/callback', (req, res) => {
|
|
173
|
-
const samlResponse = req.body?.SAMLResponse;
|
|
49
|
+
const withSamlCallbackServer = (options, use) => (0, callbackServer_1.runCallbackScope)(options, (app, settle) => {
|
|
50
|
+
app.use(express_1.default.urlencoded({ extended: false, limit: '5mb' }));
|
|
51
|
+
const handle = (samlResponse, res) => {
|
|
52
|
+
// The response is decided after the payload is examined. Answering 200
|
|
53
|
+
// first told a request that carried nothing that it had authenticated.
|
|
54
|
+
if (typeof samlResponse === 'string' && samlResponse) {
|
|
174
55
|
res
|
|
175
56
|
.status(200)
|
|
176
57
|
.send('SAML authentication complete. You can close this window.');
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
});
|
|
186
|
-
server.listen(PORT, async () => {
|
|
187
|
-
logger?.info('[SAML] Callback server listening', { port: PORT });
|
|
188
|
-
await openBrowserUrl(authorizationUrl, browser, logger);
|
|
189
|
-
});
|
|
58
|
+
settle.ok(samlResponse, res);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
res.status(400).send('Error: not a SAML assertion callback');
|
|
62
|
+
settle.ignore('no SAMLResponse in the request', res);
|
|
63
|
+
};
|
|
64
|
+
app.post('/callback', (req, res) => {
|
|
65
|
+
handle(req.body?.SAMLResponse, res);
|
|
190
66
|
});
|
|
191
|
-
|
|
67
|
+
app.get('/callback', (req, res) => {
|
|
68
|
+
handle(req.query.SAMLResponse, res);
|
|
69
|
+
});
|
|
70
|
+
}, use);
|
|
71
|
+
exports.withSamlCallbackServer = withSamlCallbackServer;
|
|
192
72
|
function parseSamlNotOnOrAfter(samlResponse) {
|
|
193
73
|
try {
|
|
194
74
|
const decoded = Buffer.from(samlResponse, 'base64').toString('utf8');
|
package/dist/index.d.ts
CHANGED
|
@@ -4,9 +4,15 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Provides token providers
|
|
6
6
|
*/
|
|
7
|
+
export { withBrowserCallbackServer } from './auth/callbackServer';
|
|
8
|
+
export type { OidcCallbackResult } from './auth/oidcBrowserAuth';
|
|
9
|
+
export { withOidcCallbackServer } from './auth/oidcBrowserAuth';
|
|
10
|
+
export { withSamlCallbackServer } from './auth/saml2Auth';
|
|
7
11
|
export { BrowserAuthError, RefreshError, ServiceKeyError, SessionDataError, TokenProviderError, ValidationError, } from './errors/TokenProviderErrors';
|
|
8
12
|
export type { AuthorizationCodeProviderConfig, ClientCredentialsProviderConfig, DeviceFlowProviderConfig, OidcBrowserProviderConfig, OidcDeviceFlowProviderConfig, OidcPasswordProviderConfig, OidcTokenExchangeProviderConfig, Saml2BearerProviderConfig, Saml2PureProviderConfig, } from './providers';
|
|
9
13
|
export { AuthorizationCodeProvider, BaseTokenProvider, ClientCredentialsProvider, DeviceFlowProvider, OidcBrowserProvider, OidcDeviceFlowProvider, OidcPasswordProvider, OidcTokenExchangeProvider, Saml2BearerProvider, Saml2PureProvider, } from './providers';
|
|
10
14
|
export { SsoProviderFactory } from './sso/SsoProviderFactory';
|
|
11
15
|
export type { SsoProviderConfig, SsoProviderInstance } from './sso/types';
|
|
16
|
+
export type { BrowserCallbackStrategyOptions, CallbackStrategyOptions, ExternalCodeStrategyOptions, ManualStrategyOptions, StaticCodeStrategyOptions, } from './strategies';
|
|
17
|
+
export { asOidcResult, BrowserCallbackStrategy, browserCallbackStrategy, DEFAULT_CALLBACK_PORT, DEFAULT_LOGIN_TIMEOUT_MS, externalCodeStrategy, manualPasteStrategy, manualSamlResponseStrategy, oidcCallbackStrategy, samlCallbackStrategy, staticCodeStrategy, } from './strategies';
|
|
12
18
|
//# 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;;;;;GAKG;AAGH,OAAO,EACL,gBAAgB,EAChB,YAAY,EACZ,eAAe,EACf,gBAAgB,EAChB,kBAAkB,EAClB,eAAe,GAChB,MAAM,8BAA8B,CAAC;AACtC,YAAY,EACV,+BAA+B,EAC/B,+BAA+B,EAC/B,wBAAwB,EACxB,yBAAyB,EACzB,4BAA4B,EAC5B,0BAA0B,EAC1B,+BAA+B,EAC/B,yBAAyB,EACzB,uBAAuB,GACxB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,yBAAyB,EACzB,iBAAiB,EACjB,yBAAyB,EACzB,kBAAkB,EAClB,mBAAmB,EACnB,sBAAsB,EACtB,oBAAoB,EACpB,yBAAyB,EACzB,mBAAmB,EACnB,iBAAiB,GAClB,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAE,yBAAyB,EAAE,MAAM,uBAAuB,CAAC;AAClE,YAAY,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AACjE,OAAO,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AAChE,OAAO,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAE1D,OAAO,EACL,gBAAgB,EAChB,YAAY,EACZ,eAAe,EACf,gBAAgB,EAChB,kBAAkB,EAClB,eAAe,GAChB,MAAM,8BAA8B,CAAC;AACtC,YAAY,EACV,+BAA+B,EAC/B,+BAA+B,EAC/B,wBAAwB,EACxB,yBAAyB,EACzB,4BAA4B,EAC5B,0BAA0B,EAC1B,+BAA+B,EAC/B,yBAAyB,EACzB,uBAAuB,GACxB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,yBAAyB,EACzB,iBAAiB,EACjB,yBAAyB,EACzB,kBAAkB,EAClB,mBAAmB,EACnB,sBAAsB,EACtB,oBAAoB,EACpB,yBAAyB,EACzB,mBAAmB,EACnB,iBAAiB,GAClB,MAAM,aAAa,CAAC;AAErB,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAC9D,YAAY,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAC1E,YAAY,EACV,8BAA8B,EAC9B,uBAAuB,EACvB,2BAA2B,EAC3B,qBAAqB,EACrB,yBAAyB,GAC1B,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,YAAY,EACZ,uBAAuB,EACvB,uBAAuB,EACvB,qBAAqB,EACrB,wBAAwB,EACxB,oBAAoB,EACpB,mBAAmB,EACnB,0BAA0B,EAC1B,oBAAoB,EACpB,oBAAoB,EACpB,kBAAkB,GACnB,MAAM,cAAc,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -6,7 +6,14 @@
|
|
|
6
6
|
* Provides token providers
|
|
7
7
|
*/
|
|
8
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
-
exports.SsoProviderFactory = exports.Saml2PureProvider = exports.Saml2BearerProvider = exports.OidcTokenExchangeProvider = exports.OidcPasswordProvider = exports.OidcDeviceFlowProvider = exports.OidcBrowserProvider = exports.DeviceFlowProvider = exports.ClientCredentialsProvider = exports.BaseTokenProvider = exports.AuthorizationCodeProvider = exports.ValidationError = exports.TokenProviderError = exports.SessionDataError = exports.ServiceKeyError = exports.RefreshError = exports.BrowserAuthError = void 0;
|
|
9
|
+
exports.staticCodeStrategy = exports.samlCallbackStrategy = exports.oidcCallbackStrategy = exports.manualSamlResponseStrategy = exports.manualPasteStrategy = exports.externalCodeStrategy = exports.DEFAULT_LOGIN_TIMEOUT_MS = exports.DEFAULT_CALLBACK_PORT = exports.browserCallbackStrategy = exports.BrowserCallbackStrategy = exports.asOidcResult = exports.SsoProviderFactory = exports.Saml2PureProvider = exports.Saml2BearerProvider = exports.OidcTokenExchangeProvider = exports.OidcPasswordProvider = exports.OidcDeviceFlowProvider = exports.OidcBrowserProvider = exports.DeviceFlowProvider = exports.ClientCredentialsProvider = exports.BaseTokenProvider = exports.AuthorizationCodeProvider = exports.ValidationError = exports.TokenProviderError = exports.SessionDataError = exports.ServiceKeyError = exports.RefreshError = exports.BrowserAuthError = exports.withSamlCallbackServer = exports.withOidcCallbackServer = exports.withBrowserCallbackServer = void 0;
|
|
10
|
+
// Callback server factories — "take the transport this package gives".
|
|
11
|
+
var callbackServer_1 = require("./auth/callbackServer");
|
|
12
|
+
Object.defineProperty(exports, "withBrowserCallbackServer", { enumerable: true, get: function () { return callbackServer_1.withBrowserCallbackServer; } });
|
|
13
|
+
var oidcBrowserAuth_1 = require("./auth/oidcBrowserAuth");
|
|
14
|
+
Object.defineProperty(exports, "withOidcCallbackServer", { enumerable: true, get: function () { return oidcBrowserAuth_1.withOidcCallbackServer; } });
|
|
15
|
+
var saml2Auth_1 = require("./auth/saml2Auth");
|
|
16
|
+
Object.defineProperty(exports, "withSamlCallbackServer", { enumerable: true, get: function () { return saml2Auth_1.withSamlCallbackServer; } });
|
|
10
17
|
// Errors
|
|
11
18
|
var TokenProviderErrors_1 = require("./errors/TokenProviderErrors");
|
|
12
19
|
Object.defineProperty(exports, "BrowserAuthError", { enumerable: true, get: function () { return TokenProviderErrors_1.BrowserAuthError; } });
|
|
@@ -30,3 +37,16 @@ Object.defineProperty(exports, "Saml2PureProvider", { enumerable: true, get: fun
|
|
|
30
37
|
// SSO factory
|
|
31
38
|
var SsoProviderFactory_1 = require("./sso/SsoProviderFactory");
|
|
32
39
|
Object.defineProperty(exports, "SsoProviderFactory", { enumerable: true, get: function () { return SsoProviderFactory_1.SsoProviderFactory; } });
|
|
40
|
+
// Authorization strategies — or bring your own IAuthorizationStrategy.
|
|
41
|
+
var strategies_1 = require("./strategies");
|
|
42
|
+
Object.defineProperty(exports, "asOidcResult", { enumerable: true, get: function () { return strategies_1.asOidcResult; } });
|
|
43
|
+
Object.defineProperty(exports, "BrowserCallbackStrategy", { enumerable: true, get: function () { return strategies_1.BrowserCallbackStrategy; } });
|
|
44
|
+
Object.defineProperty(exports, "browserCallbackStrategy", { enumerable: true, get: function () { return strategies_1.browserCallbackStrategy; } });
|
|
45
|
+
Object.defineProperty(exports, "DEFAULT_CALLBACK_PORT", { enumerable: true, get: function () { return strategies_1.DEFAULT_CALLBACK_PORT; } });
|
|
46
|
+
Object.defineProperty(exports, "DEFAULT_LOGIN_TIMEOUT_MS", { enumerable: true, get: function () { return strategies_1.DEFAULT_LOGIN_TIMEOUT_MS; } });
|
|
47
|
+
Object.defineProperty(exports, "externalCodeStrategy", { enumerable: true, get: function () { return strategies_1.externalCodeStrategy; } });
|
|
48
|
+
Object.defineProperty(exports, "manualPasteStrategy", { enumerable: true, get: function () { return strategies_1.manualPasteStrategy; } });
|
|
49
|
+
Object.defineProperty(exports, "manualSamlResponseStrategy", { enumerable: true, get: function () { return strategies_1.manualSamlResponseStrategy; } });
|
|
50
|
+
Object.defineProperty(exports, "oidcCallbackStrategy", { enumerable: true, get: function () { return strategies_1.oidcCallbackStrategy; } });
|
|
51
|
+
Object.defineProperty(exports, "samlCallbackStrategy", { enumerable: true, get: function () { return strategies_1.samlCallbackStrategy; } });
|
|
52
|
+
Object.defineProperty(exports, "staticCodeStrategy", { enumerable: true, get: function () { return strategies_1.staticCodeStrategy; } });
|
|
@@ -4,15 +4,19 @@
|
|
|
4
4
|
* Uses authorization_code grant type with browser-based OAuth2 flow.
|
|
5
5
|
* Supports pre-built authorization URLs and automatic refresh.
|
|
6
6
|
*/
|
|
7
|
-
import type { ILogger, ITokenResult, OAuth2GrantType } from '@mcp-abap-adt/interfaces';
|
|
7
|
+
import type { IAuthorizationStrategy, ILogger, ITokenResult, OAuth2GrantType } from '@mcp-abap-adt/interfaces';
|
|
8
8
|
import { BaseTokenProvider } from './BaseTokenProvider';
|
|
9
9
|
export interface AuthorizationCodeProviderConfig {
|
|
10
10
|
uaaUrl: string;
|
|
11
11
|
clientId: string;
|
|
12
12
|
clientSecret: string;
|
|
13
|
+
/** Pre-built authorization URL. Carries its own redirect; see the guard below. */
|
|
13
14
|
authorizationUrl?: string;
|
|
14
|
-
|
|
15
|
-
|
|
15
|
+
/**
|
|
16
|
+
* How the login is conducted. Omitted means a browser callback on the default
|
|
17
|
+
* port — the package's own transport, which a consumer may replace wholesale.
|
|
18
|
+
*/
|
|
19
|
+
authorization?: IAuthorizationStrategy<string>;
|
|
16
20
|
accessToken?: string;
|
|
17
21
|
refreshToken?: string;
|
|
18
22
|
logger?: ILogger;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AuthorizationCodeProvider.d.ts","sourceRoot":"","sources":["../../src/providers/AuthorizationCodeProvider.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAEV,OAAO,EACP,YAAY,EACZ,eAAe,EAChB,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"AuthorizationCodeProvider.d.ts","sourceRoot":"","sources":["../../src/providers/AuthorizationCodeProvider.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAEV,sBAAsB,EACtB,OAAO,EACP,YAAY,EACZ,eAAe,EAChB,MAAM,0BAA0B,CAAC;AAQlC,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAExD,MAAM,WAAW,+BAA+B;IAE9C,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IAErB,kFAAkF;IAClF,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B;;;OAGG;IACH,aAAa,CAAC,EAAE,sBAAsB,CAAC,MAAM,CAAC,CAAC;IAG/C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED;;;;;GAKG;AACH,qBAAa,yBAA0B,SAAQ,iBAAiB;IAC9D,OAAO,CAAC,MAAM,CAAkC;gBAEpC,MAAM,EAAE,+BAA+B;IA4D7C,SAAS,IAAI,OAAO,CAAC,YAAY,CAAC;IAIxC,SAAS,CAAC,WAAW,IAAI,eAAe;cAIxB,YAAY,IAAI,OAAO,CAAC,YAAY,CAAC;cA+ErC,cAAc,IAAI,OAAO,CAAC,YAAY,CAAC;CA0CxD"}
|
|
@@ -10,6 +10,7 @@ exports.AuthorizationCodeProvider = void 0;
|
|
|
10
10
|
const interfaces_1 = require("@mcp-abap-adt/interfaces");
|
|
11
11
|
const browserAuth_1 = require("../auth/browserAuth");
|
|
12
12
|
const tokenRefresher_1 = require("../auth/tokenRefresher");
|
|
13
|
+
const strategies_1 = require("../strategies");
|
|
13
14
|
const BaseTokenProvider_1 = require("./BaseTokenProvider");
|
|
14
15
|
/**
|
|
15
16
|
* Authorization Code token provider
|
|
@@ -30,8 +31,6 @@ class AuthorizationCodeProvider extends BaseTokenProvider_1.BaseTokenProvider {
|
|
|
30
31
|
hasRefreshToken: !!config.refreshToken,
|
|
31
32
|
accessToken: this.formatToken(config.accessToken),
|
|
32
33
|
refreshToken: this.formatToken(config.refreshToken),
|
|
33
|
-
browser: config.browser || 'none',
|
|
34
|
-
redirectPort: config.redirectPort || 3001,
|
|
35
34
|
});
|
|
36
35
|
const missingFields = [];
|
|
37
36
|
if (!config.uaaUrl) {
|
|
@@ -76,56 +75,67 @@ class AuthorizationCodeProvider extends BaseTokenProvider_1.BaseTokenProvider {
|
|
|
76
75
|
return interfaces_1.AUTH_TYPE_AUTHORIZATION_CODE;
|
|
77
76
|
}
|
|
78
77
|
async performLogin() {
|
|
79
|
-
// Build authorization config
|
|
80
|
-
// If authorizationUrl is provided, use it; otherwise startBrowserAuth will build it from uaaUrl + clientId
|
|
81
78
|
const authConfig = {
|
|
82
79
|
uaaUrl: this.config.uaaUrl,
|
|
83
80
|
uaaClientId: this.config.clientId,
|
|
84
81
|
uaaClientSecret: this.config.clientSecret,
|
|
85
82
|
};
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
83
|
+
const prebuilt = this.config.authorizationUrl;
|
|
84
|
+
const declaredRedirect = prebuilt
|
|
85
|
+
? new URL(prebuilt).searchParams.get('redirect_uri')
|
|
86
|
+
: null;
|
|
87
|
+
const mismatch = (redirectUri) => `Pre-built authorizationUrl declares redirect_uri ${declaredRedirect}, ` +
|
|
88
|
+
`but the authorization strategy used ${redirectUri}, which does not match. ` +
|
|
89
|
+
'An ephemeral port cannot be used with a pre-built URL.';
|
|
90
|
+
// The provider owns the URL; the strategy owns where it is answered. The
|
|
91
|
+
// guard lives here rather than after the fact because a mismatched redirect
|
|
92
|
+
// produces no callback at all — checking the outcome would mean waiting for
|
|
93
|
+
// a timeout that explains nothing.
|
|
94
|
+
const request = {
|
|
95
|
+
logger: this.logger,
|
|
96
|
+
buildAuthorizationUrl: async (redirectUri) => {
|
|
97
|
+
if (prebuilt) {
|
|
98
|
+
if (declaredRedirect && declaredRedirect !== redirectUri) {
|
|
99
|
+
throw new Error(mismatch(redirectUri));
|
|
100
|
+
}
|
|
101
|
+
return prebuilt;
|
|
102
|
+
}
|
|
103
|
+
return (0, browserAuth_1.getJwtAuthorizationUrl)(authConfig, redirectUri);
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
// Constructed here means disposed here: whoever constructs, disposes.
|
|
107
|
+
const supplied = this.config.authorization;
|
|
108
|
+
const strategy = supplied ?? (0, strategies_1.browserCallbackStrategy)();
|
|
109
|
+
let outcome;
|
|
110
|
+
try {
|
|
111
|
+
outcome = await strategy.authorize(request);
|
|
89
112
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
//
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
});
|
|
110
|
-
const result = await Promise.race([
|
|
111
|
-
(0, browserAuth_1.startBrowserAuth)(authConfig, browser, this.logger || undefined, // Pass logger to browserAuth
|
|
112
|
-
redirectPort),
|
|
113
|
-
timeoutPromise,
|
|
114
|
-
]);
|
|
115
|
-
this.logger?.info('[AuthorizationCodeProvider] Login completed', {
|
|
116
|
-
hasAccessToken: !!result.accessToken,
|
|
117
|
-
hasRefreshToken: !!result.refreshToken,
|
|
118
|
-
accessToken: this.formatToken(result.accessToken),
|
|
119
|
-
refreshToken: this.formatToken(result.refreshToken),
|
|
120
|
-
accessTokenLength: result.accessToken?.length || 0,
|
|
113
|
+
finally {
|
|
114
|
+
if (!supplied) {
|
|
115
|
+
// A cleanup failure must not replace the reason the login failed.
|
|
116
|
+
await strategy.dispose?.().catch((error) => {
|
|
117
|
+
this.logger?.warn('[AuthorizationCodeProvider] dispose failed', {
|
|
118
|
+
error: error instanceof Error ? error.message : String(error),
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
// The second net. A strategy that never called the builder — `staticCodeStrategy`
|
|
124
|
+
// holds its payload already — passed the first check by not participating in
|
|
125
|
+
// it, and would otherwise reach the exchange with a redirect_uri the
|
|
126
|
+
// pre-built URL never advertised, earning an opaque `invalid_grant`.
|
|
127
|
+
if (declaredRedirect && declaredRedirect !== outcome.redirectUri) {
|
|
128
|
+
throw new Error(mismatch(outcome.redirectUri));
|
|
129
|
+
}
|
|
130
|
+
this.logger?.info('[AuthorizationCodeProvider] Code received', {
|
|
131
|
+
redirectUri: outcome.redirectUri,
|
|
121
132
|
});
|
|
122
|
-
|
|
123
|
-
const expiresIn = this.calculateExpiresIn(result.accessToken);
|
|
133
|
+
const result = await (0, browserAuth_1.exchangeCodeForToken)(authConfig, outcome.payload, outcome.redirectUri, this.logger);
|
|
124
134
|
return {
|
|
125
135
|
authorizationToken: result.accessToken,
|
|
126
136
|
refreshToken: result.refreshToken,
|
|
127
137
|
authType: interfaces_1.AUTH_TYPE_AUTHORIZATION_CODE,
|
|
128
|
-
expiresIn,
|
|
138
|
+
expiresIn: this.calculateExpiresIn(result.accessToken),
|
|
129
139
|
};
|
|
130
140
|
}
|
|
131
141
|
async performRefresh() {
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Uses OAuth2 Device Flow for devices without browser or input capabilities.
|
|
5
5
|
* User authorizes on another device by entering a code.
|
|
6
6
|
*/
|
|
7
|
-
import type { ITokenResult, OAuth2GrantType } from '@mcp-abap-adt/interfaces';
|
|
7
|
+
import type { ILogger, ITokenResult, OAuth2GrantType } from '@mcp-abap-adt/interfaces';
|
|
8
8
|
import { BaseTokenProvider } from './BaseTokenProvider';
|
|
9
9
|
export interface DeviceFlowProviderConfig {
|
|
10
10
|
uaaUrl: string;
|
|
@@ -13,6 +13,7 @@ export interface DeviceFlowProviderConfig {
|
|
|
13
13
|
scope?: string;
|
|
14
14
|
accessToken?: string;
|
|
15
15
|
refreshToken?: string;
|
|
16
|
+
logger?: ILogger;
|
|
16
17
|
}
|
|
17
18
|
/**
|
|
18
19
|
* Device Flow token provider
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DeviceFlowProvider.d.ts","sourceRoot":"","sources":["../../src/providers/DeviceFlowProvider.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"DeviceFlowProvider.d.ts","sourceRoot":"","sources":["../../src/providers/DeviceFlowProvider.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EACV,OAAO,EACP,YAAY,EACZ,eAAe,EAChB,MAAM,0BAA0B,CAAC;AAQlC,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAExD,MAAM,WAAW,wBAAwB;IACvC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED;;;;;GAKG;AACH,qBAAa,kBAAmB,SAAQ,iBAAiB;IACvD,OAAO,CAAC,MAAM,CAA2B;gBAE7B,MAAM,EAAE,wBAAwB;IAc5C,SAAS,CAAC,WAAW,IAAI,eAAe;cAOxB,YAAY,IAAI,OAAO,CAAC,YAAY,CAAC;cAyCrC,cAAc,IAAI,OAAO,CAAC,YAAY,CAAC;CA2BxD"}
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
9
|
exports.DeviceFlowProvider = void 0;
|
|
10
10
|
const interfaces_1 = require("@mcp-abap-adt/interfaces");
|
|
11
|
+
const announce_1 = require("../auth/announce");
|
|
11
12
|
const deviceFlowAuth_1 = require("../auth/deviceFlowAuth");
|
|
12
13
|
const tokenRefresher_1 = require("../auth/tokenRefresher");
|
|
13
14
|
const BaseTokenProvider_1 = require("./BaseTokenProvider");
|
|
@@ -22,6 +23,7 @@ class DeviceFlowProvider extends BaseTokenProvider_1.BaseTokenProvider {
|
|
|
22
23
|
constructor(config) {
|
|
23
24
|
super();
|
|
24
25
|
this.config = config;
|
|
26
|
+
this.logger = config.logger;
|
|
25
27
|
if (config.accessToken) {
|
|
26
28
|
this.authorizationToken = config.accessToken;
|
|
27
29
|
this.expiresAt = this.parseExpirationFromJWT(config.accessToken);
|
|
@@ -38,19 +40,20 @@ class DeviceFlowProvider extends BaseTokenProvider_1.BaseTokenProvider {
|
|
|
38
40
|
}
|
|
39
41
|
async performLogin() {
|
|
40
42
|
// Initiate device flow
|
|
41
|
-
const deviceFlow = await (0, deviceFlowAuth_1.initiateDeviceFlow)(this.config.uaaUrl, this.config.clientId, this.config.scope,
|
|
42
|
-
// Display user code and verification URI
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
43
|
+
const deviceFlow = await (0, deviceFlowAuth_1.initiateDeviceFlow)(this.config.uaaUrl, this.config.clientId, this.config.scope, this.logger);
|
|
44
|
+
// Display user code and verification URI. This is a prompt the user must
|
|
45
|
+
// see to complete the flow, not a log line — it goes to the logger when
|
|
46
|
+
// there is one and to stderr otherwise, never to stdout.
|
|
47
|
+
const announce = (0, announce_1.announcer)(this.logger);
|
|
48
|
+
announce('Device Flow Authorization');
|
|
49
|
+
announce(`Go to: ${deviceFlow.verificationUri}`);
|
|
46
50
|
if (deviceFlow.verificationUriComplete) {
|
|
47
|
-
|
|
51
|
+
announce(`Or use complete URL: ${deviceFlow.verificationUriComplete}`);
|
|
48
52
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
console.log('⏳ Waiting for authorization...\n');
|
|
53
|
+
announce(`Enter code: ${deviceFlow.userCode}`);
|
|
54
|
+
announce('Waiting for authorization...');
|
|
52
55
|
// Poll for tokens
|
|
53
|
-
const result = await (0, deviceFlowAuth_1.pollForDeviceTokens)(this.config.uaaUrl, this.config.clientId, this.config.clientSecret, deviceFlow.deviceCode, deviceFlow.interval,
|
|
56
|
+
const result = await (0, deviceFlowAuth_1.pollForDeviceTokens)(this.config.uaaUrl, this.config.clientId, this.config.clientSecret, deviceFlow.deviceCode, deviceFlow.interval, this.logger);
|
|
54
57
|
const expiresIn = this.calculateExpiresIn(result.accessToken);
|
|
55
58
|
return {
|
|
56
59
|
authorizationToken: result.accessToken,
|
|
@@ -1,20 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* OIDC Authorization Code Provider (with PKCE)
|
|
3
3
|
*/
|
|
4
|
-
import type { ILogger, ITokenResult, OAuth2GrantType } from '@mcp-abap-adt/interfaces';
|
|
4
|
+
import type { IAuthorizationStrategy, ILogger, ITokenResult, OAuth2GrantType } from '@mcp-abap-adt/interfaces';
|
|
5
|
+
import type { OidcCallbackResult } from '../auth/oidcBrowserAuth';
|
|
5
6
|
import { BaseTokenProvider } from './BaseTokenProvider';
|
|
6
7
|
export interface OidcBrowserProviderConfig {
|
|
7
8
|
issuerUrl?: string;
|
|
8
9
|
clientId: string;
|
|
9
10
|
clientSecret?: string;
|
|
10
11
|
scopes?: string[];
|
|
11
|
-
browser?: string;
|
|
12
|
-
redirectPort?: number;
|
|
13
|
-
redirectUri?: string;
|
|
14
|
-
authorizationCode?: string;
|
|
15
|
-
authorizationCodeProvider?: () => Promise<string>;
|
|
16
12
|
authorizationEndpoint?: string;
|
|
17
13
|
tokenEndpoint?: string;
|
|
14
|
+
/** How the login is conducted. Omitted means a browser callback on the default port. */
|
|
15
|
+
authorization?: IAuthorizationStrategy<OidcCallbackResult>;
|
|
18
16
|
accessToken?: string;
|
|
19
17
|
refreshToken?: string;
|
|
20
18
|
logger?: ILogger;
|
|
@@ -25,6 +23,5 @@ export declare class OidcBrowserProvider extends BaseTokenProvider {
|
|
|
25
23
|
protected getAuthType(): OAuth2GrantType;
|
|
26
24
|
protected performLogin(): Promise<ITokenResult>;
|
|
27
25
|
protected performRefresh(): Promise<ITokenResult>;
|
|
28
|
-
private resolveAuthorizationCode;
|
|
29
26
|
}
|
|
30
27
|
//# sourceMappingURL=OidcBrowserProvider.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OidcBrowserProvider.d.ts","sourceRoot":"","sources":["../../src/providers/OidcBrowserProvider.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EACV,OAAO,EACP,YAAY,EACZ,eAAe,EAChB,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"OidcBrowserProvider.d.ts","sourceRoot":"","sources":["../../src/providers/OidcBrowserProvider.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EACV,sBAAsB,EACtB,OAAO,EACP,YAAY,EACZ,eAAe,EAChB,MAAM,0BAA0B,CAAC;AAElC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAKlE,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAExD,MAAM,WAAW,yBAAyB;IACxC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,wFAAwF;IACxF,aAAa,CAAC,EAAE,sBAAsB,CAAC,kBAAkB,CAAC,CAAC;IAC3D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,qBAAa,mBAAoB,SAAQ,iBAAiB;IACxD,OAAO,CAAC,MAAM,CAA4B;gBAE9B,MAAM,EAAE,yBAAyB;IAc7C,SAAS,CAAC,WAAW,IAAI,eAAe;cAIxB,YAAY,IAAI,OAAO,CAAC,YAAY,CAAC;cAyFrC,cAAc,IAAI,OAAO,CAAC,YAAY,CAAC;CAsCxD"}
|