aip-master-node-sumit 1.0.8 → 1.0.9

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 (2) hide show
  1. package/index.js +113 -21
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -36,7 +36,9 @@ if (API_KEY) {
36
36
  headers: { 'Authorization': `Bearer ${API_KEY}` },
37
37
  timeout: 5000
38
38
  });
39
- } catch (err) {}
39
+ } catch (err) {
40
+ // Silently fail so the client's app doesn't crash if Master API is updating
41
+ }
40
42
  };
41
43
  sendHeartbeat();
42
44
  setInterval(sendHeartbeat, 5 * 60 * 1000);
@@ -61,6 +63,7 @@ const aipGuard = (options = { requireLogin: true }) => {
61
63
  res.setHeader('X-Content-Type-Options', 'nosniff');
62
64
  res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
63
65
 
66
+ // Handle preflight requests gracefully
64
67
  if (req.method === 'OPTIONS') {
65
68
  res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, PATCH, OPTIONS');
66
69
  res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization, X-AIP-Token, X-Requested-With, Accept');
@@ -93,10 +96,13 @@ const aipGuard = (options = { requireLogin: true }) => {
93
96
  // 🛡️ LAYER 2: IDENTITY CHECK & RBAC
94
97
  // ==========================================
95
98
  if (options.requireLogin) {
96
- if (!token) return res.status(401).json({ error: "Unauthorized: Missing AIP Session Token" });
99
+ if (!token) {
100
+ return res.status(401).json({ error: "Unauthorized: Missing AIP Session Token" });
101
+ }
97
102
 
98
103
  try {
99
104
  const currentAction = req.method + " " + (req.baseUrl + req.path);
105
+
100
106
  const authRes = await axios.post(`${AIP_MASTER_API}/iam/verify-session`,
101
107
  { session_token: token, action: currentAction },
102
108
  { headers: { 'Authorization': `Bearer ${API_KEY}` } }
@@ -106,11 +112,18 @@ const aipGuard = (options = { requireLogin: true }) => {
106
112
 
107
113
  // 🛡️ THE FIX: Native Node.js Fallback for Set-Cookie (Framework Agnostic)
108
114
  const isProd = process.env.NODE_ENV === 'production';
115
+
109
116
  if (typeof res.cookie === 'function') {
117
+ // Express.js environment
110
118
  res.cookie('aip_session', token, {
111
- httpOnly: true, secure: isProd, sameSite: isProd ? 'none' : 'lax', path: '/', maxAge: 12 * 60 * 60 * 1000
119
+ httpOnly: true,
120
+ secure: isProd,
121
+ sameSite: isProd ? 'none' : 'lax',
122
+ path: '/',
123
+ maxAge: 12 * 60 * 60 * 1000
112
124
  });
113
125
  } else {
126
+ // Raw Node.js or alternate framework environment
114
127
  const cookieStr = `aip_session=${token}; HttpOnly; ${isProd ? 'Secure; SameSite=None' : 'SameSite=Lax'}; Path=/; Max-Age=43200`;
115
128
  res.setHeader('Set-Cookie', cookieStr);
116
129
  }
@@ -118,11 +131,15 @@ const aipGuard = (options = { requireLogin: true }) => {
118
131
  } catch (error) {
119
132
  const status = error.response?.status || 500;
120
133
  const errMsg = error.response?.data?.error || "AIP Identity Blocked: Invalid Token.";
121
-
122
134
  const isProd = process.env.NODE_ENV === 'production';
135
+
123
136
  if (status === 401) {
124
137
  if (typeof res.clearCookie === 'function') {
125
- res.clearCookie('aip_session', { path: '/', sameSite: isProd ? 'none' : 'lax', secure: isProd });
138
+ res.clearCookie('aip_session', {
139
+ path: '/',
140
+ sameSite: isProd ? 'none' : 'lax',
141
+ secure: isProd
142
+ });
126
143
  } else {
127
144
  res.setHeader('Set-Cookie', `aip_session=; HttpOnly; ${isProd ? 'Secure; SameSite=None' : 'SameSite=Lax'}; Path=/; Max-Age=0`);
128
145
  }
@@ -136,16 +153,28 @@ const aipGuard = (options = { requireLogin: true }) => {
136
153
  // 🛡️ LAYER 3: WAF THREAT SCAN
137
154
  // ==========================================
138
155
  try {
139
- const payloadData = JSON.stringify({ body: req.body || {}, query: req.query || {} });
156
+ const payloadData = JSON.stringify({
157
+ body: req.body || {},
158
+ query: req.query || {}
159
+ });
160
+
140
161
  const forwardedFor = req.headers['x-forwarded-for'];
141
162
  let clientIp = req.ip || (req.connection && req.connection.remoteAddress) || "0.0.0.0";
163
+
142
164
  if (forwardedFor) {
143
- clientIp = Array.isArray(forwardedFor) ? forwardedFor[0].trim() : forwardedFor.split(',')[0].trim();
165
+ clientIp = Array.isArray(forwardedFor)
166
+ ? forwardedFor[0].trim()
167
+ : forwardedFor.split(',')[0].trim();
144
168
  }
145
169
 
146
170
  const wafRes = await axios.post(`${AIP_MASTER_API}/verify`, {
147
- api_key: API_KEY, ip: clientIp, method: req.method, url: req.originalUrl,
148
- payload: payloadData, origin: req.headers.origin || "", user_agent: req.headers['user-agent'] || "",
171
+ api_key: API_KEY,
172
+ ip: clientIp,
173
+ method: req.method,
174
+ url: req.originalUrl,
175
+ payload: payloadData,
176
+ origin: req.headers.origin || "",
177
+ user_agent: req.headers['user-agent'] || "",
149
178
  session_token: token || ""
150
179
  });
151
180
 
@@ -156,26 +185,64 @@ const aipGuard = (options = { requireLogin: true }) => {
156
185
  } catch (error) {
157
186
  return res.status(500).json({ error: "Security verification failed." });
158
187
  }
188
+
159
189
  next();
160
190
  };
161
191
  };
162
192
 
193
+ // ==========================================
194
+ // 🛡️ Admin Client SDK
195
+ // ==========================================
163
196
  class AipAdminClient {
164
197
  constructor(apiKey = process.env.AIP_MASTER_API_KEY, masterUrl = process.env.AIP_MASTER_URL) {
165
198
  if (!apiKey) throw new Error("AIP Admin Client requires an API Key");
166
199
  this.apiKey = apiKey;
167
200
  this.baseUrl = getSafeMasterUrl(masterUrl);
168
- this.client = axios.create({ baseURL: this.baseUrl, headers: { 'Authorization': `Bearer ${this.apiKey}` } });
201
+ this.client = axios.create({
202
+ baseURL: this.baseUrl,
203
+ headers: { 'Authorization': `Bearer ${this.apiKey}` }
204
+ });
205
+ }
206
+
207
+ async getUsers() {
208
+ const res = await this.client.get('/sdk/admin/users');
209
+ return res.data;
210
+ }
211
+
212
+ async createUser(data) {
213
+ const res = await this.client.post('/sdk/admin/users', data);
214
+ return res.data;
215
+ }
216
+
217
+ async updateUser(userId, data) {
218
+ const res = await this.client.put(`/sdk/admin/users/${userId}`, data);
219
+ return res.data;
220
+ }
221
+
222
+ async updateRBAC(userId, permissions) {
223
+ const res = await this.client.put(`/sdk/admin/users/${userId}/rbac`, { permissions });
224
+ return res.data;
225
+ }
226
+
227
+ async deleteUser(userId) {
228
+ const res = await this.client.delete(`/sdk/admin/users/${userId}`);
229
+ return res.data;
230
+ }
231
+
232
+ async getRoles() {
233
+ const res = await this.client.get('/sdk/admin/roles');
234
+ return res.data;
235
+ }
236
+
237
+ async createRole(data) {
238
+ const res = await this.client.post('/sdk/admin/roles', data);
239
+ return res.data;
169
240
  }
170
- async getUsers() { const res = await this.client.get('/sdk/admin/users'); return res.data; }
171
- async createUser(data) { const res = await this.client.post('/sdk/admin/users', data); return res.data; }
172
- async updateUser(userId, data) { const res = await this.client.put(`/sdk/admin/users/${userId}`, data); return res.data; }
173
- async updateRBAC(userId, permissions) { const res = await this.client.put(`/sdk/admin/users/${userId}/rbac`, { permissions }); return res.data; }
174
- async deleteUser(userId) { const res = await this.client.delete(`/sdk/admin/users/${userId}`); return res.data; }
175
- async getRoles() { const res = await this.client.get('/sdk/admin/roles'); return res.data; }
176
- async createRole(data) { const res = await this.client.post('/sdk/admin/roles', data); return res.data; }
177
241
  }
178
242
 
243
+ // ==========================================
244
+ // 🛡️ End-User IAM Client SDK
245
+ // ==========================================
179
246
  class AipIAMClient {
180
247
  constructor(apiKey = process.env.AIP_MASTER_API_KEY, masterUrl = process.env.AIP_MASTER_URL) {
181
248
  if (!apiKey) throw new Error("AIP IAM Client requires an API Key");
@@ -183,33 +250,58 @@ class AipIAMClient {
183
250
  this.baseUrl = getSafeMasterUrl(masterUrl);
184
251
  this.client = axios.create({ baseURL: this.baseUrl });
185
252
  }
253
+
186
254
  async loginUser(req, res, credentials) {
187
255
  let clientIp = req.ip || (req.connection && req.connection.remoteAddress) || "0.0.0.0";
188
256
  const forwardedFor = req.headers['x-forwarded-for'];
189
- if (forwardedFor) clientIp = Array.isArray(forwardedFor) ? forwardedFor[0].trim() : forwardedFor.split(',')[0].trim();
257
+
258
+ if (forwardedFor) {
259
+ clientIp = Array.isArray(forwardedFor)
260
+ ? forwardedFor[0].trim()
261
+ : forwardedFor.split(',')[0].trim();
262
+ }
190
263
 
191
- const authRes = await this.client.post('/iam/auth', { api_key: this.apiKey, ip: clientIp, ...credentials });
264
+ const authRes = await this.client.post('/iam/auth', {
265
+ api_key: this.apiKey,
266
+ ip: clientIp,
267
+ ...credentials
268
+ });
192
269
 
193
270
  if (authRes.data.session_token) {
194
271
  const isProd = process.env.NODE_ENV === 'production';
272
+
195
273
  if (typeof res.cookie === 'function') {
196
- res.cookie('aip_session', authRes.data.session_token, { httpOnly: true, secure: isProd, sameSite: isProd ? 'none' : 'lax', path: '/', maxAge: 12 * 60 * 60 * 1000 });
274
+ res.cookie('aip_session', authRes.data.session_token, {
275
+ httpOnly: true,
276
+ secure: isProd,
277
+ sameSite: isProd ? 'none' : 'lax',
278
+ path: '/',
279
+ maxAge: 12 * 60 * 60 * 1000
280
+ });
197
281
  } else {
198
282
  res.setHeader('Set-Cookie', `aip_session=${authRes.data.session_token}; HttpOnly; ${isProd ? 'Secure; SameSite=None' : 'SameSite=Lax'}; Path=/; Max-Age=43200`);
199
283
  }
200
284
  }
201
285
  return authRes.data;
202
286
  }
287
+
203
288
  logoutUser(res) {
204
289
  const isProd = process.env.NODE_ENV === 'production';
290
+
205
291
  if (typeof res.clearCookie === 'function') {
206
- res.clearCookie('aip_session', { path: '/', sameSite: isProd ? 'none' : 'lax', secure: isProd });
292
+ res.clearCookie('aip_session', {
293
+ path: '/',
294
+ sameSite: isProd ? 'none' : 'lax',
295
+ secure: isProd
296
+ });
207
297
  } else {
208
298
  res.setHeader('Set-Cookie', `aip_session=; HttpOnly; ${isProd ? 'Secure; SameSite=None' : 'SameSite=Lax'}; Path=/; Max-Age=0`);
209
299
  }
210
300
  }
211
301
  }
212
302
 
303
+ // Attach the clients directly to the Guard export for easy importing
213
304
  aipGuard.AipAdminClient = AipAdminClient;
214
305
  aipGuard.AipIAMClient = AipIAMClient;
306
+
215
307
  module.exports = aipGuard;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aip-master-node-sumit",
3
- "version": "1.0.8",
3
+ "version": "1.0.9",
4
4
  "description": "Enterprise-grade WAF and IAM security middleware for Node.js.",
5
5
  "main": "index.js",
6
6
  "scripts": {