@mcp-abap-adt/auth-broker 0.2.2 → 0.2.4
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 +52 -0
- package/README.md +56 -1
- package/dist/AuthBroker.d.ts +29 -37
- package/dist/AuthBroker.d.ts.map +1 -1
- package/dist/AuthBroker.js +323 -366
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -11,6 +11,58 @@ Thank you to all contributors! See [CONTRIBUTORS.md](CONTRIBUTORS.md) for the co
|
|
|
11
11
|
|
|
12
12
|
## [Unreleased]
|
|
13
13
|
|
|
14
|
+
## [0.2.4] - 2025-12-19
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
- **Comprehensive Error Handling**: Added robust error handling for all external operations
|
|
18
|
+
- **SessionStore errors**: Handle FILE_NOT_FOUND, PARSE_ERROR from session files (graceful degradation)
|
|
19
|
+
- **ServiceKeyStore errors**: Handle FILE_NOT_FOUND, PARSE_ERROR, INVALID_CONFIG from service key files (log and fallback)
|
|
20
|
+
- **TokenProvider errors**: Handle network errors (ECONNREFUSED, ETIMEDOUT, ENOTFOUND), validation errors, browser auth failures
|
|
21
|
+
- **Write operation errors**: Handle failures when saving tokens/config to session files
|
|
22
|
+
- All errors logged with detailed context (file paths, error codes, missing fields)
|
|
23
|
+
- Broker continues with fallback mechanisms when possible instead of crashing
|
|
24
|
+
- **Token Refresh Architecture**: Removed direct UAA HTTP requests from AuthBroker
|
|
25
|
+
- `getToken()` now uses provider's `refreshTokenFromSession()` (Step 2a) and `refreshTokenFromServiceKey()` (Step 2b) methods
|
|
26
|
+
- All authentication logic delegated to providers (XsuaaTokenProvider, BtpTokenProvider)
|
|
27
|
+
- Providers handle browser-based authentication and client_credentials flow internally
|
|
28
|
+
- Better error handling with typed errors from `@mcp-abap-adt/auth-providers@0.2.0`
|
|
29
|
+
- **Error Handling**: Improved error handling in token requests
|
|
30
|
+
- Network errors (connection issues) are now handled separately from HTTP errors (401, 403)
|
|
31
|
+
- Better error messages with UAA URL context when network errors occur
|
|
32
|
+
- No retry attempts for network errors (retries cannot fix infrastructure issues)
|
|
33
|
+
|
|
34
|
+
### Fixed
|
|
35
|
+
- **Defensive Programming**: Treat all injected dependencies as untrusted
|
|
36
|
+
- File operations (session/service key stores) may fail - files missing, corrupted, permission issues
|
|
37
|
+
- Network operations (token provider) may fail - timeouts, connection refused, invalid responses
|
|
38
|
+
- All external operations wrapped in try-catch with specific error handling per operation type
|
|
39
|
+
- Prevents broker crashes when consumers misconfigure files or network issues occur
|
|
40
|
+
- **Network Error Detection**: Add proper network error detection in token requests
|
|
41
|
+
- Detect network errors: `ECONNREFUSED`, `ETIMEDOUT`, `ENOTFOUND`, `ECONNRESET`, `ENETUNREACH`, `EHOSTUNREACH`
|
|
42
|
+
- Throw network errors immediately with clear error message indicating connectivity issues
|
|
43
|
+
- Prevents confusing error messages when VPN is down or server is unreachable
|
|
44
|
+
- Network errors now clearly indicate infrastructure issues vs authentication failures
|
|
45
|
+
- **Simplified Refresh**: `refreshToken()` now simply delegates to `getToken()` for full refresh flow
|
|
46
|
+
- Ensures consistent refresh behavior across all token operations
|
|
47
|
+
- No code duplication between getToken and refreshToken methods
|
|
48
|
+
|
|
49
|
+
### Removed
|
|
50
|
+
- **Direct UAA Code**: Removed direct UAA request methods and old credential flow
|
|
51
|
+
- Removed `getTokenWithClientCredentials()` private method (logic moved to providers)
|
|
52
|
+
- Removed `refreshTokenDirect()` private method (logic moved to providers)
|
|
53
|
+
- Removed `allowClientCredentials` constructor parameter (handled by providers)
|
|
54
|
+
- Removed old "Step 2: UAA Credentials Flow" (replaced with provider-based Step 2a/2b)
|
|
55
|
+
|
|
56
|
+
### Dependencies
|
|
57
|
+
- Updated `@mcp-abap-adt/interfaces` to `^0.2.3` for STORE_ERROR_CODES and TOKEN_PROVIDER_ERROR_CODES
|
|
58
|
+
- Updated `@mcp-abap-adt/auth-stores` to `^0.2.5` for typed errors (ParseError, FileNotFoundError, etc.)
|
|
59
|
+
- Updated `@mcp-abap-adt/auth-providers` to `^0.2.0` for new refresh methods and typed errors
|
|
60
|
+
|
|
61
|
+
## [0.2.3] - 2025-12-18
|
|
62
|
+
|
|
63
|
+
### Added
|
|
64
|
+
- `allowClientCredentials` config flag (default: true). Set to `false` to skip UAA client_credentials flow and force provider/browser-based login (useful for ABAP ADT backends that reject service tokens).
|
|
65
|
+
|
|
14
66
|
## [0.2.2] - 2025-12-13
|
|
15
67
|
|
|
16
68
|
### Changed
|
package/README.md
CHANGED
|
@@ -52,6 +52,14 @@ const broker = new AuthBroker({
|
|
|
52
52
|
serviceKeyStore: new AbapServiceKeyStore('/path/to/destinations'), // optional
|
|
53
53
|
tokenProvider: new BtpTokenProvider(), // optional
|
|
54
54
|
}, 'chrome', logger);
|
|
55
|
+
|
|
56
|
+
// Disable direct client_credentials (force provider/browser flow, e.g., for ABAP ADT)
|
|
57
|
+
const brokerNoClientCreds = new AuthBroker({
|
|
58
|
+
sessionStore: new AbapSessionStore('/path/to/destinations'),
|
|
59
|
+
serviceKeyStore: new AbapServiceKeyStore('/path/to/destinations'),
|
|
60
|
+
tokenProvider: new BtpTokenProvider(),
|
|
61
|
+
allowClientCredentials: false,
|
|
62
|
+
}, 'chrome', logger);
|
|
55
63
|
```
|
|
56
64
|
|
|
57
65
|
### Session + Service Key (For Initialization)
|
|
@@ -410,6 +418,54 @@ Gets authentication token for destination. Implements a three-step flow:
|
|
|
410
418
|
- If `sessionStore` contains valid UAA credentials, neither `serviceKeyStore` nor `tokenProvider` are required. Direct UAA HTTP requests will be used automatically.
|
|
411
419
|
- `tokenProvider` is only needed for browser authentication or when direct UAA requests fail.
|
|
412
420
|
- Token validation is performed only when checking existing session. Tokens obtained through refresh/UAA/browser authentication are not validated before being saved.
|
|
421
|
+
- **Store errors are handled gracefully**: If service key files are missing or malformed, the broker logs the error and continues with fallback mechanisms (session store data or provider-based auth)
|
|
422
|
+
|
|
423
|
+
##### Error Handling
|
|
424
|
+
|
|
425
|
+
The broker implements comprehensive error handling for all external operations, treating all injected dependencies as untrusted:
|
|
426
|
+
|
|
427
|
+
```typescript
|
|
428
|
+
import { STORE_ERROR_CODES } from '@mcp-abap-adt/interfaces';
|
|
429
|
+
|
|
430
|
+
try {
|
|
431
|
+
const token = await broker.getToken('TRIAL');
|
|
432
|
+
} catch (error: any) {
|
|
433
|
+
// Broker handles errors internally where possible, but critical errors propagate
|
|
434
|
+
console.error('Failed to get token:', error.message);
|
|
435
|
+
}
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
**Error Categories** (handled by broker with graceful degradation):
|
|
439
|
+
|
|
440
|
+
**1. SessionStore Errors** (reading session files):
|
|
441
|
+
- `STORE_ERROR_CODES.FILE_NOT_FOUND` - Session file missing (logged, tries serviceKeyStore fallback)
|
|
442
|
+
- `STORE_ERROR_CODES.PARSE_ERROR` - Corrupted session file (logged with file path, tries fallback)
|
|
443
|
+
- Write failures when saving tokens (logged and thrown - critical)
|
|
444
|
+
|
|
445
|
+
**2. ServiceKeyStore Errors** (reading service key files):
|
|
446
|
+
- `STORE_ERROR_CODES.FILE_NOT_FOUND` - Service key file missing (logged, continues with session data)
|
|
447
|
+
- `STORE_ERROR_CODES.PARSE_ERROR` - Invalid JSON in service key (logged with file path and cause)
|
|
448
|
+
- `STORE_ERROR_CODES.INVALID_CONFIG` - Missing required fields (logged with missing field names)
|
|
449
|
+
- `STORE_ERROR_CODES.STORAGE_ERROR` - Permission/write errors (logged)
|
|
450
|
+
|
|
451
|
+
**3. TokenProvider Errors** (network operations):
|
|
452
|
+
- Network errors: `ECONNREFUSED`, `ETIMEDOUT`, `ENOTFOUND` (logged, throws with descriptive message)
|
|
453
|
+
- `VALIDATION_ERROR` - Missing required auth fields (logged with field names, throws)
|
|
454
|
+
- `BROWSER_AUTH_ERROR` - Browser authentication failed or cancelled (logged, throws)
|
|
455
|
+
- `REFRESH_ERROR` - Token refresh failed at UAA server (logged, throws)
|
|
456
|
+
|
|
457
|
+
**Defensive Design Principles:**
|
|
458
|
+
- **All external operations wrapped in try-catch**: Files may be missing/corrupted, network may fail
|
|
459
|
+
- **Graceful degradation**: Store errors trigger fallback mechanisms (serviceKey → session → provider)
|
|
460
|
+
- **Detailed error context**: Logs include file paths, error codes, missing fields for debugging
|
|
461
|
+
- **Fail-fast for critical errors**: Write failures and provider errors throw immediately (cannot recover)
|
|
462
|
+
- **No assumptions about injected dependencies**: All stores/providers treated as potentially unreliable
|
|
463
|
+
|
|
464
|
+
Example error scenarios handled:
|
|
465
|
+
- Session file deleted mid-operation → uses service key
|
|
466
|
+
- Service key has invalid JSON → logs parse error, uses session data
|
|
467
|
+
- Network timeout during token refresh → logs timeout, throws descriptive error
|
|
468
|
+
- File permission denied → logs error with file path, throws
|
|
413
469
|
|
|
414
470
|
##### `refreshToken(destination: string): Promise<string>`
|
|
415
471
|
|
|
@@ -596,4 +652,3 @@ Thank you to all contributors! See [CONTRIBUTORS.md](CONTRIBUTORS.md) for the co
|
|
|
596
652
|
## License
|
|
597
653
|
|
|
598
654
|
MIT
|
|
599
|
-
|
package/dist/AuthBroker.d.ts
CHANGED
|
@@ -12,9 +12,12 @@ export interface AuthBrokerConfig {
|
|
|
12
12
|
sessionStore: ISessionStore;
|
|
13
13
|
/** Service key store (optional) - stores and retrieves service keys */
|
|
14
14
|
serviceKeyStore?: IServiceKeyStore;
|
|
15
|
-
/** Token provider (
|
|
16
|
-
tokenProvider
|
|
15
|
+
/** Token provider (required) - handles token refresh and authentication flows through browser-based authorization (e.g., XSUAA provider) */
|
|
16
|
+
tokenProvider: ITokenProvider;
|
|
17
17
|
}
|
|
18
|
+
/**
|
|
19
|
+
* AuthBroker manages JWT authentication tokens for destinations
|
|
20
|
+
*/
|
|
18
21
|
export declare class AuthBroker {
|
|
19
22
|
private browser;
|
|
20
23
|
private logger;
|
|
@@ -26,63 +29,52 @@ export declare class AuthBroker {
|
|
|
26
29
|
* @param config Configuration object with stores and token provider
|
|
27
30
|
* - sessionStore: Store for session data (required)
|
|
28
31
|
* - serviceKeyStore: Store for service keys (optional)
|
|
29
|
-
* - tokenProvider: Token provider implementing ITokenProvider interface (
|
|
32
|
+
* - tokenProvider: Token provider implementing ITokenProvider interface (required) - handles browser-based authorization
|
|
30
33
|
* @param browser Optional browser name for authentication (chrome, edge, firefox, system, none).
|
|
31
34
|
* Default: 'system' (system default browser).
|
|
32
35
|
* Use 'none' to print URL instead of opening browser.
|
|
33
36
|
* @param logger Optional logger instance implementing ILogger interface. If not provided, uses no-op logger.
|
|
34
37
|
*/
|
|
35
38
|
constructor(config: AuthBrokerConfig, browser?: string, logger?: ILogger);
|
|
36
|
-
/**
|
|
37
|
-
* Refresh token using refresh_token grant type (direct UAA HTTP request)
|
|
38
|
-
* @param refreshToken Refresh token
|
|
39
|
-
* @param authConfig UAA authorization configuration
|
|
40
|
-
* @returns Promise that resolves to new tokens
|
|
41
|
-
*/
|
|
42
|
-
private refreshTokenDirect;
|
|
43
|
-
/**
|
|
44
|
-
* Get token using client_credentials grant type (direct UAA HTTP request)
|
|
45
|
-
* @param authConfig UAA authorization configuration
|
|
46
|
-
* @returns Promise that resolves to access token
|
|
47
|
-
*/
|
|
48
|
-
private getTokenWithClientCredentials;
|
|
49
39
|
/**
|
|
50
40
|
* Get authentication token for destination.
|
|
51
|
-
*
|
|
41
|
+
* Uses tokenProvider for all authentication operations (browser-based authorization).
|
|
52
42
|
*
|
|
53
43
|
* **Flow:**
|
|
54
44
|
* **Step 0: Initialize Session with Token (if needed)**
|
|
55
45
|
* - Check if session has `authorizationToken` AND UAA credentials
|
|
56
46
|
* - If both are empty AND serviceKeyStore is available:
|
|
57
|
-
* -
|
|
58
|
-
* -
|
|
59
|
-
*
|
|
47
|
+
* - Get UAA credentials from service key
|
|
48
|
+
* - Use tokenProvider for browser-based authentication
|
|
49
|
+
* - Save token and refresh token to session
|
|
50
|
+
*
|
|
51
|
+
* **Step 1: Token Validation**
|
|
52
|
+
* - If token exists in session, validate it (if provider supports validation)
|
|
53
|
+
* - If valid → return token
|
|
54
|
+
* - If invalid or no token → continue to refresh
|
|
60
55
|
*
|
|
61
|
-
* **Step
|
|
56
|
+
* **Step 2: Refresh Token Flow**
|
|
62
57
|
* - Check if refresh token exists in session
|
|
63
58
|
* - If refresh token exists:
|
|
64
|
-
* -
|
|
65
|
-
* -
|
|
66
|
-
* -
|
|
67
|
-
* - Otherwise → proceed to Step
|
|
59
|
+
* - Use tokenProvider to refresh token (browser-based or refresh grant)
|
|
60
|
+
* - Save new token to session
|
|
61
|
+
* - Return new token
|
|
62
|
+
* - Otherwise → proceed to Step 3
|
|
68
63
|
*
|
|
69
|
-
* **Step
|
|
70
|
-
* -
|
|
71
|
-
* -
|
|
72
|
-
* -
|
|
73
|
-
* -
|
|
74
|
-
* - If all failed → return error
|
|
64
|
+
* **Step 3: New Token Flow**
|
|
65
|
+
* - Get UAA credentials from session or service key
|
|
66
|
+
* - Use tokenProvider for browser-based authentication
|
|
67
|
+
* - Save new token to session
|
|
68
|
+
* - Return new token
|
|
75
69
|
*
|
|
76
70
|
* **Important Notes:**
|
|
77
|
-
* -
|
|
78
|
-
*
|
|
79
|
-
* -
|
|
80
|
-
* - Initializing session from service key via browser authentication (Step 0)
|
|
81
|
-
* - Direct UAA requests fail and fallback to provider is needed
|
|
71
|
+
* - All authentication is handled by tokenProvider (e.g., XSUAA provider)
|
|
72
|
+
* - Provider uses browser-based authorization to ensure proper role assignment
|
|
73
|
+
* - Direct UAA HTTP requests are not used to avoid role assignment issues
|
|
82
74
|
*
|
|
83
75
|
* @param destination Destination name (e.g., "TRIAL")
|
|
84
76
|
* @returns Promise that resolves to JWT token string
|
|
85
|
-
* @throws Error if session initialization fails or
|
|
77
|
+
* @throws Error if session initialization fails or authentication failed
|
|
86
78
|
*/
|
|
87
79
|
getToken(destination: string): Promise<string>;
|
|
88
80
|
/**
|
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,
|
|
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;CAC/B;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;IAEtC;;;;;;;;;;OAUG;gBAED,MAAM,EAAE,gBAAgB,EACxB,OAAO,CAAC,EAAE,MAAM,EAChB,MAAM,CAAC,EAAE,OAAO;IAsElB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAuCG;IACG,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAkWpD;;;;;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
|
@@ -2,12 +2,9 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* Main AuthBroker class for managing JWT tokens based on destinations
|
|
4
4
|
*/
|
|
5
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
6
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
7
|
-
};
|
|
8
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
6
|
exports.AuthBroker = void 0;
|
|
10
|
-
const
|
|
7
|
+
const interfaces_1 = require("@mcp-abap-adt/interfaces");
|
|
11
8
|
/**
|
|
12
9
|
* No-op logger implementation for default fallback when logger is not provided
|
|
13
10
|
*/
|
|
@@ -17,6 +14,9 @@ const noOpLogger = {
|
|
|
17
14
|
warn: () => { },
|
|
18
15
|
debug: () => { },
|
|
19
16
|
};
|
|
17
|
+
/**
|
|
18
|
+
* AuthBroker manages JWT authentication tokens for destinations
|
|
19
|
+
*/
|
|
20
20
|
class AuthBroker {
|
|
21
21
|
browser;
|
|
22
22
|
logger;
|
|
@@ -28,7 +28,7 @@ class AuthBroker {
|
|
|
28
28
|
* @param config Configuration object with stores and token provider
|
|
29
29
|
* - sessionStore: Store for session data (required)
|
|
30
30
|
* - serviceKeyStore: Store for service keys (optional)
|
|
31
|
-
* - tokenProvider: Token provider implementing ITokenProvider interface (
|
|
31
|
+
* - tokenProvider: Token provider implementing ITokenProvider interface (required) - handles browser-based authorization
|
|
32
32
|
* @param browser Optional browser name for authentication (chrome, edge, firefox, system, none).
|
|
33
33
|
* Default: 'system' (system default browser).
|
|
34
34
|
* Use 'none' to print URL instead of opening browser.
|
|
@@ -43,6 +43,10 @@ class AuthBroker {
|
|
|
43
43
|
if (!config.sessionStore) {
|
|
44
44
|
throw new Error('AuthBroker: sessionStore is required');
|
|
45
45
|
}
|
|
46
|
+
// Validate required tokenProvider
|
|
47
|
+
if (!config.tokenProvider) {
|
|
48
|
+
throw new Error('AuthBroker: tokenProvider is required');
|
|
49
|
+
}
|
|
46
50
|
// Validate that stores and provider are correctly instantiated (have required methods)
|
|
47
51
|
const sessionStore = config.sessionStore;
|
|
48
52
|
const tokenProvider = config.tokenProvider;
|
|
@@ -60,13 +64,11 @@ class AuthBroker {
|
|
|
60
64
|
if (typeof sessionStore.setConnectionConfig !== 'function') {
|
|
61
65
|
throw new Error('AuthBroker: sessionStore.setConnectionConfig must be a function');
|
|
62
66
|
}
|
|
63
|
-
// Check tokenProvider methods (
|
|
64
|
-
if (tokenProvider) {
|
|
65
|
-
|
|
66
|
-
throw new Error('AuthBroker: tokenProvider.getConnectionConfig must be a function');
|
|
67
|
-
}
|
|
68
|
-
// validateToken is optional, so we don't check it
|
|
67
|
+
// Check tokenProvider methods (required)
|
|
68
|
+
if (typeof tokenProvider.getConnectionConfig !== 'function') {
|
|
69
|
+
throw new Error('AuthBroker: tokenProvider.getConnectionConfig must be a function');
|
|
69
70
|
}
|
|
71
|
+
// validateToken is optional, so we don't check it
|
|
70
72
|
// Check serviceKeyStore methods (if provided)
|
|
71
73
|
if (serviceKeyStore) {
|
|
72
74
|
if (typeof serviceKeyStore.getServiceKey !== 'function') {
|
|
@@ -86,150 +88,105 @@ class AuthBroker {
|
|
|
86
88
|
this.logger = logger || noOpLogger;
|
|
87
89
|
// Log successful initialization
|
|
88
90
|
const hasServiceKeyStore = !!this.serviceKeyStore;
|
|
89
|
-
|
|
90
|
-
this.logger?.debug(`AuthBroker initialized: sessionStore(ok), serviceKeyStore(${hasServiceKeyStore ? 'ok' : 'none'}), tokenProvider(${hasTokenProvider ? 'ok' : 'none'})`);
|
|
91
|
-
}
|
|
92
|
-
/**
|
|
93
|
-
* Refresh token using refresh_token grant type (direct UAA HTTP request)
|
|
94
|
-
* @param refreshToken Refresh token
|
|
95
|
-
* @param authConfig UAA authorization configuration
|
|
96
|
-
* @returns Promise that resolves to new tokens
|
|
97
|
-
*/
|
|
98
|
-
async refreshTokenDirect(refreshToken, authConfig) {
|
|
99
|
-
if (!authConfig.uaaUrl || !authConfig.uaaClientId || !authConfig.uaaClientSecret) {
|
|
100
|
-
throw new Error('UAA credentials incomplete: uaaUrl, uaaClientId, and uaaClientSecret are required');
|
|
101
|
-
}
|
|
102
|
-
const tokenUrl = `${authConfig.uaaUrl}/oauth/token`;
|
|
103
|
-
const params = new URLSearchParams();
|
|
104
|
-
params.append('grant_type', 'refresh_token');
|
|
105
|
-
params.append('refresh_token', refreshToken);
|
|
106
|
-
const authString = Buffer.from(`${authConfig.uaaClientId}:${authConfig.uaaClientSecret}`).toString('base64');
|
|
107
|
-
try {
|
|
108
|
-
const response = await (0, axios_1.default)({
|
|
109
|
-
method: 'post',
|
|
110
|
-
url: tokenUrl,
|
|
111
|
-
headers: {
|
|
112
|
-
Authorization: `Basic ${authString}`,
|
|
113
|
-
'Content-Type': 'application/x-www-form-urlencoded',
|
|
114
|
-
},
|
|
115
|
-
data: params.toString(),
|
|
116
|
-
timeout: 30000,
|
|
117
|
-
});
|
|
118
|
-
if (response.data && response.data.access_token) {
|
|
119
|
-
return {
|
|
120
|
-
accessToken: response.data.access_token,
|
|
121
|
-
refreshToken: response.data.refresh_token || refreshToken,
|
|
122
|
-
expiresIn: response.data.expires_in,
|
|
123
|
-
};
|
|
124
|
-
}
|
|
125
|
-
else {
|
|
126
|
-
throw new Error('Response does not contain access_token');
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
catch (error) {
|
|
130
|
-
if (error.response) {
|
|
131
|
-
throw new Error(`Token refresh failed (${error.response.status}): ${JSON.stringify(error.response.data)}`);
|
|
132
|
-
}
|
|
133
|
-
else {
|
|
134
|
-
throw new Error(`Token refresh failed: ${error.message}`);
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
/**
|
|
139
|
-
* Get token using client_credentials grant type (direct UAA HTTP request)
|
|
140
|
-
* @param authConfig UAA authorization configuration
|
|
141
|
-
* @returns Promise that resolves to access token
|
|
142
|
-
*/
|
|
143
|
-
async getTokenWithClientCredentials(authConfig) {
|
|
144
|
-
if (!authConfig.uaaUrl || !authConfig.uaaClientId || !authConfig.uaaClientSecret) {
|
|
145
|
-
throw new Error('UAA credentials incomplete: uaaUrl, uaaClientId, and uaaClientSecret are required');
|
|
146
|
-
}
|
|
147
|
-
const tokenUrl = `${authConfig.uaaUrl}/oauth/token`;
|
|
148
|
-
const params = new URLSearchParams();
|
|
149
|
-
params.append('grant_type', 'client_credentials');
|
|
150
|
-
params.append('client_id', authConfig.uaaClientId);
|
|
151
|
-
params.append('client_secret', authConfig.uaaClientSecret);
|
|
152
|
-
try {
|
|
153
|
-
const response = await (0, axios_1.default)({
|
|
154
|
-
method: 'post',
|
|
155
|
-
url: tokenUrl,
|
|
156
|
-
headers: {
|
|
157
|
-
'Content-Type': 'application/x-www-form-urlencoded',
|
|
158
|
-
},
|
|
159
|
-
data: params.toString(),
|
|
160
|
-
timeout: 30000,
|
|
161
|
-
});
|
|
162
|
-
if (response.data && response.data.access_token) {
|
|
163
|
-
return {
|
|
164
|
-
accessToken: response.data.access_token,
|
|
165
|
-
refreshToken: response.data.refresh_token,
|
|
166
|
-
expiresIn: response.data.expires_in,
|
|
167
|
-
};
|
|
168
|
-
}
|
|
169
|
-
else {
|
|
170
|
-
throw new Error('Response does not contain access_token');
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
catch (error) {
|
|
174
|
-
if (error.response) {
|
|
175
|
-
throw new Error(`Client credentials authentication failed (${error.response.status}): ${JSON.stringify(error.response.data)}`);
|
|
176
|
-
}
|
|
177
|
-
else {
|
|
178
|
-
throw new Error(`Client credentials authentication failed: ${error.message}`);
|
|
179
|
-
}
|
|
180
|
-
}
|
|
91
|
+
this.logger?.debug(`AuthBroker initialized: sessionStore(ok), serviceKeyStore(${hasServiceKeyStore ? 'ok' : 'none'}), tokenProvider(ok)`);
|
|
181
92
|
}
|
|
182
93
|
/**
|
|
183
94
|
* Get authentication token for destination.
|
|
184
|
-
*
|
|
95
|
+
* Uses tokenProvider for all authentication operations (browser-based authorization).
|
|
185
96
|
*
|
|
186
97
|
* **Flow:**
|
|
187
98
|
* **Step 0: Initialize Session with Token (if needed)**
|
|
188
99
|
* - Check if session has `authorizationToken` AND UAA credentials
|
|
189
100
|
* - If both are empty AND serviceKeyStore is available:
|
|
190
|
-
* -
|
|
191
|
-
* -
|
|
192
|
-
*
|
|
101
|
+
* - Get UAA credentials from service key
|
|
102
|
+
* - Use tokenProvider for browser-based authentication
|
|
103
|
+
* - Save token and refresh token to session
|
|
104
|
+
*
|
|
105
|
+
* **Step 1: Token Validation**
|
|
106
|
+
* - If token exists in session, validate it (if provider supports validation)
|
|
107
|
+
* - If valid → return token
|
|
108
|
+
* - If invalid or no token → continue to refresh
|
|
193
109
|
*
|
|
194
|
-
* **Step
|
|
110
|
+
* **Step 2: Refresh Token Flow**
|
|
195
111
|
* - Check if refresh token exists in session
|
|
196
112
|
* - If refresh token exists:
|
|
197
|
-
* -
|
|
198
|
-
* -
|
|
199
|
-
* -
|
|
200
|
-
* - Otherwise → proceed to Step
|
|
113
|
+
* - Use tokenProvider to refresh token (browser-based or refresh grant)
|
|
114
|
+
* - Save new token to session
|
|
115
|
+
* - Return new token
|
|
116
|
+
* - Otherwise → proceed to Step 3
|
|
201
117
|
*
|
|
202
|
-
* **Step
|
|
203
|
-
* -
|
|
204
|
-
* -
|
|
205
|
-
* -
|
|
206
|
-
* -
|
|
207
|
-
* - If all failed → return error
|
|
118
|
+
* **Step 3: New Token Flow**
|
|
119
|
+
* - Get UAA credentials from session or service key
|
|
120
|
+
* - Use tokenProvider for browser-based authentication
|
|
121
|
+
* - Save new token to session
|
|
122
|
+
* - Return new token
|
|
208
123
|
*
|
|
209
124
|
* **Important Notes:**
|
|
210
|
-
* -
|
|
211
|
-
*
|
|
212
|
-
* -
|
|
213
|
-
* - Initializing session from service key via browser authentication (Step 0)
|
|
214
|
-
* - Direct UAA requests fail and fallback to provider is needed
|
|
125
|
+
* - All authentication is handled by tokenProvider (e.g., XSUAA provider)
|
|
126
|
+
* - Provider uses browser-based authorization to ensure proper role assignment
|
|
127
|
+
* - Direct UAA HTTP requests are not used to avoid role assignment issues
|
|
215
128
|
*
|
|
216
129
|
* @param destination Destination name (e.g., "TRIAL")
|
|
217
130
|
* @returns Promise that resolves to JWT token string
|
|
218
|
-
* @throws Error if session initialization fails or
|
|
131
|
+
* @throws Error if session initialization fails or authentication failed
|
|
219
132
|
*/
|
|
220
133
|
async getToken(destination) {
|
|
221
134
|
this.logger?.debug(`Getting token for destination: ${destination}`);
|
|
222
135
|
// Step 0: Initialize Session with Token (if needed)
|
|
223
|
-
|
|
224
|
-
|
|
136
|
+
let connConfig = null;
|
|
137
|
+
let authConfig = null;
|
|
138
|
+
try {
|
|
139
|
+
connConfig = await this.sessionStore.getConnectionConfig(destination);
|
|
140
|
+
}
|
|
141
|
+
catch (error) {
|
|
142
|
+
// Handle typed store errors from session store
|
|
143
|
+
if (error.code === interfaces_1.STORE_ERROR_CODES.FILE_NOT_FOUND) {
|
|
144
|
+
this.logger?.debug(`Session file not found for ${destination}: ${error.filePath || 'unknown path'}`);
|
|
145
|
+
}
|
|
146
|
+
else if (error.code === interfaces_1.STORE_ERROR_CODES.PARSE_ERROR) {
|
|
147
|
+
this.logger?.warn(`Failed to parse session file for ${destination}: ${error.filePath || 'unknown path'} - ${error.message}`);
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
this.logger?.warn(`Failed to get connection config from session store for ${destination}: ${error.message}`);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
try {
|
|
154
|
+
authConfig = await this.sessionStore.getAuthorizationConfig(destination);
|
|
155
|
+
}
|
|
156
|
+
catch (error) {
|
|
157
|
+
// Handle typed store errors from session store
|
|
158
|
+
if (error.code === interfaces_1.STORE_ERROR_CODES.FILE_NOT_FOUND) {
|
|
159
|
+
this.logger?.debug(`Session file not found for ${destination}: ${error.filePath || 'unknown path'}`);
|
|
160
|
+
}
|
|
161
|
+
else if (error.code === interfaces_1.STORE_ERROR_CODES.PARSE_ERROR) {
|
|
162
|
+
this.logger?.warn(`Failed to parse session file for ${destination}: ${error.filePath || 'unknown path'} - ${error.message}`);
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
this.logger?.warn(`Failed to get authorization config from session store for ${destination}: ${error.message}`);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
225
168
|
// Check if session has serviceUrl (required)
|
|
226
169
|
// If not in session, try to get it from serviceKeyStore
|
|
227
170
|
let serviceUrl = connConfig?.serviceUrl;
|
|
228
171
|
if (!serviceUrl && this.serviceKeyStore) {
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
172
|
+
try {
|
|
173
|
+
const serviceKeyConnConfig = await this.serviceKeyStore.getConnectionConfig(destination);
|
|
174
|
+
serviceUrl = serviceKeyConnConfig?.serviceUrl;
|
|
175
|
+
if (serviceUrl) {
|
|
176
|
+
this.logger?.debug(`serviceUrl not in session for ${destination}, found in serviceKeyStore`);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
catch (error) {
|
|
180
|
+
// Handle typed store errors
|
|
181
|
+
if (error.code === interfaces_1.STORE_ERROR_CODES.FILE_NOT_FOUND) {
|
|
182
|
+
this.logger?.debug(`Service key file not found for ${destination}: ${error.filePath || 'unknown path'}`);
|
|
183
|
+
}
|
|
184
|
+
else if (error.code === interfaces_1.STORE_ERROR_CODES.PARSE_ERROR) {
|
|
185
|
+
this.logger?.warn(`Failed to parse service key for ${destination}: ${error.filePath || 'unknown path'} - ${error.message}`);
|
|
186
|
+
}
|
|
187
|
+
else {
|
|
188
|
+
this.logger?.warn(`Failed to get serviceUrl from service key store for ${destination}: ${error.message}`);
|
|
189
|
+
}
|
|
233
190
|
}
|
|
234
191
|
}
|
|
235
192
|
if (!serviceUrl) {
|
|
@@ -256,32 +213,31 @@ class AuthBroker {
|
|
|
256
213
|
this.logger?.error(`Step 0: Service key for ${destination} missing UAA credentials`);
|
|
257
214
|
throw new Error(`Service key for destination "${destination}" does not contain UAA credentials`);
|
|
258
215
|
}
|
|
259
|
-
//
|
|
216
|
+
// Use tokenProvider for browser-based authentication
|
|
217
|
+
this.logger?.debug(`Step 0: Authenticating via provider (browser) for ${destination} using service key UAA credentials`);
|
|
260
218
|
let tokenResult;
|
|
261
219
|
try {
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
this.logger?.debug(`Step 0: Authenticating via provider for ${destination} using service key UAA credentials`);
|
|
277
|
-
tokenResult = await this.tokenProvider.getConnectionConfig(serviceKeyAuthConfig, {
|
|
278
|
-
browser: this.browser,
|
|
279
|
-
logger: this.logger,
|
|
280
|
-
});
|
|
220
|
+
tokenResult = await this.tokenProvider.getConnectionConfig(serviceKeyAuthConfig, {
|
|
221
|
+
browser: this.browser,
|
|
222
|
+
logger: this.logger,
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
catch (error) {
|
|
226
|
+
// Handle provider errors (network, auth, validation)
|
|
227
|
+
if (error.code === 'VALIDATION_ERROR') {
|
|
228
|
+
this.logger?.error(`Step 0: Provider validation error for ${destination}: missing ${error.missingFields?.join(', ') || 'required fields'}`);
|
|
229
|
+
throw new Error(`Cannot initialize session for destination "${destination}": provider validation failed - missing ${error.missingFields?.join(', ') || 'required fields'}`);
|
|
230
|
+
}
|
|
231
|
+
else if (error.code === 'BROWSER_AUTH_ERROR') {
|
|
232
|
+
this.logger?.error(`Step 0: Browser authentication failed for ${destination}: ${error.message}`);
|
|
233
|
+
throw new Error(`Cannot initialize session for destination "${destination}": browser authentication failed - ${error.message}`);
|
|
281
234
|
}
|
|
282
|
-
else {
|
|
283
|
-
|
|
235
|
+
else if (error.code === 'ECONNREFUSED' || error.code === 'ETIMEDOUT' || error.code === 'ENOTFOUND') {
|
|
236
|
+
this.logger?.error(`Step 0: Network error for ${destination}: ${error.code}`);
|
|
237
|
+
throw new Error(`Cannot initialize session for destination "${destination}": network error - cannot reach authentication server (${error.code})`);
|
|
284
238
|
}
|
|
239
|
+
this.logger?.error(`Step 0: Provider error for ${destination}: ${error.message}`);
|
|
240
|
+
throw new Error(`Cannot initialize session for destination "${destination}": provider error - ${error.message}`);
|
|
285
241
|
}
|
|
286
242
|
const tokenLength = tokenResult.connectionConfig.authorizationToken?.length || 0;
|
|
287
243
|
this.logger?.info(`Step 0: Token initialized for ${destination}: token(${tokenLength} chars), hasRefreshToken(${!!tokenResult.refreshToken})`);
|
|
@@ -292,31 +248,61 @@ class AuthBroker {
|
|
|
292
248
|
serviceUrl: tokenResult.connectionConfig.serviceUrl || serviceKeyConnConfig?.serviceUrl || serviceUrl,
|
|
293
249
|
};
|
|
294
250
|
// Save token and UAA credentials to session
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
251
|
+
try {
|
|
252
|
+
await this.sessionStore.setConnectionConfig(destination, connectionConfigWithServiceUrl);
|
|
253
|
+
}
|
|
254
|
+
catch (error) {
|
|
255
|
+
this.logger?.error(`Step 0: Failed to save connection config to session for ${destination}: ${error.message}`);
|
|
256
|
+
throw new Error(`Failed to save connection config for destination "${destination}": ${error.message}`);
|
|
257
|
+
}
|
|
258
|
+
try {
|
|
259
|
+
await this.sessionStore.setAuthorizationConfig(destination, {
|
|
260
|
+
...serviceKeyAuthConfig,
|
|
261
|
+
refreshToken: tokenResult.refreshToken || serviceKeyAuthConfig.refreshToken,
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
catch (error) {
|
|
265
|
+
this.logger?.error(`Step 0: Failed to save authorization config to session for ${destination}: ${error.message}`);
|
|
266
|
+
throw new Error(`Failed to save authorization config for destination "${destination}": ${error.message}`);
|
|
267
|
+
}
|
|
300
268
|
return tokenResult.connectionConfig.authorizationToken;
|
|
301
269
|
}
|
|
302
270
|
catch (error) {
|
|
271
|
+
// Handle typed store errors
|
|
272
|
+
if (error.code === interfaces_1.STORE_ERROR_CODES.FILE_NOT_FOUND) {
|
|
273
|
+
this.logger?.error(`Step 0: Service key file not found for ${destination}: ${error.filePath || 'unknown path'}`);
|
|
274
|
+
throw new Error(`Cannot initialize session for destination "${destination}": service key file not found`);
|
|
275
|
+
}
|
|
276
|
+
else if (error.code === interfaces_1.STORE_ERROR_CODES.PARSE_ERROR) {
|
|
277
|
+
this.logger?.error(`Step 0: Failed to parse service key for ${destination}: ${error.filePath || 'unknown path'} - ${error.message}`);
|
|
278
|
+
throw new Error(`Cannot initialize session for destination "${destination}": service key parsing failed - ${error.message}`);
|
|
279
|
+
}
|
|
280
|
+
else if (error.code === interfaces_1.STORE_ERROR_CODES.INVALID_CONFIG) {
|
|
281
|
+
this.logger?.error(`Step 0: Invalid service key config for ${destination}: missing fields ${error.missingFields?.join(', ') || 'unknown'}`);
|
|
282
|
+
throw new Error(`Cannot initialize session for destination "${destination}": invalid service key - missing ${error.missingFields?.join(', ') || 'required fields'}`);
|
|
283
|
+
}
|
|
303
284
|
this.logger?.error(`Step 0: Failed to initialize session for ${destination}: ${error.message}`);
|
|
304
|
-
|
|
305
|
-
`Ensure serviceKeyStore contains valid service key with UAA credentials${this.tokenProvider ? ' or provide tokenProvider for alternative authentication' : ''}.`;
|
|
306
|
-
throw new Error(errorMessage);
|
|
285
|
+
throw new Error(`Cannot initialize session for destination "${destination}": ${error.message}`);
|
|
307
286
|
}
|
|
308
287
|
}
|
|
309
288
|
// If we have a token, validate it first
|
|
310
|
-
if (hasToken && connConfig
|
|
289
|
+
if (hasToken && connConfig?.authorizationToken) {
|
|
311
290
|
this.logger?.debug(`Step 0: Token found for ${destination}, validating`);
|
|
312
291
|
// Validate token if provider supports validation and we have service URL
|
|
313
292
|
if (this.tokenProvider?.validateToken && serviceUrl) {
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
293
|
+
try {
|
|
294
|
+
const isValid = await this.tokenProvider.validateToken(connConfig.authorizationToken, serviceUrl);
|
|
295
|
+
if (isValid) {
|
|
296
|
+
this.logger?.info(`Step 0: Token valid for ${destination}: token(${connConfig.authorizationToken.length} chars)`);
|
|
297
|
+
return connConfig.authorizationToken;
|
|
298
|
+
}
|
|
299
|
+
this.logger?.debug(`Step 0: Token invalid for ${destination}, continuing to refresh`);
|
|
300
|
+
}
|
|
301
|
+
catch (error) {
|
|
302
|
+
// Validation failed due to network/server error - log and continue to refresh
|
|
303
|
+
this.logger?.warn(`Step 0: Token validation failed for ${destination} (network error): ${error.message}. Continuing to refresh.`);
|
|
304
|
+
// Don't throw - continue to refresh flow
|
|
318
305
|
}
|
|
319
|
-
this.logger?.debug(`Step 0: Token invalid for ${destination}, continuing to refresh`);
|
|
320
306
|
}
|
|
321
307
|
else {
|
|
322
308
|
// No service URL or provider doesn't support validation - just return token
|
|
@@ -324,165 +310,167 @@ class AuthBroker {
|
|
|
324
310
|
return connConfig.authorizationToken;
|
|
325
311
|
}
|
|
326
312
|
}
|
|
327
|
-
// Step
|
|
328
|
-
this.logger?.debug(`Step
|
|
313
|
+
// Step 2: Refresh Token Flow
|
|
314
|
+
this.logger?.debug(`Step 2: Attempting token refresh for ${destination}`);
|
|
315
|
+
// Get UAA credentials from session or service key
|
|
316
|
+
let serviceKeyAuthConfig = null;
|
|
317
|
+
if (!authConfig && this.serviceKeyStore) {
|
|
318
|
+
try {
|
|
319
|
+
serviceKeyAuthConfig = await this.serviceKeyStore.getAuthorizationConfig(destination);
|
|
320
|
+
}
|
|
321
|
+
catch (error) {
|
|
322
|
+
// Handle typed store errors
|
|
323
|
+
if (error.code === interfaces_1.STORE_ERROR_CODES.FILE_NOT_FOUND) {
|
|
324
|
+
this.logger?.debug(`Service key file not found for ${destination}: ${error.filePath || 'unknown path'}`);
|
|
325
|
+
}
|
|
326
|
+
else if (error.code === interfaces_1.STORE_ERROR_CODES.PARSE_ERROR) {
|
|
327
|
+
this.logger?.warn(`Failed to parse service key for ${destination}: ${error.filePath || 'unknown path'} - ${error.message}`);
|
|
328
|
+
}
|
|
329
|
+
else {
|
|
330
|
+
this.logger?.warn(`Failed to get UAA credentials from service key store for ${destination}: ${error.message}`);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
const uaaCredentials = authConfig || serviceKeyAuthConfig;
|
|
335
|
+
if (!uaaCredentials || !uaaCredentials.uaaUrl || !uaaCredentials.uaaClientId || !uaaCredentials.uaaClientSecret) {
|
|
336
|
+
const errorMessage = `Step 2: UAA credentials not found for ${destination}. ` +
|
|
337
|
+
`Session has no UAA credentials${this.serviceKeyStore ? ' and serviceKeyStore has no UAA credentials' : ' and serviceKeyStore is not available'}.`;
|
|
338
|
+
this.logger?.error(errorMessage);
|
|
339
|
+
throw new Error(errorMessage);
|
|
340
|
+
}
|
|
341
|
+
// Try refresh from session first (if refresh token exists)
|
|
329
342
|
const refreshToken = authConfig?.refreshToken;
|
|
330
343
|
if (refreshToken) {
|
|
331
344
|
try {
|
|
332
|
-
this.logger?.debug(`Step
|
|
333
|
-
|
|
334
|
-
const uaaCredentials = authConfig || (this.serviceKeyStore ? await this.serviceKeyStore.getAuthorizationConfig(destination) : null);
|
|
335
|
-
if (!uaaCredentials || !uaaCredentials.uaaUrl || !uaaCredentials.uaaClientId || !uaaCredentials.uaaClientSecret) {
|
|
336
|
-
throw new Error('UAA credentials not found in session and serviceKeyStore not available');
|
|
337
|
-
}
|
|
345
|
+
this.logger?.debug(`Step 2a: Trying refreshTokenFromSession for ${destination}`);
|
|
346
|
+
const authConfigWithRefresh = { ...uaaCredentials, refreshToken };
|
|
338
347
|
let tokenResult;
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
try {
|
|
342
|
-
this.logger?.debug(`Step 1: Trying direct UAA refresh for ${destination}`);
|
|
343
|
-
const uaaResult = await this.refreshTokenDirect(refreshToken, uaaCredentials);
|
|
344
|
-
tokenResult = {
|
|
345
|
-
connectionConfig: {
|
|
346
|
-
authorizationToken: uaaResult.accessToken,
|
|
347
|
-
},
|
|
348
|
-
refreshToken: uaaResult.refreshToken,
|
|
349
|
-
};
|
|
350
|
-
this.logger?.debug(`Step 1: Direct UAA refresh succeeded for ${destination}`);
|
|
351
|
-
}
|
|
352
|
-
catch (directError) {
|
|
353
|
-
this.logger?.debug(`Step 1: Direct UAA refresh failed for ${destination}: ${directError.message}, trying provider`);
|
|
354
|
-
// If direct UAA failed and we have provider, try provider
|
|
355
|
-
if (this.tokenProvider) {
|
|
356
|
-
const authConfigWithRefresh = { ...uaaCredentials, refreshToken };
|
|
357
|
-
tokenResult = await this.tokenProvider.getConnectionConfig(authConfigWithRefresh, {
|
|
358
|
-
browser: this.browser,
|
|
359
|
-
logger: this.logger,
|
|
360
|
-
});
|
|
361
|
-
}
|
|
362
|
-
else {
|
|
363
|
-
throw directError; // No provider, re-throw direct error
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
else if (this.tokenProvider) {
|
|
368
|
-
// No UAA credentials but have provider
|
|
369
|
-
const authConfigWithRefresh = { ...uaaCredentials, refreshToken };
|
|
370
|
-
tokenResult = await this.tokenProvider.getConnectionConfig(authConfigWithRefresh, {
|
|
348
|
+
try {
|
|
349
|
+
tokenResult = await this.tokenProvider.refreshTokenFromSession(authConfigWithRefresh, {
|
|
371
350
|
browser: this.browser,
|
|
372
351
|
logger: this.logger,
|
|
373
352
|
});
|
|
374
353
|
}
|
|
375
|
-
|
|
376
|
-
|
|
354
|
+
catch (providerError) {
|
|
355
|
+
// Handle provider network/auth errors
|
|
356
|
+
if (providerError.code === 'ECONNREFUSED' || providerError.code === 'ETIMEDOUT' || providerError.code === 'ENOTFOUND') {
|
|
357
|
+
this.logger?.debug(`Step 2a: Network error during refreshTokenFromSession for ${destination}: ${providerError.code}. Trying refreshTokenFromServiceKey`);
|
|
358
|
+
throw providerError; // Re-throw to trigger fallback to Step 2b
|
|
359
|
+
}
|
|
360
|
+
throw providerError; // Re-throw other errors
|
|
377
361
|
}
|
|
378
362
|
const tokenLength = tokenResult.connectionConfig.authorizationToken?.length || 0;
|
|
379
|
-
this.logger?.info(`Step
|
|
380
|
-
// Get serviceUrl from session or service key
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
363
|
+
this.logger?.info(`Step 2a: Token refreshed from session for ${destination}: token(${tokenLength} chars), hasRefreshToken(${!!tokenResult.refreshToken})`);
|
|
364
|
+
// Get serviceUrl from session or service key
|
|
365
|
+
let serviceKeyServiceUrl;
|
|
366
|
+
if (this.serviceKeyStore) {
|
|
367
|
+
try {
|
|
368
|
+
const serviceKeyConn = await this.serviceKeyStore.getConnectionConfig(destination);
|
|
369
|
+
serviceKeyServiceUrl = serviceKeyConn?.serviceUrl;
|
|
370
|
+
}
|
|
371
|
+
catch (error) {
|
|
372
|
+
this.logger?.debug(`Could not get serviceUrl from service key store: ${error.message}`);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
const finalServiceUrl = tokenResult.connectionConfig.serviceUrl || serviceUrl || serviceKeyServiceUrl;
|
|
384
376
|
const connectionConfigWithServiceUrl = {
|
|
385
377
|
...tokenResult.connectionConfig,
|
|
386
378
|
serviceUrl: finalServiceUrl,
|
|
387
379
|
};
|
|
388
380
|
// Update session with new token
|
|
389
|
-
|
|
381
|
+
try {
|
|
382
|
+
await this.sessionStore.setConnectionConfig(destination, connectionConfigWithServiceUrl);
|
|
383
|
+
}
|
|
384
|
+
catch (error) {
|
|
385
|
+
this.logger?.error(`Step 2a: Failed to save connection config to session for ${destination}: ${error.message}`);
|
|
386
|
+
throw new Error(`Failed to save connection config for destination "${destination}": ${error.message}`);
|
|
387
|
+
}
|
|
390
388
|
if (tokenResult.refreshToken) {
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
389
|
+
try {
|
|
390
|
+
await this.sessionStore.setAuthorizationConfig(destination, {
|
|
391
|
+
...uaaCredentials,
|
|
392
|
+
refreshToken: tokenResult.refreshToken,
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
catch (error) {
|
|
396
|
+
this.logger?.error(`Step 2a: Failed to save authorization config to session for ${destination}: ${error.message}`);
|
|
397
|
+
throw new Error(`Failed to save authorization config for destination "${destination}": ${error.message}`);
|
|
398
|
+
}
|
|
395
399
|
}
|
|
396
400
|
return tokenResult.connectionConfig.authorizationToken;
|
|
397
401
|
}
|
|
398
402
|
catch (error) {
|
|
399
|
-
this.logger?.debug(`Step
|
|
400
|
-
// Continue to
|
|
403
|
+
this.logger?.debug(`Step 2a: refreshTokenFromSession failed for ${destination}: ${error.message}, trying refreshTokenFromServiceKey`);
|
|
404
|
+
// Continue to try service key refresh
|
|
401
405
|
}
|
|
402
406
|
}
|
|
403
407
|
else {
|
|
404
|
-
this.logger?.debug(`Step
|
|
405
|
-
}
|
|
406
|
-
// Step 2: UAA Credentials Flow
|
|
407
|
-
this.logger?.debug(`Step 2: Checking UAA credentials for ${destination}`);
|
|
408
|
-
// Get UAA credentials from session or service key
|
|
409
|
-
const uaaCredentials = authConfig || (this.serviceKeyStore ? await this.serviceKeyStore.getAuthorizationConfig(destination) : null);
|
|
410
|
-
if (!uaaCredentials || !uaaCredentials.uaaUrl || !uaaCredentials.uaaClientId || !uaaCredentials.uaaClientSecret) {
|
|
411
|
-
const errorMessage = `Step 2: UAA credentials not found for ${destination}. ` +
|
|
412
|
-
`Session has no UAA credentials${this.serviceKeyStore ? ' and serviceKeyStore has no UAA credentials' : ' and serviceKeyStore is not available'}.`;
|
|
413
|
-
this.logger?.error(errorMessage);
|
|
414
|
-
throw new Error(errorMessage);
|
|
408
|
+
this.logger?.debug(`Step 2a: No refresh token in session for ${destination}, skipping to service key refresh`);
|
|
415
409
|
}
|
|
410
|
+
// Try refresh from service key (browser authentication)
|
|
416
411
|
try {
|
|
417
|
-
this.logger?.debug(`Step
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
412
|
+
this.logger?.debug(`Step 2b: Trying refreshTokenFromServiceKey for ${destination}`);
|
|
413
|
+
const tokenResult = await this.tokenProvider.refreshTokenFromServiceKey(uaaCredentials, {
|
|
414
|
+
browser: this.browser,
|
|
415
|
+
logger: this.logger,
|
|
416
|
+
});
|
|
417
|
+
const tokenLength = tokenResult.connectionConfig.authorizationToken?.length || 0;
|
|
418
|
+
this.logger?.info(`Step 2b: Token refreshed from service key for ${destination}: token(${tokenLength} chars), hasRefreshToken(${!!tokenResult.refreshToken})`);
|
|
419
|
+
// Get serviceUrl from session or service key
|
|
420
|
+
let serviceKeyServiceUrl;
|
|
421
|
+
if (this.serviceKeyStore) {
|
|
421
422
|
try {
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
},
|
|
428
|
-
refreshToken: uaaResult.refreshToken,
|
|
429
|
-
};
|
|
430
|
-
this.logger?.debug(`Step 2: Direct UAA client_credentials succeeded for ${destination}`);
|
|
431
|
-
}
|
|
432
|
-
catch (directError) {
|
|
433
|
-
this.logger?.debug(`Step 2: Direct UAA client_credentials failed for ${destination}: ${directError.message}, trying provider`);
|
|
434
|
-
// If direct UAA failed and we have provider, try provider
|
|
435
|
-
if (this.tokenProvider) {
|
|
436
|
-
const authConfigWithoutRefresh = { ...uaaCredentials, refreshToken: undefined };
|
|
437
|
-
tokenResult = await this.tokenProvider.getConnectionConfig(authConfigWithoutRefresh, {
|
|
438
|
-
browser: this.browser,
|
|
439
|
-
logger: this.logger,
|
|
440
|
-
});
|
|
441
|
-
}
|
|
442
|
-
else {
|
|
443
|
-
throw directError; // No provider, re-throw direct error
|
|
444
|
-
}
|
|
423
|
+
const serviceKeyConn = await this.serviceKeyStore.getConnectionConfig(destination);
|
|
424
|
+
serviceKeyServiceUrl = serviceKeyConn?.serviceUrl;
|
|
425
|
+
}
|
|
426
|
+
catch (error) {
|
|
427
|
+
this.logger?.debug(`Could not get serviceUrl from service key store: ${error.message}`);
|
|
445
428
|
}
|
|
446
429
|
}
|
|
447
|
-
|
|
448
|
-
// No UAA credentials but have provider
|
|
449
|
-
const authConfigWithoutRefresh = { ...uaaCredentials, refreshToken: undefined };
|
|
450
|
-
tokenResult = await this.tokenProvider.getConnectionConfig(authConfigWithoutRefresh, {
|
|
451
|
-
browser: this.browser,
|
|
452
|
-
logger: this.logger,
|
|
453
|
-
});
|
|
454
|
-
}
|
|
455
|
-
else {
|
|
456
|
-
throw new Error('UAA credentials incomplete and tokenProvider not available');
|
|
457
|
-
}
|
|
458
|
-
const tokenLength = tokenResult.connectionConfig.authorizationToken?.length || 0;
|
|
459
|
-
this.logger?.info(`Step 2: Token obtained via UAA for ${destination}: token(${tokenLength} chars), hasRefreshToken(${!!tokenResult.refreshToken})`);
|
|
460
|
-
// Get serviceUrl from session or service key (use the one we already have from the beginning of the method)
|
|
461
|
-
const finalServiceUrl = tokenResult.connectionConfig.serviceUrl ||
|
|
462
|
-
serviceUrl ||
|
|
463
|
-
(this.serviceKeyStore ? (await this.serviceKeyStore.getConnectionConfig(destination))?.serviceUrl : undefined);
|
|
430
|
+
const finalServiceUrl = tokenResult.connectionConfig.serviceUrl || serviceUrl || serviceKeyServiceUrl;
|
|
464
431
|
const connectionConfigWithServiceUrl = {
|
|
465
432
|
...tokenResult.connectionConfig,
|
|
466
433
|
serviceUrl: finalServiceUrl,
|
|
467
434
|
};
|
|
468
435
|
// Update session with new token
|
|
469
|
-
|
|
436
|
+
try {
|
|
437
|
+
await this.sessionStore.setConnectionConfig(destination, connectionConfigWithServiceUrl);
|
|
438
|
+
}
|
|
439
|
+
catch (error) {
|
|
440
|
+
this.logger?.error(`Step 2b: Failed to save connection config to session for ${destination}: ${error.message}`);
|
|
441
|
+
throw new Error(`Failed to save connection config for destination "${destination}": ${error.message}`);
|
|
442
|
+
}
|
|
470
443
|
if (tokenResult.refreshToken) {
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
444
|
+
try {
|
|
445
|
+
await this.sessionStore.setAuthorizationConfig(destination, {
|
|
446
|
+
...uaaCredentials,
|
|
447
|
+
refreshToken: tokenResult.refreshToken,
|
|
448
|
+
});
|
|
449
|
+
}
|
|
450
|
+
catch (error) {
|
|
451
|
+
this.logger?.error(`Step 2b: Failed to save authorization config to session for ${destination}: ${error.message}`);
|
|
452
|
+
throw new Error(`Failed to save authorization config for destination "${destination}": ${error.message}`);
|
|
453
|
+
}
|
|
475
454
|
}
|
|
476
455
|
return tokenResult.connectionConfig.authorizationToken;
|
|
477
456
|
}
|
|
478
457
|
catch (error) {
|
|
479
|
-
this.logger?.error(`Step
|
|
480
|
-
//
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
458
|
+
this.logger?.error(`Step 2b: refreshTokenFromServiceKey failed for ${destination}: ${error.message}`);
|
|
459
|
+
// Determine error cause and throw meaningful error
|
|
460
|
+
if (error.code === 'VALIDATION_ERROR') {
|
|
461
|
+
throw new Error(`Token refresh failed: Missing required fields in authConfig - ${error.missingFields?.join(', ')}`);
|
|
462
|
+
}
|
|
463
|
+
else if (error.code === 'BROWSER_AUTH_ERROR') {
|
|
464
|
+
throw new Error(`Token refresh failed: Browser authentication failed or was cancelled - ${error.message}`);
|
|
465
|
+
}
|
|
466
|
+
else if (error.code === 'ECONNREFUSED' || error.code === 'ETIMEDOUT' || error.code === 'ENOTFOUND') {
|
|
467
|
+
throw new Error(`Token refresh failed: Network error - ${error.code}: Cannot reach authentication server`);
|
|
468
|
+
}
|
|
469
|
+
else if (error.code === 'SERVICE_KEY_ERROR') {
|
|
470
|
+
throw new Error(`Token refresh failed: Service key not found or invalid for ${destination}`);
|
|
471
|
+
}
|
|
472
|
+
// Generic error
|
|
473
|
+
throw new Error(`Token refresh failed for ${destination}: ${error.message}`);
|
|
486
474
|
}
|
|
487
475
|
}
|
|
488
476
|
/**
|
|
@@ -493,81 +481,8 @@ class AuthBroker {
|
|
|
493
481
|
*/
|
|
494
482
|
async refreshToken(destination) {
|
|
495
483
|
this.logger?.debug(`Force refreshing token for destination: ${destination}`);
|
|
496
|
-
//
|
|
497
|
-
|
|
498
|
-
const serviceKeyAuthConfig = this.serviceKeyStore
|
|
499
|
-
? await this.serviceKeyStore.getAuthorizationConfig(destination)
|
|
500
|
-
: null;
|
|
501
|
-
const authConfig = sessionAuthConfig || serviceKeyAuthConfig;
|
|
502
|
-
if (!authConfig) {
|
|
503
|
-
this.logger?.error(`Authorization config not found for ${destination}`);
|
|
504
|
-
throw new Error(`Authorization config not found for destination "${destination}". ` +
|
|
505
|
-
`Session has no UAA credentials${this.serviceKeyStore ? ' and serviceKeyStore has no UAA credentials' : ' and serviceKeyStore is not available'}.`);
|
|
506
|
-
}
|
|
507
|
-
// Get refresh token from session or service key
|
|
508
|
-
const refreshToken = sessionAuthConfig?.refreshToken || authConfig.refreshToken;
|
|
509
|
-
this.logger?.debug(`Refresh token check for ${destination}: hasRefreshToken(${!!refreshToken})`);
|
|
510
|
-
let tokenResult;
|
|
511
|
-
// Try direct UAA request if UAA credentials are available
|
|
512
|
-
if (authConfig.uaaUrl && authConfig.uaaClientId && authConfig.uaaClientSecret && refreshToken) {
|
|
513
|
-
try {
|
|
514
|
-
this.logger?.debug(`Trying direct UAA refresh for ${destination}`);
|
|
515
|
-
const uaaResult = await this.refreshTokenDirect(refreshToken, authConfig);
|
|
516
|
-
tokenResult = {
|
|
517
|
-
connectionConfig: {
|
|
518
|
-
authorizationToken: uaaResult.accessToken,
|
|
519
|
-
},
|
|
520
|
-
refreshToken: uaaResult.refreshToken,
|
|
521
|
-
};
|
|
522
|
-
}
|
|
523
|
-
catch (directError) {
|
|
524
|
-
this.logger?.debug(`Direct UAA refresh failed for ${destination}: ${directError.message}, trying provider`);
|
|
525
|
-
// If direct UAA failed and we have provider, try provider
|
|
526
|
-
if (this.tokenProvider) {
|
|
527
|
-
const authConfigWithRefresh = { ...authConfig, refreshToken };
|
|
528
|
-
tokenResult = await this.tokenProvider.getConnectionConfig(authConfigWithRefresh, {
|
|
529
|
-
browser: this.browser,
|
|
530
|
-
logger: this.logger,
|
|
531
|
-
});
|
|
532
|
-
}
|
|
533
|
-
else {
|
|
534
|
-
throw directError; // No provider, re-throw direct error
|
|
535
|
-
}
|
|
536
|
-
}
|
|
537
|
-
}
|
|
538
|
-
else if (this.tokenProvider) {
|
|
539
|
-
// No UAA credentials or refresh token, but have provider
|
|
540
|
-
const authConfigWithRefresh = { ...authConfig, refreshToken };
|
|
541
|
-
tokenResult = await this.tokenProvider.getConnectionConfig(authConfigWithRefresh, {
|
|
542
|
-
browser: this.browser,
|
|
543
|
-
logger: this.logger,
|
|
544
|
-
});
|
|
545
|
-
}
|
|
546
|
-
else {
|
|
547
|
-
throw new Error('UAA credentials incomplete and tokenProvider not available');
|
|
548
|
-
}
|
|
549
|
-
const tokenLength = tokenResult.connectionConfig.authorizationToken?.length || 0;
|
|
550
|
-
this.logger?.info(`Token refreshed for ${destination}: token(${tokenLength} chars), hasRefreshToken(${!!tokenResult.refreshToken})`);
|
|
551
|
-
// Get serviceUrl from session or service key
|
|
552
|
-
const connConfig = await this.sessionStore.getConnectionConfig(destination);
|
|
553
|
-
const serviceKeyConnConfig = this.serviceKeyStore
|
|
554
|
-
? await this.serviceKeyStore.getConnectionConfig(destination)
|
|
555
|
-
: null;
|
|
556
|
-
const connectionConfigWithServiceUrl = {
|
|
557
|
-
...tokenResult.connectionConfig,
|
|
558
|
-
serviceUrl: tokenResult.connectionConfig.serviceUrl ||
|
|
559
|
-
connConfig?.serviceUrl ||
|
|
560
|
-
serviceKeyConnConfig?.serviceUrl,
|
|
561
|
-
};
|
|
562
|
-
// Update or create session with new token (stores handle creation if session doesn't exist)
|
|
563
|
-
await this.sessionStore.setConnectionConfig(destination, connectionConfigWithServiceUrl);
|
|
564
|
-
if (tokenResult.refreshToken) {
|
|
565
|
-
await this.sessionStore.setAuthorizationConfig(destination, {
|
|
566
|
-
...authConfig,
|
|
567
|
-
refreshToken: tokenResult.refreshToken,
|
|
568
|
-
});
|
|
569
|
-
}
|
|
570
|
-
return tokenResult.connectionConfig.authorizationToken;
|
|
484
|
+
// Call getToken to trigger full refresh flow
|
|
485
|
+
return this.getToken(destination);
|
|
571
486
|
}
|
|
572
487
|
/**
|
|
573
488
|
* Get authorization configuration for destination
|
|
@@ -578,7 +493,13 @@ class AuthBroker {
|
|
|
578
493
|
this.logger?.debug(`Getting authorization config for ${destination}`);
|
|
579
494
|
// Try session store first (has tokens)
|
|
580
495
|
this.logger?.debug(`Checking session store for authorization config: ${destination}`);
|
|
581
|
-
|
|
496
|
+
let sessionAuthConfig = null;
|
|
497
|
+
try {
|
|
498
|
+
sessionAuthConfig = await this.sessionStore.getAuthorizationConfig(destination);
|
|
499
|
+
}
|
|
500
|
+
catch (error) {
|
|
501
|
+
this.logger?.warn(`Failed to get authorization config from session store for ${destination}: ${error.message}`);
|
|
502
|
+
}
|
|
582
503
|
if (sessionAuthConfig) {
|
|
583
504
|
this.logger?.debug(`Authorization config from session for ${destination}: hasUaaUrl(${!!sessionAuthConfig.uaaUrl}), hasRefreshToken(${!!sessionAuthConfig.refreshToken})`);
|
|
584
505
|
return sessionAuthConfig;
|
|
@@ -586,7 +507,22 @@ class AuthBroker {
|
|
|
586
507
|
// Fall back to service key store (has UAA credentials) if available
|
|
587
508
|
if (this.serviceKeyStore) {
|
|
588
509
|
this.logger?.debug(`Checking service key store for authorization config: ${destination}`);
|
|
589
|
-
|
|
510
|
+
let serviceKeyAuthConfig = null;
|
|
511
|
+
try {
|
|
512
|
+
serviceKeyAuthConfig = await this.serviceKeyStore.getAuthorizationConfig(destination);
|
|
513
|
+
}
|
|
514
|
+
catch (error) {
|
|
515
|
+
// Handle typed store errors
|
|
516
|
+
if (error.code === interfaces_1.STORE_ERROR_CODES.FILE_NOT_FOUND) {
|
|
517
|
+
this.logger?.debug(`Service key file not found for ${destination}: ${error.filePath || 'unknown path'}`);
|
|
518
|
+
}
|
|
519
|
+
else if (error.code === interfaces_1.STORE_ERROR_CODES.PARSE_ERROR) {
|
|
520
|
+
this.logger?.warn(`Failed to parse service key for ${destination}: ${error.filePath || 'unknown path'} - ${error.message}`);
|
|
521
|
+
}
|
|
522
|
+
else {
|
|
523
|
+
this.logger?.warn(`Failed to get authorization config from service key store for ${destination}: ${error.message}`);
|
|
524
|
+
}
|
|
525
|
+
}
|
|
590
526
|
if (serviceKeyAuthConfig) {
|
|
591
527
|
this.logger?.debug(`Authorization config from service key for ${destination}: hasUaaUrl(${!!serviceKeyAuthConfig.uaaUrl})`);
|
|
592
528
|
return serviceKeyAuthConfig;
|
|
@@ -606,14 +542,35 @@ class AuthBroker {
|
|
|
606
542
|
async getConnectionConfig(destination) {
|
|
607
543
|
this.logger?.debug(`Getting connection config for ${destination}`);
|
|
608
544
|
// Try session store first (has tokens and URLs)
|
|
609
|
-
|
|
545
|
+
let sessionConnConfig = null;
|
|
546
|
+
try {
|
|
547
|
+
sessionConnConfig = await this.sessionStore.getConnectionConfig(destination);
|
|
548
|
+
}
|
|
549
|
+
catch (error) {
|
|
550
|
+
this.logger?.warn(`Failed to get connection config from session store for ${destination}: ${error.message}`);
|
|
551
|
+
}
|
|
610
552
|
if (sessionConnConfig) {
|
|
611
553
|
this.logger?.debug(`Connection config from session for ${destination}: token(${sessionConnConfig.authorizationToken?.length || 0} chars), serviceUrl(${sessionConnConfig.serviceUrl ? 'yes' : 'no'})`);
|
|
612
554
|
return sessionConnConfig;
|
|
613
555
|
}
|
|
614
556
|
// Fall back to service key store (has URLs but no tokens) if available
|
|
615
557
|
if (this.serviceKeyStore) {
|
|
616
|
-
|
|
558
|
+
let serviceKeyConnConfig = null;
|
|
559
|
+
try {
|
|
560
|
+
serviceKeyConnConfig = await this.serviceKeyStore.getConnectionConfig(destination);
|
|
561
|
+
}
|
|
562
|
+
catch (error) {
|
|
563
|
+
// Handle typed store errors
|
|
564
|
+
if (error.code === interfaces_1.STORE_ERROR_CODES.FILE_NOT_FOUND) {
|
|
565
|
+
this.logger?.debug(`Service key file not found for ${destination}: ${error.filePath || 'unknown path'}`);
|
|
566
|
+
}
|
|
567
|
+
else if (error.code === interfaces_1.STORE_ERROR_CODES.PARSE_ERROR) {
|
|
568
|
+
this.logger?.warn(`Failed to parse service key for ${destination}: ${error.filePath || 'unknown path'} - ${error.message}`);
|
|
569
|
+
}
|
|
570
|
+
else {
|
|
571
|
+
this.logger?.warn(`Failed to get connection config from service key store for ${destination}: ${error.message}`);
|
|
572
|
+
}
|
|
573
|
+
}
|
|
617
574
|
if (serviceKeyConnConfig) {
|
|
618
575
|
this.logger?.debug(`Connection config from service key for ${destination}: serviceUrl(${serviceKeyConnConfig.serviceUrl ? 'yes' : 'no'}), token(none)`);
|
|
619
576
|
return serviceKeyConnConfig;
|
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.4",
|
|
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",
|
|
@@ -51,12 +51,12 @@
|
|
|
51
51
|
"node": ">=18.0.0"
|
|
52
52
|
},
|
|
53
53
|
"dependencies": {
|
|
54
|
-
"@mcp-abap-adt/interfaces": "^0.
|
|
54
|
+
"@mcp-abap-adt/interfaces": "^0.2.3",
|
|
55
55
|
"axios": "^1.13.2"
|
|
56
56
|
},
|
|
57
57
|
"devDependencies": {
|
|
58
|
-
"@mcp-abap-adt/auth-providers": "^0.
|
|
59
|
-
"@mcp-abap-adt/auth-stores": "^0.2.
|
|
58
|
+
"@mcp-abap-adt/auth-providers": "^0.2.0",
|
|
59
|
+
"@mcp-abap-adt/auth-stores": "^0.2.5",
|
|
60
60
|
"@types/express": "^5.0.5",
|
|
61
61
|
"@types/jest": "^30.0.0",
|
|
62
62
|
"@types/js-yaml": "^4.0.9",
|