@mcp-abap-adt/auth-broker 0.1.11 → 0.2.1
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 +158 -0
- package/README.md +189 -52
- package/dist/AuthBroker.d.ts +57 -35
- package/dist/AuthBroker.d.ts.map +1 -1
- package/dist/AuthBroker.js +502 -142
- package/dist/__tests__/helpers/configHelpers.d.ts.map +1 -1
- package/dist/__tests__/helpers/configHelpers.js +24 -2
- package/dist/__tests__/helpers/testLogger.d.ts +6 -0
- package/dist/__tests__/helpers/testLogger.d.ts.map +1 -0
- package/dist/__tests__/helpers/testLogger.js +81 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/package.json +6 -5
package/dist/AuthBroker.js
CHANGED
|
@@ -2,8 +2,12 @@
|
|
|
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
|
+
};
|
|
5
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
9
|
exports.AuthBroker = void 0;
|
|
10
|
+
const axios_1 = __importDefault(require("axios"));
|
|
7
11
|
/**
|
|
8
12
|
* No-op logger implementation for default fallback when logger is not provided
|
|
9
13
|
*/
|
|
@@ -13,9 +17,6 @@ const noOpLogger = {
|
|
|
13
17
|
warn: () => { },
|
|
14
18
|
debug: () => { },
|
|
15
19
|
};
|
|
16
|
-
/**
|
|
17
|
-
* AuthBroker manages JWT authentication tokens for destinations
|
|
18
|
-
*/
|
|
19
20
|
class AuthBroker {
|
|
20
21
|
browser;
|
|
21
22
|
logger;
|
|
@@ -24,211 +25,542 @@ class AuthBroker {
|
|
|
24
25
|
tokenProvider;
|
|
25
26
|
/**
|
|
26
27
|
* Create a new AuthBroker instance
|
|
27
|
-
* @param
|
|
28
|
-
* -
|
|
29
|
-
* -
|
|
30
|
-
* - tokenProvider: Token provider implementing ITokenProvider interface
|
|
28
|
+
* @param config Configuration object with stores and token provider
|
|
29
|
+
* - sessionStore: Store for session data (required)
|
|
30
|
+
* - serviceKeyStore: Store for service keys (optional)
|
|
31
|
+
* - tokenProvider: Token provider implementing ITokenProvider interface (optional). If not provided, direct UAA HTTP requests will be used when UAA credentials are available
|
|
31
32
|
* @param browser Optional browser name for authentication (chrome, edge, firefox, system, none).
|
|
32
33
|
* Default: 'system' (system default browser).
|
|
33
34
|
* Use 'none' to print URL instead of opening browser.
|
|
34
35
|
* @param logger Optional logger instance implementing ILogger interface. If not provided, uses no-op logger.
|
|
35
36
|
*/
|
|
36
|
-
constructor(
|
|
37
|
-
// Validate that
|
|
38
|
-
if (!
|
|
39
|
-
throw new Error('AuthBroker:
|
|
40
|
-
}
|
|
41
|
-
if (!stores.serviceKeyStore) {
|
|
42
|
-
throw new Error('AuthBroker: serviceKeyStore is required');
|
|
37
|
+
constructor(config, browser, logger) {
|
|
38
|
+
// Validate that config is provided
|
|
39
|
+
if (!config) {
|
|
40
|
+
throw new Error('AuthBroker: config parameter is required');
|
|
43
41
|
}
|
|
44
|
-
|
|
42
|
+
// Validate required sessionStore
|
|
43
|
+
if (!config.sessionStore) {
|
|
45
44
|
throw new Error('AuthBroker: sessionStore is required');
|
|
46
45
|
}
|
|
47
|
-
|
|
48
|
-
|
|
46
|
+
// Validate that stores and provider are correctly instantiated (have required methods)
|
|
47
|
+
const sessionStore = config.sessionStore;
|
|
48
|
+
const tokenProvider = config.tokenProvider;
|
|
49
|
+
const serviceKeyStore = config.serviceKeyStore;
|
|
50
|
+
// Check sessionStore methods
|
|
51
|
+
if (typeof sessionStore.getAuthorizationConfig !== 'function') {
|
|
52
|
+
throw new Error('AuthBroker: sessionStore.getAuthorizationConfig must be a function');
|
|
53
|
+
}
|
|
54
|
+
if (typeof sessionStore.getConnectionConfig !== 'function') {
|
|
55
|
+
throw new Error('AuthBroker: sessionStore.getConnectionConfig must be a function');
|
|
56
|
+
}
|
|
57
|
+
if (typeof sessionStore.setAuthorizationConfig !== 'function') {
|
|
58
|
+
throw new Error('AuthBroker: sessionStore.setAuthorizationConfig must be a function');
|
|
59
|
+
}
|
|
60
|
+
if (typeof sessionStore.setConnectionConfig !== 'function') {
|
|
61
|
+
throw new Error('AuthBroker: sessionStore.setConnectionConfig must be a function');
|
|
62
|
+
}
|
|
63
|
+
// Check tokenProvider methods (if provided)
|
|
64
|
+
if (tokenProvider) {
|
|
65
|
+
if (typeof tokenProvider.getConnectionConfig !== 'function') {
|
|
66
|
+
throw new Error('AuthBroker: tokenProvider.getConnectionConfig must be a function');
|
|
67
|
+
}
|
|
68
|
+
// validateToken is optional, so we don't check it
|
|
69
|
+
}
|
|
70
|
+
// Check serviceKeyStore methods (if provided)
|
|
71
|
+
if (serviceKeyStore) {
|
|
72
|
+
if (typeof serviceKeyStore.getServiceKey !== 'function') {
|
|
73
|
+
throw new Error('AuthBroker: serviceKeyStore.getServiceKey must be a function');
|
|
74
|
+
}
|
|
75
|
+
if (typeof serviceKeyStore.getAuthorizationConfig !== 'function') {
|
|
76
|
+
throw new Error('AuthBroker: serviceKeyStore.getAuthorizationConfig must be a function');
|
|
77
|
+
}
|
|
78
|
+
if (typeof serviceKeyStore.getConnectionConfig !== 'function') {
|
|
79
|
+
throw new Error('AuthBroker: serviceKeyStore.getConnectionConfig must be a function');
|
|
80
|
+
}
|
|
49
81
|
}
|
|
50
|
-
this.serviceKeyStore =
|
|
51
|
-
this.sessionStore =
|
|
52
|
-
this.tokenProvider =
|
|
82
|
+
this.serviceKeyStore = serviceKeyStore;
|
|
83
|
+
this.sessionStore = sessionStore;
|
|
84
|
+
this.tokenProvider = tokenProvider;
|
|
53
85
|
this.browser = browser || 'system';
|
|
54
86
|
this.logger = logger || noOpLogger;
|
|
87
|
+
// Log successful initialization
|
|
88
|
+
const hasServiceKeyStore = !!this.serviceKeyStore;
|
|
89
|
+
const hasTokenProvider = !!this.tokenProvider;
|
|
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
|
+
}
|
|
55
181
|
}
|
|
56
182
|
/**
|
|
57
183
|
* Get authentication token for destination.
|
|
58
|
-
*
|
|
184
|
+
* Implements a three-step flow: Step 0 (initialize), Step 1 (refresh), Step 2 (UAA).
|
|
59
185
|
*
|
|
60
|
-
* **
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
186
|
+
* **Flow:**
|
|
187
|
+
* **Step 0: Initialize Session with Token (if needed)**
|
|
188
|
+
* - Check if session has `authorizationToken` AND UAA credentials
|
|
189
|
+
* - If both are empty AND serviceKeyStore is available:
|
|
190
|
+
* - Try direct UAA request from service key (if UAA credentials available)
|
|
191
|
+
* - If failed and tokenProvider available → use provider
|
|
192
|
+
* - If session has token OR UAA credentials → proceed to Step 1
|
|
64
193
|
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
194
|
+
* **Step 1: Refresh Token Flow**
|
|
195
|
+
* - Check if refresh token exists in session
|
|
196
|
+
* - If refresh token exists:
|
|
197
|
+
* - Try direct UAA refresh (if UAA credentials in session)
|
|
198
|
+
* - If failed and tokenProvider available → use provider
|
|
199
|
+
* - If successful → return new token
|
|
200
|
+
* - Otherwise → proceed to Step 2
|
|
67
201
|
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
202
|
+
* **Step 2: UAA Credentials Flow**
|
|
203
|
+
* - Check if UAA credentials exist in session or service key
|
|
204
|
+
* - Try direct UAA client_credentials request (if UAA credentials available)
|
|
205
|
+
* - If failed and tokenProvider available → use provider
|
|
206
|
+
* - If successful → return new token
|
|
207
|
+
* - If all failed → return error
|
|
71
208
|
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
* - If failed, continue to next step
|
|
79
|
-
*
|
|
80
|
-
* 6. **Throw error**: If all authentication methods failed, throw comprehensive error with details
|
|
81
|
-
*
|
|
82
|
-
* **Note**: Token validation is performed only when checking existing session (step 1).
|
|
83
|
-
* Tokens obtained through refresh/UAA/browser authentication are not validated before being saved.
|
|
209
|
+
* **Important Notes:**
|
|
210
|
+
* - If sessionStore contains valid UAA credentials, neither serviceKeyStore nor tokenProvider are required.
|
|
211
|
+
* Direct UAA HTTP requests will be used automatically.
|
|
212
|
+
* - tokenProvider is only needed when:
|
|
213
|
+
* - Initializing session from service key via browser authentication (Step 0)
|
|
214
|
+
* - Direct UAA requests fail and fallback to provider is needed
|
|
84
215
|
*
|
|
85
216
|
* @param destination Destination name (e.g., "TRIAL")
|
|
86
217
|
* @returns Promise that resolves to JWT token string
|
|
87
|
-
* @throws Error if
|
|
218
|
+
* @throws Error if session initialization fails or all authentication methods failed
|
|
88
219
|
*/
|
|
89
220
|
async getToken(destination) {
|
|
90
|
-
|
|
221
|
+
this.logger?.debug(`Getting token for destination: ${destination}`);
|
|
222
|
+
// Step 0: Initialize Session with Token (if needed)
|
|
91
223
|
const connConfig = await this.sessionStore.getConnectionConfig(destination);
|
|
92
|
-
|
|
224
|
+
const authConfig = await this.sessionStore.getAuthorizationConfig(destination);
|
|
225
|
+
// Check if session has serviceUrl (required)
|
|
226
|
+
// If not in session, try to get it from serviceKeyStore
|
|
227
|
+
let serviceUrl = connConfig?.serviceUrl;
|
|
228
|
+
if (!serviceUrl && this.serviceKeyStore) {
|
|
229
|
+
const serviceKeyConnConfig = await this.serviceKeyStore.getConnectionConfig(destination);
|
|
230
|
+
serviceUrl = serviceKeyConnConfig?.serviceUrl;
|
|
231
|
+
if (serviceUrl) {
|
|
232
|
+
this.logger?.debug(`serviceUrl not in session for ${destination}, found in serviceKeyStore`);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
if (!serviceUrl) {
|
|
236
|
+
this.logger?.error(`Session for destination "${destination}" is missing required field 'serviceUrl'. SessionStore must contain initial session with serviceUrl${this.serviceKeyStore ? ' or serviceKeyStore must contain serviceUrl' : ''}.`);
|
|
237
|
+
throw new Error(`Session for destination "${destination}" is missing required field 'serviceUrl'. ` +
|
|
238
|
+
`SessionStore must contain initial session with serviceUrl${this.serviceKeyStore ? ' or serviceKeyStore must contain serviceUrl' : ''}.`);
|
|
239
|
+
}
|
|
240
|
+
// Check if we have token or UAA credentials
|
|
241
|
+
const hasToken = !!connConfig?.authorizationToken;
|
|
242
|
+
const hasUaaCredentials = !!(authConfig?.uaaUrl && authConfig?.uaaClientId && authConfig?.uaaClientSecret);
|
|
243
|
+
this.logger?.debug(`Step 0: Session check for ${destination}: hasToken(${hasToken}), hasUaaCredentials(${hasUaaCredentials}), serviceUrl(${serviceUrl ? 'yes' : 'no'})`);
|
|
244
|
+
// If token is empty AND UAA fields are empty, try to initialize from service key
|
|
245
|
+
if (!hasToken && !hasUaaCredentials) {
|
|
246
|
+
this.logger?.debug(`Step 0: Token and UAA credentials are empty for ${destination}, attempting initialization from service key`);
|
|
247
|
+
if (!this.serviceKeyStore) {
|
|
248
|
+
this.logger?.error(`Step 0: Cannot initialize session for ${destination}: authorizationToken is empty, UAA credentials are empty, and serviceKeyStore is not available`);
|
|
249
|
+
throw new Error(`Cannot initialize session for destination "${destination}": authorizationToken is empty, UAA credentials are empty, and serviceKeyStore is not available. ` +
|
|
250
|
+
`Provide serviceKeyStore to initialize from service key.`);
|
|
251
|
+
}
|
|
252
|
+
try {
|
|
253
|
+
// Get UAA credentials from service key
|
|
254
|
+
const serviceKeyAuthConfig = await this.serviceKeyStore.getAuthorizationConfig(destination);
|
|
255
|
+
if (!serviceKeyAuthConfig || !serviceKeyAuthConfig.uaaUrl || !serviceKeyAuthConfig.uaaClientId || !serviceKeyAuthConfig.uaaClientSecret) {
|
|
256
|
+
this.logger?.error(`Step 0: Service key for ${destination} missing UAA credentials`);
|
|
257
|
+
throw new Error(`Service key for destination "${destination}" does not contain UAA credentials`);
|
|
258
|
+
}
|
|
259
|
+
// Try direct UAA request first if UAA credentials are available in service key
|
|
260
|
+
let tokenResult;
|
|
261
|
+
try {
|
|
262
|
+
// Use direct UAA HTTP request (preferred when UAA credentials are available)
|
|
263
|
+
this.logger?.debug(`Step 0: Authenticating via direct UAA request for ${destination} using service key UAA credentials`);
|
|
264
|
+
const uaaResult = await this.getTokenWithClientCredentials(serviceKeyAuthConfig);
|
|
265
|
+
tokenResult = {
|
|
266
|
+
connectionConfig: {
|
|
267
|
+
authorizationToken: uaaResult.accessToken,
|
|
268
|
+
},
|
|
269
|
+
refreshToken: uaaResult.refreshToken,
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
catch (directError) {
|
|
273
|
+
this.logger?.debug(`Step 0: Direct UAA request failed for ${destination}: ${directError.message}, trying provider`);
|
|
274
|
+
// If direct UAA failed and we have provider, try provider
|
|
275
|
+
if (this.tokenProvider) {
|
|
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
|
+
});
|
|
281
|
+
}
|
|
282
|
+
else {
|
|
283
|
+
throw directError; // No provider, re-throw direct error
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
const tokenLength = tokenResult.connectionConfig.authorizationToken?.length || 0;
|
|
287
|
+
this.logger?.info(`Step 0: Token initialized for ${destination}: token(${tokenLength} chars), hasRefreshToken(${!!tokenResult.refreshToken})`);
|
|
288
|
+
// Get serviceUrl from service key store if not in connectionConfig
|
|
289
|
+
const serviceKeyConnConfig = await this.serviceKeyStore.getConnectionConfig(destination);
|
|
290
|
+
const connectionConfigWithServiceUrl = {
|
|
291
|
+
...tokenResult.connectionConfig,
|
|
292
|
+
serviceUrl: tokenResult.connectionConfig.serviceUrl || serviceKeyConnConfig?.serviceUrl || serviceUrl,
|
|
293
|
+
};
|
|
294
|
+
// Save token and UAA credentials to session
|
|
295
|
+
await this.sessionStore.setConnectionConfig(destination, connectionConfigWithServiceUrl);
|
|
296
|
+
await this.sessionStore.setAuthorizationConfig(destination, {
|
|
297
|
+
...serviceKeyAuthConfig,
|
|
298
|
+
refreshToken: tokenResult.refreshToken || serviceKeyAuthConfig.refreshToken,
|
|
299
|
+
});
|
|
300
|
+
return tokenResult.connectionConfig.authorizationToken;
|
|
301
|
+
}
|
|
302
|
+
catch (error) {
|
|
303
|
+
this.logger?.error(`Step 0: Failed to initialize session for ${destination}: ${error.message}`);
|
|
304
|
+
const errorMessage = `Cannot initialize session for destination "${destination}": ${error.message}. ` +
|
|
305
|
+
`Ensure serviceKeyStore contains valid service key with UAA credentials${this.tokenProvider ? ' or provide tokenProvider for alternative authentication' : ''}.`;
|
|
306
|
+
throw new Error(errorMessage);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
// If we have a token, validate it first
|
|
310
|
+
if (hasToken && connConfig.authorizationToken) {
|
|
311
|
+
this.logger?.debug(`Step 0: Token found for ${destination}, validating`);
|
|
93
312
|
// Validate token if provider supports validation and we have service URL
|
|
94
|
-
if (this.tokenProvider
|
|
95
|
-
const isValid = await this.tokenProvider.validateToken(connConfig.authorizationToken,
|
|
313
|
+
if (this.tokenProvider?.validateToken && serviceUrl) {
|
|
314
|
+
const isValid = await this.tokenProvider.validateToken(connConfig.authorizationToken, serviceUrl);
|
|
96
315
|
if (isValid) {
|
|
316
|
+
this.logger?.info(`Step 0: Token valid for ${destination}: token(${connConfig.authorizationToken.length} chars)`);
|
|
97
317
|
return connConfig.authorizationToken;
|
|
98
318
|
}
|
|
319
|
+
this.logger?.debug(`Step 0: Token invalid for ${destination}, continuing to refresh`);
|
|
99
320
|
}
|
|
100
321
|
else {
|
|
101
322
|
// No service URL or provider doesn't support validation - just return token
|
|
323
|
+
this.logger?.info(`Step 0: Token found for ${destination} (no validation): token(${connConfig.authorizationToken.length} chars)`);
|
|
102
324
|
return connConfig.authorizationToken;
|
|
103
325
|
}
|
|
104
326
|
}
|
|
105
|
-
// Step
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
// No service key and no valid token
|
|
109
|
-
throw new Error(`No authentication found for destination "${destination}". ` +
|
|
110
|
-
`No session data and no service key found.`);
|
|
111
|
-
}
|
|
112
|
-
// Get authorization config from service key
|
|
113
|
-
const authConfig = await this.serviceKeyStore.getAuthorizationConfig(destination);
|
|
114
|
-
if (!authConfig) {
|
|
115
|
-
throw new Error(`Service key for destination "${destination}" does not contain UAA credentials`);
|
|
116
|
-
}
|
|
117
|
-
// Get refresh token from session (if exists)
|
|
118
|
-
const sessionAuthConfig = await this.sessionStore.getAuthorizationConfig(destination);
|
|
119
|
-
const refreshToken = sessionAuthConfig?.refreshToken || authConfig.refreshToken;
|
|
120
|
-
let tokenResult;
|
|
121
|
-
let lastError = null;
|
|
122
|
-
// Step 3: Try to refresh using refresh token (if available) via tokenProvider
|
|
327
|
+
// Step 1: Refresh Token Flow
|
|
328
|
+
this.logger?.debug(`Step 1: Checking refresh token for ${destination}`);
|
|
329
|
+
const refreshToken = authConfig?.refreshToken;
|
|
123
330
|
if (refreshToken) {
|
|
124
331
|
try {
|
|
125
|
-
this.logger
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
}
|
|
131
|
-
|
|
332
|
+
this.logger?.debug(`Step 1: Trying refresh token flow for ${destination}`);
|
|
333
|
+
// Get UAA credentials from session or service key
|
|
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
|
+
}
|
|
338
|
+
let tokenResult;
|
|
339
|
+
// Try direct UAA request if UAA credentials are available
|
|
340
|
+
if (uaaCredentials.uaaUrl && uaaCredentials.uaaClientId && uaaCredentials.uaaClientSecret) {
|
|
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, {
|
|
371
|
+
browser: this.browser,
|
|
372
|
+
logger: this.logger,
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
else {
|
|
376
|
+
throw new Error('UAA credentials incomplete and tokenProvider not available');
|
|
377
|
+
}
|
|
378
|
+
const tokenLength = tokenResult.connectionConfig.authorizationToken?.length || 0;
|
|
379
|
+
this.logger?.info(`Step 1: Token refreshed for ${destination}: token(${tokenLength} chars), hasRefreshToken(${!!tokenResult.refreshToken})`);
|
|
380
|
+
// Get serviceUrl from session or service key (use the one we already have from the beginning of the method)
|
|
381
|
+
const finalServiceUrl = tokenResult.connectionConfig.serviceUrl ||
|
|
382
|
+
serviceUrl ||
|
|
383
|
+
(this.serviceKeyStore ? (await this.serviceKeyStore.getConnectionConfig(destination))?.serviceUrl : undefined);
|
|
384
|
+
const connectionConfigWithServiceUrl = {
|
|
385
|
+
...tokenResult.connectionConfig,
|
|
386
|
+
serviceUrl: finalServiceUrl,
|
|
387
|
+
};
|
|
132
388
|
// Update session with new token
|
|
133
|
-
await this.sessionStore.setConnectionConfig(destination,
|
|
389
|
+
await this.sessionStore.setConnectionConfig(destination, connectionConfigWithServiceUrl);
|
|
134
390
|
if (tokenResult.refreshToken) {
|
|
135
391
|
await this.sessionStore.setAuthorizationConfig(destination, {
|
|
136
|
-
...
|
|
392
|
+
...uaaCredentials,
|
|
137
393
|
refreshToken: tokenResult.refreshToken,
|
|
138
394
|
});
|
|
139
395
|
}
|
|
140
396
|
return tokenResult.connectionConfig.authorizationToken;
|
|
141
397
|
}
|
|
142
398
|
catch (error) {
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
// Continue to next step
|
|
399
|
+
this.logger?.debug(`Step 1: Refresh token flow failed for ${destination}: ${error.message}, trying Step 2`);
|
|
400
|
+
// Continue to Step 2
|
|
146
401
|
}
|
|
147
402
|
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
try {
|
|
151
|
-
this.logger.debug(`Attempting to get token using UAA (client_credentials) for destination "${destination}"...`);
|
|
152
|
-
const authConfigWithoutRefresh = { ...authConfig, refreshToken: undefined };
|
|
153
|
-
tokenResult = await this.tokenProvider.getConnectionConfig(authConfigWithoutRefresh, {
|
|
154
|
-
browser: this.browser,
|
|
155
|
-
logger: this.logger,
|
|
156
|
-
});
|
|
157
|
-
this.logger.debug(`Token obtained successfully using UAA for destination "${destination}"`);
|
|
158
|
-
// Update session with new token
|
|
159
|
-
await this.sessionStore.setConnectionConfig(destination, tokenResult.connectionConfig);
|
|
160
|
-
if (tokenResult.refreshToken) {
|
|
161
|
-
await this.sessionStore.setAuthorizationConfig(destination, {
|
|
162
|
-
...authConfig,
|
|
163
|
-
refreshToken: tokenResult.refreshToken,
|
|
164
|
-
});
|
|
165
|
-
}
|
|
166
|
-
return tokenResult.connectionConfig.authorizationToken;
|
|
403
|
+
else {
|
|
404
|
+
this.logger?.debug(`Step 1: No refresh token found for ${destination}, proceeding to Step 2`);
|
|
167
405
|
}
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
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);
|
|
172
415
|
}
|
|
173
|
-
// Step 5: Try browser authentication via tokenProvider (should be last resort)
|
|
174
|
-
// TokenProvider should use browser auth if refresh token and client_credentials don't work
|
|
175
416
|
try {
|
|
176
|
-
this.logger
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
417
|
+
this.logger?.debug(`Step 2: Trying UAA (client_credentials) flow for ${destination}`);
|
|
418
|
+
let tokenResult;
|
|
419
|
+
// Try direct UAA request first if UAA credentials are available
|
|
420
|
+
if (uaaCredentials.uaaUrl && uaaCredentials.uaaClientId && uaaCredentials.uaaClientSecret) {
|
|
421
|
+
try {
|
|
422
|
+
this.logger?.debug(`Step 2: Trying direct UAA client_credentials for ${destination}`);
|
|
423
|
+
const uaaResult = await this.getTokenWithClientCredentials(uaaCredentials);
|
|
424
|
+
tokenResult = {
|
|
425
|
+
connectionConfig: {
|
|
426
|
+
authorizationToken: uaaResult.accessToken,
|
|
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
|
+
}
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
else if (this.tokenProvider) {
|
|
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);
|
|
464
|
+
const connectionConfigWithServiceUrl = {
|
|
465
|
+
...tokenResult.connectionConfig,
|
|
466
|
+
serviceUrl: finalServiceUrl,
|
|
467
|
+
};
|
|
183
468
|
// Update session with new token
|
|
184
|
-
await this.sessionStore.setConnectionConfig(destination,
|
|
469
|
+
await this.sessionStore.setConnectionConfig(destination, connectionConfigWithServiceUrl);
|
|
185
470
|
if (tokenResult.refreshToken) {
|
|
186
471
|
await this.sessionStore.setAuthorizationConfig(destination, {
|
|
187
|
-
...
|
|
472
|
+
...uaaCredentials,
|
|
188
473
|
refreshToken: tokenResult.refreshToken,
|
|
189
474
|
});
|
|
190
475
|
}
|
|
191
476
|
return tokenResult.connectionConfig.authorizationToken;
|
|
192
477
|
}
|
|
193
478
|
catch (error) {
|
|
194
|
-
|
|
195
|
-
//
|
|
479
|
+
this.logger?.error(`Step 2: UAA flow failed for ${destination}: ${error.message}`);
|
|
480
|
+
// If we have serviceKeyStore, we already tried it, so throw error
|
|
196
481
|
const errorMessage = `All authentication methods failed for destination "${destination}". ` +
|
|
197
|
-
`
|
|
198
|
-
`UAA: ${
|
|
199
|
-
|
|
200
|
-
this.logger.error(errorMessage);
|
|
482
|
+
`Step 1 (refresh token): ${refreshToken ? 'failed' : 'not available'}. ` +
|
|
483
|
+
`Step 2 (UAA credentials): failed (${error.message}).`;
|
|
484
|
+
this.logger?.error(errorMessage);
|
|
201
485
|
throw new Error(errorMessage);
|
|
202
486
|
}
|
|
203
487
|
}
|
|
204
488
|
/**
|
|
205
|
-
* Force refresh token for destination
|
|
206
|
-
*
|
|
489
|
+
* Force refresh token for destination.
|
|
490
|
+
* Uses refresh token from session if available, otherwise uses UAA credentials from session or service key.
|
|
207
491
|
* @param destination Destination name (e.g., "TRIAL")
|
|
208
492
|
* @returns Promise that resolves to new JWT token string
|
|
209
493
|
*/
|
|
210
494
|
async refreshToken(destination) {
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
const authConfig =
|
|
495
|
+
this.logger?.debug(`Force refreshing token for destination: ${destination}`);
|
|
496
|
+
// Get authorization config from session or service key
|
|
497
|
+
const sessionAuthConfig = await this.sessionStore.getAuthorizationConfig(destination);
|
|
498
|
+
const serviceKeyAuthConfig = this.serviceKeyStore
|
|
499
|
+
? await this.serviceKeyStore.getAuthorizationConfig(destination)
|
|
500
|
+
: null;
|
|
501
|
+
const authConfig = sessionAuthConfig || serviceKeyAuthConfig;
|
|
218
502
|
if (!authConfig) {
|
|
219
|
-
|
|
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'}.`);
|
|
220
506
|
}
|
|
221
|
-
// Get refresh token from session
|
|
222
|
-
const
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
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);
|
|
232
564
|
if (tokenResult.refreshToken) {
|
|
233
565
|
await this.sessionStore.setAuthorizationConfig(destination, {
|
|
234
566
|
...authConfig,
|
|
@@ -243,13 +575,28 @@ class AuthBroker {
|
|
|
243
575
|
* @returns Promise that resolves to IAuthorizationConfig or null if not found
|
|
244
576
|
*/
|
|
245
577
|
async getAuthorizationConfig(destination) {
|
|
578
|
+
this.logger?.debug(`Getting authorization config for ${destination}`);
|
|
246
579
|
// Try session store first (has tokens)
|
|
580
|
+
this.logger?.debug(`Checking session store for authorization config: ${destination}`);
|
|
247
581
|
const sessionAuthConfig = await this.sessionStore.getAuthorizationConfig(destination);
|
|
248
582
|
if (sessionAuthConfig) {
|
|
583
|
+
this.logger?.debug(`Authorization config from session for ${destination}: hasUaaUrl(${!!sessionAuthConfig.uaaUrl}), hasRefreshToken(${!!sessionAuthConfig.refreshToken})`);
|
|
249
584
|
return sessionAuthConfig;
|
|
250
585
|
}
|
|
251
|
-
// Fall back to service key store (has UAA credentials)
|
|
252
|
-
|
|
586
|
+
// Fall back to service key store (has UAA credentials) if available
|
|
587
|
+
if (this.serviceKeyStore) {
|
|
588
|
+
this.logger?.debug(`Checking service key store for authorization config: ${destination}`);
|
|
589
|
+
const serviceKeyAuthConfig = await this.serviceKeyStore.getAuthorizationConfig(destination);
|
|
590
|
+
if (serviceKeyAuthConfig) {
|
|
591
|
+
this.logger?.debug(`Authorization config from service key for ${destination}: hasUaaUrl(${!!serviceKeyAuthConfig.uaaUrl})`);
|
|
592
|
+
return serviceKeyAuthConfig;
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
else {
|
|
596
|
+
this.logger?.debug(`Service key store not available for ${destination}`);
|
|
597
|
+
}
|
|
598
|
+
this.logger?.debug(`No authorization config found for ${destination}`);
|
|
599
|
+
return null;
|
|
253
600
|
}
|
|
254
601
|
/**
|
|
255
602
|
* Get connection configuration for destination
|
|
@@ -257,13 +604,26 @@ class AuthBroker {
|
|
|
257
604
|
* @returns Promise that resolves to IConnectionConfig or null if not found
|
|
258
605
|
*/
|
|
259
606
|
async getConnectionConfig(destination) {
|
|
607
|
+
this.logger?.debug(`Getting connection config for ${destination}`);
|
|
260
608
|
// Try session store first (has tokens and URLs)
|
|
261
609
|
const sessionConnConfig = await this.sessionStore.getConnectionConfig(destination);
|
|
262
610
|
if (sessionConnConfig) {
|
|
611
|
+
this.logger?.debug(`Connection config from session for ${destination}: token(${sessionConnConfig.authorizationToken?.length || 0} chars), serviceUrl(${sessionConnConfig.serviceUrl ? 'yes' : 'no'})`);
|
|
263
612
|
return sessionConnConfig;
|
|
264
613
|
}
|
|
265
|
-
// Fall back to service key store (has URLs but no tokens)
|
|
266
|
-
|
|
614
|
+
// Fall back to service key store (has URLs but no tokens) if available
|
|
615
|
+
if (this.serviceKeyStore) {
|
|
616
|
+
const serviceKeyConnConfig = await this.serviceKeyStore.getConnectionConfig(destination);
|
|
617
|
+
if (serviceKeyConnConfig) {
|
|
618
|
+
this.logger?.debug(`Connection config from service key for ${destination}: serviceUrl(${serviceKeyConnConfig.serviceUrl ? 'yes' : 'no'}), token(none)`);
|
|
619
|
+
return serviceKeyConnConfig;
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
else {
|
|
623
|
+
this.logger?.debug(`Service key store not available for ${destination}`);
|
|
624
|
+
}
|
|
625
|
+
this.logger?.debug(`No connection config found for ${destination}`);
|
|
626
|
+
return null;
|
|
267
627
|
}
|
|
268
628
|
}
|
|
269
629
|
exports.AuthBroker = AuthBroker;
|