fa-mcp-sdk 0.2.174 → 0.2.184

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.
@@ -1,5 +1,165 @@
1
1
  let keyValuePairCount = 0;
2
2
 
3
+ // ===========================
4
+ // Token Authentication Module
5
+ // ===========================
6
+
7
+ const AUTH_TOKEN_KEY = 'adminAuthToken';
8
+ let requiresBearerToken = false;
9
+
10
+ // Get stored auth token from sessionStorage
11
+ function getStoredToken () {
12
+ return sessionStorage.getItem(AUTH_TOKEN_KEY);
13
+ }
14
+
15
+ // Store auth token in sessionStorage
16
+ function storeToken (token) {
17
+ sessionStorage.setItem(AUTH_TOKEN_KEY, token);
18
+ }
19
+
20
+ // Clear stored auth token
21
+ function clearStoredToken () {
22
+ sessionStorage.removeItem(AUTH_TOKEN_KEY);
23
+ }
24
+
25
+ // Show token authentication modal
26
+ function showTokenModal (errorMessage = null) {
27
+ const modal = document.getElementById('tokenModal');
28
+ const errorDiv = document.getElementById('tokenAuthError');
29
+
30
+ if (errorMessage) {
31
+ errorDiv.innerHTML = `<strong>Error:</strong> ${errorMessage}`;
32
+ errorDiv.style.display = 'block';
33
+ } else {
34
+ errorDiv.style.display = 'none';
35
+ }
36
+
37
+ modal.style.display = 'flex';
38
+ }
39
+
40
+ // Hide token authentication modal
41
+ function hideTokenModal () {
42
+ const modal = document.getElementById('tokenModal');
43
+ modal.style.display = 'none';
44
+ }
45
+
46
+ // Authenticated fetch wrapper - adds Authorization header if token auth is required
47
+ async function authFetch (url, options = {}) {
48
+ const token = getStoredToken();
49
+
50
+ if (requiresBearerToken && token) {
51
+ options.headers = {
52
+ ...options.headers,
53
+ 'Authorization': `Bearer ${token}`,
54
+ };
55
+ }
56
+
57
+ const response = await fetch(url, options);
58
+
59
+ // Handle 401 Unauthorized - show token modal if available
60
+ if (response.status === 401 && requiresBearerToken) {
61
+ clearStoredToken();
62
+ const errorData = await response.json().catch(() => ({}));
63
+ const errorMessage = errorData.error || 'Authentication failed';
64
+
65
+ // Try to show modal, but if it's not available, throw with descriptive error
66
+ const modal = document.getElementById('tokenModal');
67
+ if (modal) {
68
+ showTokenModal(errorMessage + '. Please enter a valid token.');
69
+ }
70
+
71
+ // Throw error with status code for form error handling
72
+ const error = new Error(`401 Unauthorized: ${errorMessage}`);
73
+ error.status = 401;
74
+ throw error;
75
+ }
76
+
77
+ return response;
78
+ }
79
+
80
+ // Check auth config and initialize authentication if needed
81
+ async function initializeAuth () {
82
+ try {
83
+ // Get auth config from public endpoint (no auth required)
84
+ const response = await fetch('/admin/api/auth-config');
85
+ const config = await response.json();
86
+
87
+ if (config.success && config.requiresBearerToken) {
88
+ requiresBearerToken = true;
89
+
90
+ // Check if we have a stored token
91
+ const storedToken = getStoredToken();
92
+ if (!storedToken) {
93
+ showTokenModal();
94
+ return false;
95
+ }
96
+
97
+ // Verify token is still valid by making an authenticated request
98
+ try {
99
+ const verifyResponse = await authFetch('/admin/api/auth-status');
100
+ const verifyData = await verifyResponse.json();
101
+
102
+ if (!verifyData.success || !verifyData.isAuthenticated) {
103
+ clearStoredToken();
104
+ showTokenModal('Token is invalid or expired.');
105
+ return false;
106
+ }
107
+ } catch (error) {
108
+ // authFetch already handles 401 and shows modal
109
+ return false;
110
+ }
111
+ }
112
+
113
+ return true;
114
+ } catch (error) {
115
+ console.error('Error checking auth config:', error);
116
+ return true; // Continue anyway if config check fails
117
+ }
118
+ }
119
+
120
+ // Handle token authentication form submission
121
+ function setupTokenAuthForm () {
122
+ const form = document.getElementById('tokenAuthForm');
123
+ if (!form) {return;}
124
+
125
+ form.addEventListener('submit', async (e) => {
126
+ e.preventDefault();
127
+
128
+ const tokenInput = document.getElementById('authTokenInput');
129
+ const token = tokenInput.value.trim();
130
+
131
+ if (!token) {
132
+ showTokenModal('Please enter a token.');
133
+ return;
134
+ }
135
+
136
+ // Store token and try to authenticate
137
+ storeToken(token);
138
+
139
+ try {
140
+ const response = await authFetch('/admin/api/auth-status');
141
+ const data = await response.json();
142
+
143
+ if (data.success && data.isAuthenticated) {
144
+ hideTokenModal();
145
+ tokenInput.value = '';
146
+ // Reload auth status and initialize form
147
+ loadAuthStatus();
148
+ initializeForm();
149
+ } else {
150
+ clearStoredToken();
151
+ showTokenModal(data.error || 'Invalid token.');
152
+ }
153
+ } catch (error) {
154
+ // Error already handled in authFetch
155
+ if (error.message !== 'Unauthorized') {
156
+ clearStoredToken();
157
+ showTokenModal('Authentication failed: ' + error.message);
158
+ }
159
+ }
160
+ });
161
+ }
162
+
3
163
  // Set primary color CSS variable
