@mcp-abap-adt/auth-broker 0.1.5 → 0.1.7

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.
Files changed (73) hide show
  1. package/CHANGELOG.md +244 -0
  2. package/README.md +181 -16
  3. package/bin/generate-env-from-service-key.ts +128 -0
  4. package/dist/AuthBroker.d.ts +47 -31
  5. package/dist/AuthBroker.d.ts.map +1 -1
  6. package/dist/AuthBroker.js +182 -134
  7. package/dist/__tests__/helpers/configHelpers.d.ts +49 -0
  8. package/dist/__tests__/helpers/configHelpers.d.ts.map +1 -0
  9. package/dist/__tests__/helpers/configHelpers.js +169 -0
  10. package/dist/index.d.ts +4 -4
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +5 -8
  13. package/dist/providers/ITokenProvider.d.ts +49 -0
  14. package/dist/providers/ITokenProvider.d.ts.map +1 -0
  15. package/dist/providers/ITokenProvider.js +10 -0
  16. package/dist/providers/index.d.ts +8 -0
  17. package/dist/providers/index.d.ts.map +1 -0
  18. package/dist/providers/index.js +8 -0
  19. package/dist/stores/index.d.ts +5 -5
  20. package/dist/stores/index.d.ts.map +1 -1
  21. package/dist/stores/index.js +4 -8
  22. package/dist/stores/interfaces.d.ts +88 -22
  23. package/dist/stores/interfaces.d.ts.map +1 -1
  24. package/dist/stores/interfaces.js +1 -2
  25. package/dist/types.d.ts +7 -31
  26. package/dist/types.d.ts.map +1 -1
  27. package/dist/types.js +2 -0
  28. package/package.json +13 -6
  29. package/dist/__tests__/testHelpers.d.ts +0 -44
  30. package/dist/__tests__/testHelpers.d.ts.map +0 -1
  31. package/dist/__tests__/testHelpers.js +0 -136
  32. package/dist/browserAuth.d.ts +0 -17
  33. package/dist/browserAuth.d.ts.map +0 -1
  34. package/dist/browserAuth.js +0 -305
  35. package/dist/cache.d.ts +0 -20
  36. package/dist/cache.d.ts.map +0 -1
  37. package/dist/cache.js +0 -46
  38. package/dist/envLoader.d.ts +0 -12
  39. package/dist/envLoader.d.ts.map +0 -1
  40. package/dist/envLoader.js +0 -90
  41. package/dist/getToken.d.ts +0 -14
  42. package/dist/getToken.d.ts.map +0 -1
  43. package/dist/getToken.js +0 -62
  44. package/dist/logger.d.ts +0 -40
  45. package/dist/logger.d.ts.map +0 -1
  46. package/dist/logger.js +0 -186
  47. package/dist/pathResolver.d.ts +0 -21
  48. package/dist/pathResolver.d.ts.map +0 -1
  49. package/dist/pathResolver.js +0 -105
  50. package/dist/refreshToken.d.ts +0 -14
  51. package/dist/refreshToken.d.ts.map +0 -1
  52. package/dist/refreshToken.js +0 -71
  53. package/dist/serviceKeyLoader.d.ts +0 -12
  54. package/dist/serviceKeyLoader.d.ts.map +0 -1
  55. package/dist/serviceKeyLoader.js +0 -72
  56. package/dist/stores/FileServiceKeyStore.d.ts +0 -38
  57. package/dist/stores/FileServiceKeyStore.d.ts.map +0 -1
  58. package/dist/stores/FileServiceKeyStore.js +0 -47
  59. package/dist/stores/FileSessionStore.d.ts +0 -50
  60. package/dist/stores/FileSessionStore.d.ts.map +0 -1
  61. package/dist/stores/FileSessionStore.js +0 -116
  62. package/dist/stores/SafeSessionStore.d.ts +0 -35
  63. package/dist/stores/SafeSessionStore.d.ts.map +0 -1
  64. package/dist/stores/SafeSessionStore.js +0 -42
  65. package/dist/tokenRefresher.d.ts +0 -17
  66. package/dist/tokenRefresher.d.ts.map +0 -1
  67. package/dist/tokenRefresher.js +0 -53
  68. package/dist/tokenStorage.d.ts +0 -15
  69. package/dist/tokenStorage.d.ts.map +0 -1
  70. package/dist/tokenStorage.js +0 -107
  71. package/dist/tokenValidator.d.ts +0 -11
  72. package/dist/tokenValidator.d.ts.map +0 -1
  73. package/dist/tokenValidator.js +0 -108
