@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 +16 -1
- package/README.md +9 -1
- package/dist/AuthBroker.d.ts +28 -2
- package/dist/AuthBroker.d.ts.map +1 -1
- package/dist/AuthBroker.js +113 -19
- package/package.json +1 -1
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.
|
|
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
|
|
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
|
|
package/dist/AuthBroker.d.ts
CHANGED
|
@@ -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
|
/**
|
package/dist/AuthBroker.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AuthBroker.d.ts","sourceRoot":"","sources":["../src/AuthBroker.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,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
|
|
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
|
@@ -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
|
-
//
|
|
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
|
-
//
|
|
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
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
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