@mcp-abap-adt/auth-providers 2.2.1 → 3.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 +98 -0
- package/README.md +231 -13
- package/dist/__tests__/integration/stand/formLogin.d.ts +68 -0
- package/dist/__tests__/integration/stand/formLogin.d.ts.map +1 -0
- package/dist/__tests__/integration/stand/formLogin.js +194 -0
- package/dist/auth/callbackServer.js +2 -2
- package/dist/auth/passcodeAuth.d.ts +25 -0
- package/dist/auth/passcodeAuth.d.ts.map +1 -0
- package/dist/auth/passcodeAuth.js +62 -0
- package/dist/auth/samlBearerAssertion.d.ts +24 -0
- package/dist/auth/samlBearerAssertion.d.ts.map +1 -0
- package/dist/auth/samlBearerAssertion.js +101 -0
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/providers/Saml2BearerProvider.d.ts.map +1 -1
- package/dist/providers/Saml2BearerProvider.js +4 -1
- package/dist/providers/UaaPasscodeProvider.d.ts +43 -0
- package/dist/providers/UaaPasscodeProvider.d.ts.map +1 -0
- package/dist/providers/UaaPasscodeProvider.js +86 -0
- package/dist/providers/index.d.ts +2 -2
- package/dist/providers/index.d.ts.map +1 -1
- package/dist/providers/index.js +3 -3
- package/dist/strategies/index.d.ts +1 -1
- package/dist/strategies/index.d.ts.map +1 -1
- package/dist/strategies/index.js +2 -1
- package/dist/strategies/manualStrategies.d.ts +7 -0
- package/dist/strategies/manualStrategies.d.ts.map +1 -1
- package/dist/strategies/manualStrategies.js +21 -0
- package/package.json +10 -5
- package/bin/auth-device-flow.ts +0 -114
- package/dist/auth/deviceFlowAuth.d.ts +0 -43
- package/dist/auth/deviceFlowAuth.d.ts.map +0 -1
- package/dist/auth/deviceFlowAuth.js +0 -168
- package/dist/providers/DeviceFlowProvider.d.ts +0 -32
- package/dist/providers/DeviceFlowProvider.d.ts.map +0 -1
- package/dist/providers/DeviceFlowProvider.js +0 -86
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* UAA / XSUAA one-time passcode provider — the login `cf login --sso` uses.
|
|
4
|
+
*
|
|
5
|
+
* Nothing is opened and nothing listens on this machine: the user fetches a
|
|
6
|
+
* code from `<uaaUrl>/passcode` in any browser, anywhere, logging in however
|
|
7
|
+
* the identity zone asks (SSO through a corporate IdP, MFA), and hands it to
|
|
8
|
+
* the strategy. The provider exchanges it for tokens and refreshes them
|
|
9
|
+
* afterwards, so the user is asked again only when the refresh token is gone.
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.UaaPasscodeProvider = void 0;
|
|
13
|
+
const interfaces_auth_1 = require("@mcp-abap-adt/interfaces-auth");
|
|
14
|
+
const passcodeAuth_1 = require("../auth/passcodeAuth");
|
|
15
|
+
const tokenRefresher_1 = require("../auth/tokenRefresher");
|
|
16
|
+
const manualStrategies_1 = require("../strategies/manualStrategies");
|
|
17
|
+
const BaseTokenProvider_1 = require("./BaseTokenProvider");
|
|
18
|
+
class UaaPasscodeProvider extends BaseTokenProvider_1.BaseTokenProvider {
|
|
19
|
+
config;
|
|
20
|
+
constructor(config) {
|
|
21
|
+
super();
|
|
22
|
+
this.config = config;
|
|
23
|
+
this.logger = config.logger;
|
|
24
|
+
if (config.accessToken) {
|
|
25
|
+
this.authorizationToken = config.accessToken;
|
|
26
|
+
this.expiresAt = this.parseExpirationFromJWT(config.accessToken);
|
|
27
|
+
}
|
|
28
|
+
if (config.refreshToken) {
|
|
29
|
+
this.refreshToken = config.refreshToken;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
getAuthType() {
|
|
33
|
+
return interfaces_auth_1.AUTH_TYPE_PASSWORD;
|
|
34
|
+
}
|
|
35
|
+
get baseUrl() {
|
|
36
|
+
return this.config.uaaUrl.replace(/\/+$/, '');
|
|
37
|
+
}
|
|
38
|
+
async performLogin() {
|
|
39
|
+
const supplied = this.config.authorization;
|
|
40
|
+
const strategy = supplied ?? (0, manualStrategies_1.manualPasscodeStrategy)();
|
|
41
|
+
let passcode;
|
|
42
|
+
try {
|
|
43
|
+
// The passcode page takes no redirect: the code travels by hand.
|
|
44
|
+
const outcome = await strategy.authorize({
|
|
45
|
+
logger: this.logger,
|
|
46
|
+
buildAuthorizationUrl: async () => `${this.baseUrl}/passcode`,
|
|
47
|
+
});
|
|
48
|
+
passcode = outcome.payload;
|
|
49
|
+
}
|
|
50
|
+
finally {
|
|
51
|
+
if (!supplied) {
|
|
52
|
+
await strategy.dispose?.().catch((error) => {
|
|
53
|
+
this.logger?.warn('[UaaPasscodeProvider] dispose failed', {
|
|
54
|
+
error: error instanceof Error ? error.message : String(error),
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
const tokens = await (0, passcodeAuth_1.exchangePasscode)(this.baseUrl, this.config.clientId, this.config.clientSecret, passcode, this.logger);
|
|
60
|
+
return {
|
|
61
|
+
authorizationToken: tokens.accessToken,
|
|
62
|
+
refreshToken: tokens.refreshToken,
|
|
63
|
+
authType: interfaces_auth_1.AUTH_TYPE_PASSWORD,
|
|
64
|
+
expiresIn: tokens.expiresIn,
|
|
65
|
+
tokenType: 'jwt',
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* A failure is thrown, not handled: BaseTokenProvider drops the refresh
|
|
70
|
+
* token and asks for a new passcode through performLogin().
|
|
71
|
+
*/
|
|
72
|
+
async performRefresh() {
|
|
73
|
+
if (!this.refreshToken) {
|
|
74
|
+
throw new Error('Refresh token is required for refresh');
|
|
75
|
+
}
|
|
76
|
+
const result = await (0, tokenRefresher_1.refreshJwtToken)(this.refreshToken, this.baseUrl, this.config.clientId, this.config.clientSecret ?? '');
|
|
77
|
+
return {
|
|
78
|
+
authorizationToken: result.accessToken,
|
|
79
|
+
refreshToken: result.refreshToken || this.refreshToken,
|
|
80
|
+
authType: interfaces_auth_1.AUTH_TYPE_PASSWORD,
|
|
81
|
+
expiresIn: result.expiresIn,
|
|
82
|
+
tokenType: 'jwt',
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
exports.UaaPasscodeProvider = UaaPasscodeProvider;
|
|
@@ -9,8 +9,6 @@ export { AuthorizationCodeProvider } from './AuthorizationCodeProvider';
|
|
|
9
9
|
export { BaseTokenProvider } from './BaseTokenProvider';
|
|
10
10
|
export type { ClientCredentialsProviderConfig } from './ClientCredentialsProvider';
|
|
11
11
|
export { ClientCredentialsProvider } from './ClientCredentialsProvider';
|
|
12
|
-
export type { DeviceFlowProviderConfig } from './DeviceFlowProvider';
|
|
13
|
-
export { DeviceFlowProvider } from './DeviceFlowProvider';
|
|
14
12
|
export type { OidcBrowserProviderConfig } from './OidcBrowserProvider';
|
|
15
13
|
export { OidcBrowserProvider } from './OidcBrowserProvider';
|
|
16
14
|
export type { OidcDeviceFlowProviderConfig } from './OidcDeviceFlowProvider';
|
|
@@ -23,4 +21,6 @@ export type { Saml2BearerProviderConfig } from './Saml2BearerProvider';
|
|
|
23
21
|
export { Saml2BearerProvider } from './Saml2BearerProvider';
|
|
24
22
|
export type { Saml2PureProviderConfig } from './Saml2PureProvider';
|
|
25
23
|
export { Saml2PureProvider } from './Saml2PureProvider';
|
|
24
|
+
export type { UaaPasscodeProviderConfig } from './UaaPasscodeProvider';
|
|
25
|
+
export { UaaPasscodeProvider } from './UaaPasscodeProvider';
|
|
26
26
|
//# 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,EAAE,+BAA+B,EAAE,MAAM,6BAA6B,CAAC;AACnF,OAAO,EAAE,yBAAyB,EAAE,MAAM,6BAA6B,CAAC;AACxE,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,YAAY,EAAE,+BAA+B,EAAE,MAAM,6BAA6B,CAAC;AACnF,OAAO,EAAE,yBAAyB,EAAE,MAAM,6BAA6B,CAAC;AACxE,YAAY,EAAE,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/providers/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,YAAY,EAAE,+BAA+B,EAAE,MAAM,6BAA6B,CAAC;AACnF,OAAO,EAAE,yBAAyB,EAAE,MAAM,6BAA6B,CAAC;AACxE,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,YAAY,EAAE,+BAA+B,EAAE,MAAM,6BAA6B,CAAC;AACnF,OAAO,EAAE,yBAAyB,EAAE,MAAM,6BAA6B,CAAC;AACxE,YAAY,EAAE,yBAAyB,EAAE,MAAM,uBAAuB,CAAC;AACvE,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,YAAY,EAAE,4BAA4B,EAAE,MAAM,0BAA0B,CAAC;AAC7E,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAClE,YAAY,EAAE,0BAA0B,EAAE,MAAM,wBAAwB,CAAC;AACzE,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAC9D,YAAY,EAAE,+BAA+B,EAAE,MAAM,6BAA6B,CAAC;AACnF,OAAO,EAAE,yBAAyB,EAAE,MAAM,6BAA6B,CAAC;AACxE,YAAY,EAAE,yBAAyB,EAAE,MAAM,uBAAuB,CAAC;AACvE,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,YAAY,EAAE,uBAAuB,EAAE,MAAM,qBAAqB,CAAC;AACnE,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AACxD,YAAY,EAAE,yBAAyB,EAAE,MAAM,uBAAuB,CAAC;AACvE,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC"}
|
package/dist/providers/index.js
CHANGED
|
@@ -6,15 +6,13 @@
|
|
|
6
6
|
* All providers extend BaseTokenProvider and implement ITokenProvider.
|
|
7
7
|
*/
|
|
8
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
-
exports.Saml2PureProvider = exports.Saml2BearerProvider = exports.OidcTokenExchangeProvider = exports.OidcPasswordProvider = exports.OidcDeviceFlowProvider = exports.OidcBrowserProvider = exports.
|
|
9
|
+
exports.UaaPasscodeProvider = exports.Saml2PureProvider = exports.Saml2BearerProvider = exports.OidcTokenExchangeProvider = exports.OidcPasswordProvider = exports.OidcDeviceFlowProvider = exports.OidcBrowserProvider = exports.ClientCredentialsProvider = exports.BaseTokenProvider = exports.AuthorizationCodeProvider = void 0;
|
|
10
10
|
var AuthorizationCodeProvider_1 = require("./AuthorizationCodeProvider");
|
|
11
11
|
Object.defineProperty(exports, "AuthorizationCodeProvider", { enumerable: true, get: function () { return AuthorizationCodeProvider_1.AuthorizationCodeProvider; } });
|
|
12
12
|
var BaseTokenProvider_1 = require("./BaseTokenProvider");
|
|
13
13
|
Object.defineProperty(exports, "BaseTokenProvider", { enumerable: true, get: function () { return BaseTokenProvider_1.BaseTokenProvider; } });
|
|
14
14
|
var ClientCredentialsProvider_1 = require("./ClientCredentialsProvider");
|
|
15
15
|
Object.defineProperty(exports, "ClientCredentialsProvider", { enumerable: true, get: function () { return ClientCredentialsProvider_1.ClientCredentialsProvider; } });
|
|
16
|
-
var DeviceFlowProvider_1 = require("./DeviceFlowProvider");
|
|
17
|
-
Object.defineProperty(exports, "DeviceFlowProvider", { enumerable: true, get: function () { return DeviceFlowProvider_1.DeviceFlowProvider; } });
|
|
18
16
|
var OidcBrowserProvider_1 = require("./OidcBrowserProvider");
|
|
19
17
|
Object.defineProperty(exports, "OidcBrowserProvider", { enumerable: true, get: function () { return OidcBrowserProvider_1.OidcBrowserProvider; } });
|
|
20
18
|
var OidcDeviceFlowProvider_1 = require("./OidcDeviceFlowProvider");
|
|
@@ -27,3 +25,5 @@ var Saml2BearerProvider_1 = require("./Saml2BearerProvider");
|
|
|
27
25
|
Object.defineProperty(exports, "Saml2BearerProvider", { enumerable: true, get: function () { return Saml2BearerProvider_1.Saml2BearerProvider; } });
|
|
28
26
|
var Saml2PureProvider_1 = require("./Saml2PureProvider");
|
|
29
27
|
Object.defineProperty(exports, "Saml2PureProvider", { enumerable: true, get: function () { return Saml2PureProvider_1.Saml2PureProvider; } });
|
|
28
|
+
var UaaPasscodeProvider_1 = require("./UaaPasscodeProvider");
|
|
29
|
+
Object.defineProperty(exports, "UaaPasscodeProvider", { enumerable: true, get: function () { return UaaPasscodeProvider_1.UaaPasscodeProvider; } });
|
|
@@ -4,5 +4,5 @@ export { BrowserCallbackStrategy, browserCallbackStrategy, DEFAULT_CALLBACK_PORT
|
|
|
4
4
|
export type { ExternalCodeStrategyOptions, StaticCodeStrategyOptions, } from './codeStrategies';
|
|
5
5
|
export { externalCodeStrategy, staticCodeStrategy, } from './codeStrategies';
|
|
6
6
|
export type { ManualStrategyOptions } from './manualStrategies';
|
|
7
|
-
export { manualPasteStrategy, manualSamlResponseStrategy, } from './manualStrategies';
|
|
7
|
+
export { manualPasscodeStrategy, manualPasteStrategy, manualSamlResponseStrategy, } from './manualStrategies';
|
|
8
8
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/strategies/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,YAAY,EACV,8BAA8B,EAC9B,uBAAuB,GACxB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACL,uBAAuB,EACvB,uBAAuB,EACvB,qBAAqB,EACrB,wBAAwB,EACxB,oBAAoB,EACpB,oBAAoB,GACrB,MAAM,2BAA2B,CAAC;AACnC,YAAY,EACV,2BAA2B,EAC3B,yBAAyB,GAC1B,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,oBAAoB,EACpB,kBAAkB,GACnB,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAChE,OAAO,EACL,mBAAmB,EACnB,0BAA0B,GAC3B,MAAM,oBAAoB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/strategies/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,YAAY,EACV,8BAA8B,EAC9B,uBAAuB,GACxB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACL,uBAAuB,EACvB,uBAAuB,EACvB,qBAAqB,EACrB,wBAAwB,EACxB,oBAAoB,EACpB,oBAAoB,GACrB,MAAM,2BAA2B,CAAC;AACnC,YAAY,EACV,2BAA2B,EAC3B,yBAAyB,GAC1B,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,oBAAoB,EACpB,kBAAkB,GACnB,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAChE,OAAO,EACL,sBAAsB,EACtB,mBAAmB,EACnB,0BAA0B,GAC3B,MAAM,oBAAoB,CAAC"}
|
package/dist/strategies/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.manualSamlResponseStrategy = exports.manualPasteStrategy = exports.staticCodeStrategy = exports.externalCodeStrategy = exports.samlCallbackStrategy = exports.oidcCallbackStrategy = exports.DEFAULT_LOGIN_TIMEOUT_MS = exports.DEFAULT_CALLBACK_PORT = exports.browserCallbackStrategy = exports.BrowserCallbackStrategy = exports.asOidcResult = void 0;
|
|
3
|
+
exports.manualSamlResponseStrategy = exports.manualPasteStrategy = exports.manualPasscodeStrategy = exports.staticCodeStrategy = exports.externalCodeStrategy = exports.samlCallbackStrategy = exports.oidcCallbackStrategy = exports.DEFAULT_LOGIN_TIMEOUT_MS = exports.DEFAULT_CALLBACK_PORT = exports.browserCallbackStrategy = exports.BrowserCallbackStrategy = exports.asOidcResult = void 0;
|
|
4
4
|
var asOidcResult_1 = require("./asOidcResult");
|
|
5
5
|
Object.defineProperty(exports, "asOidcResult", { enumerable: true, get: function () { return asOidcResult_1.asOidcResult; } });
|
|
6
6
|
var BrowserCallbackStrategy_1 = require("./BrowserCallbackStrategy");
|
|
@@ -14,5 +14,6 @@ var codeStrategies_1 = require("./codeStrategies");
|
|
|
14
14
|
Object.defineProperty(exports, "externalCodeStrategy", { enumerable: true, get: function () { return codeStrategies_1.externalCodeStrategy; } });
|
|
15
15
|
Object.defineProperty(exports, "staticCodeStrategy", { enumerable: true, get: function () { return codeStrategies_1.staticCodeStrategy; } });
|
|
16
16
|
var manualStrategies_1 = require("./manualStrategies");
|
|
17
|
+
Object.defineProperty(exports, "manualPasscodeStrategy", { enumerable: true, get: function () { return manualStrategies_1.manualPasscodeStrategy; } });
|
|
17
18
|
Object.defineProperty(exports, "manualPasteStrategy", { enumerable: true, get: function () { return manualStrategies_1.manualPasteStrategy; } });
|
|
18
19
|
Object.defineProperty(exports, "manualSamlResponseStrategy", { enumerable: true, get: function () { return manualStrategies_1.manualSamlResponseStrategy; } });
|
|
@@ -17,4 +17,11 @@ export interface ManualStrategyOptions {
|
|
|
17
17
|
export declare function manualPasteStrategy(options?: ManualStrategyOptions): IAuthorizationStrategy<string>;
|
|
18
18
|
/** The user lifts `SAMLResponse` from the POST body — it never reaches the URL. */
|
|
19
19
|
export declare function manualSamlResponseStrategy(options?: ManualStrategyOptions): IAuthorizationStrategy<string>;
|
|
20
|
+
/**
|
|
21
|
+
* The UAA passcode, typed in by the user: shows where to fetch it —
|
|
22
|
+
* `<uaa>/passcode`, opened in any browser, on any machine — and reads the
|
|
23
|
+
* code they copy from that page. The default for `UaaPasscodeProvider`, so a
|
|
24
|
+
* login works on a machine with no browser at all, as `cf login --sso` does.
|
|
25
|
+
*/
|
|
26
|
+
export declare function manualPasscodeStrategy(options?: ManualStrategyOptions): IAuthorizationStrategy<string>;
|
|
20
27
|
//# sourceMappingURL=manualStrategies.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manualStrategies.d.ts","sourceRoot":"","sources":["../../src/strategies/manualStrategies.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAGV,sBAAsB,EACvB,MAAM,+BAA+B,CAAC;AAIvC,MAAM,WAAW,qBAAqB;IACpC,mFAAmF;IACnF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gFAAgF;IAChF,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CAC5C;AAiCD,4EAA4E;AAC5E,wBAAgB,mBAAmB,CACjC,OAAO,GAAE,qBAA0B,GAClC,sBAAsB,CAAC,MAAM,CAAC,CAmBhC;AAED,mFAAmF;AACnF,wBAAgB,0BAA0B,CACxC,OAAO,GAAE,qBAA0B,GAClC,sBAAsB,CAAC,MAAM,CAAC,CAiBhC"}
|
|
1
|
+
{"version":3,"file":"manualStrategies.d.ts","sourceRoot":"","sources":["../../src/strategies/manualStrategies.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAGV,sBAAsB,EACvB,MAAM,+BAA+B,CAAC;AAIvC,MAAM,WAAW,qBAAqB;IACpC,mFAAmF;IACnF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gFAAgF;IAChF,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CAC5C;AAiCD,4EAA4E;AAC5E,wBAAgB,mBAAmB,CACjC,OAAO,GAAE,qBAA0B,GAClC,sBAAsB,CAAC,MAAM,CAAC,CAmBhC;AAED,mFAAmF;AACnF,wBAAgB,0BAA0B,CACxC,OAAO,GAAE,qBAA0B,GAClC,sBAAsB,CAAC,MAAM,CAAC,CAiBhC;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,GAAE,qBAA0B,GAClC,sBAAsB,CAAC,MAAM,CAAC,CAgBhC"}
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
11
|
exports.manualPasteStrategy = manualPasteStrategy;
|
|
12
12
|
exports.manualSamlResponseStrategy = manualSamlResponseStrategy;
|
|
13
|
+
exports.manualPasscodeStrategy = manualPasscodeStrategy;
|
|
13
14
|
const node_readline_1 = require("node:readline");
|
|
14
15
|
const browserAuth_1 = require("../auth/browserAuth");
|
|
15
16
|
const BrowserCallbackStrategy_1 = require("./BrowserCallbackStrategy");
|
|
@@ -75,3 +76,23 @@ function manualSamlResponseStrategy(options = {}) {
|
|
|
75
76
|
},
|
|
76
77
|
};
|
|
77
78
|
}
|
|
79
|
+
/**
|
|
80
|
+
* The UAA passcode, typed in by the user: shows where to fetch it —
|
|
81
|
+
* `<uaa>/passcode`, opened in any browser, on any machine — and reads the
|
|
82
|
+
* code they copy from that page. The default for `UaaPasscodeProvider`, so a
|
|
83
|
+
* login works on a machine with no browser at all, as `cf login --sso` does.
|
|
84
|
+
*/
|
|
85
|
+
function manualPasscodeStrategy(options = {}) {
|
|
86
|
+
const redirectUri = options.redirectUri ?? defaultRedirectUri();
|
|
87
|
+
const read = options.read ?? readFromTerminal;
|
|
88
|
+
return {
|
|
89
|
+
async authorize(request) {
|
|
90
|
+
const url = await request.buildAuthorizationUrl(redirectUri);
|
|
91
|
+
announce(request, url);
|
|
92
|
+
const code = (await read('Paste the Temporary Authentication Code (passcode): ')).trim();
|
|
93
|
+
if (!code)
|
|
94
|
+
throw new Error('No passcode was provided');
|
|
95
|
+
return { payload: code, redirectUri };
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mcp-abap-adt/auth-providers",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
4
|
"description": "Token providers for MCP ABAP ADT auth-broker",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -40,8 +40,7 @@
|
|
|
40
40
|
},
|
|
41
41
|
"bin": {
|
|
42
42
|
"auth-authorization-code": "./bin/auth-authorization-code.ts",
|
|
43
|
-
"auth-client-credentials": "./bin/auth-client-credentials.ts"
|
|
44
|
-
"auth-device-flow": "./bin/auth-device-flow.ts"
|
|
43
|
+
"auth-client-credentials": "./bin/auth-client-credentials.ts"
|
|
45
44
|
},
|
|
46
45
|
"scripts": {
|
|
47
46
|
"chrono": "./tools/version-stats.sh",
|
|
@@ -53,15 +52,20 @@
|
|
|
53
52
|
"build:fast": "npx tsc -p tsconfig.json",
|
|
54
53
|
"test": "NODE_OPTIONS=--experimental-vm-modules jest",
|
|
55
54
|
"test:check": "npx tsc --noEmit",
|
|
56
|
-
"prepublishOnly": "npm run build"
|
|
55
|
+
"prepublishOnly": "npm run build",
|
|
56
|
+
"stand:up": "tests/stand/up.sh",
|
|
57
|
+
"stand:down": "tests/stand/down.sh",
|
|
58
|
+
"test:stand": "tests/stand/run.sh",
|
|
59
|
+
"test:xsuaa": "tests/xsuaa/run.sh"
|
|
57
60
|
},
|
|
58
61
|
"engines": {
|
|
59
|
-
"node": "
|
|
62
|
+
"node": "^22 || ^24"
|
|
60
63
|
},
|
|
61
64
|
"dependencies": {
|
|
62
65
|
"@mcp-abap-adt/interfaces-auth": "^1.2.0",
|
|
63
66
|
"@mcp-abap-adt/interfaces-auth-sap": "^1.0.0",
|
|
64
67
|
"@mcp-abap-adt/interfaces-utils": "^1.1.0",
|
|
68
|
+
"@xmldom/xmldom": "^0.9.12",
|
|
65
69
|
"axios": "^1.13.5",
|
|
66
70
|
"express": "^5.1.0",
|
|
67
71
|
"open": "^11.0.0"
|
|
@@ -69,6 +73,7 @@
|
|
|
69
73
|
"devDependencies": {
|
|
70
74
|
"@biomejs/biome": "^2.3.14",
|
|
71
75
|
"@jest/globals": "^30.2.0",
|
|
76
|
+
"@mcp-abap-adt/auth-mocks": "^0.3.0",
|
|
72
77
|
"@mcp-abap-adt/auth-stores": "^1.0.1",
|
|
73
78
|
"@mcp-abap-adt/logger": "^0.4.0",
|
|
74
79
|
"@types/express": "^5.0.5",
|
package/bin/auth-device-flow.ts
DELETED
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env tsx
|
|
2
|
-
/**
|
|
3
|
-
* Device Flow Provider Test Command
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { DeviceFlowProvider } from '../src/providers/DeviceFlowProvider';
|
|
7
|
-
import {
|
|
8
|
-
getUaaCredentials,
|
|
9
|
-
parseEnvFile,
|
|
10
|
-
parseServiceKey,
|
|
11
|
-
writeEnvFile,
|
|
12
|
-
} from './utils/parseConfig';
|
|
13
|
-
|
|
14
|
-
async function main() {
|
|
15
|
-
const args = process.argv.slice(2);
|
|
16
|
-
let serviceKeyPath: string | undefined;
|
|
17
|
-
let inputEnvPath: string | undefined;
|
|
18
|
-
let outputEnvPath: string | undefined;
|
|
19
|
-
let scope: string | undefined;
|
|
20
|
-
|
|
21
|
-
for (let i = 0; i < args.length; i++) {
|
|
22
|
-
const arg = args[i];
|
|
23
|
-
if (arg === '--service-key' && args[i + 1]) {
|
|
24
|
-
serviceKeyPath = args[++i];
|
|
25
|
-
} else if (arg === '--input-env' && args[i + 1]) {
|
|
26
|
-
inputEnvPath = args[++i];
|
|
27
|
-
} else if (arg === '--output-env' && args[i + 1]) {
|
|
28
|
-
outputEnvPath = args[++i];
|
|
29
|
-
} else if (arg === '--scope' && args[i + 1]) {
|
|
30
|
-
scope = args[++i];
|
|
31
|
-
} else if (arg === '--help' || arg === '-h') {
|
|
32
|
-
console.log(`
|
|
33
|
-
Usage: auth-device-flow [options]
|
|
34
|
-
|
|
35
|
-
Options:
|
|
36
|
-
--service-key <path> Path to service key JSON file
|
|
37
|
-
--input-env <path> Path to .env file for reading existing tokens (for refresh)
|
|
38
|
-
--output-env <path> Path to .env file for saving tokens
|
|
39
|
-
--scope <scope> Optional scope (space-separated)
|
|
40
|
-
--help, -h Show this help message
|
|
41
|
-
`);
|
|
42
|
-
process.exit(0);
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
if (!serviceKeyPath && !inputEnvPath) {
|
|
47
|
-
console.error('❌ Error: Either --service-key or --input-env must be provided');
|
|
48
|
-
process.exit(1);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
if (!outputEnvPath) {
|
|
52
|
-
console.error('❌ Error: --output-env is required to save tokens');
|
|
53
|
-
process.exit(1);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
try {
|
|
57
|
-
// Read config from service key or input env
|
|
58
|
-
const serviceKey = serviceKeyPath ? parseServiceKey(serviceKeyPath) : undefined;
|
|
59
|
-
const inputEnv = inputEnvPath ? parseEnvFile(inputEnvPath) : undefined;
|
|
60
|
-
const { uaaUrl, clientId, clientSecret } = getUaaCredentials(serviceKey, inputEnv);
|
|
61
|
-
|
|
62
|
-
// Read existing tokens from input env if provided
|
|
63
|
-
const existingAccessToken = inputEnv?.AUTHORIZATION_TOKEN;
|
|
64
|
-
const existingRefreshToken = inputEnv?.REFRESH_TOKEN;
|
|
65
|
-
|
|
66
|
-
const provider = new DeviceFlowProvider({
|
|
67
|
-
uaaUrl,
|
|
68
|
-
clientId,
|
|
69
|
-
clientSecret,
|
|
70
|
-
scope,
|
|
71
|
-
accessToken: existingAccessToken,
|
|
72
|
-
refreshToken: existingRefreshToken,
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
console.log('🔐 Getting tokens using Device Flow...');
|
|
76
|
-
if (serviceKeyPath) {
|
|
77
|
-
console.log(`📁 Service Key: ${serviceKeyPath}`);
|
|
78
|
-
}
|
|
79
|
-
if (inputEnvPath) {
|
|
80
|
-
console.log(`📁 Input Env: ${inputEnvPath}`);
|
|
81
|
-
}
|
|
82
|
-
console.log(`💾 Output Env: ${outputEnvPath}\n`);
|
|
83
|
-
|
|
84
|
-
const result = await provider.getTokens();
|
|
85
|
-
|
|
86
|
-
// Save tokens to output env file
|
|
87
|
-
writeEnvFile(outputEnvPath, {
|
|
88
|
-
authorizationToken: result.authorizationToken,
|
|
89
|
-
refreshToken: result.refreshToken,
|
|
90
|
-
uaaUrl,
|
|
91
|
-
clientId,
|
|
92
|
-
clientSecret,
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
console.log('✅ Tokens obtained and saved successfully!');
|
|
96
|
-
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
97
|
-
console.log(`🔑 Authorization Token: ${result.authorizationToken.substring(0, 50)}...`);
|
|
98
|
-
if (result.refreshToken) {
|
|
99
|
-
console.log(`🔄 Refresh Token: ${result.refreshToken.substring(0, 50)}...`);
|
|
100
|
-
}
|
|
101
|
-
console.log(`📋 Auth Type: ${result.authType}`);
|
|
102
|
-
if (result.expiresIn) {
|
|
103
|
-
console.log(`⏰ Expires In: ${result.expiresIn} seconds`);
|
|
104
|
-
}
|
|
105
|
-
console.log(`💾 Saved to: ${outputEnvPath}`);
|
|
106
|
-
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
107
|
-
} catch (error) {
|
|
108
|
-
console.error('❌ Error:', error instanceof Error ? error.message : String(error));
|
|
109
|
-
process.exit(1);
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
main();
|
|
114
|
-
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Device Flow authentication
|
|
3
|
-
*
|
|
4
|
-
* OAuth2 Device Flow for devices without browser or input capabilities.
|
|
5
|
-
* User authorizes on another device by entering a code.
|
|
6
|
-
*/
|
|
7
|
-
import type { ILogger } from '@mcp-abap-adt/interfaces-utils';
|
|
8
|
-
export interface DeviceFlowResult {
|
|
9
|
-
deviceCode: string;
|
|
10
|
-
userCode: string;
|
|
11
|
-
verificationUri: string;
|
|
12
|
-
verificationUriComplete?: string;
|
|
13
|
-
expiresIn: number;
|
|
14
|
-
interval?: number;
|
|
15
|
-
}
|
|
16
|
-
export interface DeviceFlowTokens {
|
|
17
|
-
accessToken: string;
|
|
18
|
-
refreshToken?: string;
|
|
19
|
-
expiresIn?: number;
|
|
20
|
-
}
|
|
21
|
-
/**
|
|
22
|
-
* Initiate device flow - get device code and user code
|
|
23
|
-
* @param uaaUrl UAA URL
|
|
24
|
-
* @param clientId Client ID
|
|
25
|
-
* @param scope Optional scope (space-separated)
|
|
26
|
-
* @param logger Optional logger
|
|
27
|
-
* @returns Promise that resolves to device flow result
|
|
28
|
-
* @internal - Internal function, not exported from package
|
|
29
|
-
*/
|
|
30
|
-
export declare function initiateDeviceFlow(uaaUrl: string, clientId: string, scope?: string, logger?: ILogger): Promise<DeviceFlowResult>;
|
|
31
|
-
/**
|
|
32
|
-
* Poll for tokens using device code
|
|
33
|
-
* @param uaaUrl UAA URL
|
|
34
|
-
* @param clientId Client ID
|
|
35
|
-
* @param clientSecret Client secret (optional for public clients)
|
|
36
|
-
* @param deviceCode Device code from initiateDeviceFlow
|
|
37
|
-
* @param interval Polling interval in seconds
|
|
38
|
-
* @param logger Optional logger
|
|
39
|
-
* @returns Promise that resolves to tokens
|
|
40
|
-
* @internal - Internal function, not exported from package
|
|
41
|
-
*/
|
|
42
|
-
export declare function pollForDeviceTokens(uaaUrl: string, clientId: string, clientSecret: string | undefined, deviceCode: string, interval?: number, logger?: ILogger): Promise<DeviceFlowTokens>;
|
|
43
|
-
//# sourceMappingURL=deviceFlowAuth.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"deviceFlowAuth.d.ts","sourceRoot":"","sources":["../../src/auth/deviceFlowAuth.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gCAAgC,CAAC;AAG9D,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,MAAM,CAAC;IACxB,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,gBAAgB;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;GAQG;AACH,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,KAAK,CAAC,EAAE,MAAM,EACd,MAAM,CAAC,EAAE,OAAO,GACf,OAAO,CAAC,gBAAgB,CAAC,CA4D3B;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,GAAG,SAAS,EAChC,UAAU,EAAE,MAAM,EAClB,QAAQ,GAAE,MAAU,EACpB,MAAM,CAAC,EAAE,OAAO,GACf,OAAO,CAAC,gBAAgB,CAAC,CAoG3B"}
|
|
@@ -1,168 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Device Flow authentication
|
|
4
|
-
*
|
|
5
|
-
* OAuth2 Device Flow for devices without browser or input capabilities.
|
|
6
|
-
* User authorizes on another device by entering a code.
|
|
7
|
-
*/
|
|
8
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
9
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
10
|
-
};
|
|
11
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.initiateDeviceFlow = initiateDeviceFlow;
|
|
13
|
-
exports.pollForDeviceTokens = pollForDeviceTokens;
|
|
14
|
-
const axios_1 = __importDefault(require("axios"));
|
|
15
|
-
/**
|
|
16
|
-
* Initiate device flow - get device code and user code
|
|
17
|
-
* @param uaaUrl UAA URL
|
|
18
|
-
* @param clientId Client ID
|
|
19
|
-
* @param scope Optional scope (space-separated)
|
|
20
|
-
* @param logger Optional logger
|
|
21
|
-
* @returns Promise that resolves to device flow result
|
|
22
|
-
* @internal - Internal function, not exported from package
|
|
23
|
-
*/
|
|
24
|
-
async function initiateDeviceFlow(uaaUrl, clientId, scope, logger) {
|
|
25
|
-
try {
|
|
26
|
-
const deviceUrl = `${uaaUrl}/oauth/device_authorization`;
|
|
27
|
-
const params = new URLSearchParams();
|
|
28
|
-
params.append('client_id', clientId);
|
|
29
|
-
if (scope) {
|
|
30
|
-
params.append('scope', scope);
|
|
31
|
-
}
|
|
32
|
-
logger?.info(`Initiating device flow: ${deviceUrl}`);
|
|
33
|
-
const response = await (0, axios_1.default)({
|
|
34
|
-
method: 'post',
|
|
35
|
-
url: deviceUrl,
|
|
36
|
-
headers: {
|
|
37
|
-
'Content-Type': 'application/x-www-form-urlencoded',
|
|
38
|
-
},
|
|
39
|
-
data: params.toString(),
|
|
40
|
-
timeout: 30000,
|
|
41
|
-
});
|
|
42
|
-
if (response.data?.device_code &&
|
|
43
|
-
response.data?.user_code &&
|
|
44
|
-
response.data?.verification_uri) {
|
|
45
|
-
return {
|
|
46
|
-
deviceCode: response.data.device_code,
|
|
47
|
-
userCode: response.data.user_code,
|
|
48
|
-
verificationUri: response.data.verification_uri,
|
|
49
|
-
verificationUriComplete: response.data.verification_uri_complete,
|
|
50
|
-
expiresIn: response.data.expires_in || 1800, // Default 30 minutes
|
|
51
|
-
interval: response.data.interval || 5, // Default 5 seconds
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
else {
|
|
55
|
-
throw new Error('Response does not contain required device flow fields');
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
catch (error) {
|
|
59
|
-
if (error &&
|
|
60
|
-
typeof error === 'object' &&
|
|
61
|
-
'response' in error &&
|
|
62
|
-
error.response &&
|
|
63
|
-
typeof error.response === 'object' &&
|
|
64
|
-
'status' in error.response &&
|
|
65
|
-
'data' in error.response) {
|
|
66
|
-
const axiosError = error;
|
|
67
|
-
throw new Error(`Device flow initiation failed (${axiosError.response.status}): ${JSON.stringify(axiosError.response.data)}`);
|
|
68
|
-
}
|
|
69
|
-
else {
|
|
70
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
71
|
-
throw new Error(`Device flow initiation failed: ${errorMessage}`);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
/**
|
|
76
|
-
* Poll for tokens using device code
|
|
77
|
-
* @param uaaUrl UAA URL
|
|
78
|
-
* @param clientId Client ID
|
|
79
|
-
* @param clientSecret Client secret (optional for public clients)
|
|
80
|
-
* @param deviceCode Device code from initiateDeviceFlow
|
|
81
|
-
* @param interval Polling interval in seconds
|
|
82
|
-
* @param logger Optional logger
|
|
83
|
-
* @returns Promise that resolves to tokens
|
|
84
|
-
* @internal - Internal function, not exported from package
|
|
85
|
-
*/
|
|
86
|
-
async function pollForDeviceTokens(uaaUrl, clientId, clientSecret, deviceCode, interval = 5, logger) {
|
|
87
|
-
const tokenUrl = `${uaaUrl}/oauth/token`;
|
|
88
|
-
const params = new URLSearchParams();
|
|
89
|
-
params.append('grant_type', 'urn:ietf:params:oauth:grant-type:device_code');
|
|
90
|
-
params.append('device_code', deviceCode);
|
|
91
|
-
params.append('client_id', clientId);
|
|
92
|
-
const headers = {
|
|
93
|
-
'Content-Type': 'application/x-www-form-urlencoded',
|
|
94
|
-
};
|
|
95
|
-
// Add client secret if provided (for confidential clients)
|
|
96
|
-
if (clientSecret) {
|
|
97
|
-
const authString = Buffer.from(`${clientId}:${clientSecret}`).toString('base64');
|
|
98
|
-
headers.Authorization = `Basic ${authString}`;
|
|
99
|
-
}
|
|
100
|
-
// Poll until authorization is complete or expires
|
|
101
|
-
const maxAttempts = 120; // 10 minutes max (120 * 5 seconds)
|
|
102
|
-
let attempts = 0;
|
|
103
|
-
while (attempts < maxAttempts) {
|
|
104
|
-
try {
|
|
105
|
-
logger?.debug(`Polling for device tokens (attempt ${attempts + 1})`);
|
|
106
|
-
const response = await (0, axios_1.default)({
|
|
107
|
-
method: 'post',
|
|
108
|
-
url: tokenUrl,
|
|
109
|
-
headers,
|
|
110
|
-
data: params.toString(),
|
|
111
|
-
timeout: 30000,
|
|
112
|
-
});
|
|
113
|
-
if (response.data?.access_token) {
|
|
114
|
-
logger?.info('Device flow authorization successful');
|
|
115
|
-
return {
|
|
116
|
-
accessToken: response.data.access_token,
|
|
117
|
-
refreshToken: response.data.refresh_token,
|
|
118
|
-
expiresIn: response.data.expires_in,
|
|
119
|
-
};
|
|
120
|
-
}
|
|
121
|
-
else {
|
|
122
|
-
throw new Error('Response does not contain access_token');
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
catch (error) {
|
|
126
|
-
// Check if it's an "authorization_pending" error (expected during polling)
|
|
127
|
-
if (error &&
|
|
128
|
-
typeof error === 'object' &&
|
|
129
|
-
'response' in error &&
|
|
130
|
-
error.response &&
|
|
131
|
-
typeof error.response === 'object' &&
|
|
132
|
-
'status' in error.response &&
|
|
133
|
-
'data' in error.response) {
|
|
134
|
-
const axiosError = error;
|
|
135
|
-
if (axiosError.response.data?.error === 'authorization_pending') {
|
|
136
|
-
// Still waiting for user authorization - continue polling
|
|
137
|
-
attempts++;
|
|
138
|
-
if (attempts >= maxAttempts) {
|
|
139
|
-
throw new Error('Device flow authorization timeout - user did not authorize in time');
|
|
140
|
-
}
|
|
141
|
-
// Wait for interval before next poll
|
|
142
|
-
await new Promise((resolve) => setTimeout(resolve, interval * 1000));
|
|
143
|
-
continue;
|
|
144
|
-
}
|
|
145
|
-
else if (axiosError.response.data?.error === 'slow_down') {
|
|
146
|
-
// Server requests slower polling - increase interval by 5 seconds
|
|
147
|
-
interval += 5;
|
|
148
|
-
attempts++;
|
|
149
|
-
if (attempts >= maxAttempts) {
|
|
150
|
-
throw new Error('Device flow authorization timeout - user did not authorize in time');
|
|
151
|
-
}
|
|
152
|
-
await new Promise((resolve) => setTimeout(resolve, interval * 1000));
|
|
153
|
-
continue;
|
|
154
|
-
}
|
|
155
|
-
else if (axiosError.response.data?.error === 'expired_token') {
|
|
156
|
-
throw new Error('Device code expired - please restart device flow');
|
|
157
|
-
}
|
|
158
|
-
else if (axiosError.response.data?.error === 'access_denied') {
|
|
159
|
-
throw new Error('User denied device authorization');
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
// Other error - rethrow
|
|
163
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
164
|
-
throw new Error(`Device flow polling failed: ${errorMessage}`);
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
throw new Error('Device flow authorization timeout - maximum polling attempts reached');
|
|
168
|
-
}
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Device Flow Token Provider
|
|
3
|
-
*
|
|
4
|
-
* Uses OAuth2 Device Flow for devices without browser or input capabilities.
|
|
5
|
-
* User authorizes on another device by entering a code.
|
|
6
|
-
*/
|
|
7
|
-
import type { ITokenResult, OAuth2GrantType } from '@mcp-abap-adt/interfaces-auth';
|
|
8
|
-
import type { ILogger } from '@mcp-abap-adt/interfaces-utils';
|
|
9
|
-
import { BaseTokenProvider } from './BaseTokenProvider';
|
|
10
|
-
export interface DeviceFlowProviderConfig {
|
|
11
|
-
uaaUrl: string;
|
|
12
|
-
clientId: string;
|
|
13
|
-
clientSecret?: string;
|
|
14
|
-
scope?: string;
|
|
15
|
-
accessToken?: string;
|
|
16
|
-
refreshToken?: string;
|
|
17
|
-
logger?: ILogger;
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
|
-
* Device Flow token provider
|
|
21
|
-
*
|
|
22
|
-
* Uses OAuth2 Device Flow - user authorizes on another device.
|
|
23
|
-
* Supports refresh token if provided by server.
|
|
24
|
-
*/
|
|
25
|
-
export declare class DeviceFlowProvider extends BaseTokenProvider {
|
|
26
|
-
private config;
|
|
27
|
-
constructor(config: DeviceFlowProviderConfig);
|
|
28
|
-
protected getAuthType(): OAuth2GrantType;
|
|
29
|
-
protected performLogin(): Promise<ITokenResult>;
|
|
30
|
-
protected performRefresh(): Promise<ITokenResult>;
|
|
31
|
-
}
|
|
32
|
-
//# sourceMappingURL=DeviceFlowProvider.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"DeviceFlowProvider.d.ts","sourceRoot":"","sources":["../../src/providers/DeviceFlowProvider.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EACV,YAAY,EACZ,eAAe,EAChB,MAAM,+BAA+B,CAAC;AAEvC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gCAAgC,CAAC;AAO9D,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"}
|