@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.
Files changed (50) hide show
  1. package/CHANGELOG.md +94 -0
  2. package/CONTRIBUTORS.md +33 -0
  3. package/LICENSE +22 -0
  4. package/README.md +166 -0
  5. package/dist/AuthBroker.d.ts +62 -0
  6. package/dist/AuthBroker.d.ts.map +1 -0
  7. package/dist/AuthBroker.js +219 -0
  8. package/dist/__tests__/testHelpers.d.ts +44 -0
  9. package/dist/__tests__/testHelpers.d.ts.map +1 -0
  10. package/dist/__tests__/testHelpers.js +129 -0
  11. package/dist/browserAuth.d.ts +17 -0
  12. package/dist/browserAuth.d.ts.map +1 -0
  13. package/dist/browserAuth.js +301 -0
  14. package/dist/cache.d.ts +20 -0
  15. package/dist/cache.d.ts.map +1 -0
  16. package/dist/cache.js +46 -0
  17. package/dist/envLoader.d.ts +12 -0
  18. package/dist/envLoader.d.ts.map +1 -0
  19. package/dist/envLoader.js +90 -0
  20. package/dist/getToken.d.ts +14 -0
  21. package/dist/getToken.d.ts.map +1 -0
  22. package/dist/getToken.js +62 -0
  23. package/dist/index.d.ts +8 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +12 -0
  26. package/dist/logger.d.ts +29 -0
  27. package/dist/logger.d.ts.map +1 -0
  28. package/dist/logger.js +131 -0
  29. package/dist/pathResolver.d.ts +21 -0
  30. package/dist/pathResolver.d.ts.map +1 -0
  31. package/dist/pathResolver.js +105 -0
  32. package/dist/refreshToken.d.ts +14 -0
  33. package/dist/refreshToken.d.ts.map +1 -0
  34. package/dist/refreshToken.js +71 -0
  35. package/dist/serviceKeyLoader.d.ts +12 -0
  36. package/dist/serviceKeyLoader.d.ts.map +1 -0
  37. package/dist/serviceKeyLoader.js +72 -0
  38. package/dist/tokenRefresher.d.ts +17 -0
  39. package/dist/tokenRefresher.d.ts.map +1 -0
  40. package/dist/tokenRefresher.js +53 -0
  41. package/dist/tokenStorage.d.ts +15 -0
  42. package/dist/tokenStorage.d.ts.map +1 -0
  43. package/dist/tokenStorage.js +107 -0
  44. package/dist/tokenValidator.d.ts +11 -0
  45. package/dist/tokenValidator.d.ts.map +1 -0
  46. package/dist/tokenValidator.js +108 -0
  47. package/dist/types.d.ts +37 -0
  48. package/dist/types.d.ts.map +1 -0
  49. package/dist/types.js +5 -0
  50. package/package.json +64 -0
