@mcp-abap-adt/auth-broker 0.1.1 → 0.1.3

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,6 +9,50 @@ 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.3] - 2025-12-01
13
+
14
+ ### Added
15
+ - **Configurable Log Levels** - Added log level control via environment variable `AUTH_LOG_LEVEL`
16
+ - `error` - only errors
17
+ - `warn` - errors and warnings
18
+ - `info` - errors, warnings, and info (default)
19
+ - `debug` - all messages
20
+ - Backward compatible: `DEBUG_AUTH_LOG=true` still works (sets level to debug)
21
+ - New `warn()` method in Logger interface for warning messages
22
+
23
+ ### Fixed
24
+ - **Error Handling for Consumer** - Improved error handling to ensure consumer can distinguish different error types
25
+ - Service key missing error: throws `Error` with message containing "No authentication found" or "Service key file not found"
26
+ - Browser opening failed error: throws `Error` with message containing "Browser opening failed"
27
+ - Both errors are now properly thrown and can be caught by consumer in `catch` blocks
28
+ - Errors are distinct and can be programmatically handled differently
29
+
30
+ ### Changed
31
+ - **Logger Implementation** - Enhanced logger with log level filtering
32
+ - All log methods now respect the configured log level
33
+ - `info()`, `debug()`, `error()`, and new `warn()` methods filter output based on `AUTH_LOG_LEVEL`
34
+ - Default log level is `info` (shows errors, warnings, and info messages)
35
+
36
+ ## [0.1.2] - 2025-11-30
37
+
38
+ ### Added
39
+ - **Storage Interfaces** - New interfaces for custom storage implementations
40
+ - `ServiceKeyStore` interface - for reading service keys
41
+ - `SessionStore` interface - for reading/writing session data (tokens, configuration)
42
+ - `FileServiceKeyStore` - default file-based implementation for service keys
43
+ - `FileSessionStore` - default file-based implementation for sessions
44
+ - **Dependency Injection Support** - AuthBroker now accepts custom stores via constructor
45
+ - Can provide custom `ServiceKeyStore` and `SessionStore` implementations
46
+ - Default to file-based stores if not provided (backward compatible)
47
+ - Enables custom storage backends (database, cloud, etc.) without creating new packages
48
+ - New API: constructor accepts object with `serviceKeyStore` and `sessionStore` properties
49
+ - Backward compatible: still accepts `searchPaths` as first parameter (string/array)
50
+
51
+ ### Technical Details
52
+ - Storage abstraction allows consumers to provide custom implementations
53
+ - No breaking changes - existing code continues to work
54
+ - File-based stores remain the default implementation
55
+
12
56
  ## [0.1.1] - 2025-11-30
13
57
 
14
58
  ### Added
@@ -2,6 +2,7 @@
2
2
  * Main AuthBroker class for managing JWT tokens based on destinations
3
3
  */
4
4
  import { Logger } from './logger';
5
+ import { ServiceKeyStore, SessionStore } from './stores/interfaces';
5
6
  /**
6
7
  * AuthBroker manages JWT authentication tokens for destinations
7
8
  */
@@ -9,26 +10,33 @@ export declare class AuthBroker {
9
10
  private searchPaths;
10
11
  private browser;
11
12
  private logger;
13
+ private serviceKeyStore;
14
+ private sessionStore;
12
15
  /**
13
16
  * 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)
17
+ * @param searchPathsOrStores Optional search paths for .env and .json files (backward compatibility),
18
+ * OR object with custom stores.
19
+ * If string/array: creates default file-based stores with these paths.
20
+ * If object: uses provided stores (searchPaths ignored).
21
+ * Priority for searchPaths:
22
+ * 1. Constructor parameter (highest)
23
+ * 2. AUTH_BROKER_PATH environment variable (colon/semicolon-separated)
24
+ * 3. Current working directory (lowest)
20
25
  * @param browser Optional browser name for authentication (chrome, edge, firefox, system, none).
21
26
  * Default: 'system' (system default browser).
22
27
  * Use 'none' to print URL instead of opening browser.
23
28
  * @param logger Optional logger instance. If not provided, uses default logger.
24
29
  */
25
- constructor(searchPaths?: string | string[], browser?: string, logger?: Logger);
30
+ constructor(searchPathsOrStores?: string | string[] | {
31
+ serviceKeyStore?: ServiceKeyStore;
32
+ sessionStore?: SessionStore;
33
+ }, browser?: string, logger?: Logger);
26
34
  /**
27
35
  * Get authentication token for destination.
28
- * Tries to load from .env file, validates it, and refreshes if needed.
36
+ * Tries to load from session store, validates it, and refreshes if needed.
29
37
  * @param destination Destination name (e.g., "TRIAL")
30
38
  * @returns Promise that resolves to JWT token string
31
- * @throws Error if neither .env file nor service key found
39
+ * @throws Error if neither session data nor service key found
32
40
  */
33
41
  getToken(destination: string): Promise<string>;
34
42
  /**
@@ -38,24 +46,18 @@ export declare class AuthBroker {
38
46
  * @returns Promise that resolves to new JWT token string
39
47
  */
40
48
  refreshToken(destination: string): Promise<string>;
49
+ /**
50
+ * Internal refresh token implementation
51
+ * @private
52
+ */
53
+ private refreshTokenInternal;
41
54
  /**
42
55
  * Get SAP URL for destination.
43
- * Tries to load from .env file first, then from service key.
56
+ * Tries to load from session store first, then from service key store.
44
57
  * @param destination Destination name (e.g., "TRIAL")
45
58
  * @returns Promise that resolves to SAP URL string, or undefined if not found
46
59
  */
47
60
  getSapUrl(destination: string): Promise<string | undefined>;
48
- /**
49
- * Save token to {destination}.env file
50
- * Creates .env file similar to sap-abap-auth utility format
51
- * @private
52
- */
53
- private saveTokenToEnv;
54
- /**
55
- * Get token expiry information from JWT token
56
- * @private
57
- */
58
- private getTokenExpiry;
59
61
  /**
60
62
  * Clear cached token for specific destination
61
63
  * @param destination Destination name
@@ -65,5 +67,10 @@ export declare class AuthBroker {
65
67
  * Clear all cached tokens
66
68
  */
67
69
  clearAllCache(): void;
70
+ /**
71
+ * Get search paths for error messages (from file stores if available)
72
+ * @private
73
+ */
74
+ private getSearchPathsForError;
68
75
  }
69
76
  //# sourceMappingURL=AuthBroker.d.ts.map