@@ -1,8 +1,9 @@
1
1
  /**
2
2
  * Main AuthBroker class for managing JWT tokens based on destinations
3
3
  */
4
- import { Logger } from './logger';
5
- import { IServiceKeyStore, ISessionStore } from './stores/interfaces';
4
+ import { Logger } from '@mcp-abap-adt/logger';
5
+ import { IServiceKeyStore, ISessionStore, IAuthorizationConfig, IConnectionConfig } from './stores/interfaces';
6
+ import { ITokenProvider } from './providers';
6
7
  /**
7
8
  * AuthBroker manages JWT authentication tokens for destinations
8
9
  */
@@ -11,26 +12,55 @@ export declare class AuthBroker {
11
12
  private logger;
12
13
  private serviceKeyStore;
13
14
  private sessionStore;
15
+ private tokenProvider;
14
16
  /**
15
17
  * Create a new AuthBroker instance
16
- * @param stores Object with custom stores. If not provided, creates default file-based stores.
17
- * - serviceKeyStore: Store for service keys (default: FileServiceKeyStore)
18
- * - sessionStore: Store for session data (default: FileSessionStore)
18
+ * @param stores Object with stores and token provider
19
+ * - serviceKeyStore: Store for service keys
20
+ * - sessionStore: Store for session data
21
+ * - tokenProvider: Token provider implementing ITokenProvider interface
19
22
  * @param browser Optional browser name for authentication (chrome, edge, firefox, system, none).
20
23
  * Default: 'system' (system default browser).
21
24
  * Use 'none' to print URL instead of opening browser.
22
25
  * @param logger Optional logger instance. If not provided, uses default logger.
23
26
  */
24
- constructor(stores?: {
25
- serviceKeyStore?: IServiceKeyStore;
26
- sessionStore?: ISessionStore;
27
+ constructor(stores: {
28
+ serviceKeyStore: IServiceKeyStore;
29
+ sessionStore: ISessionStore;
30
+ tokenProvider: ITokenProvider;
27
31
  }, browser?: string, logger?: Logger);
28
32
  /**
29
33
  * Get authentication token for destination.
30
- * Tries to load from session store, validates it, and refreshes if needed.
34
+ * Tries to load from session store, validates it, and refreshes if needed using a fallback chain.
35
+ *
36
+ * **Fallback Chain:**
37
+ * 1. **Check session**: Load token from session store and validate it
38
+ * - If token is valid, return it immediately
39
+ * - If token is invalid or missing, continue to next step
40
+ *
41
+ * 2. **Check service key**: Verify that service key exists
42
+ * - If no service key found, throw error
43
+ *
44
+ * 3. **Try refresh token**: If refresh token is available in session, attempt to refresh using it (via tokenProvider)
45
+ * - If successful, save new token to session and return it
46
+ * - If failed, continue to next step
47
+ *
48
+ * 4. **Try UAA (client_credentials)**: Attempt to get token using UAA credentials (via tokenProvider)
49
+ * - If UAA parameters are available and authentication succeeds, save token to session and return it
50
+ * - If failed or parameters missing, continue to next step
51
+ *
52
+ * 5. **Try browser authentication**: Attempt browser-based OAuth2 flow using service key (via tokenProvider)
53
+ * - If successful, save token and refresh token to session and return it
54
+ * - If failed, continue to next step
55
+ *
56
+ * 6. **Throw error**: If all authentication methods failed, throw comprehensive error with details
57
+ *
58
+ * **Note**: Token validation is performed only when checking existing session (step 1).
59
+ * Tokens obtained through refresh/UAA/browser authentication are not validated before being saved.
60
+ *
31
61
  * @param destination Destination name (e.g., "TRIAL")
32
62
  * @returns Promise that resolves to JWT token string
33
- * @throws Error if neither session data nor service key found
63
+ * @throws Error if neither session data nor service key found, or if all authentication methods failed
34
64
  */
35
65
  getToken(destination: string): Promise<string>;
36
66
  /**
@@ -41,30 +71,16 @@ export declare class AuthBroker {
41
71
  */
42
72
  refreshToken(destination: string): Promise<string>;
43
73
  /**
44
- * Internal refresh token implementation
45
- * @private
46
- */
47
- private refreshTokenInternal;
48
- /**
49
- * Get SAP URL for destination.
50
- * Tries to load from session store first, then from service key store.
74
+ * Get authorization configuration for destination
51
75
  * @param destination Destination name (e.g., "TRIAL")
52
- * @returns Promise that resolves to SAP URL string, or undefined if not found
53
- */
54
- getSapUrl(destination: string): Promise<string | undefined>;
55
- /**
56
- * Clear cached token for specific destination
57
- * @param destination Destination name
76
+ * @returns Promise that resolves to IAuthorizationConfig or null if not found
58
77
  */
59
- clearCache(destination: string): void;
78
+ getAuthorizationConfig(destination: string): Promise<IAuthorizationConfig | null>;
60
79
  /**
61
- * Clear all cached tokens
62
- */
63
- clearAllCache(): void;
64
- /**
65
- * Get search paths for error messages (from file stores if available)
66
- * @private
80
+ * Get connection configuration for destination
81
+ * @param destination Destination name (e.g., "TRIAL")
82
+ * @returns Promise that resolves to IConnectionConfig or null if not found
67
83
  */
68
- private getSearchPathsForError;
84
+ getConnectionConfig(destination: string): Promise<IConnectionConfig | null>;
69
85
  }
70
86
  //# sourceMappingURL=AuthBroker.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"AuthBroker.d.ts","sourceRoot":"","sources":["../src/AuthBroker.ts"],"names":[],"mappings":"AAAA;;GAEG;AAOH,OAAO,EAAE,MAAM,EAAiB,MAAM,UAAU,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAGtE;;GAEG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,OAAO,CAAqB;IACpC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,eAAe,CAAmB;IAC1C,OAAO,CAAC,YAAY,CAAgB;IAEpC;;;;;;;;;OASG;gBAED,MAAM,CAAC,EAAE;QAAE,eAAe,CAAC,EAAE,gBAAgB,CAAC;QAAC,YAAY,CAAC,EAAE,aAAa,CAAA;KAAE,EAC7E,OAAO,CAAC,EAAE,MAAM,EAChB,MAAM,CAAC,EAAE,MAAM;IAQjB;;;;;;OAMG;IACG,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAiDpD;;;;;OAKG;IACG,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAoBxD;;;OAGG;YACW,oBAAoB;IAuDlC;;;;;OAKG;IACG,SAAS,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAiBjE;;;OAGG;IACH,UAAU,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI;IAIrC;;OAEG;IACH,aAAa,IAAI,IAAI;IAIrB;;;OAGG;IACH,OAAO,CAAC,sBAAsB;CAW/B"}
1
+ {"version":3,"file":"AuthBroker.d.ts","sourceRoot":"","sources":["../src/AuthBroker.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,MAAM,EAAiB,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAC/G,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE7C;;GAEG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,OAAO,CAAqB;IACpC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,eAAe,CAAmB;IAC1C,OAAO,CAAC,YAAY,CAAgB;IACpC,OAAO,CAAC,aAAa,CAAiB;IAEtC;;;;;;;;;;OAUG;gBAED,MAAM,EAAE;QAAE,eAAe,EAAE,gBAAgB,CAAC;QAAC,YAAY,EAAE,aAAa,CAAC;QAAC,aAAa,EAAE,cAAc,CAAA;KAAE,EACzG,OAAO,CAAC,EAAE,MAAM,EAChB,MAAM,CAAC,EAAE,MAAM;IASjB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAgCG;IACG,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAmIpD;;;;;OAKG;IACG,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAuCxD;;;;OAIG;IACG,sBAAsB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC;IAWvF;;;;OAIG;IACG,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC;CAWlF"}
@@ -4,12 +4,7 @@
4
4
  */
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.AuthBroker = void 0;
7
- const tokenValidator_1 = require("./tokenValidator");
8
- const tokenRefresher_1 = require("./tokenRefresher");
9
- const browserAuth_1 = require("./browserAuth");
10
- const cache_1 = require("./cache");
11
- const logger_1 = require("./logger");
12
- const stores_1 = require("./stores");
7
+ const logger_1 = require("@mcp-abap-adt/logger");
13
8
  /**
14
9
  * AuthBroker manages JWT authentication tokens for destinations
15
10
  */
@@ -18,71 +13,172 @@ class AuthBroker {
18
13
  logger;
19
14
  serviceKeyStore;
20
15
  sessionStore;
16
+ tokenProvider;
21
17
  /**
22
18
  * Create a new AuthBroker instance
23
- * @param stores Object with custom stores. If not provided, creates default file-based stores.
24
- * - serviceKeyStore: Store for service keys (default: FileServiceKeyStore)
25
- * - sessionStore: Store for session data (default: FileSessionStore)
19
+ * @param stores Object with stores and token provider
20
+ * - serviceKeyStore: Store for service keys
21
+ * - sessionStore: Store for session data
22
+ * - tokenProvider: Token provider implementing ITokenProvider interface
26
23
  * @param browser Optional browser name for authentication (chrome, edge, firefox, system, none).
27
24
  * Default: 'system' (system default browser).
28
25
  * Use 'none' to print URL instead of opening browser.
29
26
  * @param logger Optional logger instance. If not provided, uses default logger.
30
27
  */
31
28
  constructor(stores, browser, logger) {
32
- this.serviceKeyStore = stores?.serviceKeyStore || new stores_1.FileServiceKeyStore();
33
- this.sessionStore = stores?.sessionStore || new stores_1.FileSessionStore();
29
+ this.serviceKeyStore = stores.serviceKeyStore;
30
+ this.sessionStore = stores.sessionStore;
31
+ this.tokenProvider = stores.tokenProvider;
34
32
  this.browser = browser || 'system';
35
33
  this.logger = logger || logger_1.defaultLogger;
36
34
  }
37
35
  /**
38
36
  * Get authentication token for destination.
39
- * Tries to load from session store, validates it, and refreshes if needed.
37
+ * Tries to load from session store, validates it, and refreshes if needed using a fallback chain.
38
+ *
39
+ * **Fallback Chain:**
40
+ * 1. **Check session**: Load token from session store and validate it
41
+ * - If token is valid, return it immediately
42
+ * - If token is invalid or missing, continue to next step
43
+ *
44
+ * 2. **Check service key**: Verify that service key exists
45
+ * - If no service key found, throw error
46
+ *
47
+ * 3. **Try refresh token**: If refresh token is available in session, attempt to refresh using it (via tokenProvider)
48
+ * - If successful, save new token to session and return it
49
+ * - If failed, continue to next step
50
+ *
51
+ * 4. **Try UAA (client_credentials)**: Attempt to get token using UAA credentials (via tokenProvider)
52
+ * - If UAA parameters are available and authentication succeeds, save token to session and return it
53
+ * - If failed or parameters missing, continue to next step
54
+ *
55
+ * 5. **Try browser authentication**: Attempt browser-based OAuth2 flow using service key (via tokenProvider)
56
+ * - If successful, save token and refresh token to session and return it
57
+ * - If failed, continue to next step
58
+ *
59
+ * 6. **Throw error**: If all authentication methods failed, throw comprehensive error with details
60
+ *
61
+ * **Note**: Token validation is performed only when checking existing session (step 1).
62
+ * Tokens obtained through refresh/UAA/browser authentication are not validated before being saved.
63
+ *
40
64
  * @param destination Destination name (e.g., "TRIAL")
41
65
  * @returns Promise that resolves to JWT token string
42
- * @throws Error if neither session data nor service key found
66
+ * @throws Error if neither session data nor service key found, or if all authentication methods failed
43
67
  */
44
68
  async getToken(destination) {
45
- // Check cache first
46
- const cachedToken = (0, cache_1.getCachedToken)(destination);
47
- if (cachedToken) {
48
- // Validate cached token
49
- const envConfig = await this.sessionStore.loadSession(destination);
50
- if (envConfig) {
51
- const isValid = await (0, tokenValidator_1.validateToken)(cachedToken, envConfig.sapUrl);
69
+ // Step 1: Check if session exists and token is valid
70
+ const connConfig = await this.sessionStore.getConnectionConfig(destination);
71
+ if (connConfig?.authorizationToken) {
72
+ // Validate token if provider supports validation and we have service URL
73
+ if (this.tokenProvider.validateToken && connConfig.serviceUrl) {
74
+ const isValid = await this.tokenProvider.validateToken(connConfig.authorizationToken, connConfig.serviceUrl);
52
75
  if (isValid) {
53
- return cachedToken;
76
+ return connConfig.authorizationToken;
54
77
  }
55
- // Token expired, remove from cache
56
78
  }
57
- }
58
- // Load from session store
59
- const envConfig = await this.sessionStore.loadSession(destination);
60
- if (envConfig && envConfig.jwtToken) {
61
- // Validate token
62
- const isValid = await (0, tokenValidator_1.validateToken)(envConfig.jwtToken, envConfig.sapUrl);
63
- if (isValid) {
64
- (0, cache_1.setCachedToken)(destination, envConfig.jwtToken);
65
- return envConfig.jwtToken;
79
+ else {
80
+ // No service URL or provider doesn't support validation - just return token
81
+ return connConfig.authorizationToken;
66
82
  }
67
83
  }
68
- // Token not found or expired, check if we have service key for browser auth
84
+ // Step 2: No valid session, check if we have service key
69
85
  const serviceKey = await this.serviceKeyStore.getServiceKey(destination);
70
86
  if (!serviceKey) {
71
- // No service key and no valid token - throw error with helpful message
72
- const searchPaths = this.getSearchPathsForError();
73
- const searchedPaths = searchPaths.length > 0
74
- ? `\nSearched in:\n${searchPaths.map(p => ` - ${p}`).join('\n')}`
75
- : '';
87
+ // No service key and no valid token
76
88
  throw new Error(`No authentication found for destination "${destination}". ` +
77
- `Neither ${destination}.env file nor ${destination}.json service key found.\n` +
78
- `Please create one of:\n` +
79
- ` - ${destination}.env (with SAP_JWT_TOKEN)\n` +
80
- ` - ${destination}.json (service key)${searchedPaths}`);
89
+ `No session data and no service key found.`);
90
+ }
91
+ // Get authorization config from service key
92
+ const authConfig = await this.serviceKeyStore.getAuthorizationConfig(destination);
93
+ if (!authConfig) {
94
+ throw new Error(`Service key for destination "${destination}" does not contain UAA credentials`);
95
+ }
96
+ // Get refresh token from session (if exists)
97
+ const sessionAuthConfig = await this.sessionStore.getAuthorizationConfig(destination);
98
+ const refreshToken = sessionAuthConfig?.refreshToken || authConfig.refreshToken;
99
+ let tokenResult;
100
+ let lastError = null;
101
+ // Step 3: Try to refresh using refresh token (if available) via tokenProvider
102
+ if (refreshToken) {
103
+ try {
104
+ this.logger.debug(`Attempting to refresh token using refresh token for destination "${destination}"...`);
105
+ const authConfigWithRefresh = { ...authConfig, refreshToken };
106
+ tokenResult = await this.tokenProvider.getConnectionConfig(authConfigWithRefresh, {
107
+ browser: this.browser,
108
+ logger: this.logger,
109
+ });
110
+ this.logger.debug(`Token refreshed successfully using refresh token for destination "${destination}"`);
111
+ // Update session with new token
112
+ await this.sessionStore.setConnectionConfig(destination, tokenResult.connectionConfig);
113
+ if (tokenResult.refreshToken) {
114
+ await this.sessionStore.setAuthorizationConfig(destination, {
115
+ ...authConfig,
116
+ refreshToken: tokenResult.refreshToken,
117
+ });
118
+ }
119
+ return tokenResult.connectionConfig.authorizationToken;
120
+ }
121
+ catch (error) {
122
+ lastError = error;
123
+ this.logger.debug(`Token refresh failed for destination "${destination}": ${error.message}. Trying without refresh token...`);
124
+ // Continue to next step
125
+ }
126
+ }
127
+ // Step 4: Try UAA (client_credentials) via tokenProvider (without refresh token)
128
+ // TokenProvider should try client_credentials if refresh token is not provided
129
+ try {
130
+ this.logger.debug(`Attempting to get token using UAA (client_credentials) for destination "${destination}"...`);
131
+ const authConfigWithoutRefresh = { ...authConfig, refreshToken: undefined };
132
+ tokenResult = await this.tokenProvider.getConnectionConfig(authConfigWithoutRefresh, {
133
+ browser: this.browser,
134
+ logger: this.logger,
135
+ });
136
+ this.logger.debug(`Token obtained successfully using UAA for destination "${destination}"`);
137
+ // Update session with new token
138
+ await this.sessionStore.setConnectionConfig(destination, tokenResult.connectionConfig);
139
+ if (tokenResult.refreshToken) {
140
+ await this.sessionStore.setAuthorizationConfig(destination, {
141
+ ...authConfig,
142
+ refreshToken: tokenResult.refreshToken,
143
+ });
144
+ }
145
+ return tokenResult.connectionConfig.authorizationToken;
146
+ }
147
+ catch (error) {
148
+ lastError = error;
149
+ this.logger.debug(`UAA authentication failed for destination "${destination}": ${error.message}. Trying browser authentication...`);
150
+ // Continue to next step
151
+ }
152
+ // Step 5: Try browser authentication via tokenProvider (should be last resort)
153
+ // TokenProvider should use browser auth if refresh token and client_credentials don't work
154
+ try {
155
+ this.logger.debug(`Starting browser authentication flow for destination "${destination}"...`);
156
+ const authConfigForBrowser = { ...authConfig, refreshToken: undefined };
157
+ tokenResult = await this.tokenProvider.getConnectionConfig(authConfigForBrowser, {
158
+ browser: this.browser,
159
+ logger: this.logger,
160
+ });
161
+ this.logger.debug(`Token obtained successfully using browser authentication for destination "${destination}"`);
162
+ // Update session with new token
163
+ await this.sessionStore.setConnectionConfig(destination, tokenResult.connectionConfig);
164
+ if (tokenResult.refreshToken) {
165
+ await this.sessionStore.setAuthorizationConfig(destination, {
166
+ ...authConfig,
167
+ refreshToken: tokenResult.refreshToken,
168
+ });
169
+ }
170
+ return tokenResult.connectionConfig.authorizationToken;
171
+ }
172
+ catch (error) {
173
+ lastError = error;
174
+ // Step 6: All methods failed - throw error
175
+ const errorMessage = `All authentication methods failed for destination "${destination}". ` +
176
+ `Refresh token: ${refreshToken ? 'failed' : 'not available'}. ` +
177
+ `UAA: ${authConfig.uaaUrl && authConfig.uaaClientId && authConfig.uaaClientSecret ? 'failed' : 'parameters missing'}. ` +
178
+ `Browser authentication: failed (${error.message})`;
179
+ this.logger.error(errorMessage);
180
+ throw new Error(errorMessage);
81
181
  }
82
- // Try to refresh (will use browser auth if no refresh token)
83
- const newToken = await this.refreshTokenInternal(destination, serviceKey, envConfig);
84
- (0, cache_1.setCachedToken)(destination, newToken);
85
- return newToken;
86
182
  }
87
183
  /**
88
184
  * Force refresh token for destination using service key.
@@ -94,107 +190,59 @@ class AuthBroker {
94
190
  // Load service key
95
191
  const serviceKey = await this.serviceKeyStore.getServiceKey(destination);
96
192
  if (!serviceKey) {
97
- const searchPaths = this.getSearchPathsForError();
98
- const searchedPaths = searchPaths.length > 0
99
- ? `\nSearched in:\n${searchPaths.map(p => ` - ${p}`).join('\n')}`
100
- : '';
101
- throw new Error(`Service key file not found for destination "${destination}".\n` +
102
- `Please create file: ${destination}.json${searchedPaths}`);
193
+ throw new Error(`Service key not found for destination "${destination}".`);
103
194
  }
104
- // Load existing session (for refresh token)
105
- const envConfig = await this.sessionStore.loadSession(destination);
106
- return this.refreshTokenInternal(destination, serviceKey, envConfig);
107
- }
108
- /**
109
- * Internal refresh token implementation
110
- * @private
111
- */
112
- async refreshTokenInternal(destination, serviceKey, envConfig) {
113
- // Extract UAA configuration
114
- const { url: uaaUrl, clientid: clientId, clientsecret: clientSecret } = serviceKey.uaa;
115
- if (!uaaUrl || !clientId || !clientSecret) {
116
- throw new Error(`Invalid service key for destination "${destination}". ` +
117
- `Missing required UAA fields: url, clientid, clientsecret`);
195
+ // Get authorization config from service key
196
+ const authConfig = await this.serviceKeyStore.getAuthorizationConfig(destination);
197
+ if (!authConfig) {
198
+ throw new Error(`Service key for destination "${destination}" does not contain UAA credentials`);
118
199
  }
119
- // Validate SAP URL early (before starting browser auth or refresh)
120
- const sapUrl = serviceKey.url || serviceKey.abap?.url || serviceKey.sap_url;
121
- if (!sapUrl) {
122
- throw new Error(`Service key for destination "${destination}" does not contain SAP URL. ` +
123
- `Expected field: url, abap.url, or sap_url`);
124
- }
125
- // Try to load existing refresh token from session store
126
- let refreshTokenValue = envConfig?.refreshToken;
127
- let result;
128
- // If no refresh token, start browser authentication flow
129
- if (!refreshTokenValue) {
130
- this.logger.debug(`No refresh token found for destination "${destination}". Starting browser authentication...`);
131
- result = await (0, browserAuth_1.startBrowserAuth)(serviceKey, this.browser || 'system', this.logger);
132
- }
133
- else {
134
- // Refresh token using refresh token
135
- result = await (0, tokenRefresher_1.refreshJwtToken)(refreshTokenValue, uaaUrl, clientId, clientSecret);
136
- }
137
- // Save new token to session store
138
- await this.sessionStore.saveSession(destination, {
139
- sapUrl,
140
- jwtToken: result.accessToken,
141
- refreshToken: result.refreshToken || refreshTokenValue,
142
- uaaUrl,
143
- uaaClientId: clientId,
144
- uaaClientSecret: clientSecret,
145
- sapClient: envConfig?.sapClient,
146
- language: envConfig?.language,
200
+ // Get refresh token from session
201
+ const sessionAuthConfig = await this.sessionStore.getAuthorizationConfig(destination);
202
+ const authConfigWithRefresh = { ...authConfig, refreshToken: sessionAuthConfig?.refreshToken || authConfig.refreshToken };
203
+ // Get connection config with token from provider
204
+ const tokenResult = await this.tokenProvider.getConnectionConfig(authConfigWithRefresh, {
205
+ browser: this.browser,
206
+ logger: this.logger,
147
207
  });
148
- // Update cache with new token
149
- (0, cache_1.setCachedToken)(destination, result.accessToken);
150
- return result.accessToken;
208
+ // Update session with new token
209
+ await this.sessionStore.setConnectionConfig(destination, tokenResult.connectionConfig);
210
+ // Update authorization config with new refresh token if available
211
+ if (tokenResult.refreshToken) {
212
+ await this.sessionStore.setAuthorizationConfig(destination, {
213
+ ...authConfig,
214
+ refreshToken: tokenResult.refreshToken,
215
+ });
216
+ }
217
+ return tokenResult.connectionConfig.authorizationToken;
151
218
  }
152
219
  /**
153
- * Get SAP URL for destination.
154
- * Tries to load from session store first, then from service key store.
220
+ * Get authorization configuration for destination
155
221
  * @param destination Destination name (e.g., "TRIAL")
156
- * @returns Promise that resolves to SAP URL string, or undefined if not found
222
+ * @returns Promise that resolves to IAuthorizationConfig or null if not found
157
223
  */
158
- async getSapUrl(destination) {
159
- // Try to load from session store first
160
- const envConfig = await this.sessionStore.loadSession(destination);
161
- if (envConfig?.sapUrl) {
162
- return envConfig.sapUrl;
224
+ async getAuthorizationConfig(destination) {
225
+ // Try session store first (has tokens)
226
+ const sessionAuthConfig = await this.sessionStore.getAuthorizationConfig(destination);
227
+ if (sessionAuthConfig) {
228
+ return sessionAuthConfig;
163
229
  }
164
- // Try service key store
165
- const serviceKey = await this.serviceKeyStore.getServiceKey(destination);
166
- if (serviceKey) {
167
- return serviceKey.url || serviceKey.abap?.url || serviceKey.sap_url;
168
- }
169
- return undefined;
170
- }
171
- /**
172
- * Clear cached token for specific destination
173
- * @param destination Destination name
174
- */
175
- clearCache(destination) {
176
- (0, cache_1.clearCache)(destination);
230
+ // Fall back to service key store (has UAA credentials)
231
+ return await this.serviceKeyStore.getAuthorizationConfig(destination);
177
232
  }
178
233
  /**
179
- * Clear all cached tokens
180
- */
181
- clearAllCache() {
182
- (0, cache_1.clearAllCache)();
183
- }
184
- /**
185
- * Get search paths for error messages (from file stores if available)
186
- * @private
234
+ * Get connection configuration for destination
235
+ * @param destination Destination name (e.g., "TRIAL")
236
+ * @returns Promise that resolves to IConnectionConfig or null if not found
187
237
  */
188
- getSearchPathsForError() {
189
- // Try to get search paths from file stores
190
- if (this.serviceKeyStore instanceof stores_1.FileServiceKeyStore) {
191
- return this.serviceKeyStore.getSearchPaths();
192
- }
193
- if (this.sessionStore instanceof stores_1.FileSessionStore) {
194
- return this.sessionStore.getSearchPaths();
238
+ async getConnectionConfig(destination) {
239
+ // Try session store first (has tokens and URLs)
240
+ const sessionConnConfig = await this.sessionStore.getConnectionConfig(destination);
241
+ if (sessionConnConfig) {
242
+ return sessionConnConfig;
195
243
  }
196
- // No file stores, return empty array
197
- return [];
244
+ // Fall back to service key store (has URLs but no tokens)
245
+ return await this.serviceKeyStore.getConnectionConfig(destination);
198
246
  }
199
247
  }
200
248
  exports.AuthBroker = AuthBroker;
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Configuration helpers for auth-broker tests
3
+ * Loads test configuration from test-config.yaml
4
+ */
5
+ export interface TestConfig {
6
+ auth_broker?: {
7
+ paths?: {
8
+ service_keys_dir?: string;
9
+ sessions_dir?: string;
10
+ };
11
+ abap?: {
12
+ destination?: string;
13
+ };
14
+ xsuaa?: {
15
+ btp_destination?: string;
16
+ mcp_destination?: string;
17
+ mcp_url?: string;
18
+ };
19
+ };
20
+ }
21
+ /**
22
+ * Load test configuration from YAML
23
+ * Uses test-config.yaml from tests/ directory
24
+ */
25
+ export declare function loadTestConfig(): TestConfig;
26
+ /**
27
+ * Check if test config has real values (not placeholders)
28
+ */
29
+ export declare function hasRealConfig(config: TestConfig, section: 'abap' | 'xsuaa'): boolean;
30
+ /**
31
+ * Get ABAP destination from config
32
+ */
33
+ export declare function getAbapDestination(config?: TestConfig): string | null;
34
+ /**
35
+ * Get XSUAA destinations from config
36
+ */
37
+ export declare function getXsuaaDestinations(config?: TestConfig): {
38
+ btp_destination: string | null;
39
+ mcp_url: string | null;
40
+ };
41
+ /**
42
+ * Get service keys directory from config
43
+ */
44
+ export declare function getServiceKeysDir(config?: TestConfig): string | null;
45
+ /**
46
+ * Get sessions directory from config
47
+ */
48
+ export declare function getSessionsDir(config?: TestConfig): string | null;
49
+ //# sourceMappingURL=configHelpers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"configHelpers.d.ts","sourceRoot":"","sources":["../../../src/__tests__/helpers/configHelpers.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAQH,MAAM,WAAW,UAAU;IACzB,WAAW,CAAC,EAAE;QACZ,KAAK,CAAC,EAAE;YACN,gBAAgB,CAAC,EAAE,MAAM,CAAC;YAC1B,YAAY,CAAC,EAAE,MAAM,CAAC;SACvB,CAAC;QACF,IAAI,CAAC,EAAE;YACL,WAAW,CAAC,EAAE,MAAM,CAAC;SACtB,CAAC;QACF,KAAK,CAAC,EAAE;YACN,eAAe,CAAC,EAAE,MAAM,CAAC;YACzB,eAAe,CAAC,EAAE,MAAM,CAAC;YACzB,OAAO,CAAC,EAAE,MAAM,CAAC;SAClB,CAAC;KACH,CAAC;CACH;AAkBD;;;GAGG;AACH,wBAAgB,cAAc,IAAI,UAAU,CA6C3C;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,GAAG,OAAO,CA2BpF;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,CAAC,EAAE,UAAU,GAAG,MAAM,GAAG,IAAI,CAGrE;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,CAAC,EAAE,UAAU,GAAG;IACzD,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB,CAOA;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,CAAC,EAAE,UAAU,GAAG,MAAM,GAAG,IAAI,CAGpE;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,MAAM,CAAC,EAAE,UAAU,GAAG,MAAM,GAAG,IAAI,CAGjE"}