4
164
  function setPrimaryColor (color) {
5
165
  if (color) {
@@ -181,7 +341,7 @@ function renderAuthStatus (data) {
181
341
  // Load authentication status from API
182
342
  async function loadAuthStatus () {
183
343
  try {
184
- const response = await fetch('/admin/api/auth-status');
344
+ const response = await authFetch('/admin/api/auth-status');
185
345
  const data = await response.json();
186
346
  if (data.success) {
187
347
  renderAuthStatus(data);
@@ -214,7 +374,7 @@ document.getElementById('generateForm').addEventListener('submit', async (e) =>
214
374
  };
215
375
 
216
376
  try {
217
- const response = await fetch('/admin/api/generate-token', {
377
+ const response = await authFetch('/admin/api/generate-token', {
218
378
  method: 'POST',
219
379
  headers: { 'Content-Type': 'application/json' },
220
380
  body: JSON.stringify(requestData),
@@ -263,7 +423,7 @@ document.getElementById('validateForm').addEventListener('submit', async (e) =>
263
423
  const token = formData.get('token').trim();
264
424
 
265
425
  try {
266
- const response = await fetch('/admin/api/validate-token', {
426
+ const response = await authFetch('/admin/api/validate-token', {
267
427
  method: 'POST',
268
428
  headers: { 'Content-Type': 'application/json' },
269
429
  body: JSON.stringify({ token }),
@@ -319,19 +479,25 @@ Reason: ${result.error}
319
479
  async function initializeForm () {
320
480
  try {
321
481
  // Getting information about the service
322
- const response = await fetch('/admin/api/service-info');
482
+ const response = await authFetch('/admin/api/service-info');
323
483
  const data = await response.json();
324
484
  const serviceName = data.serviceName;
325
485
 
326
486
  // Set theme color
327
487
  setPrimaryColor(data.primaryColor);
328
488
 
489
+ // Clear existing key-value pairs before re-initializing
490
+ const container = document.getElementById('keyValuePairs');
491
+ container.innerHTML = '';
492
+ keyValuePairCount = 0;
493
+
329
494
  // Adding a pre-filled pair serviceName
330
495
  addKeyValuePair('service', serviceName, true);
331
496
  addKeyValuePair('issue', '', true, 'URL of request for the issuance of a token in JIRA');
332
497
 
333
498
  } catch (error) {
334
499
  console.error('Error loading service info:', error);
500
+ return;
335
501
  }
336
502
  // Add one empty pair for the user
337
503
  addKeyValuePair();
@@ -340,6 +506,19 @@ async function initializeForm () {
340
506
  // Logout function
341
507
  async function logout () {
342
508
  try {
509
+ // For token-based auth, just clear the stored token
510
+ if (requiresBearerToken) {
511
+ clearStoredToken();
512
+ showTokenModal();
513
+ // Clear auth status display
514
+ const container = document.getElementById('authStatusContainer');
515
+ if (container) {
516
+ container.style.display = 'none';
517
+ }
518
+ return;
519
+ }
520
+
521
+ // For other auth types (NTLM, Basic), make logout request
343
522
  const response = await fetch('/admin/logout', {
344
523
  method: 'GET',
345
524
  credentials: 'include',
@@ -359,7 +538,16 @@ async function logout () {
359
538
  }
360
539
 
361
540
  // Initialization on page load
362
- document.addEventListener('DOMContentLoaded', () => {
363
- loadAuthStatus();
364
- initializeForm();
541
+ document.addEventListener('DOMContentLoaded', async () => {
542
+ // Setup token auth form handler
543
+ setupTokenAuthForm();
544
+
545
+ // Initialize authentication (check if token is needed and valid)
546
+ const authOk = await initializeAuth();
547
+
548
+ if (authOk) {
549
+ // Load auth status and form only if authenticated
550
+ loadAuthStatus();
551
+ initializeForm();
552
+ }
365
553
  });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "fa-mcp-sdk",
3
3
  "productName": "FA MCP SDK",
4
- "version": "0.2.174",
4
+ "version": "0.2.184",
5
5
  "description": "Core infrastructure and templates for building Model Context Protocol (MCP) servers with TypeScript",
6
6
  "type": "module",
7
7
  "main": "dist/core/index.js",
@@ -66,7 +66,7 @@
66
66
  "homepage": "https://github.com/Bazilio-san/fa-mcp-sdk#readme",
67
67
  "dependencies": {
68
68
  "@modelcontextprotocol/sdk": "^1.24.3",
69
- "af-ad-ts": "^0.0.67",
69
+ "af-ad-ts": "^0.0.69",
70
70
  "af-db-ts": "^3.0.23",
71
71
  "af-logger-ts": "^0.0.24",
72
72
  "chalk": "^5.6.2",
@@ -96,11 +96,7 @@
96
96
  "@types/node": "^24.10.1",
97
97
  "@types/swagger-jsdoc": "^6.0.4",
98
98
  "@types/swagger-ui-express": "^4.1.8",
99
- "@typescript-eslint/eslint-plugin": "^8.48.1",
100
- "@typescript-eslint/parser": "^8.48.1",
101
99
  "eslint": "^9.39.1",
102
- "eslint-plugin-import": "^2.32.0",
103
- "eslint-plugin-unused-imports": "^4.3.0",
104
100
  "rimraf": "^6.1.2",
105
101
  "typescript": "^5.9.3"
106
102
  }