@@ -1 +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;;;;;OAKG;IACG,SAAS,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAgBjE;;;;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"}
1
+ {"version":3,"file":"AuthBroker.d.ts","sourceRoot":"","sources":["../src/AuthBroker.ts"],"names":[],"mappings":"AAAA;;GAEG;AAQH,OAAO,EAAE,MAAM,EAAiB,MAAM,UAAU,CAAC;AACjD,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAGpE;;GAEG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,WAAW,CAAW;IAC9B,OAAO,CAAC,OAAO,CAAqB;IACpC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,eAAe,CAAkB;IACzC,OAAO,CAAC,YAAY,CAAe;IAEnC;;;;;;;;;;;;;;OAcG;gBAED,mBAAmB,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG;QAAE,eAAe,CAAC,EAAE,eAAe,CAAC;QAAC,YAAY,CAAC,EAAE,YAAY,CAAA;KAAE,EAC5G,OAAO,CAAC,EAAE,MAAM,EAChB,MAAM,CAAC,EAAE,MAAM;IAmBjB;;;;;;OAMG;IACG,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAgDpD;;;;;OAKG;IACG,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAmBxD;;;OAGG;YACW,oBAAoB;IAuDlC;;;;;OAKG;IACG,SAAS,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAiBjE;;;OAGG;IACH,UAAU,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI;IAIrC;;OAEG;IACH,aAAa,IAAI,IAAI;IAIrB;;;OAGG;IACH,OAAO,CAAC,sBAAsB;CAW/B"}
@@ -2,50 +2,15 @@
2
2
  /**
3
3
  * Main AuthBroker class for managing JWT tokens based on destinations
4
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
5
  Object.defineProperty(exports, "__esModule", { value: true });
39
6
  exports.AuthBroker = void 0;
40
- const fs = __importStar(require("fs"));
41
- const path = __importStar(require("path"));
42
- const envLoader_1 = require("./envLoader");
43
- const serviceKeyLoader_1 = require("./serviceKeyLoader");
7
+ const tokenValidator_1 = require("./tokenValidator");
8
+ const tokenRefresher_1 = require("./tokenRefresher");
9
+ const browserAuth_1 = require("./browserAuth");
44
10
  const cache_1 = require("./cache");
45
11
  const pathResolver_1 = require("./pathResolver");
46
12
  const logger_1 = require("./logger");
47
- const refreshToken_1 = require("./refreshToken");
48
- const getToken_1 = require("./getToken");
13
+ const stores_1 = require("./stores");
49
14
  /**
50
15
  * AuthBroker manages JWT authentication tokens for destinations
51
16
  */
