@mcp-abap-adt/auth-broker 0.1.0
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 +94 -0
- package/CONTRIBUTORS.md +33 -0
- package/LICENSE +22 -0
- package/README.md +166 -0
- package/dist/AuthBroker.d.ts +62 -0
- package/dist/AuthBroker.d.ts.map +1 -0
- package/dist/AuthBroker.js +219 -0
- package/dist/__tests__/testHelpers.d.ts +44 -0
- package/dist/__tests__/testHelpers.d.ts.map +1 -0
- package/dist/__tests__/testHelpers.js +129 -0
- package/dist/browserAuth.d.ts +17 -0
- package/dist/browserAuth.d.ts.map +1 -0
- package/dist/browserAuth.js +301 -0
- package/dist/cache.d.ts +20 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +46 -0
- package/dist/envLoader.d.ts +12 -0
- package/dist/envLoader.d.ts.map +1 -0
- package/dist/envLoader.js +90 -0
- package/dist/getToken.d.ts +14 -0
- package/dist/getToken.d.ts.map +1 -0
- package/dist/getToken.js +62 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +12 -0
- package/dist/logger.d.ts +29 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +131 -0
- package/dist/pathResolver.d.ts +21 -0
- package/dist/pathResolver.d.ts.map +1 -0
- package/dist/pathResolver.js +105 -0
- package/dist/refreshToken.d.ts +14 -0
- package/dist/refreshToken.d.ts.map +1 -0
- package/dist/refreshToken.js +71 -0
- package/dist/serviceKeyLoader.d.ts +12 -0
- package/dist/serviceKeyLoader.d.ts.map +1 -0
- package/dist/serviceKeyLoader.js +72 -0
- package/dist/tokenRefresher.d.ts +17 -0
- package/dist/tokenRefresher.d.ts.map +1 -0
- package/dist/tokenRefresher.js +53 -0
- package/dist/tokenStorage.d.ts +15 -0
- package/dist/tokenStorage.d.ts.map +1 -0
- package/dist/tokenStorage.js +107 -0
- package/dist/tokenValidator.d.ts +11 -0
- package/dist/tokenValidator.d.ts.map +1 -0
- package/dist/tokenValidator.js +108 -0
- package/dist/types.d.ts +37 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +5 -0
- package/package.json +64 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## Contributors
|
|
9
|
+
|
|
10
|
+
Thank you to all contributors! See [CONTRIBUTORS.md](CONTRIBUTORS.md) for the complete list.
|
|
11
|
+
|
|
12
|
+
## [0.1.0] - 2025-01-XX
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
- **AuthBroker class** - Main class for managing JWT authentication tokens
|
|
16
|
+
- `getToken(destination)` - Get token for destination (loads, validates, refreshes if needed)
|
|
17
|
+
- `refreshToken(destination)` - Force refresh token using service key or browser authentication
|
|
18
|
+
- `clearCache(destination)` - Clear cached token for specific destination
|
|
19
|
+
- `clearAllCache()` - Clear all cached tokens
|
|
20
|
+
- **Multi-path file search** - Configurable search paths for `.env` and `.json` files
|
|
21
|
+
- Constructor parameter (highest priority)
|
|
22
|
+
- `AUTH_BROKER_PATH` environment variable
|
|
23
|
+
- Current working directory (fallback)
|
|
24
|
+
- **Token management**
|
|
25
|
+
- Automatic token validation before use
|
|
26
|
+
- Automatic token refresh when expired
|
|
27
|
+
- In-memory token caching for performance
|
|
28
|
+
- Token expiry information in `.env` file comments
|
|
29
|
+
- **Browser-based OAuth2 authentication**
|
|
30
|
+
- Automatic browser opening for initial authentication
|
|
31
|
+
- Configurable browser selection (chrome, edge, firefox, system, none)
|
|
32
|
+
- Manual URL copy option when browser cannot be opened
|
|
33
|
+
- OAuth2 callback server with success page
|
|
34
|
+
- **Service key support**
|
|
35
|
+
- Load service keys from `{destination}.json` files
|
|
36
|
+
- Support for multiple service key formats (url, abap.url, sap_url)
|
|
37
|
+
- Extract UAA credentials for token refresh
|
|
38
|
+
- **Environment file management**
|
|
39
|
+
- Load tokens from `{destination}.env` files
|
|
40
|
+
- Automatic `.env` file creation after authentication
|
|
41
|
+
- Atomic file writes for safe updates
|
|
42
|
+
- Format compatible with `sap-abap-auth` utility
|
|
43
|
+
- **Comprehensive error handling**
|
|
44
|
+
- Clear error messages with file location instructions
|
|
45
|
+
- Searched paths listed in error messages
|
|
46
|
+
- Graceful handling of missing files and invalid configurations
|
|
47
|
+
- **TypeScript support**
|
|
48
|
+
- Full TypeScript definitions
|
|
49
|
+
- Type-safe API
|
|
50
|
+
- Exported types: `EnvConfig`, `ServiceKey`
|
|
51
|
+
- **Configurable logging system**
|
|
52
|
+
- Injectable logger interface for custom logging implementations
|
|
53
|
+
- Environment variable `DEBUG_AUTH_LOG` to control debug output
|
|
54
|
+
- Minimal logging by default (only errors and manual URLs)
|
|
55
|
+
- Detailed debug logging when `DEBUG_AUTH_LOG=true`
|
|
56
|
+
- Custom logger support via constructor injection
|
|
57
|
+
- **Testing infrastructure**
|
|
58
|
+
- Unit tests for all components
|
|
59
|
+
- Integration tests for authentication flows
|
|
60
|
+
- Sequential test execution for reliable results
|
|
61
|
+
- Test scenarios covering error cases, browser auth, and token refresh
|
|
62
|
+
- Silent test output (no verbose logging during test execution)
|
|
63
|
+
- Clean test output focusing on failures
|
|
64
|
+
- **Documentation**
|
|
65
|
+
- Complete API documentation
|
|
66
|
+
- Architecture documentation
|
|
67
|
+
- Installation guide
|
|
68
|
+
- Usage guide with examples
|
|
69
|
+
- Testing methodology guide
|
|
70
|
+
- Logging configuration guide
|
|
71
|
+
|
|
72
|
+
### Changed
|
|
73
|
+
- **Logging behavior**
|
|
74
|
+
- Default logger now shows minimal output (only errors and manual URLs)
|
|
75
|
+
- Debug messages only visible when `DEBUG_AUTH_LOG=true`
|
|
76
|
+
- Browser opening notifications moved to debug level
|
|
77
|
+
- Token refresh operations logged at debug level
|
|
78
|
+
- **Test output**
|
|
79
|
+
- Removed verbose logging from test files
|
|
80
|
+
- Tests now produce clean, focused output
|
|
81
|
+
- Only test results and errors are displayed
|
|
82
|
+
- Improved readability and CI/CD integration
|
|
83
|
+
|
|
84
|
+
### Technical Details
|
|
85
|
+
- **Dependencies**
|
|
86
|
+
- `@mcp-abap-adt/connection` - Token refresh utilities
|
|
87
|
+
- `axios` - HTTP requests
|
|
88
|
+
- `express` - OAuth callback server
|
|
89
|
+
- `open` - Browser opening utility
|
|
90
|
+
- **Node.js version**: >= 18.0.0
|
|
91
|
+
- **Module system**: CommonJS
|
|
92
|
+
- **Build output**: TypeScript compiled to JavaScript with type definitions
|
|
93
|
+
- **Logging**: Injectable logger interface with environment variable control
|
|
94
|
+
|
package/CONTRIBUTORS.md
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Contributors
|
|
2
|
+
|
|
3
|
+
Thank you to all contributors who have helped make `@mcp-abap-adt/auth-broker` better!
|
|
4
|
+
|
|
5
|
+
## How to Contribute
|
|
6
|
+
|
|
7
|
+
We welcome contributions! Please see the project documentation for details on how to get started.
|
|
8
|
+
|
|
9
|
+
## Contributors
|
|
10
|
+
|
|
11
|
+
<!-- Contributors are listed alphabetically by last name -->
|
|
12
|
+
|
|
13
|
+
### Core Contributors
|
|
14
|
+
|
|
15
|
+
- **Oleksii Kyslytsia** - Project creator and maintainer
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Recognition
|
|
20
|
+
|
|
21
|
+
We appreciate every contribution, whether it's:
|
|
22
|
+
- 🐛 Bug reports and fixes
|
|
23
|
+
- ✨ New features and enhancements
|
|
24
|
+
- 📚 Documentation improvements
|
|
25
|
+
- 🧪 Test coverage
|
|
26
|
+
- 💬 Feedback and suggestions
|
|
27
|
+
|
|
28
|
+
Every contribution helps make `@mcp-abap-adt/auth-broker` better for everyone!
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
**Note:** This list is manually maintained. If you've contributed but don't see your name here, please open an issue or submit a PR to update this file.
|
|
33
|
+
|
package/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Oleksii Kyslytsia
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
package/README.md
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
# @mcp-abap-adt/auth-broker
|
|
2
|
+
|
|
3
|
+
JWT authentication broker for MCP ABAP ADT server. Manages authentication tokens based on destination headers, automatically loading tokens from `.env` files and refreshing them using service keys when needed.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 🔐 **Destination-based Authentication**: Load tokens based on `x-mcp-destination` header
|
|
8
|
+
- 📁 **Environment File Support**: Automatically loads tokens from `{destination}.env` files
|
|
9
|
+
- 🔄 **Automatic Token Refresh**: Refreshes expired tokens using service keys from `{destination}.json` files
|
|
10
|
+
- ✅ **Token Validation**: Validates tokens by testing connection to SAP system
|
|
11
|
+
- 💾 **Token Caching**: In-memory caching for improved performance
|
|
12
|
+
- 🔧 **Configurable Base Path**: Customize where `.env` and `.json` files are stored
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install @mcp-abap-adt/auth-broker
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Usage
|
|
21
|
+
|
|
22
|
+
```typescript
|
|
23
|
+
import { AuthBroker } from '@mcp-abap-adt/auth-broker';
|
|
24
|
+
|
|
25
|
+
const broker = new AuthBroker('/path/to/destinations', 'chrome');
|
|
26
|
+
|
|
27
|
+
// Get token for destination (loads from .env, validates, refreshes if needed)
|
|
28
|
+
const token = await broker.getToken('TRIAL');
|
|
29
|
+
|
|
30
|
+
// Force refresh token using service key
|
|
31
|
+
const newToken = await broker.refreshToken('TRIAL');
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Configuration
|
|
35
|
+
|
|
36
|
+
### Environment Variables
|
|
37
|
+
|
|
38
|
+
- `AUTH_BROKER_PATH` - Colon/semicolon-separated paths for searching `.env` and `.json` files (default: current working directory)
|
|
39
|
+
- `DEBUG_AUTH_LOG` - Set to `true` to enable debug logging (default: `false`)
|
|
40
|
+
|
|
41
|
+
### File Structure
|
|
42
|
+
|
|
43
|
+
#### Environment File (`{destination}.env`)
|
|
44
|
+
|
|
45
|
+
```env
|
|
46
|
+
SAP_URL=https://your-system.abap.us10.hana.ondemand.com
|
|
47
|
+
SAP_CLIENT=100
|
|
48
|
+
SAP_JWT_TOKEN=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...
|
|
49
|
+
SAP_REFRESH_TOKEN=refresh_token_string
|
|
50
|
+
SAP_UAA_URL=https://your-account.authentication.us10.hana.ondemand.com
|
|
51
|
+
SAP_UAA_CLIENT_ID=client_id
|
|
52
|
+
SAP_UAA_CLIENT_SECRET=client_secret
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
#### Service Key File (`{destination}.json`)
|
|
56
|
+
|
|
57
|
+
```json
|
|
58
|
+
{
|
|
59
|
+
"url": "https://your-system.abap.us10.hana.ondemand.com",
|
|
60
|
+
"uaa": {
|
|
61
|
+
"url": "https://your-account.authentication.us10.hana.ondemand.com",
|
|
62
|
+
"clientid": "your_client_id",
|
|
63
|
+
"clientsecret": "your_client_secret"
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## API
|
|
69
|
+
|
|
70
|
+
### `AuthBroker`
|
|
71
|
+
|
|
72
|
+
#### Constructor
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
new AuthBroker(searchPaths?: string | string[], browser?: string)
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
- `searchPaths` - Optional base directory or array of directories for searching `.env` and `.json` files
|
|
79
|
+
- `browser` - Optional browser name for authentication (`chrome`, `edge`, `firefox`, `system`, `none`). Default: `system`
|
|
80
|
+
|
|
81
|
+
#### Methods
|
|
82
|
+
|
|
83
|
+
##### `getToken(destination: string): Promise<string>`
|
|
84
|
+
|
|
85
|
+
Gets authentication token for destination. Tries to load from `.env` file, validates it, and refreshes if needed.
|
|
86
|
+
|
|
87
|
+
##### `refreshToken(destination: string): Promise<string>`
|
|
88
|
+
|
|
89
|
+
Force refresh token for destination using service key from `{destination}.json` file.
|
|
90
|
+
|
|
91
|
+
##### `clearCache(destination: string): void`
|
|
92
|
+
|
|
93
|
+
Clear cached token for specific destination.
|
|
94
|
+
|
|
95
|
+
##### `clearAllCache(): void`
|
|
96
|
+
|
|
97
|
+
Clear all cached tokens.
|
|
98
|
+
|
|
99
|
+
## Testing
|
|
100
|
+
|
|
101
|
+
Tests are located in `src/__tests__/` and use Jest as the test runner.
|
|
102
|
+
|
|
103
|
+
### Running Tests
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
# Run all tests
|
|
107
|
+
npm test
|
|
108
|
+
|
|
109
|
+
# Run specific test file (all tests in that file)
|
|
110
|
+
npm test -- getToken.test.ts
|
|
111
|
+
npm test -- refreshToken.test.ts
|
|
112
|
+
|
|
113
|
+
# Run specific test by name/pattern
|
|
114
|
+
npm test -- getToken.test.ts -t "Test 1"
|
|
115
|
+
npm test -- getToken.test.ts -t "Test 2"
|
|
116
|
+
npm test -- getToken.test.ts -t "Test 3"
|
|
117
|
+
|
|
118
|
+
# Run test group (e.g., all getToken tests)
|
|
119
|
+
npm test -- getToken.test.ts
|
|
120
|
+
|
|
121
|
+
# Note: Test 2 requires Test 1 to pass first (test1Passed flag)
|
|
122
|
+
# To run Test 2 alone, you may need to run all tests in the file:
|
|
123
|
+
npm test -- getToken.test.ts
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Test Structure
|
|
127
|
+
|
|
128
|
+
Tests are designed to run sequentially (guaranteed by `maxWorkers: 1` and `maxConcurrency: 1` in `jest.config.js`):
|
|
129
|
+
|
|
130
|
+
1. **Test 1**: Verifies error handling for non-existent destination (`NO_EXISTS`)
|
|
131
|
+
- Requires: `NO_EXISTS.json` should NOT exist
|
|
132
|
+
|
|
133
|
+
2. **Test 2**: Tests browser authentication when service key exists but `.env` file doesn't
|
|
134
|
+
- Requires: `TRIAL.json` must exist, `TRIAL.env` should NOT exist
|
|
135
|
+
- Will open browser for OAuth authentication
|
|
136
|
+
|
|
137
|
+
3. **Test 3**: Tests token refresh using existing `.env` file
|
|
138
|
+
- Requires: `TRIAL.json` and `TRIAL.env` must exist
|
|
139
|
+
- Can run independently if `.env` file exists (created manually or by Test 2)
|
|
140
|
+
|
|
141
|
+
### Test Setup
|
|
142
|
+
|
|
143
|
+
Place your service key file in `./test-destinations/TRIAL.json` to run tests 2 and 3.
|
|
144
|
+
|
|
145
|
+
Tests will automatically skip if required files are missing or present when they shouldn't be.
|
|
146
|
+
|
|
147
|
+
## Documentation
|
|
148
|
+
|
|
149
|
+
Complete documentation is available in the [`docs/`](docs/) directory:
|
|
150
|
+
|
|
151
|
+
- **[Architecture](docs/architecture/ARCHITECTURE.md)** - System architecture and design decisions
|
|
152
|
+
- **[Development](docs/development/)** - Testing methodology and development roadmap
|
|
153
|
+
- **[Development Roadmap](docs/development/DEVELOPMENT_ROADMAP.md)** - Development roadmap and future plans
|
|
154
|
+
- **[Installation](docs/installing/INSTALLATION.md)** - Installation and setup guide
|
|
155
|
+
- **[Usage](docs/using/USAGE.md)** - API reference and usage examples
|
|
156
|
+
|
|
157
|
+
See [docs/README.md](docs/README.md) for the complete documentation index.
|
|
158
|
+
|
|
159
|
+
## Contributors
|
|
160
|
+
|
|
161
|
+
Thank you to all contributors! See [CONTRIBUTORS.md](CONTRIBUTORS.md) for the complete list.
|
|
162
|
+
|
|
163
|
+
## License
|
|
164
|
+
|
|
165
|
+
MIT
|
|
166
|
+
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Main AuthBroker class for managing JWT tokens based on destinations
|
|
3
|
+
*/
|
|
4
|
+
import { Logger } from './logger';
|
|
5
|
+
/**
|
|
6
|
+
* AuthBroker manages JWT authentication tokens for destinations
|
|
7
|
+
*/
|
|
8
|
+
export declare class AuthBroker {
|
|
9
|
+
private searchPaths;
|
|
10
|
+
private browser;
|
|
11
|
+
private logger;
|
|
12
|
+
/**
|
|
13
|
+
* Create a new AuthBroker instance
|
|
14
|
+
* @param searchPaths Optional search paths for .env and .json files.
|
|
15
|
+
* Can be a single path (string) or array of paths.
|
|
16
|
+
* Priority:
|
|
17
|
+
* 1. Constructor parameter (highest)
|
|
18
|
+
* 2. AUTH_BROKER_PATH environment variable (colon/semicolon-separated)
|
|
19
|
+
* 3. Current working directory (lowest)
|
|
20
|
+
* @param browser Optional browser name for authentication (chrome, edge, firefox, system, none).
|
|
21
|
+
* Default: 'system' (system default browser).
|
|
22
|
+
* Use 'none' to print URL instead of opening browser.
|
|
23
|
+
* @param logger Optional logger instance. If not provided, uses default logger.
|
|
24
|
+
*/
|
|
25
|
+
constructor(searchPaths?: string | string[], browser?: string, logger?: Logger);
|
|
26
|
+
/**
|
|
27
|
+
* Get authentication token for destination.
|
|
28
|
+
* Tries to load from .env file, validates it, and refreshes if needed.
|
|
29
|
+
* @param destination Destination name (e.g., "TRIAL")
|
|
30
|
+
* @returns Promise that resolves to JWT token string
|
|
31
|
+
* @throws Error if neither .env file nor service key found
|
|
32
|
+
*/
|
|
33
|
+
getToken(destination: string): Promise<string>;
|
|
34
|
+
/**
|
|
35
|
+
* Force refresh token for destination using service key.
|
|
36
|
+
* If no refresh token exists, starts browser authentication flow.
|
|
37
|
+
* @param destination Destination name (e.g., "TRIAL")
|
|
38
|
+
* @returns Promise that resolves to new JWT token string
|
|
39
|
+
*/
|
|
40
|
+
refreshToken(destination: string): Promise<string>;
|
|
41
|
+
/**
|
|
42
|
+
* Save token to {destination}.env file
|
|
43
|
+
* Creates .env file similar to sap-abap-auth utility format
|
|
44
|
+
* @private
|
|
45
|
+
*/
|
|
46
|
+
private saveTokenToEnv;
|
|
47
|
+
/**
|
|
48
|
+
* Get token expiry information from JWT token
|
|
49
|
+
* @private
|
|
50
|
+
*/
|
|
51
|
+
private getTokenExpiry;
|
|
52
|
+
/**
|
|
53
|
+
* Clear cached token for specific destination
|
|
54
|
+
* @param destination Destination name
|
|
55
|
+
*/
|
|
56
|
+
clearCache(destination: string): void;
|
|
57
|
+
/**
|
|
58
|
+
* Clear all cached tokens
|
|
59
|
+
*/
|
|
60
|
+
clearAllCache(): void;
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=AuthBroker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AuthBroker.d.ts","sourceRoot":"","sources":["../src/AuthBroker.ts"],"names":[],"mappings":"AAAA;;GAEG;AAYH,OAAO,EAAE,MAAM,EAAiB,MAAM,UAAU,CAAC;AAIjD;;GAEG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,WAAW,CAAW;IAC9B,OAAO,CAAC,OAAO,CAAqB;IACpC,OAAO,CAAC,MAAM,CAAS;IAEvB;;;;;;;;;;;;OAYG;gBACS,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM;IAM9E;;;;;;OAMG;IACG,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAKpD;;;;;OAKG;IACG,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAKxD;;;;OAIG;YACW,cAAc;IA6E5B;;;OAGG;IACH,OAAO,CAAC,cAAc;IAyCtB;;;OAGG;IACH,UAAU,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI;IAIrC;;OAEG;IACH,aAAa,IAAI,IAAI;CAGtB"}
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Main AuthBroker class for managing JWT tokens based on destinations
|
|
4
|
+
*/
|
|
5
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
6
|
+
if (k2 === undefined) k2 = k;
|
|
7
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
8
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
9
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
10
|
+
}
|
|
11
|
+
Object.defineProperty(o, k2, desc);
|
|
12
|
+
}) : (function(o, m, k, k2) {
|
|
13
|
+
if (k2 === undefined) k2 = k;
|
|
14
|
+
o[k2] = m[k];
|
|
15
|
+
}));
|
|
16
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
17
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
18
|
+
}) : function(o, v) {
|
|
19
|
+
o["default"] = v;
|
|
20
|
+
});
|
|
21
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
22
|
+
var ownKeys = function(o) {
|
|
23
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
24
|
+
var ar = [];
|
|
25
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
26
|
+
return ar;
|
|
27
|
+
};
|
|
28
|
+
return ownKeys(o);
|
|
29
|
+
};
|
|
30
|
+
return function (mod) {
|
|
31
|
+
if (mod && mod.__esModule) return mod;
|
|
32
|
+
var result = {};
|
|
33
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
34
|
+
__setModuleDefault(result, mod);
|
|
35
|
+
return result;
|
|
36
|
+
};
|
|
37
|
+
})();
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.AuthBroker = void 0;
|
|
40
|
+
const fs = __importStar(require("fs"));
|
|
41
|
+
const path = __importStar(require("path"));
|
|
42
|
+
const cache_1 = require("./cache");
|
|
43
|
+
const pathResolver_1 = require("./pathResolver");
|
|
44
|
+
const logger_1 = require("./logger");
|
|
45
|
+
const refreshToken_1 = require("./refreshToken");
|
|
46
|
+
const getToken_1 = require("./getToken");
|
|
47
|
+
/**
|
|
48
|
+
* AuthBroker manages JWT authentication tokens for destinations
|
|
49
|
+
*/
|
|
50
|
+
class AuthBroker {
|
|
51
|
+
searchPaths;
|
|
52
|
+
browser;
|
|
53
|
+
logger;
|
|
54
|
+
/**
|
|
55
|
+
* Create a new AuthBroker instance
|
|
56
|
+
* @param searchPaths Optional search paths for .env and .json files.
|
|
57
|
+
* Can be a single path (string) or array of paths.
|
|
58
|
+
* Priority:
|
|
59
|
+
* 1. Constructor parameter (highest)
|
|
60
|
+
* 2. AUTH_BROKER_PATH environment variable (colon/semicolon-separated)
|
|
61
|
+
* 3. Current working directory (lowest)
|
|
62
|
+
* @param browser Optional browser name for authentication (chrome, edge, firefox, system, none).
|
|
63
|
+
* Default: 'system' (system default browser).
|
|
64
|
+
* Use 'none' to print URL instead of opening browser.
|
|
65
|
+
* @param logger Optional logger instance. If not provided, uses default logger.
|
|
66
|
+
*/
|
|
67
|
+
constructor(searchPaths, browser, logger) {
|
|
68
|
+
this.searchPaths = (0, pathResolver_1.resolveSearchPaths)(searchPaths);
|
|
69
|
+
this.browser = browser || 'system';
|
|
70
|
+
this.logger = logger || logger_1.defaultLogger;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Get authentication token for destination.
|
|
74
|
+
* Tries to load from .env file, validates it, and refreshes if needed.
|
|
75
|
+
* @param destination Destination name (e.g., "TRIAL")
|
|
76
|
+
* @returns Promise that resolves to JWT token string
|
|
77
|
+
* @throws Error if neither .env file nor service key found
|
|
78
|
+
*/
|
|
79
|
+
async getToken(destination) {
|
|
80
|
+
// Use getToken function with logger
|
|
81
|
+
return (0, getToken_1.getToken)(destination, this.searchPaths, this.logger);
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Force refresh token for destination using service key.
|
|
85
|
+
* If no refresh token exists, starts browser authentication flow.
|
|
86
|
+
* @param destination Destination name (e.g., "TRIAL")
|
|
87
|
+
* @returns Promise that resolves to new JWT token string
|
|
88
|
+
*/
|
|
89
|
+
async refreshToken(destination) {
|
|
90
|
+
// Use refreshToken function with logger and browser
|
|
91
|
+
return (0, refreshToken_1.refreshToken)(destination, this.searchPaths, this.logger);
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Save token to {destination}.env file
|
|
95
|
+
* Creates .env file similar to sap-abap-auth utility format
|
|
96
|
+
* @private
|
|
97
|
+
*/
|
|
98
|
+
async saveTokenToEnv(destination, savePath, config) {
|
|
99
|
+
// Ensure directory exists
|
|
100
|
+
if (!fs.existsSync(savePath)) {
|
|
101
|
+
fs.mkdirSync(savePath, { recursive: true });
|
|
102
|
+
}
|
|
103
|
+
const envFilePath = path.join(savePath, `${destination}.env`);
|
|
104
|
+
const tempFilePath = `${envFilePath}.tmp`;
|
|
105
|
+
// Write to temporary file first (atomic write)
|
|
106
|
+
// Format similar to sap-abap-auth utility - always create fresh file
|
|
107
|
+
const envLines = [];
|
|
108
|
+
// Add token expiry information if we can decode JWT
|
|
109
|
+
const jwtExpiry = this.getTokenExpiry(config.jwtToken);
|
|
110
|
+
const refreshExpiry = config.refreshToken ? this.getTokenExpiry(config.refreshToken) : null;
|
|
111
|
+
if (jwtExpiry || refreshExpiry) {
|
|
112
|
+
envLines.push('# Token Expiry Information (auto-generated)');
|
|
113
|
+
if (jwtExpiry) {
|
|
114
|
+
envLines.push(`# JWT Token expires: ${jwtExpiry.readableDate} (UTC)`);
|
|
115
|
+
envLines.push(`# JWT Token expires at: ${jwtExpiry.dateString}`);
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
envLines.push('# JWT Token expiry: Unable to determine (token may not be a standard JWT)');
|
|
119
|
+
}
|
|
120
|
+
if (refreshExpiry) {
|
|
121
|
+
envLines.push(`# Refresh Token expires: ${refreshExpiry.readableDate} (UTC)`);
|
|
122
|
+
envLines.push(`# Refresh Token expires at: ${refreshExpiry.dateString}`);
|
|
123
|
+
}
|
|
124
|
+
else if (config.refreshToken) {
|
|
125
|
+
envLines.push('# Refresh Token expiry: Unable to determine (token may not be a standard JWT)');
|
|
126
|
+
}
|
|
127
|
+
envLines.push('');
|
|
128
|
+
}
|
|
129
|
+
// Write JWT auth parameters (similar to sap-abap-auth format)
|
|
130
|
+
// Required fields
|
|
131
|
+
envLines.push(`SAP_URL=${config.sapUrl}`);
|
|
132
|
+
if (config.sapClient) {
|
|
133
|
+
envLines.push(`SAP_CLIENT=${config.sapClient}`);
|
|
134
|
+
}
|
|
135
|
+
if (config.language) {
|
|
136
|
+
envLines.push(`SAP_LANGUAGE=${config.language}`);
|
|
137
|
+
}
|
|
138
|
+
envLines.push('TLS_REJECT_UNAUTHORIZED=0');
|
|
139
|
+
envLines.push('SAP_AUTH_TYPE=jwt');
|
|
140
|
+
envLines.push(`SAP_JWT_TOKEN=${config.jwtToken}`);
|
|
141
|
+
if (config.refreshToken) {
|
|
142
|
+
envLines.push(`SAP_REFRESH_TOKEN=${config.refreshToken}`);
|
|
143
|
+
}
|
|
144
|
+
if (config.uaaUrl) {
|
|
145
|
+
envLines.push(`SAP_UAA_URL=${config.uaaUrl}`);
|
|
146
|
+
}
|
|
147
|
+
if (config.uaaClientId) {
|
|
148
|
+
envLines.push(`SAP_UAA_CLIENT_ID=${config.uaaClientId}`);
|
|
149
|
+
}
|
|
150
|
+
if (config.uaaClientSecret) {
|
|
151
|
+
envLines.push(`SAP_UAA_CLIENT_SECRET=${config.uaaClientSecret}`);
|
|
152
|
+
}
|
|
153
|
+
envLines.push('');
|
|
154
|
+
envLines.push('# For JWT authentication');
|
|
155
|
+
envLines.push('# SAP_USERNAME=your_username');
|
|
156
|
+
envLines.push('# SAP_PASSWORD=your_password');
|
|
157
|
+
const envContent = envLines.join('\n') + '\n';
|
|
158
|
+
// Write to temp file
|
|
159
|
+
fs.writeFileSync(tempFilePath, envContent, 'utf8');
|
|
160
|
+
// Atomic rename
|
|
161
|
+
fs.renameSync(tempFilePath, envFilePath);
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Get token expiry information from JWT token
|
|
165
|
+
* @private
|
|
166
|
+
*/
|
|
167
|
+
getTokenExpiry(token) {
|
|
168
|
+
try {
|
|
169
|
+
// JWT tokens have format: header.payload.signature
|
|
170
|
+
const parts = token.split('.');
|
|
171
|
+
if (parts.length !== 3) {
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
174
|
+
// Decode payload (base64url)
|
|
175
|
+
const payload = parts[1];
|
|
176
|
+
// Add padding if needed
|
|
177
|
+
const padded = payload + '='.repeat((4 - payload.length % 4) % 4);
|
|
178
|
+
const decoded = Buffer.from(padded.replace(/-/g, '+').replace(/_/g, '/'), 'base64').toString('utf8');
|
|
179
|
+
const parsed = JSON.parse(decoded);
|
|
180
|
+
// Check for exp claim
|
|
181
|
+
if (parsed.exp) {
|
|
182
|
+
const expiryDate = new Date(parsed.exp * 1000);
|
|
183
|
+
const readableDate = expiryDate.toLocaleString('en-US', {
|
|
184
|
+
weekday: 'long',
|
|
185
|
+
year: 'numeric',
|
|
186
|
+
month: 'long',
|
|
187
|
+
day: 'numeric',
|
|
188
|
+
hour: '2-digit',
|
|
189
|
+
minute: '2-digit',
|
|
190
|
+
second: '2-digit',
|
|
191
|
+
timeZone: 'UTC',
|
|
192
|
+
timeZoneName: 'short',
|
|
193
|
+
});
|
|
194
|
+
return {
|
|
195
|
+
dateString: expiryDate.toISOString(),
|
|
196
|
+
readableDate: readableDate,
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
return null;
|
|
200
|
+
}
|
|
201
|
+
catch {
|
|
202
|
+
return null;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Clear cached token for specific destination
|
|
207
|
+
* @param destination Destination name
|
|
208
|
+
*/
|
|
209
|
+
clearCache(destination) {
|
|
210
|
+
(0, cache_1.clearCache)(destination);
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Clear all cached tokens
|
|
214
|
+
*/
|
|
215
|
+
clearAllCache() {
|
|
216
|
+
(0, cache_1.clearAllCache)();
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
exports.AuthBroker = AuthBroker;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Common test helpers for AuthBroker tests
|
|
3
|
+
*/
|
|
4
|
+
import { AuthBroker } from '../AuthBroker';
|
|
5
|
+
export declare const TEST_DESTINATIONS_PATH: string;
|
|
6
|
+
export interface TestBrokers {
|
|
7
|
+
tempDir: string;
|
|
8
|
+
broker: AuthBroker;
|
|
9
|
+
testDestinationsBroker: AuthBroker;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Setup test brokers for a test suite
|
|
13
|
+
*/
|
|
14
|
+
export declare function setupTestBrokers(testName: string): TestBrokers;
|
|
15
|
+
/**
|
|
16
|
+
* Cleanup test brokers
|
|
17
|
+
*/
|
|
18
|
+
export declare function cleanupTestBrokers(brokers: TestBrokers): void;
|
|
19
|
+
/**
|
|
20
|
+
* Check if NO_EXISTS.json exists and skip test if it does
|
|
21
|
+
*/
|
|
22
|
+
export declare function checkNoExistsFile(): boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Prepare Test 2: Remove TRIAL.env if exists, check for TRIAL.json
|
|
25
|
+
*/
|
|
26
|
+
export declare function prepareTest2(): {
|
|
27
|
+
envFile: string;
|
|
28
|
+
serviceKeyPath: string;
|
|
29
|
+
shouldSkip: boolean;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Prepare Test 3: Check for TRIAL.json and TRIAL.env
|
|
33
|
+
*/
|
|
34
|
+
export declare function prepareTest3(): {
|
|
35
|
+
envFile: string;
|
|
36
|
+
serviceKeyPath: string;
|
|
37
|
+
sapUrl: string;
|
|
38
|
+
shouldSkip: boolean;
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* Verify .env file contains required tokens
|
|
42
|
+
*/
|
|
43
|
+
export declare function verifyEnvFile(envFile: string, requireRefreshToken?: boolean): void;
|
|
44
|
+
//# sourceMappingURL=testHelpers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"testHelpers.d.ts","sourceRoot":"","sources":["../../src/__tests__/testHelpers.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAI3C,eAAO,MAAM,sBAAsB,QAAsF,CAAC;AAE1H,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,UAAU,CAAC;IACnB,sBAAsB,EAAE,UAAU,CAAC;CACpC;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,WAAW,CAM9D;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI,CAM7D;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,OAAO,CAM3C;AAED;;GAEG;AACH,wBAAgB,YAAY,IAAI;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,OAAO,CAAA;CAAE,CAe/F;AAED;;GAEG;AACH,wBAAgB,YAAY,IAAI;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,cAAc,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,OAAO,CAAA;CAAE,CAsB/G;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,mBAAmB,GAAE,OAAe,GAAG,IAAI,CAOzF"}
|