@mcp-abap-adt/auth-broker 0.1.6 → 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 CHANGED
@@ -9,7 +9,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
9
9
 
10
10
  Thank you to all contributors! See [CONTRIBUTORS.md](CONTRIBUTORS.md) for the complete list.
11
11
 
12
- ## [0.1.6] - 2025-01-XX
12
+ ## [0.1.7] - 2025-12-04
13
+
14
+ ### Changed
15
+ - **getToken() Fallback Chain** - Improved authentication reliability with multi-step fallback chain
16
+ - **Step 1**: Check if session exists and token is valid (returns immediately if valid)
17
+ - **Step 2**: Verify service key exists (throws error if missing)
18
+ - **Step 3**: Try refresh token authentication (via tokenProvider) if refresh token is available
19
+ - **Step 4**: Try UAA client_credentials authentication (via tokenProvider) if refresh token missing or failed
20
+ - **Step 5**: Try browser authentication (via tokenProvider) if UAA failed or parameters missing
21
+ - **Step 6**: Throw comprehensive error if all methods failed
22
+ - All authentication attempts use `ITokenProvider` interface (no direct implementation imports)
23
+ - Token validation is performed only when checking existing session (step 1)
24
+ - Tokens obtained through refresh/UAA/browser authentication are not validated before being saved
25
+ - Improved error messages with details about which authentication methods failed
26
+
27
+ ## [0.1.6] - 2025-12-04
13
28
 
14
29
  ### Changed
15
30
  - **Package Split** - Extracted store and provider implementations into separate packages
package/README.md CHANGED
@@ -191,7 +191,15 @@ new AuthBroker(
191
191
 
192
192
  ##### `getToken(destination: string): Promise<string>`
193
193
 
194
- Gets authentication token for destination. Tries to load from `.env` file, validates it, and refreshes if needed.
194
+ Gets authentication token for destination. Tries to load from session store, validates it, and refreshes if needed using a fallback chain:
195
+
196
+ 1. **Check session**: Load token from session store and validate it
197
+ 2. **Try refresh token**: If refresh token is available, attempt to refresh using it (via tokenProvider)
198
+ 3. **Try UAA (client_credentials)**: Attempt to get token using UAA credentials (via tokenProvider)
199
+ 4. **Try browser authentication**: Attempt browser-based OAuth2 flow using service key (via tokenProvider)
200
+ 5. **Throw error**: If all authentication methods failed
201
+
202
+ **Note**: Token validation is performed only when checking existing session. Tokens obtained through refresh/UAA/browser authentication are not validated before being saved.
195
203
 
196
204
  ##### `refreshToken(destination: string): Promise<string>`
197
205
 
@@ -31,10 +31,36 @@ export declare class AuthBroker {
31
31
  }, browser?: string, logger?: Logger);
32
32
  /**
33
33
  * Get authentication token for destination.
34
- * 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
+ *
35
61
  * @param destination Destination name (e.g., "TRIAL")
36
62
  * @returns Promise that resolves to JWT token string
37
- * @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
38
64
  */
39
65
  getToken(destination: string): Promise<string>;
40
66
  /**
@@ -1 +1 @@
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;;;;;;OAMG;IACG,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAwDpD;;;;;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"}
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"}
@@ -34,13 +34,39 @@ class AuthBroker {
34
34
  }
35
35
  /**
36
36
  * Get authentication token for destination.
37
- * 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
+ *
38
64
  * @param destination Destination name (e.g., "TRIAL")
39
65
  * @returns Promise that resolves to JWT token string
40
- * @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
41
67
  */
42
68
  async getToken(destination) {
43
- // Load connection config from session store
69
+ // Step 1: Check if session exists and token is valid
44
70
  const connConfig = await this.sessionStore.getConnectionConfig(destination);
45
71
  if (connConfig?.authorizationToken) {
46
72
  // Validate token if provider supports validation and we have service URL
@@ -55,7 +81,7 @@ class AuthBroker {
55
81
  return connConfig.authorizationToken;
56
82
  }
57
83
  }
58
- // Token not found or expired, check if we have service key
84
+ // Step 2: No valid session, check if we have service key
59
85
  const serviceKey = await this.serviceKeyStore.getServiceKey(destination);
60
86
  if (!serviceKey) {
61
87
  // No service key and no valid token
@@ -67,24 +93,92 @@ class AuthBroker {
67
93
  if (!authConfig) {
68
94
  throw new Error(`Service key for destination "${destination}" does not contain UAA credentials`);
69
95
  }
70
- // Get refresh token from session
96
+ // Get refresh token from session (if exists)
71
97
  const sessionAuthConfig = await this.sessionStore.getAuthorizationConfig(destination);
72
- const authConfigWithRefresh = { ...authConfig, refreshToken: sessionAuthConfig?.refreshToken || authConfig.refreshToken };
73
- // Get connection config with token from provider
74
- const tokenResult = await this.tokenProvider.getConnectionConfig(authConfigWithRefresh, {
75
- browser: this.browser,
76
- logger: this.logger,
77
- });
78
- // Update session with new token
79
- await this.sessionStore.setConnectionConfig(destination, tokenResult.connectionConfig);
80
- // Update authorization config with new refresh token if available
81
- if (tokenResult.refreshToken) {
82
- await this.sessionStore.setAuthorizationConfig(destination, {
83
- ...authConfig,
84
- refreshToken: tokenResult.refreshToken,
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,
85
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);
86
181
  }
87
- return tokenResult.connectionConfig.authorizationToken;
88
182
  }
89
183
  /**
90
184
  * Force refresh token for destination using service key.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mcp-abap-adt/auth-broker",
3
- "version": "0.1.6",
3
+ "version": "0.1.7",
4
4
  "description": "JWT authentication broker for MCP ABAP ADT - manages tokens based on destination headers",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",