@@ -53,34 +18,88 @@ class AuthBroker {
53
18
  searchPaths;
54
19
  browser;
55
20
  logger;
21
+ serviceKeyStore;
22
+ sessionStore;
56
23
  /**
57
24
  * Create a new AuthBroker instance
58
- * @param searchPaths Optional search paths for .env and .json files.
59
- * Can be a single path (string) or array of paths.
60
- * Priority:
61
- * 1. Constructor parameter (highest)
62
- * 2. AUTH_BROKER_PATH environment variable (colon/semicolon-separated)
63
- * 3. Current working directory (lowest)
25
+ * @param searchPathsOrStores Optional search paths for .env and .json files (backward compatibility),
26
+ * OR object with custom stores.
27
+ * If string/array: creates default file-based stores with these paths.
28
+ * If object: uses provided stores (searchPaths ignored).
29
+ * Priority for searchPaths:
30
+ * 1. Constructor parameter (highest)
31
+ * 2. AUTH_BROKER_PATH environment variable (colon/semicolon-separated)
32
+ * 3. Current working directory (lowest)
64
33
  * @param browser Optional browser name for authentication (chrome, edge, firefox, system, none).
65
34
  * Default: 'system' (system default browser).
66
35
  * Use 'none' to print URL instead of opening browser.
67
36
  * @param logger Optional logger instance. If not provided, uses default logger.
68
37
  */
69
- constructor(searchPaths, browser, logger) {
70
- this.searchPaths = (0, pathResolver_1.resolveSearchPaths)(searchPaths);
38
+ constructor(searchPathsOrStores, browser, logger) {
39
+ // Handle backward compatibility: if first param is string/array, treat as searchPaths
40
+ if (typeof searchPathsOrStores === 'string' || Array.isArray(searchPathsOrStores) || searchPathsOrStores === undefined) {
41
+ this.searchPaths = (0, pathResolver_1.resolveSearchPaths)(searchPathsOrStores);
42
+ // Create default file-based stores
43
+ this.serviceKeyStore = new stores_1.FileServiceKeyStore(this.searchPaths);
44
+ this.sessionStore = new stores_1.FileSessionStore(this.searchPaths);
45
+ }
46
+ else {
47
+ // New API: stores provided
48
+ this.searchPaths = (0, pathResolver_1.resolveSearchPaths)(undefined); // Still resolve for backward compatibility in internal functions
49
+ this.serviceKeyStore = searchPathsOrStores.serviceKeyStore || new stores_1.FileServiceKeyStore();
50
+ this.sessionStore = searchPathsOrStores.sessionStore || new stores_1.FileSessionStore();
51
+ }
71
52
  this.browser = browser || 'system';
72
53
  this.logger = logger || logger_1.defaultLogger;
73
54
  }
74
55
  /**
75
56
  * Get authentication token for destination.
76
- * Tries to load from .env file, validates it, and refreshes if needed.
57
+ * Tries to load from session store, validates it, and refreshes if needed.
77
58
  * @param destination Destination name (e.g., "TRIAL")
78
59
  * @returns Promise that resolves to JWT token string
79
- * @throws Error if neither .env file nor service key found
60
+ * @throws Error if neither session data nor service key found
80
61
  */
81
62
  async getToken(destination) {
82
- // Use getToken function with logger
83
- return (0, getToken_1.getToken)(destination, this.searchPaths, this.logger);
63
+ // Check cache first
64
+ const cachedToken = (0, cache_1.getCachedToken)(destination);
65
+ if (cachedToken) {
66
+ // Validate cached token
67
+ const envConfig = await this.sessionStore.loadSession(destination);
68
+ if (envConfig) {
69
+ const isValid = await (0, tokenValidator_1.validateToken)(cachedToken, envConfig.sapUrl);
70
+ if (isValid) {
71
+ return cachedToken;
72
+ }
73
+ // Token expired, remove from cache
74
+ }
75
+ }
76
+ // Load from session store
77
+ const envConfig = await this.sessionStore.loadSession(destination);
78
+ if (envConfig && envConfig.jwtToken) {
79
+ // Validate token
80
+ const isValid = await (0, tokenValidator_1.validateToken)(envConfig.jwtToken, envConfig.sapUrl);
81
+ if (isValid) {
82
+ (0, cache_1.setCachedToken)(destination, envConfig.jwtToken);
83
+ return envConfig.jwtToken;
84
+ }
85
+ }
86
+ // Token not found or expired, check if we have service key for browser auth
87
+ const serviceKey = await this.serviceKeyStore.getServiceKey(destination);
88
+ if (!serviceKey) {
89
+ // No service key and no valid token - throw error with helpful message
90
+ const searchPaths = this.getSearchPathsForError();
91
+ const searchedPaths = searchPaths.map(p => ` - ${p}`).join('\n');
92
+ throw new Error(`No authentication found for destination "${destination}". ` +
93
+ `Neither ${destination}.env file nor ${destination}.json service key found.\n` +
94
+ `Please create one of:\n` +
95
+ ` - ${destination}.env (with SAP_JWT_TOKEN)\n` +
96
+ ` - ${destination}.json (service key)\n` +
97
+ `Searched in:\n${searchedPaths}`);
98
+ }
99
+ // Try to refresh (will use browser auth if no refresh token)
100
+ const newToken = await this.refreshTokenInternal(destination, serviceKey, envConfig);
101
+ (0, cache_1.setCachedToken)(destination, newToken);
102
+ return newToken;
84
103
  }
85
104
  /**
86
105
  * Force refresh token for destination using service key.
@@ -89,139 +108,81 @@ class AuthBroker {
89
108
  * @returns Promise that resolves to new JWT token string
90
109
  */
91
110
  async refreshToken(destination) {
92
- // Use refreshToken function with logger and browser
93
- return (0, refreshToken_1.refreshToken)(destination, this.searchPaths, this.logger);
94
- }
95
- /**
96
- * Get SAP URL for destination.
97
- * Tries to load from .env file first, then from service key.
98
- * @param destination Destination name (e.g., "TRIAL")
99
- * @returns Promise that resolves to SAP URL string, or undefined if not found
100
- */
101
- async getSapUrl(destination) {
102
- // Try to load from .env file first
103
- const envConfig = await (0, envLoader_1.loadEnvFile)(destination, this.searchPaths);
104
- if (envConfig?.sapUrl) {
105
- return envConfig.sapUrl;
106
- }
107
- // Try service key
108
- const serviceKey = await (0, serviceKeyLoader_1.loadServiceKey)(destination, this.searchPaths);
109
- if (serviceKey) {
110
- return serviceKey.url || serviceKey.abap?.url || serviceKey.sap_url;
111
+ // Load service key
112
+ const serviceKey = await this.serviceKeyStore.getServiceKey(destination);
113
+ if (!serviceKey) {
114
+ const searchPaths = this.getSearchPathsForError();
115
+ const searchedPaths = searchPaths.map(p => ` - ${p}`).join('\n');
116
+ throw new Error(`Service key file not found for destination "${destination}".\n` +
117
+ `Please create file: ${destination}.json\n` +
118
+ `Searched in:\n${searchedPaths}`);
111
119
  }
112
- return undefined;
120
+ // Load existing session (for refresh token)
121
+ const envConfig = await this.sessionStore.loadSession(destination);
122
+ return this.refreshTokenInternal(destination, serviceKey, envConfig);
113
123
  }
114
124
  /**
115
- * Save token to {destination}.env file
116
- * Creates .env file similar to sap-abap-auth utility format
125
+ * Internal refresh token implementation
117
126
  * @private
118
127
  */
119
- async saveTokenToEnv(destination, savePath, config) {
120
- // Ensure directory exists
121
- if (!fs.existsSync(savePath)) {
122
- fs.mkdirSync(savePath, { recursive: true });
128
+ async refreshTokenInternal(destination, serviceKey, envConfig) {
129
+ // Extract UAA configuration
130
+ const { url: uaaUrl, clientid: clientId, clientsecret: clientSecret } = serviceKey.uaa;
131
+ if (!uaaUrl || !clientId || !clientSecret) {
132
+ throw new Error(`Invalid service key for destination "${destination}". ` +
133
+ `Missing required UAA fields: url, clientid, clientsecret`);
123
134
  }
124
- const envFilePath = path.join(savePath, `${destination}.env`);
125
- const tempFilePath = `${envFilePath}.tmp`;
126
- // Write to temporary file first (atomic write)
127
- // Format similar to sap-abap-auth utility - always create fresh file
128
- const envLines = [];
129
- // Add token expiry information if we can decode JWT
130
- const jwtExpiry = this.getTokenExpiry(config.jwtToken);
131
- const refreshExpiry = config.refreshToken ? this.getTokenExpiry(config.refreshToken) : null;
132
- if (jwtExpiry || refreshExpiry) {
133
- envLines.push('# Token Expiry Information (auto-generated)');
134
- if (jwtExpiry) {
135
- envLines.push(`# JWT Token expires: ${jwtExpiry.readableDate} (UTC)`);
136
- envLines.push(`# JWT Token expires at: ${jwtExpiry.dateString}`);
137
- }
138
- else {
139
- envLines.push('# JWT Token expiry: Unable to determine (token may not be a standard JWT)');
140
- }
141
- if (refreshExpiry) {
142
- envLines.push(`# Refresh Token expires: ${refreshExpiry.readableDate} (UTC)`);
143
- envLines.push(`# Refresh Token expires at: ${refreshExpiry.dateString}`);
144
- }
145
- else if (config.refreshToken) {
146
- envLines.push('# Refresh Token expiry: Unable to determine (token may not be a standard JWT)');
147
- }
148
- envLines.push('');
149
- }
150
- // Write JWT auth parameters (similar to sap-abap-auth format)
151
- // Required fields
152
- envLines.push(`SAP_URL=${config.sapUrl}`);
153
- if (config.sapClient) {
154
- envLines.push(`SAP_CLIENT=${config.sapClient}`);
155
- }
156
- if (config.language) {
157
- envLines.push(`SAP_LANGUAGE=${config.language}`);
135
+ // Validate SAP URL early (before starting browser auth or refresh)
136
+ const sapUrl = serviceKey.url || serviceKey.abap?.url || serviceKey.sap_url;
137
+ if (!sapUrl) {
138
+ throw new Error(`Service key for destination "${destination}" does not contain SAP URL. ` +
139
+ `Expected field: url, abap.url, or sap_url`);
158
140
  }
159
- envLines.push('TLS_REJECT_UNAUTHORIZED=0');
160
- envLines.push('SAP_AUTH_TYPE=jwt');
161
- envLines.push(`SAP_JWT_TOKEN=${config.jwtToken}`);
162
- if (config.refreshToken) {
163
- envLines.push(`SAP_REFRESH_TOKEN=${config.refreshToken}`);
141
+ // Try to load existing refresh token from session store
142
+ let refreshTokenValue = envConfig?.refreshToken;
143
+ let result;
144
+ // If no refresh token, start browser authentication flow
145
+ if (!refreshTokenValue) {
146
+ this.logger.debug(`No refresh token found for destination "${destination}". Starting browser authentication...`);
147
+ result = await (0, browserAuth_1.startBrowserAuth)(serviceKey, this.browser || 'system', this.logger);
164
148
  }
165
- if (config.uaaUrl) {
166
- envLines.push(`SAP_UAA_URL=${config.uaaUrl}`);
149
+ else {
150
+ // Refresh token using refresh token
151
+ result = await (0, tokenRefresher_1.refreshJwtToken)(refreshTokenValue, uaaUrl, clientId, clientSecret);
167
152
  }
168
- if (config.uaaClientId) {
169
- envLines.push(`SAP_UAA_CLIENT_ID=${config.uaaClientId}`);
170
- }
171
- if (config.uaaClientSecret) {
172
- envLines.push(`SAP_UAA_CLIENT_SECRET=${config.uaaClientSecret}`);
173
- }
174
- envLines.push('');
175
- envLines.push('# For JWT authentication');
176
- envLines.push('# SAP_USERNAME=your_username');
177
- envLines.push('# SAP_PASSWORD=your_password');
178
- const envContent = envLines.join('\n') + '\n';
179
- // Write to temp file
180
- fs.writeFileSync(tempFilePath, envContent, 'utf8');
181
- // Atomic rename
182
- fs.renameSync(tempFilePath, envFilePath);
153
+ // Save new token to session store
154
+ await this.sessionStore.saveSession(destination, {
155
+ sapUrl,
156
+ jwtToken: result.accessToken,
157
+ refreshToken: result.refreshToken || refreshTokenValue,
158
+ uaaUrl,
159
+ uaaClientId: clientId,
160
+ uaaClientSecret: clientSecret,
161
+ sapClient: envConfig?.sapClient,
162
+ language: envConfig?.language,
163
+ });
164
+ // Update cache with new token
165
+ (0, cache_1.setCachedToken)(destination, result.accessToken);
166
+ return result.accessToken;
183
167
  }
184
168
  /**
185
- * Get token expiry information from JWT token
186
- * @private
169
+ * Get SAP URL for destination.
170
+ * Tries to load from session store first, then from service key store.
171
+ * @param destination Destination name (e.g., "TRIAL")
172
+ * @returns Promise that resolves to SAP URL string, or undefined if not found
187
173
  */
188
- getTokenExpiry(token) {
189
- try {
190
- // JWT tokens have format: header.payload.signature
191
- const parts = token.split('.');
192
- if (parts.length !== 3) {
193
- return null;
194
- }
195
- // Decode payload (base64url)
196
- const payload = parts[1];
197
- // Add padding if needed
198
- const padded = payload + '='.repeat((4 - payload.length % 4) % 4);
199
- const decoded = Buffer.from(padded.replace(/-/g, '+').replace(/_/g, '/'), 'base64').toString('utf8');
200
- const parsed = JSON.parse(decoded);
201
- // Check for exp claim
202
- if (parsed.exp) {
203
- const expiryDate = new Date(parsed.exp * 1000);
204
- const readableDate = expiryDate.toLocaleString('en-US', {
205
- weekday: 'long',
206
- year: 'numeric',
207
- month: 'long',
208
- day: 'numeric',
209
- hour: '2-digit',
210
- minute: '2-digit',
211
- second: '2-digit',
212
- timeZone: 'UTC',
213
- timeZoneName: 'short',
214
- });
215
- return {
216
- dateString: expiryDate.toISOString(),
217
- readableDate: readableDate,
218
- };
219
- }
220
- return null;
174
+ async getSapUrl(destination) {
175
+ // Try to load from session store first
176
+ const envConfig = await this.sessionStore.loadSession(destination);
177
+ if (envConfig?.sapUrl) {
178
+ return envConfig.sapUrl;
221
179
  }
222
- catch {
223
- return null;
180
+ // Try service key store
181
+ const serviceKey = await this.serviceKeyStore.getServiceKey(destination);
182
+ if (serviceKey) {
183
+ return serviceKey.url || serviceKey.abap?.url || serviceKey.sap_url;
224
184
  }
185
+ return undefined;
225
186
  }
226
187
  /**
227
188
  * Clear cached token for specific destination
@@ -236,5 +197,20 @@ class AuthBroker {
236
197
  clearAllCache() {
237
198
  (0, cache_1.clearAllCache)();
238
199
  }
200
+ /**
201
+ * Get search paths for error messages (from file stores if available)
202
+ * @private
203
+ */
204
+ getSearchPathsForError() {
205
+ // Try to get search paths from file stores
206
+ if (this.serviceKeyStore instanceof stores_1.FileServiceKeyStore) {
207
+ return this.serviceKeyStore.getSearchPaths();
208
+ }
209
+ if (this.sessionStore instanceof stores_1.FileSessionStore) {
210
+ return this.sessionStore.getSearchPaths();
211
+ }
212
+ // Fallback to stored searchPaths (for backward compatibility)
213
+ return this.searchPaths;
214
+ }
239
215
  }
240
216
  exports.AuthBroker = AuthBroker;
@@ -1 +1 @@
1
- {"version":3,"file":"browserAuth.d.ts","sourceRoot":"","sources":["../src/browserAuth.ts"],"names":[],"mappings":"AAAA;;GAEG;AAOH,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,MAAM,EAAiB,MAAM,UAAU,CAAC;AA4DjD;;;;;;GAMG;AACH,wBAAsB,gBAAgB,CACpC,UAAU,EAAE,UAAU,EACtB,OAAO,GAAE,MAAiB,EAC1B,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAqMzD"}
1
+ {"version":3,"file":"browserAuth.d.ts","sourceRoot":"","sources":["../src/browserAuth.ts"],"names":[],"mappings":"AAAA;;GAEG;AAOH,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,MAAM,EAAiB,MAAM,UAAU,CAAC;AA4DjD;;;;;;GAMG;AACH,wBAAsB,gBAAgB,CACpC,UAAU,EAAE,UAAU,EACtB,OAAO,GAAE,MAAiB,EAC1B,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CAyMzD"}
@@ -284,7 +284,11 @@ async function startBrowserAuth(serviceKey, browser = 'system', logger) {
284
284
  }
285
285
  }
286
286
  catch (error) {
287
- log.error(`❌ Failed to open browser: ${error.message}. Please open manually: ${authorizationUrl}`);
287
+ // If browser cannot be opened, show URL and throw error for consumer to catch
288
+ log.error(`❌ Failed to open browser: ${error?.message || String(error)}. Please open manually: ${authorizationUrl}`);
289
+ log.browserUrl(authorizationUrl);
290
+ // Throw error so consumer can distinguish this from "service key missing" error
291
+ reject(new Error(`Browser opening failed for destination authentication. Please open manually: ${authorizationUrl}`));
288
292
  }
289
293
  }
290
294
  });
package/dist/index.d.ts CHANGED
@@ -5,4 +5,6 @@
5
5
  export { AuthBroker } from './AuthBroker';
6
6
  export type { EnvConfig, ServiceKey } from './types';
7
7
  export { resolveSearchPaths, findFileInPaths } from './pathResolver';
8
+ export { ServiceKeyStore, SessionStore } from './stores/interfaces';
9
+ export { FileServiceKeyStore, FileSessionStore } from './stores';
8
10
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,YAAY,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrD,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,YAAY,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrD,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAGrE,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACpE,OAAO,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC"}
package/dist/index.js CHANGED
@@ -4,9 +4,12 @@
4
4
  * JWT authentication broker for MCP ABAP ADT server
5
5
  */
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.findFileInPaths = exports.resolveSearchPaths = exports.AuthBroker = void 0;
7
+ exports.FileSessionStore = exports.FileServiceKeyStore = exports.findFileInPaths = exports.resolveSearchPaths = exports.AuthBroker = void 0;
8
8
  var AuthBroker_1 = require("./AuthBroker");
9
9
  Object.defineProperty(exports, "AuthBroker", { enumerable: true, get: function () { return AuthBroker_1.AuthBroker; } });
10
10
  var pathResolver_1 = require("./pathResolver");
11
11
  Object.defineProperty(exports, "resolveSearchPaths", { enumerable: true, get: function () { return pathResolver_1.resolveSearchPaths; } });
12
12
  Object.defineProperty(exports, "findFileInPaths", { enumerable: true, get: function () { return pathResolver_1.findFileInPaths; } });
13
+ var stores_1 = require("./stores");
14
+ Object.defineProperty(exports, "FileServiceKeyStore", { enumerable: true, get: function () { return stores_1.FileServiceKeyStore; } });
15
+ Object.defineProperty(exports, "FileSessionStore", { enumerable: true, get: function () { return stores_1.FileSessionStore; } });
package/dist/logger.d.ts CHANGED
@@ -1,6 +1,15 @@
1
1
  /**
2
2
  * Logger interface and implementations for auth-broker package
3
3
  */
4
+ /**
5
+ * Log levels
6
+ */
7
+ export declare enum LogLevel {
8
+ ERROR = 0,
9
+ WARN = 1,
10
+ INFO = 2,
11
+ DEBUG = 3
12
+ }
4
13
  /**
5
14
  * Logger interface - defines logging methods
6
15
  */
@@ -8,6 +17,7 @@ export interface Logger {
8
17
  info(message: string): void;
9
18
  debug(message: string): void;
10
19
  error(message: string): void;
20
+ warn(message: string): void;
11
21
  browserAuth(message: string): void;
12
22
  refresh(message: string): void;
13
23
  success(message: string): void;
@@ -26,4 +36,5 @@ export declare function success(message: string): void;
26
36
  export declare function browserUrl(url: string): void;
27
37
  export declare function browserOpening(): void;
28
38
  export declare function testSkip(message: string): void;
39
+ export declare function warn(message: string): void;
29
40
  //# sourceMappingURL=logger.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;GAEG;AACH,MAAM,WAAW,MAAM;IACrB,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,cAAc,IAAI,IAAI,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC;AA6GD,eAAO,MAAM,aAAa,EAAE,MAA4B,CAAC;AAGzD,eAAO,MAAM,UAAU,EAAE,MAAyB,CAAC;AAGnD,wBAAgB,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAE1C;AAED,wBAAgB,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAE3C;AAED,wBAAgB,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAE3C;AAED,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAEjD;AAED,wBAAgB,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAE7C;AAED,wBAAgB,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAE7C;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAE5C;AAED,wBAAgB,cAAc,IAAI,IAAI,CAErC;AAED,wBAAgB,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAE9C"}
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;GAEG;AACH,oBAAY,QAAQ;IAClB,KAAK,IAAI;IACT,IAAI,IAAI;IACR,IAAI,IAAI;IACR,KAAK,IAAI;CACV;AAED;;GAEG;AACH,MAAM,WAAW,MAAM;IACrB,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,cAAc,IAAI,IAAI,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACjC;AAoJD,eAAO,MAAM,aAAa,EAAE,MAA4B,CAAC;AAGzD,eAAO,MAAM,UAAU,EAAE,MAAyB,CAAC;AAGnD,wBAAgB,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAE1C;AAED,wBAAgB,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAE3C;AAED,wBAAgB,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAE3C;AAED,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAEjD;AAED,wBAAgB,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAE7C;AAED,wBAAgB,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAE7C;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAE5C;AAED,wBAAgB,cAAc,IAAI,IAAI,CAErC;AAED,wBAAgB,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAE9C;AAED,wBAAgB,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAE1C"}
package/dist/logger.js CHANGED
@@ -3,7 +3,7 @@
3
3
  * Logger interface and implementations for auth-broker package
4
4
  */
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.testLogger = exports.defaultLogger = void 0;
6
+ exports.testLogger = exports.defaultLogger = exports.LogLevel = void 0;
7
7
  exports.info = info;
8
8
  exports.debug = debug;
9
9
  exports.error = error;
@@ -13,27 +13,70 @@ exports.success = success;
13
13
  exports.browserUrl = browserUrl;
14
14
  exports.browserOpening = browserOpening;
15
15
  exports.testSkip = testSkip;
16
+ exports.warn = warn;
17
+ /**
18
+ * Log levels
19
+ */
20
+ var LogLevel;
21
+ (function (LogLevel) {
22
+ LogLevel[LogLevel["ERROR"] = 0] = "ERROR";
23
+ LogLevel[LogLevel["WARN"] = 1] = "WARN";
24
+ LogLevel[LogLevel["INFO"] = 2] = "INFO";
25
+ LogLevel[LogLevel["DEBUG"] = 3] = "DEBUG";
26
+ })(LogLevel || (exports.LogLevel = LogLevel = {}));
27
+ /**
28
+ * Get log level from environment variable
29
+ * AUTH_LOG_LEVEL can be: error, warn, info, debug
30
+ * DEBUG_AUTH_LOG=true is also supported for backward compatibility (sets level to debug)
31
+ */
32
+ function getLogLevel() {
33
+ const level = process.env.AUTH_LOG_LEVEL?.toLowerCase();
34
+ if (level === 'error')
35
+ return LogLevel.ERROR;
36
+ if (level === 'warn')
37
+ return LogLevel.WARN;
38
+ if (level === 'info')
39
+ return LogLevel.INFO;
40
+ if (level === 'debug')
41
+ return LogLevel.DEBUG;
42
+ // Backward compatibility
43
+ if (process.env.DEBUG_AUTH_LOG === 'true')
44
+ return LogLevel.DEBUG;
45
+ // Default: info level
46
+ return LogLevel.INFO;
47
+ }
16
48
  /**
17
49
  * Default logger implementation
18
- * Controls output based on DEBUG_AUTH_LOG environment variable:
19
- * - If DEBUG_AUTH_LOG=true: shows debug messages
20
- * - Otherwise: only shows info messages (concise, one line)
50
+ * Controls output based on AUTH_LOG_LEVEL environment variable:
51
+ * - error: only errors
52
+ * - warn: errors and warnings
53
+ * - info: errors, warnings, and info (default)
54
+ * - debug: all messages
21
55
  */
22
56
  class DefaultLogger {
23
- debugEnabled;
24
- constructor(debugEnabled = process.env.DEBUG_AUTH_LOG === 'true') {
25
- this.debugEnabled = debugEnabled;
57
+ logLevel;
58
+ constructor(logLevel) {
59
+ this.logLevel = logLevel ?? getLogLevel();
26
60
  }
27
61
  info(message) {
28
- console.info(message);
62
+ if (this.logLevel >= LogLevel.INFO) {
63
+ console.info(message);
64
+ }
29
65
  }
30
66
  debug(message) {
31
- if (this.debugEnabled) {
67
+ if (this.logLevel >= LogLevel.DEBUG) {
32
68
  console.debug(`[DEBUG] ${message}`);
33
69
  }
34
70
  }
35
71
  error(message) {
36
- console.error(message);
72
+ if (this.logLevel >= LogLevel.ERROR) {
73
+ console.error(message);
74
+ }
75
+ }
76
+ warn(message) {
77
+ if (this.logLevel >= LogLevel.WARN) {
78
+ console.warn(`[WARN] ${message}`);
79
+ }
37
80
  }
38
81
  browserAuth(message) {
39
82
  this.info(`🌐 ${message}`);
@@ -58,23 +101,32 @@ class DefaultLogger {
58
101
  }
59
102
  /**
60
103
  * Test logger implementation
61
- * Always shows info messages, debug only if DEBUG_AUTH_LOG=true
104
+ * Uses same log levels as DefaultLogger
62
105
  */
63
106
  class TestLogger {
64
- debugEnabled;
65
- constructor(debugEnabled = process.env.DEBUG_AUTH_LOG === 'true') {
66
- this.debugEnabled = debugEnabled;
107
+ logLevel;
108
+ constructor(logLevel) {
109
+ this.logLevel = logLevel ?? getLogLevel();
67
110
  }
68
111
  info(message) {
69
- console.info(message);
112
+ if (this.logLevel >= LogLevel.INFO) {
113
+ console.info(message);
114
+ }
70
115
  }
71
116
  debug(message) {
72
- if (this.debugEnabled) {
117
+ if (this.logLevel >= LogLevel.DEBUG) {
73
118
  console.info(`[DEBUG] ${message}`);
74
119
  }
75
120
  }
76
121
  error(message) {
77
- console.error(message);
122
+ if (this.logLevel >= LogLevel.ERROR) {
123
+ console.error(message);
124
+ }
125
+ }
126
+ warn(message) {
127
+ if (this.logLevel >= LogLevel.WARN) {
128
+ console.warn(`[WARN] ${message}`);
129
+ }
78
130
  }
79
131
  browserAuth(message) {
80
132
  this.info(`🌐 ${message}`);
@@ -129,3 +181,6 @@ function browserOpening() {
129
181
  function testSkip(message) {
130
182
  exports.defaultLogger.testSkip(message);
131
183
  }
184
+ function warn(message) {
185
+ exports.defaultLogger.warn(message);
186
+ }
@@ -0,0 +1,38 @@
1
+ /**
2
+ * File-based implementation of ServiceKeyStore
3
+ *
4
+ * Reads service keys from {destination}.json files in search paths.
5
+ */
6
+ import { ServiceKeyStore } from './interfaces';
7
+ import { ServiceKey } from '../types';
8
+ /**
9
+ * File-based service key store implementation
10
+ *
11
+ * Searches for {destination}.json files in configured search paths.
12
+ * Search paths priority:
13
+ * 1. Constructor parameter (highest)
14
+ * 2. AUTH_BROKER_PATH environment variable
15
+ * 3. Current working directory (lowest)
16
+ */
17
+ export declare class FileServiceKeyStore implements ServiceKeyStore {
18
+ private searchPaths;
19
+ /**
20
+ * Create a new FileServiceKeyStore instance
21
+ * @param searchPaths Optional search paths for .json files.
22
+ * Can be a single path (string) or array of paths.
23
+ * If not provided, uses AUTH_BROKER_PATH env var or current working directory.
24
+ */
25
+ constructor(searchPaths?: string | string[]);
26
+ /**
27
+ * Get service key for destination
28
+ * @param destination Destination name (e.g., "TRIAL")
29
+ * @returns ServiceKey object or null if not found
30
+ */
31
+ getServiceKey(destination: string): Promise<ServiceKey | null>;
32
+ /**
33
+ * Get search paths (for error messages)
34
+ * @returns Array of search paths
35
+ */
36
+ getSearchPaths(): string[];
37
+ }
38
+ //# sourceMappingURL=FileServiceKeyStore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FileServiceKeyStore.d.ts","sourceRoot":"","sources":["../../src/stores/FileServiceKeyStore.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAItC;;;;;;;;GAQG;AACH,qBAAa,mBAAoB,YAAW,eAAe;IACzD,OAAO,CAAC,WAAW,CAAW;IAE9B;;;;;OAKG;gBACS,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE;IAI3C;;;;OAIG;IACG,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAIpE;;;OAGG;IACH,cAAc,IAAI,MAAM,EAAE;CAG3B"}
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ /**
3
+ * File-based implementation of ServiceKeyStore
4
+ *
5
+ * Reads service keys from {destination}.json files in search paths.
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.FileServiceKeyStore = void 0;
9
+ const serviceKeyLoader_1 = require("../serviceKeyLoader");
10
+ const pathResolver_1 = require("../pathResolver");
11
+ /**
12
+ * File-based service key store implementation
13
+ *
14
+ * Searches for {destination}.json files in configured search paths.
15
+ * Search paths priority:
16
+ * 1. Constructor parameter (highest)
17
+ * 2. AUTH_BROKER_PATH environment variable
18
+ * 3. Current working directory (lowest)
19
+ */
20
+ class FileServiceKeyStore {
21
+ searchPaths;
22
+ /**
23
+ * Create a new FileServiceKeyStore instance
24
+ * @param searchPaths Optional search paths for .json files.
25
+ * Can be a single path (string) or array of paths.
26
+ * If not provided, uses AUTH_BROKER_PATH env var or current working directory.
27
+ */
28
+ constructor(searchPaths) {
29
+ this.searchPaths = (0, pathResolver_1.resolveSearchPaths)(searchPaths);
30
+ }
31
+ /**
32
+ * Get service key for destination
33
+ * @param destination Destination name (e.g., "TRIAL")
34
+ * @returns ServiceKey object or null if not found
35
+ */
36
+ async getServiceKey(destination) {
37
+ return (0, serviceKeyLoader_1.loadServiceKey)(destination, this.searchPaths);
38
+ }
39
+ /**
40
+ * Get search paths (for error messages)
41
+ * @returns Array of search paths
42
+ */
43
+ getSearchPaths() {
44
+ return [...this.searchPaths];
45
+ }
46
+ }
47
+ exports.FileServiceKeyStore = FileServiceKeyStore;
@@ -0,0 +1,50 @@
1
+ /**
2
+ * File-based implementation of SessionStore
3
+ *
4
+ * Reads/writes session data from/to {destination}.env files in search paths.
5
+ */
6
+ import { SessionStore } from './interfaces';
7
+ import { EnvConfig } from '../types';
8
+ /**
9
+ * File-based session store implementation
10
+ *
11
+ * Searches for {destination}.env files in configured search paths.
12
+ * Writes to first search path (highest priority).
13
+ * Search paths priority:
14
+ * 1. Constructor parameter (highest)
15
+ * 2. AUTH_BROKER_PATH environment variable
16
+ * 3. Current working directory (lowest)
17
+ */
18
+ export declare class FileSessionStore implements SessionStore {
19
+ private searchPaths;
20
+ /**
21
+ * Create a new FileSessionStore instance
22
+ * @param searchPaths Optional search paths for .env files.
23
+ * Can be a single path (string) or array of paths.
24
+ * If not provided, uses AUTH_BROKER_PATH env var or current working directory.
25
+ */
26
+ constructor(searchPaths?: string | string[]);
27
+ /**
28
+ * Load session configuration for destination
29
+ * @param destination Destination name (e.g., "TRIAL")
30
+ * @returns EnvConfig object or null if not found
31
+ */
32
+ loadSession(destination: string): Promise<EnvConfig | null>;
33
+ /**
34
+ * Save session configuration for destination
35
+ * @param destination Destination name (e.g., "TRIAL")
36
+ * @param config Session configuration to save
37
+ */
38
+ saveSession(destination: string, config: EnvConfig): Promise<void>;
39
+ /**
40
+ * Delete session for destination
41
+ * @param destination Destination name (e.g., "TRIAL")
42
+ */
43
+ deleteSession(destination: string): Promise<void>;
44
+ /**
45
+ * Get search paths (for error messages)
46
+ * @returns Array of search paths
47
+ */
48
+ getSearchPaths(): string[];
49
+ }
50
+ //# sourceMappingURL=FileSessionStore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FileSessionStore.d.ts","sourceRoot":"","sources":["../../src/stores/FileSessionStore.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAKrC;;;;;;;;;GASG;AACH,qBAAa,gBAAiB,YAAW,YAAY;IACnD,OAAO,CAAC,WAAW,CAAW;IAE9B;;;;;OAKG;gBACS,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE;IAI3C;;;;OAIG;IACG,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;IAIjE;;;;OAIG;IACG,WAAW,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAiBxE;;;OAGG;IACG,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAYvD;;;OAGG;IACH,cAAc,IAAI,MAAM,EAAE;CAG3B"}
@@ -0,0 +1,116 @@
1
+ "use strict";
2
+ /**
3
+ * File-based implementation of SessionStore
4
+ *
5
+ * Reads/writes session data from/to {destination}.env files in search paths.
6
+ */
7
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
8
+ if (k2 === undefined) k2 = k;
9
+ var desc = Object.getOwnPropertyDescriptor(m, k);
10
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
11
+ desc = { enumerable: true, get: function() { return m[k]; } };
12
+ }
13
+ Object.defineProperty(o, k2, desc);
14
+ }) : (function(o, m, k, k2) {
15
+ if (k2 === undefined) k2 = k;
16
+ o[k2] = m[k];
17
+ }));
18
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
19
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
20
+ }) : function(o, v) {
21
+ o["default"] = v;
22
+ });
23
+ var __importStar = (this && this.__importStar) || (function () {
24
+ var ownKeys = function(o) {
25
+ ownKeys = Object.getOwnPropertyNames || function (o) {
26
+ var ar = [];
27
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
28
+ return ar;
29
+ };
30
+ return ownKeys(o);
31
+ };
32
+ return function (mod) {
33
+ if (mod && mod.__esModule) return mod;
34
+ var result = {};
35
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
36
+ __setModuleDefault(result, mod);
37
+ return result;
38
+ };
39
+ })();
40
+ Object.defineProperty(exports, "__esModule", { value: true });
41
+ exports.FileSessionStore = void 0;
42
+ const envLoader_1 = require("../envLoader");
43
+ const tokenStorage_1 = require("../tokenStorage");
44
+ const pathResolver_1 = require("../pathResolver");
45
+ /**
46
+ * File-based session store implementation
47
+ *
48
+ * Searches for {destination}.env files in configured search paths.
49
+ * Writes to first search path (highest priority).
50
+ * Search paths priority:
51
+ * 1. Constructor parameter (highest)
52
+ * 2. AUTH_BROKER_PATH environment variable
53
+ * 3. Current working directory (lowest)
54
+ */
55
+ class FileSessionStore {
56
+ searchPaths;
57
+ /**
58
+ * Create a new FileSessionStore instance
59
+ * @param searchPaths Optional search paths for .env files.
60
+ * Can be a single path (string) or array of paths.
61
+ * If not provided, uses AUTH_BROKER_PATH env var or current working directory.
62
+ */
63
+ constructor(searchPaths) {
64
+ this.searchPaths = (0, pathResolver_1.resolveSearchPaths)(searchPaths);
65
+ }
66
+ /**
67
+ * Load session configuration for destination
68
+ * @param destination Destination name (e.g., "TRIAL")
69
+ * @returns EnvConfig object or null if not found
70
+ */
71
+ async loadSession(destination) {
72
+ return (0, envLoader_1.loadEnvFile)(destination, this.searchPaths);
73
+ }
74
+ /**
75
+ * Save session configuration for destination
76
+ * @param destination Destination name (e.g., "TRIAL")
77
+ * @param config Session configuration to save
78
+ */
79
+ async saveSession(destination, config) {
80
+ // Save to first search path (highest priority)
81
+ const savePath = this.searchPaths[0];
82
+ // Convert EnvConfig to format expected by saveTokenToEnv
83
+ await (0, tokenStorage_1.saveTokenToEnv)(destination, savePath, {
84
+ sapUrl: config.sapUrl,
85
+ jwtToken: config.jwtToken,
86
+ refreshToken: config.refreshToken,
87
+ uaaUrl: config.uaaUrl,
88
+ uaaClientId: config.uaaClientId,
89
+ uaaClientSecret: config.uaaClientSecret,
90
+ sapClient: config.sapClient,
91
+ language: config.language,
92
+ });
93
+ }
94
+ /**
95
+ * Delete session for destination
96
+ * @param destination Destination name (e.g., "TRIAL")
97
+ */
98
+ async deleteSession(destination) {
99
+ // Find .env file in search paths
100
+ const fileName = `${destination}.env`;
101
+ const { findFileInPaths } = await Promise.resolve().then(() => __importStar(require('../pathResolver')));
102
+ const fs = await Promise.resolve().then(() => __importStar(require('fs')));
103
+ const envFilePath = findFileInPaths(fileName, this.searchPaths);
104
+ if (envFilePath && fs.existsSync(envFilePath)) {
105
+ fs.unlinkSync(envFilePath);
106
+ }
107
+ }
108
+ /**
109
+ * Get search paths (for error messages)
110
+ * @returns Array of search paths
111
+ */
112
+ getSearchPaths() {
113
+ return [...this.searchPaths];
114
+ }
115
+ }
116
+ exports.FileSessionStore = FileSessionStore;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Storage implementations for AuthBroker
3
+ */
4
+ export { ServiceKeyStore, SessionStore } from './interfaces';
5
+ export { FileServiceKeyStore } from './FileServiceKeyStore';
6
+ export { FileSessionStore } from './FileSessionStore';
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/stores/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC7D,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC"}
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ /**
3
+ * Storage implementations for AuthBroker
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.FileSessionStore = exports.FileServiceKeyStore = void 0;
7
+ var FileServiceKeyStore_1 = require("./FileServiceKeyStore");
8
+ Object.defineProperty(exports, "FileServiceKeyStore", { enumerable: true, get: function () { return FileServiceKeyStore_1.FileServiceKeyStore; } });
9
+ var FileSessionStore_1 = require("./FileSessionStore");
10
+ Object.defineProperty(exports, "FileSessionStore", { enumerable: true, get: function () { return FileSessionStore_1.FileSessionStore; } });
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Storage interfaces for AuthBroker
3
+ *
4
+ * These interfaces allow consumers to provide custom storage implementations
5
+ * for service keys and session data (tokens, configuration).
6
+ */
7
+ import { ServiceKey, EnvConfig } from '../types';
8
+ /**
9
+ * Interface for storing and retrieving service keys
10
+ *
11
+ * Service keys contain UAA credentials and SAP URL for a destination.
12
+ * Default implementation: FileServiceKeyStore (reads from {destination}.json files)
13
+ */
14
+ export interface ServiceKeyStore {
15
+ /**
16
+ * Get service key for destination
17
+ * @param destination Destination name (e.g., "TRIAL")
18
+ * @returns ServiceKey object or null if not found
19
+ */
20
+ getServiceKey(destination: string): Promise<ServiceKey | null>;
21
+ }
22
+ /**
23
+ * Interface for storing and retrieving session data (tokens, configuration)
24
+ *
25
+ * Session data contains JWT tokens, refresh tokens, UAA config, and SAP URL.
26
+ * Default implementation: FileSessionStore (reads/writes {destination}.env files)
27
+ */
28
+ export interface SessionStore {
29
+ /**
30
+ * Load session configuration for destination
31
+ * @param destination Destination name (e.g., "TRIAL")
32
+ * @returns EnvConfig object or null if not found
33
+ */
34
+ loadSession(destination: string): Promise<EnvConfig | null>;
35
+ /**
36
+ * Save session configuration for destination
37
+ * @param destination Destination name (e.g., "TRIAL")
38
+ * @param config Session configuration to save
39
+ */
40
+ saveSession(destination: string, config: EnvConfig): Promise<void>;
41
+ /**
42
+ * Delete session for destination (optional)
43
+ * @param destination Destination name (e.g., "TRIAL")
44
+ */
45
+ deleteSession?(destination: string): Promise<void>;
46
+ }
47
+ //# sourceMappingURL=interfaces.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interfaces.d.ts","sourceRoot":"","sources":["../../src/stores/interfaces.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAEjD;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC9B;;;;OAIG;IACH,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC;CAChE;AAED;;;;;GAKG;AACH,MAAM,WAAW,YAAY;IAC3B;;;;OAIG;IACH,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC,CAAC;IAE5D;;;;OAIG;IACH,WAAW,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAEnE;;;OAGG;IACH,aAAa,CAAC,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACpD"}
@@ -0,0 +1,8 @@
1
+ "use strict";
2
+ /**
3
+ * Storage interfaces for AuthBroker
4
+ *
5
+ * These interfaces allow consumers to provide custom storage implementations
6
+ * for service keys and session data (tokens, configuration).
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mcp-abap-adt/auth-broker",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
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",