@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.
- package/CHANGELOG.md +244 -0
- package/README.md +181 -16
- package/bin/generate-env-from-service-key.ts +128 -0
- package/dist/AuthBroker.d.ts +47 -31
- package/dist/AuthBroker.d.ts.map +1 -1
- package/dist/AuthBroker.js +182 -134
- package/dist/__tests__/helpers/configHelpers.d.ts +49 -0
- package/dist/__tests__/helpers/configHelpers.d.ts.map +1 -0
- package/dist/__tests__/helpers/configHelpers.js +169 -0
- package/dist/index.d.ts +4 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -8
- package/dist/providers/ITokenProvider.d.ts +49 -0
- package/dist/providers/ITokenProvider.d.ts.map +1 -0
- package/dist/providers/ITokenProvider.js +10 -0
- package/dist/providers/index.d.ts +8 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/index.js +8 -0
- package/dist/stores/index.d.ts +5 -5
- package/dist/stores/index.d.ts.map +1 -1
- package/dist/stores/index.js +4 -8
- package/dist/stores/interfaces.d.ts +88 -22
- package/dist/stores/interfaces.d.ts.map +1 -1
- package/dist/stores/interfaces.js +1 -2
- package/dist/types.d.ts +7 -31
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +2 -0
- package/package.json +13 -6
- package/dist/__tests__/testHelpers.d.ts +0 -44
- package/dist/__tests__/testHelpers.d.ts.map +0 -1
- package/dist/__tests__/testHelpers.js +0 -136
- package/dist/browserAuth.d.ts +0 -17
- package/dist/browserAuth.d.ts.map +0 -1
- package/dist/browserAuth.js +0 -305
- package/dist/cache.d.ts +0 -20
- package/dist/cache.d.ts.map +0 -1
- package/dist/cache.js +0 -46
- package/dist/envLoader.d.ts +0 -12
- package/dist/envLoader.d.ts.map +0 -1
- package/dist/envLoader.js +0 -90
- package/dist/getToken.d.ts +0 -14
- package/dist/getToken.d.ts.map +0 -1
- package/dist/getToken.js +0 -62
- package/dist/logger.d.ts +0 -40
- package/dist/logger.d.ts.map +0 -1
- package/dist/logger.js +0 -186
- package/dist/pathResolver.d.ts +0 -21
- package/dist/pathResolver.d.ts.map +0 -1
- package/dist/pathResolver.js +0 -105
- package/dist/refreshToken.d.ts +0 -14
- package/dist/refreshToken.d.ts.map +0 -1
- package/dist/refreshToken.js +0 -71
- package/dist/serviceKeyLoader.d.ts +0 -12
- package/dist/serviceKeyLoader.d.ts.map +0 -1
- package/dist/serviceKeyLoader.js +0 -72
- package/dist/stores/FileServiceKeyStore.d.ts +0 -38
- package/dist/stores/FileServiceKeyStore.d.ts.map +0 -1
- package/dist/stores/FileServiceKeyStore.js +0 -47
- package/dist/stores/FileSessionStore.d.ts +0 -50
- package/dist/stores/FileSessionStore.d.ts.map +0 -1
- package/dist/stores/FileSessionStore.js +0 -116
- package/dist/stores/SafeSessionStore.d.ts +0 -35
- package/dist/stores/SafeSessionStore.d.ts.map +0 -1
- package/dist/stores/SafeSessionStore.js +0 -42
- package/dist/tokenRefresher.d.ts +0 -17
- package/dist/tokenRefresher.d.ts.map +0 -1
- package/dist/tokenRefresher.js +0 -53
- package/dist/tokenStorage.d.ts +0 -15
- package/dist/tokenStorage.d.ts.map +0 -1
- package/dist/tokenStorage.js +0 -107
- package/dist/tokenValidator.d.ts +0 -11
- package/dist/tokenValidator.d.ts.map +0 -1
- package/dist/tokenValidator.js +0 -108
package/dist/AuthBroker.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Main AuthBroker class for managing JWT tokens based on destinations
|
|
3
3
|
*/
|
|
4
|
-
import { Logger } from '
|
|
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
|
|
17
|
-
* - serviceKeyStore: Store for service keys
|
|
18
|
-
* - sessionStore: Store for session data
|
|
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
|
|
26
|
-
sessionStore
|
|
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
|
-
*
|
|
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
|
|
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
|
-
|
|
78
|
+
getAuthorizationConfig(destination: string): Promise<IAuthorizationConfig | null>;
|
|
60
79
|
/**
|
|
61
|
-
*
|
|
62
|
-
|
|
63
|
-
|
|
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
|
-
|
|
84
|
+
getConnectionConfig(destination: string): Promise<IConnectionConfig | null>;
|
|
69
85
|
}
|
|
70
86
|
//# sourceMappingURL=AuthBroker.d.ts.map
|
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;
|
|
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"}
|
package/dist/AuthBroker.js
CHANGED
|
@@ -4,12 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.AuthBroker = void 0;
|
|
7
|
-
const
|
|
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
|
|
24
|
-
* - serviceKeyStore: Store for service keys
|
|
25
|
-
* - sessionStore: Store for session data
|
|
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
|
|
33
|
-
this.sessionStore = stores
|
|
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
|
|
46
|
-
const
|
|
47
|
-
if (
|
|
48
|
-
// Validate
|
|
49
|
-
|
|
50
|
-
|
|
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
|
|
76
|
+
return connConfig.authorizationToken;
|
|
54
77
|
}
|
|
55
|
-
// Token expired, remove from cache
|
|
56
78
|
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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
|
-
//
|
|
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
|
|
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
|
-
`
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
105
|
-
const
|
|
106
|
-
|
|
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
|
-
//
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
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
|
|
149
|
-
|
|
150
|
-
|
|
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
|
|
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
|
|
222
|
+
* @returns Promise that resolves to IAuthorizationConfig or null if not found
|
|
157
223
|
*/
|
|
158
|
-
async
|
|
159
|
-
// Try
|
|
160
|
-
const
|
|
161
|
-
if (
|
|
162
|
-
return
|
|
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
|
-
//
|
|
165
|
-
|
|
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
|
-
*
|
|
180
|
-
|
|
181
|
-
|
|
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
|
-
|
|
189
|
-
// Try
|
|
190
|
-
|
|
191
|
-
|
|
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
|
-
//
|
|
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"}
|