@mcp-abap-adt/auth-providers 0.1.2 → 0.1.4
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 +53 -0
- package/README.md +34 -1
- package/dist/__tests__/helpers/testLogger.d.ts +6 -0
- package/dist/__tests__/helpers/testLogger.d.ts.map +1 -0
- package/dist/__tests__/helpers/testLogger.js +44 -0
- package/dist/auth/browserAuth.d.ts +11 -1
- package/dist/auth/browserAuth.d.ts.map +1 -1
- package/dist/auth/browserAuth.js +38 -27
- package/dist/auth/clientCredentialsAuth.d.ts +1 -0
- package/dist/auth/clientCredentialsAuth.d.ts.map +1 -1
- package/dist/auth/clientCredentialsAuth.js +1 -0
- package/dist/auth/tokenRefresher.d.ts +1 -0
- package/dist/auth/tokenRefresher.d.ts.map +1 -1
- package/dist/auth/tokenRefresher.js +1 -0
- package/dist/index.d.ts +0 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -8
- package/dist/providers/BtpTokenProvider.d.ts +11 -0
- package/dist/providers/BtpTokenProvider.d.ts.map +1 -1
- package/dist/providers/BtpTokenProvider.js +22 -3
- package/dist/providers/XsuaaTokenProvider.d.ts +4 -0
- package/dist/providers/XsuaaTokenProvider.d.ts.map +1 -1
- package/dist/providers/XsuaaTokenProvider.js +9 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,59 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.1.4] - 2025-12-08
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- **Integration Tests for browserAuth**: Added real integration test that uses actual service keys and OAuth flow
|
|
14
|
+
- Test verifies token retrieval with real credentials from service keys
|
|
15
|
+
- Shows all authentication stages with logging when `DEBUG_AUTH_PROVIDERS=true` is set
|
|
16
|
+
- Tests both access token and refresh token retrieval
|
|
17
|
+
- **Test Logger Helper**: Added `createTestLogger` helper for tests with environment variable control
|
|
18
|
+
- Supports log levels (debug, info, warn, error) via `LOG_LEVEL` environment variable
|
|
19
|
+
- Only outputs logs when `DEBUG_AUTH_PROVIDERS=true` or `DEBUG_BROWSER_AUTH=true` is set
|
|
20
|
+
- Provides clean, controlled logging for test scenarios
|
|
21
|
+
|
|
22
|
+
### Changed
|
|
23
|
+
- **Improved Logging in browserAuth**: Made logging more concise and informative
|
|
24
|
+
- All log messages are now single-line strings without verbose objects
|
|
25
|
+
- Logs show key information: what we send, what we receive, token lengths
|
|
26
|
+
- Example: `Tokens received: accessToken(2263 chars), refreshToken(34 chars)`
|
|
27
|
+
- Logging only works when logger is provided (no default console output)
|
|
28
|
+
- **exchangeCodeForToken Function**: Exported for testing purposes
|
|
29
|
+
- Function is marked as `@internal` but exported to enable unit testing
|
|
30
|
+
- Allows testing token exchange logic without full browser auth flow
|
|
31
|
+
|
|
32
|
+
### Fixed
|
|
33
|
+
- **Test Error Logging**: Fixed error test to use mock logger without console output
|
|
34
|
+
- Error test no longer pollutes console with error messages
|
|
35
|
+
- Still verifies that error logging occurs via spy
|
|
36
|
+
|
|
37
|
+
## [0.1.3] - 2025-12-07
|
|
38
|
+
|
|
39
|
+
### Added
|
|
40
|
+
- **Configurable Browser Auth Port**: Added optional `browserAuthPort` parameter to `BtpTokenProvider` constructor
|
|
41
|
+
- Allows configuring the OAuth callback server port (default: 3001)
|
|
42
|
+
- Prevents port conflicts when proxy server runs on the same port
|
|
43
|
+
- Port is passed through to `startBrowserAuth` and `exchangeCodeForToken` functions
|
|
44
|
+
- Enables proxy to configure browser auth port via CLI parameter or YAML config
|
|
45
|
+
|
|
46
|
+
### Changed
|
|
47
|
+
- **BtpTokenProvider Constructor**: Now accepts optional `browserAuthPort?: number` parameter
|
|
48
|
+
- Defaults to 3001 if not specified (maintains backward compatibility)
|
|
49
|
+
- **startBrowserAuth Function**: Added optional `port: number = 3001` parameter
|
|
50
|
+
- Port is used for OAuth callback server and redirect URI
|
|
51
|
+
- **exchangeCodeForToken Function**: Added optional `port: number = 3001` parameter
|
|
52
|
+
- Port is used in redirect URI when exchanging authorization code for tokens
|
|
53
|
+
- **Implementation Isolation**: Internal authentication functions are no longer exported from package
|
|
54
|
+
- `startBrowserAuth`, `refreshJwtToken`, and `getTokenWithClientCredentials` are now internal functions
|
|
55
|
+
- Providers use private method wrappers to call these functions
|
|
56
|
+
- Constructor parameters (like `browserAuthPort`) are passed through private methods to internal functions
|
|
57
|
+
- This ensures proper encapsulation and prevents direct usage of internal implementation details
|
|
58
|
+
- **Test Improvements**: Unit tests now use provider methods instead of direct internal function imports
|
|
59
|
+
- Tests use `jest.spyOn` to mock private provider methods instead of mocking internal functions
|
|
60
|
+
- Tests now properly test the public API of providers, ensuring better isolation
|
|
61
|
+
- This aligns with encapsulation principles and makes tests more maintainable
|
|
62
|
+
|
|
10
63
|
## [0.1.2] - 2025-12-05
|
|
11
64
|
|
|
12
65
|
### Changed
|
package/README.md
CHANGED
|
@@ -130,10 +130,11 @@ const btpBroker = new AuthBroker({
|
|
|
130
130
|
const abapServiceKeyStore = new AbapServiceKeyStore('/path/to/service-keys');
|
|
131
131
|
const abapSessionStore = new AbapSessionStore('/path/to/sessions');
|
|
132
132
|
|
|
133
|
+
// Use custom port if running alongside other services (e.g., proxy on port 3001)
|
|
133
134
|
const abapBroker = new AuthBroker({
|
|
134
135
|
serviceKeyStore: abapServiceKeyStore,
|
|
135
136
|
sessionStore: abapSessionStore,
|
|
136
|
-
tokenProvider: new BtpTokenProvider(), //
|
|
137
|
+
tokenProvider: new BtpTokenProvider(4001), // Custom port to avoid conflicts
|
|
137
138
|
});
|
|
138
139
|
```
|
|
139
140
|
|
|
@@ -171,8 +172,12 @@ Uses browser-based OAuth2 flow or refresh token:
|
|
|
171
172
|
import { BtpTokenProvider } from '@mcp-abap-adt/auth-providers';
|
|
172
173
|
import type { IAuthorizationConfig } from '@mcp-abap-adt/auth-broker';
|
|
173
174
|
|
|
175
|
+
// Create provider with default port (3001)
|
|
174
176
|
const provider = new BtpTokenProvider();
|
|
175
177
|
|
|
178
|
+
// Or specify custom port for OAuth callback server (useful to avoid port conflicts)
|
|
179
|
+
const providerWithCustomPort = new BtpTokenProvider(4001);
|
|
180
|
+
|
|
176
181
|
const authConfig: IAuthorizationConfig = {
|
|
177
182
|
uaaUrl: 'https://...authentication...hana.ondemand.com',
|
|
178
183
|
uaaClientId: '...',
|
|
@@ -191,6 +196,8 @@ const result = await provider.getConnectionConfig(authConfig, {
|
|
|
191
196
|
// result.refreshToken contains refresh token (if browser flow was used)
|
|
192
197
|
```
|
|
193
198
|
|
|
199
|
+
**Note**: The `browserAuthPort` parameter (default: 3001) configures the OAuth callback server port. This is useful when running the provider in environments where port 3001 is already in use (e.g., when running alongside a proxy server).
|
|
200
|
+
|
|
194
201
|
### Token Validation
|
|
195
202
|
|
|
196
203
|
Both providers support token validation:
|
|
@@ -237,6 +244,32 @@ Integration tests will skip if `test-config.yaml` is not configured or contains
|
|
|
237
244
|
- ABAP integration tests use `abap.destination` and require `AbapServiceKeyStore`/`AbapSessionStore` (with `sapUrl`)
|
|
238
245
|
- BTP/ABAP integration tests may open a browser for authentication if no refresh token is available. This is expected behavior.
|
|
239
246
|
|
|
247
|
+
### Debug Logging
|
|
248
|
+
|
|
249
|
+
To enable detailed logging during tests or runtime, set environment variables:
|
|
250
|
+
|
|
251
|
+
```bash
|
|
252
|
+
# Enable logging for auth providers
|
|
253
|
+
DEBUG_AUTH_PROVIDERS=true npm test
|
|
254
|
+
|
|
255
|
+
# Or enable browser auth specific logging
|
|
256
|
+
DEBUG_BROWSER_AUTH=true npm test
|
|
257
|
+
|
|
258
|
+
# Set log level (debug, info, warn, error)
|
|
259
|
+
LOG_LEVEL=debug npm test
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
Logging shows:
|
|
263
|
+
- Token exchange stages (what we send, what we receive)
|
|
264
|
+
- Token information (lengths, previews)
|
|
265
|
+
- Errors with details
|
|
266
|
+
|
|
267
|
+
Example output:
|
|
268
|
+
```
|
|
269
|
+
[INTEGRATION] Exchanging code for token: https://.../oauth/token
|
|
270
|
+
[INTEGRATION] Tokens received: accessToken(2263 chars), refreshToken(34 chars)
|
|
271
|
+
```
|
|
272
|
+
|
|
240
273
|
## Dependencies
|
|
241
274
|
|
|
242
275
|
- `@mcp-abap-adt/auth-broker` (^0.1.6) - Interface definitions
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"testLogger.d.ts","sourceRoot":"","sources":["../../../src/__tests__/helpers/testLogger.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAC;AAgBxD,wBAAgB,gBAAgB,CAAC,MAAM,GAAE,MAAe,GAAG,OAAO,CA4BjE"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Test logger with environment variable control
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.createTestLogger = createTestLogger;
|
|
7
|
+
function getLogLevel() {
|
|
8
|
+
const level = process.env.LOG_LEVEL?.toLowerCase() || 'info';
|
|
9
|
+
const levels = ['debug', 'info', 'warn', 'error'];
|
|
10
|
+
return levels.includes(level) ? level : 'info';
|
|
11
|
+
}
|
|
12
|
+
function shouldLog(level) {
|
|
13
|
+
const currentLevel = getLogLevel();
|
|
14
|
+
const levels = ['debug', 'info', 'warn', 'error'];
|
|
15
|
+
return levels.indexOf(level) >= levels.indexOf(currentLevel);
|
|
16
|
+
}
|
|
17
|
+
function createTestLogger(prefix = 'TEST') {
|
|
18
|
+
const level = getLogLevel();
|
|
19
|
+
const enabled = process.env.DEBUG_AUTH_PROVIDERS === 'true' ||
|
|
20
|
+
process.env.DEBUG_BROWSER_AUTH === 'true' ||
|
|
21
|
+
process.env.DEBUG === 'true';
|
|
22
|
+
return {
|
|
23
|
+
debug: (message, meta) => {
|
|
24
|
+
if (enabled && shouldLog('debug')) {
|
|
25
|
+
console.debug(`[${prefix}] [DEBUG] ${message}`, meta || '');
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
info: (message, meta) => {
|
|
29
|
+
if (enabled && shouldLog('info')) {
|
|
30
|
+
console.info(`[${prefix}] ${message}`, meta || '');
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
warn: (message, meta) => {
|
|
34
|
+
if (enabled && shouldLog('warn')) {
|
|
35
|
+
console.warn(`[${prefix}] [WARN] ${message}`, meta || '');
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
error: (message, meta) => {
|
|
39
|
+
if (enabled && shouldLog('error')) {
|
|
40
|
+
console.error(`[${prefix}] [ERROR] ${message}`, meta || '');
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
}
|
|
@@ -2,14 +2,24 @@
|
|
|
2
2
|
* Browser authentication - OAuth2 flow for obtaining tokens
|
|
3
3
|
*/
|
|
4
4
|
import type { IAuthorizationConfig, ILogger } from '@mcp-abap-adt/interfaces';
|
|
5
|
+
/**
|
|
6
|
+
* Exchange authorization code for tokens
|
|
7
|
+
* @internal - Exported for testing
|
|
8
|
+
*/
|
|
9
|
+
export declare function exchangeCodeForToken(authConfig: IAuthorizationConfig, code: string, port?: number, log?: ILogger | null): Promise<{
|
|
10
|
+
accessToken: string;
|
|
11
|
+
refreshToken?: string;
|
|
12
|
+
}>;
|
|
5
13
|
/**
|
|
6
14
|
* Start browser authentication flow
|
|
7
15
|
* @param authConfig Authorization configuration with UAA credentials
|
|
8
16
|
* @param browser Browser name (chrome, edge, firefox, system, none)
|
|
9
17
|
* @param logger Optional logger instance. If not provided, uses default logger.
|
|
18
|
+
* @param port Port for OAuth callback server (default: 3001)
|
|
10
19
|
* @returns Promise that resolves to tokens
|
|
20
|
+
* @internal - Internal function, not exported from package
|
|
11
21
|
*/
|
|
12
|
-
export declare function startBrowserAuth(authConfig: IAuthorizationConfig, browser?: string, logger?: ILogger): Promise<{
|
|
22
|
+
export declare function startBrowserAuth(authConfig: IAuthorizationConfig, browser?: string, logger?: ILogger, port?: number): Promise<{
|
|
13
23
|
accessToken: string;
|
|
14
24
|
refreshToken?: string;
|
|
15
25
|
}>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"browserAuth.d.ts","sourceRoot":"","sources":["../../src/auth/browserAuth.ts"],"names":[],"mappings":"AAAA;;GAEG;AAMH,OAAO,KAAK,EAAE,oBAAoB,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"browserAuth.d.ts","sourceRoot":"","sources":["../../src/auth/browserAuth.ts"],"names":[],"mappings":"AAAA;;GAEG;AAMH,OAAO,KAAK,EAAE,oBAAoB,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAC;AAyB9E;;;GAGG;AACH,wBAAsB,oBAAoB,CACxC,UAAU,EAAE,oBAAoB,EAChC,IAAI,EAAE,MAAM,EACZ,IAAI,GAAE,MAAa,EACnB,GAAG,CAAC,EAAE,OAAO,GAAG,IAAI,GACnB,OAAO,CAAC;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAsCzD;AAaD;;;;;;;;GAQG;AACH,wBAAsB,gBAAgB,CACpC,UAAU,EAAE,oBAAoB,EAChC,OAAO,GAAE,MAAiB,EAC1B,MAAM,CAAC,EAAE,OAAO,EAChB,IAAI,GAAE,MAAa,GAClB,OAAO,CAAC;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAuSzD"}
|
package/dist/auth/browserAuth.js
CHANGED
|
@@ -39,6 +39,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
39
39
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
40
40
|
};
|
|
41
41
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
42
|
+
exports.exchangeCodeForToken = exchangeCodeForToken;
|
|
42
43
|
exports.startBrowserAuth = startBrowserAuth;
|
|
43
44
|
const http = __importStar(require("http"));
|
|
44
45
|
const child_process = __importStar(require("child_process"));
|
|
@@ -65,16 +66,18 @@ function getJwtAuthorizationUrl(authConfig, port = 3001) {
|
|
|
65
66
|
}
|
|
66
67
|
/**
|
|
67
68
|
* Exchange authorization code for tokens
|
|
69
|
+
* @internal - Exported for testing
|
|
68
70
|
*/
|
|
69
|
-
async function exchangeCodeForToken(authConfig, code) {
|
|
71
|
+
async function exchangeCodeForToken(authConfig, code, port = 3001, log) {
|
|
70
72
|
const { uaaUrl: url, uaaClientId: clientid, uaaClientSecret: clientsecret } = authConfig;
|
|
71
73
|
const tokenUrl = `${url}/oauth/token`;
|
|
72
|
-
const redirectUri =
|
|
74
|
+
const redirectUri = `http://localhost:${port}/callback`;
|
|
73
75
|
const params = new URLSearchParams();
|
|
74
76
|
params.append('grant_type', 'authorization_code');
|
|
75
77
|
params.append('code', code);
|
|
76
78
|
params.append('redirect_uri', redirectUri);
|
|
77
79
|
const authString = Buffer.from(`${clientid}:${clientsecret}`).toString('base64');
|
|
80
|
+
log?.info(`Exchanging code for token: ${tokenUrl}`);
|
|
78
81
|
const response = await (0, axios_1.default)({
|
|
79
82
|
method: 'post',
|
|
80
83
|
url: tokenUrl,
|
|
@@ -85,40 +88,41 @@ async function exchangeCodeForToken(authConfig, code) {
|
|
|
85
88
|
data: params.toString(),
|
|
86
89
|
});
|
|
87
90
|
if (response.data && response.data.access_token) {
|
|
91
|
+
const accessToken = response.data.access_token;
|
|
92
|
+
const refreshToken = response.data.refresh_token;
|
|
93
|
+
log?.info(`Tokens received: accessToken(${accessToken.length} chars), refreshToken(${refreshToken?.length || 0} chars)`);
|
|
88
94
|
return {
|
|
89
|
-
accessToken
|
|
90
|
-
refreshToken
|
|
95
|
+
accessToken,
|
|
96
|
+
refreshToken,
|
|
91
97
|
};
|
|
92
98
|
}
|
|
93
99
|
else {
|
|
100
|
+
log?.error(`Token exchange failed: status ${response.status}, error: ${response.data?.error || 'unknown'}`);
|
|
94
101
|
throw new Error('Response does not contain access_token');
|
|
95
102
|
}
|
|
96
103
|
}
|
|
97
104
|
/**
|
|
98
|
-
*
|
|
105
|
+
* Check if debug logging is enabled for auth providers
|
|
99
106
|
*/
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
}
|
|
107
|
+
function isDebugEnabled() {
|
|
108
|
+
return process.env.DEBUG_AUTH_PROVIDERS === 'true' ||
|
|
109
|
+
process.env.DEBUG_BROWSER_AUTH === 'true' ||
|
|
110
|
+
process.env.DEBUG === 'true' ||
|
|
111
|
+
process.env.DEBUG?.includes('auth-providers') === true ||
|
|
112
|
+
process.env.DEBUG?.includes('browser-auth') === true;
|
|
113
|
+
}
|
|
107
114
|
/**
|
|
108
115
|
* Start browser authentication flow
|
|
109
116
|
* @param authConfig Authorization configuration with UAA credentials
|
|
110
117
|
* @param browser Browser name (chrome, edge, firefox, system, none)
|
|
111
118
|
* @param logger Optional logger instance. If not provided, uses default logger.
|
|
119
|
+
* @param port Port for OAuth callback server (default: 3001)
|
|
112
120
|
* @returns Promise that resolves to tokens
|
|
121
|
+
* @internal - Internal function, not exported from package
|
|
113
122
|
*/
|
|
114
|
-
async function startBrowserAuth(authConfig, browser = 'system', logger) {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
debug: (msg) => logger.debug(msg),
|
|
118
|
-
error: (msg) => logger.error(msg),
|
|
119
|
-
browserUrl: (url) => logger.info(`🔗 Open in browser: ${url}`),
|
|
120
|
-
browserOpening: () => logger.debug('🌐 Opening browser for authentication...'),
|
|
121
|
-
} : defaultLogger;
|
|
123
|
+
async function startBrowserAuth(authConfig, browser = 'system', logger, port = 3001) {
|
|
124
|
+
// Use logger if provided, otherwise null (no logging)
|
|
125
|
+
const log = logger || null;
|
|
122
126
|
return new Promise((originalResolve, originalReject) => {
|
|
123
127
|
let timeoutId = null;
|
|
124
128
|
const resolve = (value) => {
|
|
@@ -133,15 +137,18 @@ async function startBrowserAuth(authConfig, browser = 'system', logger) {
|
|
|
133
137
|
};
|
|
134
138
|
const app = (0, express_1.default)();
|
|
135
139
|
const server = http.createServer(app);
|
|
136
|
-
const PORT =
|
|
140
|
+
const PORT = port;
|
|
137
141
|
let serverInstance = null;
|
|
138
142
|
const authorizationUrl = getJwtAuthorizationUrl(authConfig, PORT);
|
|
139
143
|
// OAuth2 callback handler
|
|
140
144
|
app.get('/callback', async (req, res) => {
|
|
141
145
|
try {
|
|
146
|
+
log?.info(`Callback received: ${req.url}`);
|
|
147
|
+
log?.debug(`Callback query: ${JSON.stringify(req.query)}`);
|
|
142
148
|
// Check for OAuth2 error parameters
|
|
143
149
|
const { error, error_description, error_uri } = req.query;
|
|
144
150
|
if (error) {
|
|
151
|
+
log?.error(`Callback error: ${error}${error_description ? ` - ${error_description}` : ''}`);
|
|
145
152
|
const errorMsg = error_description
|
|
146
153
|
? `${error}: ${error_description}`
|
|
147
154
|
: String(error);
|
|
@@ -212,10 +219,13 @@ async function startBrowserAuth(authConfig, browser = 'system', logger) {
|
|
|
212
219
|
return reject(new Error(`OAuth2 authentication failed: ${errorMsg}${error_uri ? ` (${error_uri})` : ''}`));
|
|
213
220
|
}
|
|
214
221
|
const { code } = req.query;
|
|
222
|
+
log?.debug(`Callback code received: ${code ? 'yes' : 'no'}`);
|
|
215
223
|
if (!code || typeof code !== 'string') {
|
|
224
|
+
log?.error(`Callback code missing`);
|
|
216
225
|
res.status(400).send('Error: Authorization code missing');
|
|
217
226
|
return reject(new Error('Authorization code missing'));
|
|
218
227
|
}
|
|
228
|
+
log?.debug(`Exchanging code for token`);
|
|
219
229
|
// Send success page
|
|
220
230
|
const html = `<!DOCTYPE html>
|
|
221
231
|
<html lang="en">
|
|
@@ -278,7 +288,8 @@ async function startBrowserAuth(authConfig, browser = 'system', logger) {
|
|
|
278
288
|
res.send(html);
|
|
279
289
|
// Exchange code for tokens and close server
|
|
280
290
|
try {
|
|
281
|
-
const tokens = await exchangeCodeForToken(authConfig, code);
|
|
291
|
+
const tokens = await exchangeCodeForToken(authConfig, code, PORT, log);
|
|
292
|
+
log?.info(`Tokens received: accessToken(${tokens.accessToken?.length || 0} chars), refreshToken(${tokens.refreshToken?.length || 0} chars)`);
|
|
282
293
|
// Close all connections and server immediately after getting tokens
|
|
283
294
|
if (typeof server.closeAllConnections === 'function') {
|
|
284
295
|
server.closeAllConnections();
|
|
@@ -309,7 +320,7 @@ async function startBrowserAuth(authConfig, browser = 'system', logger) {
|
|
|
309
320
|
serverInstance = server.listen(PORT, async () => {
|
|
310
321
|
const browserApp = BROWSER_MAP[browser];
|
|
311
322
|
if (!browser || browser === 'none' || browserApp === null) {
|
|
312
|
-
log
|
|
323
|
+
log?.info(`🔗 Open in browser: ${authorizationUrl}`, { url: authorizationUrl });
|
|
313
324
|
// For 'none' browser, don't wait for callback - throw error immediately
|
|
314
325
|
// User must open browser manually and we can't wait for callback in automated tests
|
|
315
326
|
if (serverInstance) {
|
|
@@ -321,7 +332,7 @@ async function startBrowserAuth(authConfig, browser = 'system', logger) {
|
|
|
321
332
|
return;
|
|
322
333
|
}
|
|
323
334
|
else {
|
|
324
|
-
log
|
|
335
|
+
log?.debug('🌐 Opening browser for authentication...');
|
|
325
336
|
try {
|
|
326
337
|
// Try dynamic import first (for ES modules)
|
|
327
338
|
let open;
|
|
@@ -366,7 +377,7 @@ async function startBrowserAuth(authConfig, browser = 'system', logger) {
|
|
|
366
377
|
// Use child_process as fallback (non-blocking)
|
|
367
378
|
child_process.exec(`${command} "${authorizationUrl}"`, (error) => {
|
|
368
379
|
if (error) {
|
|
369
|
-
log
|
|
380
|
+
log?.error(`❌ Failed to open browser: ${error.message}. Please open manually: ${authorizationUrl}`, { error: error.message, url: authorizationUrl });
|
|
370
381
|
}
|
|
371
382
|
});
|
|
372
383
|
return; // Exit early since we're using child_process (non-blocking)
|
|
@@ -381,8 +392,8 @@ async function startBrowserAuth(authConfig, browser = 'system', logger) {
|
|
|
381
392
|
}
|
|
382
393
|
catch (error) {
|
|
383
394
|
// If browser cannot be opened, show URL and throw error for consumer to catch
|
|
384
|
-
log
|
|
385
|
-
log
|
|
395
|
+
log?.error(`❌ Failed to open browser: ${error?.message || String(error)}. Please open manually: ${authorizationUrl}`, { error: error?.message || String(error), url: authorizationUrl });
|
|
396
|
+
log?.info(`🔗 Open in browser: ${authorizationUrl}`, { url: authorizationUrl });
|
|
386
397
|
// Throw error so consumer can distinguish this from "service key missing" error
|
|
387
398
|
reject(new Error(`Browser opening failed for destination authentication. Please open manually: ${authorizationUrl}`));
|
|
388
399
|
}
|
|
@@ -14,6 +14,7 @@ export interface ClientCredentialsResult {
|
|
|
14
14
|
* @param clientId UAA client ID
|
|
15
15
|
* @param clientSecret UAA client secret
|
|
16
16
|
* @returns Promise that resolves to access token
|
|
17
|
+
* @internal - Internal function, not exported from package
|
|
17
18
|
*/
|
|
18
19
|
export declare function getTokenWithClientCredentials(uaaUrl: string, clientId: string, clientSecret: string): Promise<ClientCredentialsResult>;
|
|
19
20
|
//# sourceMappingURL=clientCredentialsAuth.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"clientCredentialsAuth.d.ts","sourceRoot":"","sources":["../../src/auth/clientCredentialsAuth.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,MAAM,WAAW,uBAAuB;IACtC,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED
|
|
1
|
+
{"version":3,"file":"clientCredentialsAuth.d.ts","sourceRoot":"","sources":["../../src/auth/clientCredentialsAuth.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,MAAM,WAAW,uBAAuB;IACtC,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;GAOG;AACH,wBAAsB,6BAA6B,CACjD,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,uBAAuB,CAAC,CAoClC"}
|
|
@@ -17,6 +17,7 @@ const axios_1 = __importDefault(require("axios"));
|
|
|
17
17
|
* @param clientId UAA client ID
|
|
18
18
|
* @param clientSecret UAA client secret
|
|
19
19
|
* @returns Promise that resolves to access token
|
|
20
|
+
* @internal - Internal function, not exported from package
|
|
20
21
|
*/
|
|
21
22
|
async function getTokenWithClientCredentials(uaaUrl, clientId, clientSecret) {
|
|
22
23
|
try {
|
|
@@ -12,6 +12,7 @@ export interface TokenRefreshResult {
|
|
|
12
12
|
* @param clientId UAA client ID
|
|
13
13
|
* @param clientSecret UAA client secret
|
|
14
14
|
* @returns Promise that resolves to new tokens
|
|
15
|
+
* @internal - Internal function, not exported from package
|
|
15
16
|
*/
|
|
16
17
|
export declare function refreshJwtToken(refreshToken: string, uaaUrl: string, clientId: string, clientSecret: string): Promise<TokenRefreshResult>;
|
|
17
18
|
//# sourceMappingURL=tokenRefresher.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tokenRefresher.d.ts","sourceRoot":"","sources":["../../src/auth/tokenRefresher.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,MAAM,WAAW,kBAAkB;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED
|
|
1
|
+
{"version":3,"file":"tokenRefresher.d.ts","sourceRoot":"","sources":["../../src/auth/tokenRefresher.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,MAAM,WAAW,kBAAkB;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;;GAQG;AACH,wBAAsB,eAAe,CACnC,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,kBAAkB,CAAC,CAqC7B"}
|
|
@@ -15,6 +15,7 @@ const axios_1 = __importDefault(require("axios"));
|
|
|
15
15
|
* @param clientId UAA client ID
|
|
16
16
|
* @param clientSecret UAA client secret
|
|
17
17
|
* @returns Promise that resolves to new tokens
|
|
18
|
+
* @internal - Internal function, not exported from package
|
|
18
19
|
*/
|
|
19
20
|
async function refreshJwtToken(refreshToken, uaaUrl, clientId, clientSecret) {
|
|
20
21
|
try {
|
package/dist/index.d.ts
CHANGED
|
@@ -6,9 +6,4 @@
|
|
|
6
6
|
*/
|
|
7
7
|
export { XsuaaTokenProvider } from './providers/XsuaaTokenProvider';
|
|
8
8
|
export { BtpTokenProvider } from './providers/BtpTokenProvider';
|
|
9
|
-
export { startBrowserAuth } from './auth/browserAuth';
|
|
10
|
-
export { getTokenWithClientCredentials } from './auth/clientCredentialsAuth';
|
|
11
|
-
export { refreshJwtToken } from './auth/tokenRefresher';
|
|
12
|
-
export type { ClientCredentialsResult } from './auth/clientCredentialsAuth';
|
|
13
|
-
export type { TokenRefreshResult } from './auth/tokenRefresher';
|
|
14
9
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AACpE,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AACpE,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -6,16 +6,9 @@
|
|
|
6
6
|
* Provides XSUAA and BTP token providers
|
|
7
7
|
*/
|
|
8
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
-
exports.
|
|
9
|
+
exports.BtpTokenProvider = exports.XsuaaTokenProvider = void 0;
|
|
10
10
|
// Token providers
|
|
11
11
|
var XsuaaTokenProvider_1 = require("./providers/XsuaaTokenProvider");
|
|
12
12
|
Object.defineProperty(exports, "XsuaaTokenProvider", { enumerable: true, get: function () { return XsuaaTokenProvider_1.XsuaaTokenProvider; } });
|
|
13
13
|
var BtpTokenProvider_1 = require("./providers/BtpTokenProvider");
|
|
14
14
|
Object.defineProperty(exports, "BtpTokenProvider", { enumerable: true, get: function () { return BtpTokenProvider_1.BtpTokenProvider; } });
|
|
15
|
-
// Auth functions (for advanced usage)
|
|
16
|
-
var browserAuth_1 = require("./auth/browserAuth");
|
|
17
|
-
Object.defineProperty(exports, "startBrowserAuth", { enumerable: true, get: function () { return browserAuth_1.startBrowserAuth; } });
|
|
18
|
-
var clientCredentialsAuth_1 = require("./auth/clientCredentialsAuth");
|
|
19
|
-
Object.defineProperty(exports, "getTokenWithClientCredentials", { enumerable: true, get: function () { return clientCredentialsAuth_1.getTokenWithClientCredentials; } });
|
|
20
|
-
var tokenRefresher_1 = require("./auth/tokenRefresher");
|
|
21
|
-
Object.defineProperty(exports, "refreshJwtToken", { enumerable: true, get: function () { return tokenRefresher_1.refreshJwtToken; } });
|
|
@@ -11,6 +11,17 @@ import type { ITokenProvider, ITokenProviderOptions, ITokenProviderResult, IAuth
|
|
|
11
11
|
* Uses browser-based OAuth2 (if no refresh token) or refresh token flow.
|
|
12
12
|
*/
|
|
13
13
|
export declare class BtpTokenProvider implements ITokenProvider {
|
|
14
|
+
private readonly browserAuthPort;
|
|
15
|
+
constructor(browserAuthPort?: number);
|
|
16
|
+
/**
|
|
17
|
+
* Private method wrapper for browser authentication
|
|
18
|
+
* Proxies port from constructor to internal function
|
|
19
|
+
*/
|
|
20
|
+
private startBrowserAuth;
|
|
21
|
+
/**
|
|
22
|
+
* Private method wrapper for token refresh
|
|
23
|
+
*/
|
|
24
|
+
private refreshJwtToken;
|
|
14
25
|
getConnectionConfig(authConfig: IAuthorizationConfig, options?: ITokenProviderOptions): Promise<ITokenProviderResult>;
|
|
15
26
|
validateToken(token: string, serviceUrl?: string): Promise<boolean>;
|
|
16
27
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BtpTokenProvider.d.ts","sourceRoot":"","sources":["../../src/providers/BtpTokenProvider.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,oBAAoB,
|
|
1
|
+
{"version":3,"file":"BtpTokenProvider.d.ts","sourceRoot":"","sources":["../../src/providers/BtpTokenProvider.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,oBAAoB,EAAW,MAAM,0BAA0B,CAAC;AAO3I;;;;GAIG;AACH,qBAAa,gBAAiB,YAAW,cAAc;IACrD,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAS;gBAE7B,eAAe,CAAC,EAAE,MAAM;IAKpC;;;OAGG;YACW,gBAAgB;IAQ9B;;OAEG;YACW,eAAe;IASvB,mBAAmB,CACvB,UAAU,EAAE,oBAAoB,EAChC,OAAO,CAAC,EAAE,qBAAqB,GAC9B,OAAO,CAAC,oBAAoB,CAAC;IAmC1B,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;CA0C1E"}
|
|
@@ -10,15 +10,34 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.BtpTokenProvider = void 0;
|
|
13
|
+
const axios_1 = __importDefault(require("axios"));
|
|
14
|
+
// Import internal functions (not exported)
|
|
13
15
|
const browserAuth_1 = require("../auth/browserAuth");
|
|
14
16
|
const tokenRefresher_1 = require("../auth/tokenRefresher");
|
|
15
|
-
const axios_1 = __importDefault(require("axios"));
|
|
16
17
|
/**
|
|
17
18
|
* BTP/ABAP token provider implementation
|
|
18
19
|
*
|
|
19
20
|
* Uses browser-based OAuth2 (if no refresh token) or refresh token flow.
|
|
20
21
|
*/
|
|
21
22
|
class BtpTokenProvider {
|
|
23
|
+
browserAuthPort;
|
|
24
|
+
constructor(browserAuthPort) {
|
|
25
|
+
// Default to 3001 if not specified
|
|
26
|
+
this.browserAuthPort = browserAuthPort ?? 3001;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Private method wrapper for browser authentication
|
|
30
|
+
* Proxies port from constructor to internal function
|
|
31
|
+
*/
|
|
32
|
+
async startBrowserAuth(authConfig, browser, logger) {
|
|
33
|
+
return (0, browserAuth_1.startBrowserAuth)(authConfig, browser, logger, this.browserAuthPort);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Private method wrapper for token refresh
|
|
37
|
+
*/
|
|
38
|
+
async refreshJwtToken(refreshToken, uaaUrl, clientId, clientSecret) {
|
|
39
|
+
return (0, tokenRefresher_1.refreshJwtToken)(refreshToken, uaaUrl, clientId, clientSecret);
|
|
40
|
+
}
|
|
22
41
|
async getConnectionConfig(authConfig, options) {
|
|
23
42
|
const logger = options?.logger;
|
|
24
43
|
const browser = options?.browser || 'system';
|
|
@@ -28,14 +47,14 @@ class BtpTokenProvider {
|
|
|
28
47
|
if (logger) {
|
|
29
48
|
logger.debug('No refresh token found. Starting browser authentication...');
|
|
30
49
|
}
|
|
31
|
-
result = await
|
|
50
|
+
result = await this.startBrowserAuth(authConfig, browser, logger);
|
|
32
51
|
}
|
|
33
52
|
else {
|
|
34
53
|
// Use refresh token to get new access token
|
|
35
54
|
if (logger) {
|
|
36
55
|
logger.debug('Refreshing token using refresh token...');
|
|
37
56
|
}
|
|
38
|
-
result = await
|
|
57
|
+
result = await this.refreshJwtToken(authConfig.refreshToken, authConfig.uaaUrl, authConfig.uaaClientId, authConfig.uaaClientSecret);
|
|
39
58
|
}
|
|
40
59
|
return {
|
|
41
60
|
connectionConfig: {
|
|
@@ -11,6 +11,10 @@ import type { ITokenProvider, ITokenProviderOptions, ITokenProviderResult, IAuth
|
|
|
11
11
|
* Uses client_credentials grant type - no browser, no refresh token needed.
|
|
12
12
|
*/
|
|
13
13
|
export declare class XsuaaTokenProvider implements ITokenProvider {
|
|
14
|
+
/**
|
|
15
|
+
* Private method wrapper for client credentials authentication
|
|
16
|
+
*/
|
|
17
|
+
private getTokenWithClientCredentials;
|
|
14
18
|
getConnectionConfig(authConfig: IAuthorizationConfig, options?: ITokenProviderOptions): Promise<ITokenProviderResult>;
|
|
15
19
|
validateToken(token: string, serviceUrl?: string): Promise<boolean>;
|
|
16
20
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"XsuaaTokenProvider.d.ts","sourceRoot":"","sources":["../../src/providers/XsuaaTokenProvider.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"XsuaaTokenProvider.d.ts","sourceRoot":"","sources":["../../src/providers/XsuaaTokenProvider.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAMlI;;;;GAIG;AACH,qBAAa,kBAAmB,YAAW,cAAc;IACvD;;OAEG;YACW,6BAA6B;IAQrC,mBAAmB,CACvB,UAAU,EAAE,oBAAoB,EAChC,OAAO,CAAC,EAAE,qBAAqB,GAC9B,OAAO,CAAC,oBAAoB,CAAC;IAyB1B,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;CAgD1E"}
|
|
@@ -10,21 +10,28 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.XsuaaTokenProvider = void 0;
|
|
13
|
-
const clientCredentialsAuth_1 = require("../auth/clientCredentialsAuth");
|
|
14
13
|
const axios_1 = __importDefault(require("axios"));
|
|
14
|
+
// Import internal function (not exported)
|
|
15
|
+
const clientCredentialsAuth_1 = require("../auth/clientCredentialsAuth");
|
|
15
16
|
/**
|
|
16
17
|
* XSUAA token provider implementation
|
|
17
18
|
*
|
|
18
19
|
* Uses client_credentials grant type - no browser, no refresh token needed.
|
|
19
20
|
*/
|
|
20
21
|
class XsuaaTokenProvider {
|
|
22
|
+
/**
|
|
23
|
+
* Private method wrapper for client credentials authentication
|
|
24
|
+
*/
|
|
25
|
+
async getTokenWithClientCredentials(uaaUrl, clientId, clientSecret) {
|
|
26
|
+
return (0, clientCredentialsAuth_1.getTokenWithClientCredentials)(uaaUrl, clientId, clientSecret);
|
|
27
|
+
}
|
|
21
28
|
async getConnectionConfig(authConfig, options) {
|
|
22
29
|
const logger = options?.logger;
|
|
23
30
|
if (logger) {
|
|
24
31
|
logger.debug('Using client_credentials grant type for XSUAA...');
|
|
25
32
|
}
|
|
26
33
|
// XSUAA uses client_credentials - no refresh token needed
|
|
27
|
-
const result = await
|
|
34
|
+
const result = await this.getTokenWithClientCredentials(authConfig.uaaUrl, authConfig.uaaClientId, authConfig.uaaClientSecret);
|
|
28
35
|
// XSUAA doesn't provide serviceUrl in authorization config
|
|
29
36
|
// It's provided separately (from YAML, parameter, or request header)
|
|
30
37
|
return {
|
package/package.json
CHANGED