@@ -0,0 +1,107 @@
1
+ "use strict";
2
+ /**
3
+ * Token storage - saves tokens to .env files
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.saveTokenToEnv = saveTokenToEnv;
40
+ const fs = __importStar(require("fs"));
41
+ const path = __importStar(require("path"));
42
+ /**
43
+ * Save token to {destination}.env file
44
+ * @param destination Destination name
45
+ * @param savePath Path where to save the file
46
+ * @param config Configuration to save
47
+ */
48
+ async function saveTokenToEnv(destination, savePath, config) {
49
+ // Ensure directory exists
50
+ if (!fs.existsSync(savePath)) {
51
+ fs.mkdirSync(savePath, { recursive: true });
52
+ }
53
+ const envFilePath = path.join(savePath, `${destination}.env`);
54
+ const tempFilePath = `${envFilePath}.tmp`;
55
+ // Read existing .env file if it exists
56
+ let existingContent = '';
57
+ if (fs.existsSync(envFilePath)) {
58
+ existingContent = fs.readFileSync(envFilePath, 'utf8');
59
+ }
60
+ // Parse existing content to preserve other values
61
+ const lines = existingContent.split('\n');
62
+ const existingVars = new Map();
63
+ for (const line of lines) {
64
+ const trimmed = line.trim();
65
+ if (!trimmed || trimmed.startsWith('#')) {
66
+ continue;
67
+ }
68
+ const match = trimmed.match(/^([^=]+)=(.*)$/);
69
+ if (match) {
70
+ const key = match[1].trim();
71
+ const value = match[2].trim().replace(/^["']|["']$/g, ''); // Remove quotes
72
+ existingVars.set(key, value);
73
+ }
74
+ }
75
+ // Update with new values
76
+ existingVars.set('SAP_URL', config.sapUrl);
77
+ existingVars.set('SAP_JWT_TOKEN', config.jwtToken);
78
+ if (config.sapClient) {
79
+ existingVars.set('SAP_CLIENT', config.sapClient);
80
+ }
81
+ if (config.refreshToken) {
82
+ existingVars.set('SAP_REFRESH_TOKEN', config.refreshToken);
83
+ }
84
+ if (config.uaaUrl) {
85
+ existingVars.set('SAP_UAA_URL', config.uaaUrl);
86
+ }
87
+ if (config.uaaClientId) {
88
+ existingVars.set('SAP_UAA_CLIENT_ID', config.uaaClientId);
89
+ }
90
+ if (config.uaaClientSecret) {
91
+ existingVars.set('SAP_UAA_CLIENT_SECRET', config.uaaClientSecret);
92
+ }
93
+ // Write to temporary file first (atomic write)
94
+ const envLines = [];
95
+ for (const [key, value] of existingVars.entries()) {
96
+ // Escape value if it contains spaces or special characters
97
+ const escapedValue = value.includes(' ') || value.includes('=') || value.includes('#')
98
+ ? `"${value.replace(/"/g, '\\"')}"`
99
+ : value;
100
+ envLines.push(`${key}=${escapedValue}`);
101
+ }
102
+ const envContent = envLines.join('\n') + '\n';
103
+ // Write to temp file
104
+ fs.writeFileSync(tempFilePath, envContent, 'utf8');
105
+ // Atomic rename
106
+ fs.renameSync(tempFilePath, envFilePath);
107
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Token validator - validates JWT tokens by testing connection
3
+ */
4
+ /**
5
+ * Validate JWT token by making a test request to SAP system
6
+ * @param token JWT token to validate
7
+ * @param sapUrl SAP system URL
8
+ * @returns true if token is valid, false if expired/invalid
9
+ */
10
+ export declare function validateToken(token: string, sapUrl: string): Promise<boolean>;
11
+ //# sourceMappingURL=tokenValidator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tokenValidator.d.ts","sourceRoot":"","sources":["../src/tokenValidator.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH;;;;;GAKG;AACH,wBAAsB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CA0EnF"}
@@ -0,0 +1,108 @@
1
+ "use strict";
2
+ /**
3
+ * Token validator - validates JWT tokens by testing connection
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.validateToken = validateToken;
40
+ const axios_1 = __importStar(require("axios"));
41
+ /**
42
+ * Validate JWT token by making a test request to SAP system
43
+ * @param token JWT token to validate
44
+ * @param sapUrl SAP system URL
45
+ * @returns true if token is valid, false if expired/invalid
46
+ */
47
+ async function validateToken(token, sapUrl) {
48
+ try {
49
+ // Make a lightweight request to test token validity
50
+ // Using /sap/bc/adt/core/discovery as it's a simple endpoint
51
+ const testUrl = `${sapUrl}/sap/bc/adt/core/discovery`;
52
+ const response = await axios_1.default.get(testUrl, {
53
+ headers: {
54
+ 'Authorization': `Bearer ${token}`,
55
+ 'Accept': 'application/xml',
56
+ },
57
+ timeout: 10000, // 10 second timeout
58
+ validateStatus: (status) => status < 500, // Don't throw on 4xx errors
59
+ });
60
+ // 200-299: Token is valid
61
+ if (response.status >= 200 && response.status < 300) {
62
+ return true;
63
+ }
64
+ // 401/403: Token expired or invalid (but distinguish from permission errors)
65
+ if (response.status === 401 || response.status === 403) {
66
+ const responseText = typeof response.data === 'string'
67
+ ? response.data
68
+ : JSON.stringify(response.data || '');
69
+ // Check if it's a permission error (not auth error)
70
+ if (responseText.includes('ExceptionResourceNoAccess') ||
71
+ responseText.includes('No authorization') ||
72
+ responseText.includes('Missing authorization')) {
73
+ // Permission error - token is valid but user doesn't have access
74
+ // We consider this as valid token
75
+ return true;
76
+ }
77
+ // Auth error - token expired or invalid
78
+ return false;
79
+ }
80
+ // Other errors - assume token is valid (might be network issues, etc.)
81
+ return true;
82
+ }
83
+ catch (error) {
84
+ if (error instanceof axios_1.AxiosError) {
85
+ // Network errors or timeouts - assume token might be valid
86
+ // (we don't want to invalidate tokens due to network issues)
87
+ if (error.code === 'ECONNABORTED' || error.code === 'ENOTFOUND' || error.code === 'ECONNREFUSED') {
88
+ // Network issue - assume token is valid
89
+ return true;
90
+ }
91
+ // 401/403 errors
92
+ if (error.response?.status === 401 || error.response?.status === 403) {
93
+ const responseText = typeof error.response.data === 'string'
94
+ ? error.response.data
95
+ : JSON.stringify(error.response.data || '');
96
+ // Check if it's a permission error
97
+ if (responseText.includes('ExceptionResourceNoAccess') ||
98
+ responseText.includes('No authorization') ||
99
+ responseText.includes('Missing authorization')) {
100
+ return true;
101
+ }
102
+ return false;
103
+ }
104
+ }
105
+ // Unknown error - assume token is valid to avoid false negatives
106
+ return true;
107
+ }
108
+ }
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Type definitions for auth-broker package
3
+ */
4
+ /**
5
+ * Environment configuration loaded from .env file
6
+ */
7
+ export interface EnvConfig {
8
+ sapUrl: string;
9
+ sapClient?: string;
10
+ jwtToken: string;
11
+ refreshToken?: string;
12
+ uaaUrl?: string;
13
+ uaaClientId?: string;
14
+ uaaClientSecret?: string;
15
+ language?: string;
16
+ }
17
+ /**
18
+ * Service key structure from JSON file
19
+ */
20
+ export interface ServiceKey {
21
+ url?: string;
22
+ abap?: {
23
+ url?: string;
24
+ client?: string;
25
+ language?: string;
26
+ };
27
+ sap_url?: string;
28
+ client?: string;
29
+ sap_client?: string;
30
+ language?: string;
31
+ uaa: {
32
+ url: string;
33
+ clientid: string;
34
+ clientsecret: string;
35
+ };
36
+ }
37
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE;QACL,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,CAAC;IACF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE;QACH,GAAG,EAAE,MAAM,CAAC;QACZ,QAAQ,EAAE,MAAM,CAAC;QACjB,YAAY,EAAE,MAAM,CAAC;KACtB,CAAC;CACH"}
package/dist/types.js ADDED
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ /**
3
+ * Type definitions for auth-broker package
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
package/package.json ADDED
@@ -0,0 +1,64 @@
1
+ {
2
+ "name": "@mcp-abap-adt/auth-broker",
3
+ "version": "0.1.0",
4
+ "description": "JWT authentication broker for MCP ABAP ADT - manages tokens based on destination headers",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "files": [
8
+ "dist",
9
+ "README.md",
10
+ "CHANGELOG.md",
11
+ "CONTRIBUTORS.md",
12
+ "LICENSE"
13
+ ],
14
+ "keywords": [
15
+ "abap",
16
+ "sap",
17
+ "adt",
18
+ "jwt",
19
+ "authentication",
20
+ "token",
21
+ "broker",
22
+ "mcp",
23
+ "abap-adt"
24
+ ],
25
+ "author": "Oleksii Kyslytsia <oleksij.kyslytsja@gmail.com>",
26
+ "license": "MIT",
27
+ "homepage": "https://github.com/fr0ster/mcp-abap-adt-auth-broker#readme",
28
+ "bugs": {
29
+ "url": "https://github.com/fr0ster/mcp-abap-adt-auth-broker/issues"
30
+ },
31
+ "repository": {
32
+ "type": "git",
33
+ "url": "git+https://github.com/fr0ster/mcp-abap-adt-auth-broker.git"
34
+ },
35
+ "publishConfig": {
36
+ "access": "public"
37
+ },
38
+ "scripts": {
39
+ "clean": "rm -rf dist tsconfig.tsbuildinfo",
40
+ "build": "npm run clean --silent && npx tsc -p tsconfig.json",
41
+ "build:fast": "npx tsc -p tsconfig.json",
42
+ "test": "NODE_OPTIONS=--experimental-vm-modules jest",
43
+ "test:check": "npx tsc --noEmit",
44
+ "prepublishOnly": "npm run build"
45
+ },
46
+ "engines": {
47
+ "node": ">=18.0.0"
48
+ },
49
+ "dependencies": {
50
+ "@mcp-abap-adt/connection": "^0.1.12",
51
+ "axios": "^1.11.0",
52
+ "dotenv": "^17.2.1",
53
+ "express": "^5.1.0",
54
+ "open": "^11.0.0"
55
+ },
56
+ "devDependencies": {
57
+ "@types/express": "^5.0.5",
58
+ "@types/jest": "^30.0.0",
59
+ "@types/node": "^24.2.1",
60
+ "jest": "^30.2.0",
61
+ "ts-jest": "^29.2.5",
62
+ "typescript": "^5.9.2"
63
+ }
64
+ }