@mcp-abap-adt/auth-broker 0.2.3 → 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 +48 -0
- package/README.md +48 -0
- package/dist/AuthBroker.d.ts +29 -40
- package/dist/AuthBroker.d.ts.map +1 -1
- package/dist/AuthBroker.js +323 -370
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,54 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
10
10
|
Thank you to all contributors! See [CONTRIBUTORS.md](CONTRIBUTORS.md) for the complete list.
|
|
11
11
|
|
|
12
12
|
## [Unreleased]
|
|
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
|
+
|
|
13
61
|
## [0.2.3] - 2025-12-18
|
|
14
62
|
|
|
15
63
|
### Added
|
package/README.md
CHANGED
|
@@ -418,6 +418,54 @@ Gets authentication token for destination. Implements a three-step flow:
|
|
|
418
418
|
- If `sessionStore` contains valid UAA credentials, neither `serviceKeyStore` nor `tokenProvider` are required. Direct UAA HTTP requests will be used automatically.
|
|
419
419
|
- `tokenProvider` is only needed for browser authentication or when direct UAA requests fail.
|
|
420
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
|
|
421
469
|
|
|
422
470
|
##### `refreshToken(destination: string): Promise<string>`
|
|
423
471
|
|
package/dist/AuthBroker.d.ts
CHANGED
|
@@ -12,80 +12,69 @@ 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
|
|
17
|
-
/** Allow direct UAA client_credentials flow (default: true). Set false to force provider/interactive login (e.g., ABAP ADT). */
|
|
18
|
-
allowClientCredentials?: boolean;
|
|
15
|
+
/** Token provider (required) - handles token refresh and authentication flows through browser-based authorization (e.g., XSUAA provider) */
|
|
16
|
+
tokenProvider: ITokenProvider;
|
|
19
17
|
}
|
|
18
|
+
/**
|
|
19
|
+
* AuthBroker manages JWT authentication tokens for destinations
|
|
20
|
+
*/
|
|
20
21
|
export declare class AuthBroker {
|
|
21
22
|
private browser;
|
|
22
23
|
private logger;
|
|
23
24
|
private serviceKeyStore;
|
|
24
25
|
private sessionStore;
|
|
25
26
|
private tokenProvider;
|
|
26
|
-
private allowClientCredentials;
|
|
27
27
|
/**
|
|
28
28
|
* Create a new AuthBroker instance
|
|
29
29
|
* @param config Configuration object with stores and token provider
|
|
30
30
|
* - sessionStore: Store for session data (required)
|
|
31
31
|
* - serviceKeyStore: Store for service keys (optional)
|
|
32
|
-
* - tokenProvider: Token provider implementing ITokenProvider interface (
|
|
32
|
+
* - tokenProvider: Token provider implementing ITokenProvider interface (required) - handles browser-based authorization
|
|
33
33
|
* @param browser Optional browser name for authentication (chrome, edge, firefox, system, none).
|
|
34
34
|
* Default: 'system' (system default browser).
|
|
35
35
|
* Use 'none' to print URL instead of opening browser.
|
|
36
36
|
* @param logger Optional logger instance implementing ILogger interface. If not provided, uses no-op logger.
|
|
37
37
|
*/
|
|
38
38
|
constructor(config: AuthBrokerConfig, browser?: string, logger?: ILogger);
|
|
39
|
-
/**
|
|
40
|
-
* Refresh token using refresh_token grant type (direct UAA HTTP request)
|
|
41
|
-
* @param refreshToken Refresh token
|
|
42
|
-
* @param authConfig UAA authorization configuration
|
|
43
|
-
* @returns Promise that resolves to new tokens
|
|
44
|
-
*/
|
|
45
|
-
private refreshTokenDirect;
|
|
46
|
-
/**
|
|
47
|
-
* Get token using client_credentials grant type (direct UAA HTTP request)
|
|
48
|
-
* @param authConfig UAA authorization configuration
|
|
49
|
-
* @returns Promise that resolves to access token
|
|
50
|
-
*/
|
|
51
|
-
private getTokenWithClientCredentials;
|
|
52
39
|
/**
|
|
53
40
|
* Get authentication token for destination.
|
|
54
|
-
*
|
|
41
|
+
* Uses tokenProvider for all authentication operations (browser-based authorization).
|
|
55
42
|
*
|
|
56
43
|
* **Flow:**
|
|
57
44
|
* **Step 0: Initialize Session with Token (if needed)**
|
|
58
45
|
* - Check if session has `authorizationToken` AND UAA credentials
|
|
59
46
|
* - If both are empty AND serviceKeyStore is available:
|
|
60
|
-
* -
|
|
61
|
-
* -
|
|
62
|
-
*
|
|
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
|
|
63
55
|
*
|
|
64
|
-
* **Step
|
|
56
|
+
* **Step 2: Refresh Token Flow**
|
|
65
57
|
* - Check if refresh token exists in session
|
|
66
58
|
* - If refresh token exists:
|
|
67
|
-
* -
|
|
68
|
-
* -
|
|
69
|
-
* -
|
|
70
|
-
* - 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
|
|
71
63
|
*
|
|
72
|
-
* **Step
|
|
73
|
-
* -
|
|
74
|
-
* -
|
|
75
|
-
* -
|
|
76
|
-
* -
|
|
77
|
-
* - 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
|
|
78
69
|
*
|
|
79
70
|
* **Important Notes:**
|
|
80
|
-
* -
|
|
81
|
-
*
|
|
82
|
-
* -
|
|
83
|
-
* - Initializing session from service key via browser authentication (Step 0)
|
|
84
|
-
* - 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
|
|
85
74
|
*
|
|
86
75
|
* @param destination Destination name (e.g., "TRIAL")
|
|
87
76
|
* @returns Promise that resolves to JWT token string
|
|
88
|
-
* @throws Error if session initialization fails or
|
|
77
|
+
* @throws Error if session initialization fails or authentication failed
|
|
89
78
|
*/
|
|
90
79
|
getToken(destination: string): Promise<string>;
|
|
91
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,19 +14,21 @@ 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;
|
|
23
23
|
serviceKeyStore;
|
|
24
24
|
sessionStore;
|
|
25
25
|
tokenProvider;
|
|
26
|
-
allowClientCredentials;
|
|
27
26
|
/**
|
|
28
27
|
* Create a new AuthBroker instance
|
|
29
28
|
* @param config Configuration object with stores and token provider
|
|
30
29
|
* - sessionStore: Store for session data (required)
|
|
31
30
|
* - serviceKeyStore: Store for service keys (optional)
|
|
32
|
-
* - tokenProvider: Token provider implementing ITokenProvider interface (
|
|
31
|
+
* - tokenProvider: Token provider implementing ITokenProvider interface (required) - handles browser-based authorization
|
|
33
32
|
* @param browser Optional browser name for authentication (chrome, edge, firefox, system, none).
|
|
34
33
|
* Default: 'system' (system default browser).
|
|
35
34
|
* Use 'none' to print URL instead of opening browser.
|
|
@@ -44,6 +43,10 @@ class AuthBroker {
|
|
|
44
43
|
if (!config.sessionStore) {
|
|
45
44
|
throw new Error('AuthBroker: sessionStore is required');
|
|
46
45
|
}
|
|
46
|
+
// Validate required tokenProvider
|
|
47
|
+
if (!config.tokenProvider) {
|
|
48
|
+
throw new Error('AuthBroker: tokenProvider is required');
|
|
49
|
+
}
|
|
47
50
|
// Validate that stores and provider are correctly instantiated (have required methods)
|
|
48
51
|
const sessionStore = config.sessionStore;
|
|
49
52
|
const tokenProvider = config.tokenProvider;
|
|
@@ -61,13 +64,11 @@ class AuthBroker {
|
|
|
61
64
|
if (typeof sessionStore.setConnectionConfig !== 'function') {
|
|
62
65
|
throw new Error('AuthBroker: sessionStore.setConnectionConfig must be a function');
|
|
63
66
|
}
|
|
64
|
-
// Check tokenProvider methods (
|
|
65
|
-
if (tokenProvider) {
|
|
66
|
-
|
|
67
|
-
throw new Error('AuthBroker: tokenProvider.getConnectionConfig must be a function');
|
|
68
|
-
}
|
|
69
|
-
// 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');
|
|
70
70
|
}
|
|
71
|
+
// validateToken is optional, so we don't check it
|
|
71
72
|
// Check serviceKeyStore methods (if provided)
|
|
72
73
|
if (serviceKeyStore) {
|
|
73
74
|
if (typeof serviceKeyStore.getServiceKey !== 'function') {
|
|
@@ -85,153 +86,107 @@ class AuthBroker {
|
|
|
85
86
|
this.tokenProvider = tokenProvider;
|
|
86
87
|
this.browser = browser || 'system';
|
|
87
88
|
this.logger = logger || noOpLogger;
|
|
88
|
-
this.allowClientCredentials = config.allowClientCredentials !== false;
|
|
89
89
|
// Log successful initialization
|
|
90
90
|
const hasServiceKeyStore = !!this.serviceKeyStore;
|
|
91
|
-
|
|
92
|
-
this.logger?.debug(`AuthBroker initialized: sessionStore(ok), serviceKeyStore(${hasServiceKeyStore ? 'ok' : 'none'}), tokenProvider(${hasTokenProvider ? 'ok' : 'none'}), allowClientCredentials(${this.allowClientCredentials})`);
|
|
93
|
-
}
|
|
94
|
-
/**
|
|
95
|
-
* Refresh token using refresh_token grant type (direct UAA HTTP request)
|
|
96
|
-
* @param refreshToken Refresh token
|
|
97
|
-
* @param authConfig UAA authorization configuration
|
|
98
|
-
* @returns Promise that resolves to new tokens
|
|
99
|
-
*/
|
|
100
|
-
async refreshTokenDirect(refreshToken, authConfig) {
|
|
101
|
-
if (!authConfig.uaaUrl || !authConfig.uaaClientId || !authConfig.uaaClientSecret) {
|
|
102
|
-
throw new Error('UAA credentials incomplete: uaaUrl, uaaClientId, and uaaClientSecret are required');
|
|
103
|
-
}
|
|
104
|
-
const tokenUrl = `${authConfig.uaaUrl}/oauth/token`;
|
|
105
|
-
const params = new URLSearchParams();
|
|
106
|
-
params.append('grant_type', 'refresh_token');
|
|
107
|
-
params.append('refresh_token', refreshToken);
|
|
108
|
-
const authString = Buffer.from(`${authConfig.uaaClientId}:${authConfig.uaaClientSecret}`).toString('base64');
|
|
109
|
-
try {
|
|
110
|
-
const response = await (0, axios_1.default)({
|
|
111
|
-
method: 'post',
|
|
112
|
-
url: tokenUrl,
|
|
113
|
-
headers: {
|
|
114
|
-
Authorization: `Basic ${authString}`,
|
|
115
|
-
'Content-Type': 'application/x-www-form-urlencoded',
|
|
116
|
-
},
|
|
117
|
-
data: params.toString(),
|
|
118
|
-
timeout: 30000,
|
|
119
|
-
});
|
|
120
|
-
if (response.data && response.data.access_token) {
|
|
121
|
-
return {
|
|
122
|
-
accessToken: response.data.access_token,
|
|
123
|
-
refreshToken: response.data.refresh_token || refreshToken,
|
|
124
|
-
expiresIn: response.data.expires_in,
|
|
125
|
-
};
|
|
126
|
-
}
|
|
127
|
-
else {
|
|
128
|
-
throw new Error('Response does not contain access_token');
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
catch (error) {
|
|
132
|
-
if (error.response) {
|
|
133
|
-
throw new Error(`Token refresh failed (${error.response.status}): ${JSON.stringify(error.response.data)}`);
|
|
134
|
-
}
|
|
135
|
-
else {
|
|
136
|
-
throw new Error(`Token refresh failed: ${error.message}`);
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
/**
|
|
141
|
-
* Get token using client_credentials grant type (direct UAA HTTP request)
|
|
142
|
-
* @param authConfig UAA authorization configuration
|
|
143
|
-
* @returns Promise that resolves to access token
|
|
144
|
-
*/
|
|
145
|
-
async getTokenWithClientCredentials(authConfig) {
|
|
146
|
-
if (!authConfig.uaaUrl || !authConfig.uaaClientId || !authConfig.uaaClientSecret) {
|
|
147
|
-
throw new Error('UAA credentials incomplete: uaaUrl, uaaClientId, and uaaClientSecret are required');
|
|
148
|
-
}
|
|
149
|
-
const tokenUrl = `${authConfig.uaaUrl}/oauth/token`;
|
|
150
|
-
const params = new URLSearchParams();
|
|
151
|
-
params.append('grant_type', 'client_credentials');
|
|
152
|
-
params.append('client_id', authConfig.uaaClientId);
|
|
153
|
-
params.append('client_secret', authConfig.uaaClientSecret);
|
|
154
|
-
try {
|
|
155
|
-
const response = await (0, axios_1.default)({
|
|
156
|
-
method: 'post',
|
|
157
|
-
url: tokenUrl,
|
|
158
|
-
headers: {
|
|
159
|
-
'Content-Type': 'application/x-www-form-urlencoded',
|
|
160
|
-
},
|
|
161
|
-
data: params.toString(),
|
|
162
|
-
timeout: 30000,
|
|
163
|
-
});
|
|
164
|
-
if (response.data && response.data.access_token) {
|
|
165
|
-
return {
|
|
166
|
-
accessToken: response.data.access_token,
|
|
167
|
-
refreshToken: response.data.refresh_token,
|
|
168
|
-
expiresIn: response.data.expires_in,
|
|
169
|
-
};
|
|
170
|
-
}
|
|
171
|
-
else {
|
|
172
|
-
throw new Error('Response does not contain access_token');
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
catch (error) {
|
|
176
|
-
if (error.response) {
|
|
177
|
-
throw new Error(`Client credentials authentication failed (${error.response.status}): ${JSON.stringify(error.response.data)}`);
|
|
178
|
-
}
|
|
179
|
-
else {
|
|
180
|
-
throw new Error(`Client credentials authentication failed: ${error.message}`);
|
|
181
|
-
}
|
|
182
|
-
}
|
|
91
|
+
this.logger?.debug(`AuthBroker initialized: sessionStore(ok), serviceKeyStore(${hasServiceKeyStore ? 'ok' : 'none'}), tokenProvider(ok)`);
|
|
183
92
|
}
|
|
184
93
|
/**
|
|
185
94
|
* Get authentication token for destination.
|
|
186
|
-
*
|
|
95
|
+
* Uses tokenProvider for all authentication operations (browser-based authorization).
|
|
187
96
|
*
|
|
188
97
|
* **Flow:**
|
|
189
98
|
* **Step 0: Initialize Session with Token (if needed)**
|
|
190
99
|
* - Check if session has `authorizationToken` AND UAA credentials
|
|
191
100
|
* - If both are empty AND serviceKeyStore is available:
|
|
192
|
-
* -
|
|
193
|
-
* -
|
|
194
|
-
*
|
|
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
|
|
195
109
|
*
|
|
196
|
-
* **Step
|
|
110
|
+
* **Step 2: Refresh Token Flow**
|
|
197
111
|
* - Check if refresh token exists in session
|
|
198
112
|
* - If refresh token exists:
|
|
199
|
-
* -
|
|
200
|
-
* -
|
|
201
|
-
* -
|
|
202
|
-
* - 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
|
|
203
117
|
*
|
|
204
|
-
* **Step
|
|
205
|
-
* -
|
|
206
|
-
* -
|
|
207
|
-
* -
|
|
208
|
-
* -
|
|
209
|
-
* - 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
|
|
210
123
|
*
|
|
211
124
|
* **Important Notes:**
|
|
212
|
-
* -
|
|
213
|
-
*
|
|
214
|
-
* -
|
|
215
|
-
* - Initializing session from service key via browser authentication (Step 0)
|
|
216
|
-
* - 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
|
|
217
128
|
*
|
|
218
129
|
* @param destination Destination name (e.g., "TRIAL")
|
|
219
130
|
* @returns Promise that resolves to JWT token string
|
|
220
|
-
* @throws Error if session initialization fails or
|
|
131
|
+
* @throws Error if session initialization fails or authentication failed
|
|
221
132
|
*/
|
|
222
133
|
async getToken(destination) {
|
|
223
134
|
this.logger?.debug(`Getting token for destination: ${destination}`);
|
|
224
135
|
// Step 0: Initialize Session with Token (if needed)
|
|
225
|
-
|
|
226
|
-
|
|
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
|
+
}
|
|
227
168
|
// Check if session has serviceUrl (required)
|
|
228
169
|
// If not in session, try to get it from serviceKeyStore
|
|
229
170
|
let serviceUrl = connConfig?.serviceUrl;
|
|
230
171
|
if (!serviceUrl && this.serviceKeyStore) {
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
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
|
+
}
|
|
235
190
|
}
|
|
236
191
|
}
|
|
237
192
|
if (!serviceUrl) {
|
|
@@ -258,32 +213,31 @@ class AuthBroker {
|
|
|
258
213
|
this.logger?.error(`Step 0: Service key for ${destination} missing UAA credentials`);
|
|
259
214
|
throw new Error(`Service key for destination "${destination}" does not contain UAA credentials`);
|
|
260
215
|
}
|
|
261
|
-
//
|
|
216
|
+
// Use tokenProvider for browser-based authentication
|
|
217
|
+
this.logger?.debug(`Step 0: Authenticating via provider (browser) for ${destination} using service key UAA credentials`);
|
|
262
218
|
let tokenResult;
|
|
263
219
|
try {
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
this.logger?.debug(`Step 0: Authenticating via provider for ${destination} using service key UAA credentials`);
|
|
279
|
-
tokenResult = await this.tokenProvider.getConnectionConfig(serviceKeyAuthConfig, {
|
|
280
|
-
browser: this.browser,
|
|
281
|
-
logger: this.logger,
|
|
282
|
-
});
|
|
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}`);
|
|
283
234
|
}
|
|
284
|
-
else {
|
|
285
|
-
|
|
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})`);
|
|
286
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}`);
|
|
287
241
|
}
|
|
288
242
|
const tokenLength = tokenResult.connectionConfig.authorizationToken?.length || 0;
|
|
289
243
|
this.logger?.info(`Step 0: Token initialized for ${destination}: token(${tokenLength} chars), hasRefreshToken(${!!tokenResult.refreshToken})`);
|
|
@@ -294,31 +248,61 @@ class AuthBroker {
|
|
|
294
248
|
serviceUrl: tokenResult.connectionConfig.serviceUrl || serviceKeyConnConfig?.serviceUrl || serviceUrl,
|
|
295
249
|
};
|
|
296
250
|
// Save token and UAA credentials to session
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
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
|
+
}
|
|
302
268
|
return tokenResult.connectionConfig.authorizationToken;
|
|
303
269
|
}
|
|
304
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
|
+
}
|
|
305
284
|
this.logger?.error(`Step 0: Failed to initialize session for ${destination}: ${error.message}`);
|
|
306
|
-
|
|
307
|
-
`Ensure serviceKeyStore contains valid service key with UAA credentials${this.tokenProvider ? ' or provide tokenProvider for alternative authentication' : ''}.`;
|
|
308
|
-
throw new Error(errorMessage);
|
|
285
|
+
throw new Error(`Cannot initialize session for destination "${destination}": ${error.message}`);
|
|
309
286
|
}
|
|
310
287
|
}
|
|
311
288
|
// If we have a token, validate it first
|
|
312
|
-
if (hasToken && connConfig
|
|
289
|
+
if (hasToken && connConfig?.authorizationToken) {
|
|
313
290
|
this.logger?.debug(`Step 0: Token found for ${destination}, validating`);
|
|
314
291
|
// Validate token if provider supports validation and we have service URL
|
|
315
292
|
if (this.tokenProvider?.validateToken && serviceUrl) {
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
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
|
|
320
305
|
}
|
|
321
|
-
this.logger?.debug(`Step 0: Token invalid for ${destination}, continuing to refresh`);
|
|
322
306
|
}
|
|
323
307
|
else {
|
|
324
308
|
// No service URL or provider doesn't support validation - just return token
|
|
@@ -326,167 +310,167 @@ class AuthBroker {
|
|
|
326
310
|
return connConfig.authorizationToken;
|
|
327
311
|
}
|
|
328
312
|
}
|
|
329
|
-
// Step
|
|
330
|
-
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)
|
|
331
342
|
const refreshToken = authConfig?.refreshToken;
|
|
332
343
|
if (refreshToken) {
|
|
333
344
|
try {
|
|
334
|
-
this.logger?.debug(`Step
|
|
335
|
-
|
|
336
|
-
const uaaCredentials = authConfig || (this.serviceKeyStore ? await this.serviceKeyStore.getAuthorizationConfig(destination) : null);
|
|
337
|
-
if (!uaaCredentials || !uaaCredentials.uaaUrl || !uaaCredentials.uaaClientId || !uaaCredentials.uaaClientSecret) {
|
|
338
|
-
throw new Error('UAA credentials not found in session and serviceKeyStore not available');
|
|
339
|
-
}
|
|
345
|
+
this.logger?.debug(`Step 2a: Trying refreshTokenFromSession for ${destination}`);
|
|
346
|
+
const authConfigWithRefresh = { ...uaaCredentials, refreshToken };
|
|
340
347
|
let tokenResult;
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
try {
|
|
344
|
-
this.logger?.debug(`Step 1: Trying direct UAA refresh for ${destination}`);
|
|
345
|
-
const uaaResult = await this.refreshTokenDirect(refreshToken, uaaCredentials);
|
|
346
|
-
tokenResult = {
|
|
347
|
-
connectionConfig: {
|
|
348
|
-
authorizationToken: uaaResult.accessToken,
|
|
349
|
-
},
|
|
350
|
-
refreshToken: uaaResult.refreshToken,
|
|
351
|
-
};
|
|
352
|
-
this.logger?.debug(`Step 1: Direct UAA refresh succeeded for ${destination}`);
|
|
353
|
-
}
|
|
354
|
-
catch (directError) {
|
|
355
|
-
this.logger?.debug(`Step 1: Direct UAA refresh failed for ${destination}: ${directError.message}, trying provider`);
|
|
356
|
-
// If direct UAA failed and we have provider, try provider
|
|
357
|
-
if (this.tokenProvider) {
|
|
358
|
-
const authConfigWithRefresh = { ...uaaCredentials, refreshToken };
|
|
359
|
-
tokenResult = await this.tokenProvider.getConnectionConfig(authConfigWithRefresh, {
|
|
360
|
-
browser: this.browser,
|
|
361
|
-
logger: this.logger,
|
|
362
|
-
});
|
|
363
|
-
}
|
|
364
|
-
else {
|
|
365
|
-
throw directError; // No provider, re-throw direct error
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
else if (this.tokenProvider) {
|
|
370
|
-
// No UAA credentials but have provider
|
|
371
|
-
const authConfigWithRefresh = { ...uaaCredentials, refreshToken };
|
|
372
|
-
tokenResult = await this.tokenProvider.getConnectionConfig(authConfigWithRefresh, {
|
|
348
|
+
try {
|
|
349
|
+
tokenResult = await this.tokenProvider.refreshTokenFromSession(authConfigWithRefresh, {
|
|
373
350
|
browser: this.browser,
|
|
374
351
|
logger: this.logger,
|
|
375
352
|
});
|
|
376
353
|
}
|
|
377
|
-
|
|
378
|
-
|
|
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
|
|
379
361
|
}
|
|
380
362
|
const tokenLength = tokenResult.connectionConfig.authorizationToken?.length || 0;
|
|
381
|
-
this.logger?.info(`Step
|
|
382
|
-
// Get serviceUrl from session or service key
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
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;
|
|
386
376
|
const connectionConfigWithServiceUrl = {
|
|
387
377
|
...tokenResult.connectionConfig,
|
|
388
378
|
serviceUrl: finalServiceUrl,
|
|
389
379
|
};
|
|
390
380
|
// Update session with new token
|
|
391
|
-
|
|
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
|
+
}
|
|
392
388
|
if (tokenResult.refreshToken) {
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
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
|
+
}
|
|
397
399
|
}
|
|
398
400
|
return tokenResult.connectionConfig.authorizationToken;
|
|
399
401
|
}
|
|
400
402
|
catch (error) {
|
|
401
|
-
this.logger?.debug(`Step
|
|
402
|
-
// Continue to
|
|
403
|
+
this.logger?.debug(`Step 2a: refreshTokenFromSession failed for ${destination}: ${error.message}, trying refreshTokenFromServiceKey`);
|
|
404
|
+
// Continue to try service key refresh
|
|
403
405
|
}
|
|
404
406
|
}
|
|
405
407
|
else {
|
|
406
|
-
this.logger?.debug(`Step
|
|
407
|
-
}
|
|
408
|
-
// Step 2: UAA Credentials Flow
|
|
409
|
-
this.logger?.debug(`Step 2: Checking UAA credentials for ${destination}`);
|
|
410
|
-
// Get UAA credentials from session or service key
|
|
411
|
-
const uaaCredentials = authConfig || (this.serviceKeyStore ? await this.serviceKeyStore.getAuthorizationConfig(destination) : null);
|
|
412
|
-
if (!uaaCredentials || !uaaCredentials.uaaUrl || !uaaCredentials.uaaClientId || !uaaCredentials.uaaClientSecret) {
|
|
413
|
-
const errorMessage = `Step 2: UAA credentials not found for ${destination}. ` +
|
|
414
|
-
`Session has no UAA credentials${this.serviceKeyStore ? ' and serviceKeyStore has no UAA credentials' : ' and serviceKeyStore is not available'}.`;
|
|
415
|
-
this.logger?.error(errorMessage);
|
|
416
|
-
throw new Error(errorMessage);
|
|
408
|
+
this.logger?.debug(`Step 2a: No refresh token in session for ${destination}, skipping to service key refresh`);
|
|
417
409
|
}
|
|
410
|
+
// Try refresh from service key (browser authentication)
|
|
418
411
|
try {
|
|
419
|
-
this.logger?.debug(`Step
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
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) {
|
|
423
422
|
try {
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
},
|
|
430
|
-
refreshToken: uaaResult.refreshToken,
|
|
431
|
-
};
|
|
432
|
-
this.logger?.debug(`Step 2: Direct UAA client_credentials succeeded for ${destination}`);
|
|
433
|
-
}
|
|
434
|
-
catch (directError) {
|
|
435
|
-
this.logger?.debug(`Step 2: Direct UAA client_credentials failed for ${destination}: ${directError.message}, trying provider`);
|
|
436
|
-
// If direct UAA failed and we have provider, try provider
|
|
437
|
-
if (this.tokenProvider) {
|
|
438
|
-
const authConfigWithoutRefresh = { ...uaaCredentials, refreshToken: undefined };
|
|
439
|
-
tokenResult = await this.tokenProvider.getConnectionConfig(authConfigWithoutRefresh, {
|
|
440
|
-
browser: this.browser,
|
|
441
|
-
logger: this.logger,
|
|
442
|
-
});
|
|
443
|
-
}
|
|
444
|
-
else {
|
|
445
|
-
throw directError; // No provider, re-throw direct error
|
|
446
|
-
}
|
|
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}`);
|
|
447
428
|
}
|
|
448
429
|
}
|
|
449
|
-
|
|
450
|
-
// No client_credentials (disabled) or missing UAA creds -> use provider
|
|
451
|
-
const authConfigWithoutRefresh = { ...uaaCredentials, refreshToken: undefined };
|
|
452
|
-
tokenResult = await this.tokenProvider.getConnectionConfig(authConfigWithoutRefresh, {
|
|
453
|
-
browser: this.browser,
|
|
454
|
-
logger: this.logger,
|
|
455
|
-
});
|
|
456
|
-
}
|
|
457
|
-
else {
|
|
458
|
-
throw new Error(this.allowClientCredentials
|
|
459
|
-
? 'UAA credentials incomplete and tokenProvider not available'
|
|
460
|
-
: 'Client credentials flow disabled and no tokenProvider available for interactive login');
|
|
461
|
-
}
|
|
462
|
-
const tokenLength = tokenResult.connectionConfig.authorizationToken?.length || 0;
|
|
463
|
-
this.logger?.info(`Step 2: Token obtained via UAA for ${destination}: token(${tokenLength} chars), hasRefreshToken(${!!tokenResult.refreshToken})`);
|
|
464
|
-
// Get serviceUrl from session or service key (use the one we already have from the beginning of the method)
|
|
465
|
-
const finalServiceUrl = tokenResult.connectionConfig.serviceUrl ||
|
|
466
|
-
serviceUrl ||
|
|
467
|
-
(this.serviceKeyStore ? (await this.serviceKeyStore.getConnectionConfig(destination))?.serviceUrl : undefined);
|
|
430
|
+
const finalServiceUrl = tokenResult.connectionConfig.serviceUrl || serviceUrl || serviceKeyServiceUrl;
|
|
468
431
|
const connectionConfigWithServiceUrl = {
|
|
469
432
|
...tokenResult.connectionConfig,
|
|
470
433
|
serviceUrl: finalServiceUrl,
|
|
471
434
|
};
|
|
472
435
|
// Update session with new token
|
|
473
|
-
|
|
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
|
+
}
|
|
474
443
|
if (tokenResult.refreshToken) {
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
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
|
+
}
|
|
479
454
|
}
|
|
480
455
|
return tokenResult.connectionConfig.authorizationToken;
|
|
481
456
|
}
|
|
482
457
|
catch (error) {
|
|
483
|
-
this.logger?.error(`Step
|
|
484
|
-
//
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
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}`);
|
|
490
474
|
}
|
|
491
475
|
}
|
|
492
476
|
/**
|
|
@@ -497,81 +481,8 @@ class AuthBroker {
|
|
|
497
481
|
*/
|
|
498
482
|
async refreshToken(destination) {
|
|
499
483
|
this.logger?.debug(`Force refreshing token for destination: ${destination}`);
|
|
500
|
-
//
|
|
501
|
-
|
|
502
|
-
const serviceKeyAuthConfig = this.serviceKeyStore
|
|
503
|
-
? await this.serviceKeyStore.getAuthorizationConfig(destination)
|
|
504
|
-
: null;
|
|
505
|
-
const authConfig = sessionAuthConfig || serviceKeyAuthConfig;
|
|
506
|
-
if (!authConfig) {
|
|
507
|
-
this.logger?.error(`Authorization config not found for ${destination}`);
|
|
508
|
-
throw new Error(`Authorization config not found for destination "${destination}". ` +
|
|
509
|
-
`Session has no UAA credentials${this.serviceKeyStore ? ' and serviceKeyStore has no UAA credentials' : ' and serviceKeyStore is not available'}.`);
|
|
510
|
-
}
|
|
511
|
-
// Get refresh token from session or service key
|
|
512
|
-
const refreshToken = sessionAuthConfig?.refreshToken || authConfig.refreshToken;
|
|
513
|
-
this.logger?.debug(`Refresh token check for ${destination}: hasRefreshToken(${!!refreshToken})`);
|
|
514
|
-
let tokenResult;
|
|
515
|
-
// Try direct UAA request if UAA credentials are available
|
|
516
|
-
if (authConfig.uaaUrl && authConfig.uaaClientId && authConfig.uaaClientSecret && refreshToken) {
|
|
517
|
-
try {
|
|
518
|
-
this.logger?.debug(`Trying direct UAA refresh for ${destination}`);
|
|
519
|
-
const uaaResult = await this.refreshTokenDirect(refreshToken, authConfig);
|
|
520
|
-
tokenResult = {
|
|
521
|
-
connectionConfig: {
|
|
522
|
-
authorizationToken: uaaResult.accessToken,
|
|
523
|
-
},
|
|
524
|
-
refreshToken: uaaResult.refreshToken,
|
|
525
|
-
};
|
|
526
|
-
}
|
|
527
|
-
catch (directError) {
|
|
528
|
-
this.logger?.debug(`Direct UAA refresh failed for ${destination}: ${directError.message}, trying provider`);
|
|
529
|
-
// If direct UAA failed and we have provider, try provider
|
|
530
|
-
if (this.tokenProvider) {
|
|
531
|
-
const authConfigWithRefresh = { ...authConfig, refreshToken };
|
|
532
|
-
tokenResult = await this.tokenProvider.getConnectionConfig(authConfigWithRefresh, {
|
|
533
|
-
browser: this.browser,
|
|
534
|
-
logger: this.logger,
|
|
535
|
-
});
|
|
536
|
-
}
|
|
537
|
-
else {
|
|
538
|
-
throw directError; // No provider, re-throw direct error
|
|
539
|
-
}
|
|
540
|
-
}
|
|
541
|
-
}
|
|
542
|
-
else if (this.tokenProvider) {
|
|
543
|
-
// No UAA credentials or refresh token, but have provider
|
|
544
|
-
const authConfigWithRefresh = { ...authConfig, refreshToken };
|
|
545
|
-
tokenResult = await this.tokenProvider.getConnectionConfig(authConfigWithRefresh, {
|
|
546
|
-
browser: this.browser,
|
|
547
|
-
logger: this.logger,
|
|
548
|
-
});
|
|
549
|
-
}
|
|
550
|
-
else {
|
|
551
|
-
throw new Error('UAA credentials incomplete and tokenProvider not available');
|
|
552
|
-
}
|
|
553
|
-
const tokenLength = tokenResult.connectionConfig.authorizationToken?.length || 0;
|
|
554
|
-
this.logger?.info(`Token refreshed for ${destination}: token(${tokenLength} chars), hasRefreshToken(${!!tokenResult.refreshToken})`);
|
|
555
|
-
// Get serviceUrl from session or service key
|
|
556
|
-
const connConfig = await this.sessionStore.getConnectionConfig(destination);
|
|
557
|
-
const serviceKeyConnConfig = this.serviceKeyStore
|
|
558
|
-
? await this.serviceKeyStore.getConnectionConfig(destination)
|
|
559
|
-
: null;
|
|
560
|
-
const connectionConfigWithServiceUrl = {
|
|
561
|
-
...tokenResult.connectionConfig,
|
|
562
|
-
serviceUrl: tokenResult.connectionConfig.serviceUrl ||
|
|
563
|
-
connConfig?.serviceUrl ||
|
|
564
|
-
serviceKeyConnConfig?.serviceUrl,
|
|
565
|
-
};
|
|
566
|
-
// Update or create session with new token (stores handle creation if session doesn't exist)
|
|
567
|
-
await this.sessionStore.setConnectionConfig(destination, connectionConfigWithServiceUrl);
|
|
568
|
-
if (tokenResult.refreshToken) {
|
|
569
|
-
await this.sessionStore.setAuthorizationConfig(destination, {
|
|
570
|
-
...authConfig,
|
|
571
|
-
refreshToken: tokenResult.refreshToken,
|
|
572
|
-
});
|
|
573
|
-
}
|
|
574
|
-
return tokenResult.connectionConfig.authorizationToken;
|
|
484
|
+
// Call getToken to trigger full refresh flow
|
|
485
|
+
return this.getToken(destination);
|
|
575
486
|
}
|
|
576
487
|
/**
|
|
577
488
|
* Get authorization configuration for destination
|
|
@@ -582,7 +493,13 @@ class AuthBroker {
|
|
|
582
493
|
this.logger?.debug(`Getting authorization config for ${destination}`);
|
|
583
494
|
// Try session store first (has tokens)
|
|
584
495
|
this.logger?.debug(`Checking session store for authorization config: ${destination}`);
|
|
585
|
-
|
|
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
|
+
}
|
|
586
503
|
if (sessionAuthConfig) {
|
|
587
504
|
this.logger?.debug(`Authorization config from session for ${destination}: hasUaaUrl(${!!sessionAuthConfig.uaaUrl}), hasRefreshToken(${!!sessionAuthConfig.refreshToken})`);
|
|
588
505
|
return sessionAuthConfig;
|
|
@@ -590,7 +507,22 @@ class AuthBroker {
|
|
|
590
507
|
// Fall back to service key store (has UAA credentials) if available
|
|
591
508
|
if (this.serviceKeyStore) {
|
|
592
509
|
this.logger?.debug(`Checking service key store for authorization config: ${destination}`);
|
|
593
|
-
|
|
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
|
+
}
|
|
594
526
|
if (serviceKeyAuthConfig) {
|
|
595
527
|
this.logger?.debug(`Authorization config from service key for ${destination}: hasUaaUrl(${!!serviceKeyAuthConfig.uaaUrl})`);
|
|
596
528
|
return serviceKeyAuthConfig;
|
|
@@ -610,14 +542,35 @@ class AuthBroker {
|
|
|
610
542
|
async getConnectionConfig(destination) {
|
|
611
543
|
this.logger?.debug(`Getting connection config for ${destination}`);
|
|
612
544
|
// Try session store first (has tokens and URLs)
|
|
613
|
-
|
|
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
|
+
}
|
|
614
552
|
if (sessionConnConfig) {
|
|
615
553
|
this.logger?.debug(`Connection config from session for ${destination}: token(${sessionConnConfig.authorizationToken?.length || 0} chars), serviceUrl(${sessionConnConfig.serviceUrl ? 'yes' : 'no'})`);
|
|
616
554
|
return sessionConnConfig;
|
|
617
555
|
}
|
|
618
556
|
// Fall back to service key store (has URLs but no tokens) if available
|
|
619
557
|
if (this.serviceKeyStore) {
|
|
620
|
-
|
|
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
|
+
}
|
|
621
574
|
if (serviceKeyConnConfig) {
|
|
622
575
|
this.logger?.debug(`Connection config from service key for ${destination}: serviceUrl(${serviceKeyConnConfig.serviceUrl ? 'yes' : 'no'}), token(none)`);
|
|
623
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",
|