@mcp-abap-adt/auth-broker 0.2.4 → 0.2.6
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 +16 -0
- package/README.md +7 -3
- package/dist/AuthBroker.d.ts +7 -0
- package/dist/AuthBroker.d.ts.map +1 -1
- package/dist/AuthBroker.js +25 -0
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -11,6 +11,22 @@ Thank you to all contributors! See [CONTRIBUTORS.md](CONTRIBUTORS.md) for the co
|
|
|
11
11
|
|
|
12
12
|
## [Unreleased]
|
|
13
13
|
|
|
14
|
+
## [0.2.5] - 2025-12-20
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
- **`allowBrowserAuth` Option**: New configuration option to control browser-based authentication
|
|
18
|
+
- When `allowBrowserAuth: false`, broker throws `BROWSER_AUTH_REQUIRED` error instead of blocking on browser auth
|
|
19
|
+
- Useful for headless/non-interactive environments (e.g., MCP stdio transport with Cline)
|
|
20
|
+
- Error includes `code: 'BROWSER_AUTH_REQUIRED'` and `destination` property for programmatic handling
|
|
21
|
+
- Broker still works with valid session tokens or refresh tokens when browser auth is disabled
|
|
22
|
+
|
|
23
|
+
### Dependencies
|
|
24
|
+
- Updated `@mcp-abap-adt/auth-providers` to `^0.2.2` for automatic port selection, improved server shutdown, and process termination cleanup
|
|
25
|
+
- Browser auth server now automatically finds an available port if the requested port is in use
|
|
26
|
+
- Improved server shutdown ensures ports are properly freed after authentication completes
|
|
27
|
+
- Prevents `EADDRINUSE` errors when multiple stdio servers run simultaneously
|
|
28
|
+
- Ports are properly released after server shutdown, preventing lingering port occupation
|
|
29
|
+
|
|
14
30
|
## [0.2.4] - 2025-12-19
|
|
15
31
|
|
|
16
32
|
### Changed
|
package/README.md
CHANGED
|
@@ -53,12 +53,12 @@ const broker = new AuthBroker({
|
|
|
53
53
|
tokenProvider: new BtpTokenProvider(), // optional
|
|
54
54
|
}, 'chrome', logger);
|
|
55
55
|
|
|
56
|
-
// Disable
|
|
57
|
-
const
|
|
56
|
+
// Disable browser authentication for headless/stdio environments (e.g., MCP with Cline)
|
|
57
|
+
const brokerNoBrowser = new AuthBroker({
|
|
58
58
|
sessionStore: new AbapSessionStore('/path/to/destinations'),
|
|
59
59
|
serviceKeyStore: new AbapServiceKeyStore('/path/to/destinations'),
|
|
60
60
|
tokenProvider: new BtpTokenProvider(),
|
|
61
|
-
|
|
61
|
+
allowBrowserAuth: false, // Throws BROWSER_AUTH_REQUIRED if browser auth needed
|
|
62
62
|
}, 'chrome', logger);
|
|
63
63
|
```
|
|
64
64
|
|
|
@@ -98,6 +98,8 @@ const broker = new AuthBroker({
|
|
|
98
98
|
}, 'chrome');
|
|
99
99
|
```
|
|
100
100
|
|
|
101
|
+
**Note**: The `BtpTokenProvider` automatically finds an available port if the requested port is in use. This prevents `EADDRINUSE` errors when multiple stdio servers run simultaneously. The server properly closes all connections and frees the port after authentication completes, ensuring no lingering port occupation.
|
|
102
|
+
|
|
101
103
|
### Getting Tokens
|
|
102
104
|
|
|
103
105
|
```typescript
|
|
@@ -496,6 +498,8 @@ The package uses `ITokenProvider` interface for token acquisition. Two implement
|
|
|
496
498
|
|
|
497
499
|
- **`BtpTokenProvider`** - For BTP/ABAP authentication (full scope)
|
|
498
500
|
- Constructor accepts optional `browserAuthPort?: number` parameter (default: 3001)
|
|
501
|
+
- Automatically finds an available port if the requested port is in use (prevents `EADDRINUSE` errors)
|
|
502
|
+
- Server properly closes all connections and frees the port after authentication completes
|
|
499
503
|
- Use custom port to avoid conflicts when running alongside other services (e.g., proxy server)
|
|
500
504
|
- Uses browser-based OAuth2 flow (if no refresh token)
|
|
501
505
|
- Uses refresh token if available
|
package/dist/AuthBroker.d.ts
CHANGED
|
@@ -14,6 +14,12 @@ export interface AuthBrokerConfig {
|
|
|
14
14
|
serviceKeyStore?: IServiceKeyStore;
|
|
15
15
|
/** Token provider (required) - handles token refresh and authentication flows through browser-based authorization (e.g., XSUAA provider) */
|
|
16
16
|
tokenProvider: ITokenProvider;
|
|
17
|
+
/**
|
|
18
|
+
* Allow browser-based authentication (optional, default: true)
|
|
19
|
+
* When false, getToken() will throw BROWSER_AUTH_REQUIRED error instead of blocking on browser auth.
|
|
20
|
+
* Use this for headless/non-interactive environments (e.g., MCP stdio transport).
|
|
21
|
+
*/
|
|
22
|
+
allowBrowserAuth?: boolean;
|
|
17
23
|
}
|
|
18
24
|
/**
|
|
19
25
|
* AuthBroker manages JWT authentication tokens for destinations
|
|
@@ -24,6 +30,7 @@ export declare class AuthBroker {
|
|
|
24
30
|
private serviceKeyStore;
|
|
25
31
|
private sessionStore;
|
|
26
32
|
private tokenProvider;
|
|
33
|
+
private allowBrowserAuth;
|
|
27
34
|
/**
|
|
28
35
|
* Create a new AuthBroker instance
|
|
29
36
|
* @param config Configuration object with stores and token provider
|
package/dist/AuthBroker.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AuthBroker.d.ts","sourceRoot":"","sources":["../src/AuthBroker.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,OAAO,EAA8C,MAAM,0BAA0B,CAAC;AAC/F,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAC/G,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAa7C;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,mEAAmE;IACnE,YAAY,EAAE,aAAa,CAAC;IAC5B,uEAAuE;IACvE,eAAe,CAAC,EAAE,gBAAgB,CAAC;IACnC,4IAA4I;IAC5I,aAAa,EAAE,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"AuthBroker.d.ts","sourceRoot":"","sources":["../src/AuthBroker.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,OAAO,EAA8C,MAAM,0BAA0B,CAAC;AAC/F,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAC/G,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAa7C;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,mEAAmE;IACnE,YAAY,EAAE,aAAa,CAAC;IAC5B,uEAAuE;IACvE,eAAe,CAAC,EAAE,gBAAgB,CAAC;IACnC,4IAA4I;IAC5I,aAAa,EAAE,cAAc,CAAC;IAC9B;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED;;GAEG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,OAAO,CAAqB;IACpC,OAAO,CAAC,MAAM,CAAU;IACxB,OAAO,CAAC,eAAe,CAA+B;IACtD,OAAO,CAAC,YAAY,CAAgB;IACpC,OAAO,CAAC,aAAa,CAAiB;IACtC,OAAO,CAAC,gBAAgB,CAAU;IAElC;;;;;;;;;;OAUG;gBAED,MAAM,EAAE,gBAAgB,EACxB,OAAO,CAAC,EAAE,MAAM,EAChB,MAAM,CAAC,EAAE,OAAO;IAuElB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAuCG;IACG,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAgYpD;;;;;OAKG;IACG,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAOxD;;;;OAIG;IACG,sBAAsB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC;IA4CvF;;;;OAIG;IACG,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC;CA0ClF"}
|
package/dist/AuthBroker.js
CHANGED
|
@@ -23,6 +23,7 @@ class AuthBroker {
|
|
|
23
23
|
serviceKeyStore;
|
|
24
24
|
sessionStore;
|
|
25
25
|
tokenProvider;
|
|
26
|
+
allowBrowserAuth;
|
|
26
27
|
/**
|
|
27
28
|
* Create a new AuthBroker instance
|
|
28
29
|
* @param config Configuration object with stores and token provider
|
|
@@ -86,6 +87,7 @@ class AuthBroker {
|
|
|
86
87
|
this.tokenProvider = tokenProvider;
|
|
87
88
|
this.browser = browser || 'system';
|
|
88
89
|
this.logger = logger || noOpLogger;
|
|
90
|
+
this.allowBrowserAuth = config.allowBrowserAuth ?? true;
|
|
89
91
|
// Log successful initialization
|
|
90
92
|
const hasServiceKeyStore = !!this.serviceKeyStore;
|
|
91
93
|
this.logger?.debug(`AuthBroker initialized: sessionStore(ok), serviceKeyStore(${hasServiceKeyStore ? 'ok' : 'none'}), tokenProvider(ok)`);
|
|
@@ -213,6 +215,15 @@ class AuthBroker {
|
|
|
213
215
|
this.logger?.error(`Step 0: Service key for ${destination} missing UAA credentials`);
|
|
214
216
|
throw new Error(`Service key for destination "${destination}" does not contain UAA credentials`);
|
|
215
217
|
}
|
|
218
|
+
// Check if browser auth is allowed
|
|
219
|
+
if (!this.allowBrowserAuth) {
|
|
220
|
+
const error = new Error(`Browser authentication required for destination "${destination}" but allowBrowserAuth is disabled. ` +
|
|
221
|
+
`Either enable browser auth or provide a valid session with token.`);
|
|
222
|
+
error.code = 'BROWSER_AUTH_REQUIRED';
|
|
223
|
+
error.destination = destination;
|
|
224
|
+
this.logger?.error(`Step 0: Browser auth required but disabled for ${destination}`);
|
|
225
|
+
throw error;
|
|
226
|
+
}
|
|
216
227
|
// Use tokenProvider for browser-based authentication
|
|
217
228
|
this.logger?.debug(`Step 0: Authenticating via provider (browser) for ${destination} using service key UAA credentials`);
|
|
218
229
|
let tokenResult;
|
|
@@ -268,6 +279,10 @@ class AuthBroker {
|
|
|
268
279
|
return tokenResult.connectionConfig.authorizationToken;
|
|
269
280
|
}
|
|
270
281
|
catch (error) {
|
|
282
|
+
// Re-throw BROWSER_AUTH_REQUIRED error without wrapping
|
|
283
|
+
if (error.code === 'BROWSER_AUTH_REQUIRED') {
|
|
284
|
+
throw error;
|
|
285
|
+
}
|
|
271
286
|
// Handle typed store errors
|
|
272
287
|
if (error.code === interfaces_1.STORE_ERROR_CODES.FILE_NOT_FOUND) {
|
|
273
288
|
this.logger?.error(`Step 0: Service key file not found for ${destination}: ${error.filePath || 'unknown path'}`);
|
|
@@ -408,6 +423,16 @@ class AuthBroker {
|
|
|
408
423
|
this.logger?.debug(`Step 2a: No refresh token in session for ${destination}, skipping to service key refresh`);
|
|
409
424
|
}
|
|
410
425
|
// Try refresh from service key (browser authentication)
|
|
426
|
+
// Check if browser auth is allowed
|
|
427
|
+
if (!this.allowBrowserAuth) {
|
|
428
|
+
const error = new Error(`Browser authentication required for destination "${destination}" but allowBrowserAuth is disabled. ` +
|
|
429
|
+
`Token refresh via session failed and browser auth is not allowed. ` +
|
|
430
|
+
`Either enable browser auth or ensure a valid refresh token exists in session.`);
|
|
431
|
+
error.code = 'BROWSER_AUTH_REQUIRED';
|
|
432
|
+
error.destination = destination;
|
|
433
|
+
this.logger?.error(`Step 2b: Browser auth required but disabled for ${destination}`);
|
|
434
|
+
throw error;
|
|
435
|
+
}
|
|
411
436
|
try {
|
|
412
437
|
this.logger?.debug(`Step 2b: Trying refreshTokenFromServiceKey for ${destination}`);
|
|
413
438
|
const tokenResult = await this.tokenProvider.refreshTokenFromServiceKey(uaaCredentials, {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mcp-abap-adt/auth-broker",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.6",
|
|
4
4
|
"description": "JWT authentication broker for MCP ABAP ADT - manages tokens based on destination headers",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"axios": "^1.13.2"
|
|
56
56
|
},
|
|
57
57
|
"devDependencies": {
|
|
58
|
-
"@mcp-abap-adt/auth-providers": "^0.2.
|
|
58
|
+
"@mcp-abap-adt/auth-providers": "^0.2.2",
|
|
59
59
|
"@mcp-abap-adt/auth-stores": "^0.2.5",
|
|
60
60
|
"@types/express": "^5.0.5",
|
|
61
61
|
"@types/jest": "^30.0.0",
|