aip-master-node-sumit 1.0.8 → 1.0.11

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 +127 -28
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -10,7 +10,7 @@ const getSafeMasterUrl = (url) => {
10
10
  return clean;
11
11
  };
12
12
 
13
- // 🛡️ THE FIX: Bulletproof Native Cookie Parser (Handles JWT padding '=')
13
+ // 🛡️ Bulletproof Native Cookie Parser (Handles JWT padding '=')
14
14
  const parseRawCookies = (cookieHeader) => {
15
15
  if (!cookieHeader) return {};
16
16
 
@@ -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');
@@ -79,24 +82,34 @@ const aipGuard = (options = { requireLogin: true }) => {
79
82
  return res.status(413).json({ error: "AIP Infrastructure Shield: Payload Too Large." });
80
83
  }
81
84
 
82
- // 🛡️ THE FIX: Smart Cookie Extraction & Removed Vulnerable Query Tokens
83
- const cookies = req.cookies || parseRawCookies(req.headers.cookie);
84
- let token = cookies.aip_session;
85
+ // ==========================================
86
+ // 🛡️ THE FIX: Strict Header Priority (Bearer overrides Cookies)
87
+ // ==========================================
88
+ let token = null;
85
89
 
86
- if (!token && req.headers.authorization && req.headers.authorization.startsWith('Bearer ')) {
90
+ // 1. Check Explicit Headers FIRST
91
+ if (req.headers.authorization && req.headers.authorization.startsWith('Bearer ')) {
87
92
  token = req.headers.authorization.split(' ')[1];
88
- } else if (!token && req.headers['x-aip-token']) {
93
+ } else if (req.headers['x-aip-token']) {
89
94
  token = req.headers['x-aip-token'];
95
+ }
96
+ // 2. Fallback to browser cookies ONLY if no explicit headers exist
97
+ else {
98
+ const cookies = req.cookies || parseRawCookies(req.headers.cookie);
99
+ token = cookies.aip_session;
90
100
  }
91
101
 
92
102
  // ==========================================
93
103
  // 🛡️ LAYER 2: IDENTITY CHECK & RBAC
94
104
  // ==========================================
95
105
  if (options.requireLogin) {
96
- if (!token) return res.status(401).json({ error: "Unauthorized: Missing AIP Session Token" });
106
+ if (!token) {
107
+ return res.status(401).json({ error: "Unauthorized: Missing AIP Session Token" });
108
+ }
97
109
 
98
110
  try {
99
111
  const currentAction = req.method + " " + (req.baseUrl + req.path);
112
+
100
113
  const authRes = await axios.post(`${AIP_MASTER_API}/iam/verify-session`,
101
114
  { session_token: token, action: currentAction },
102
115
  { headers: { 'Authorization': `Bearer ${API_KEY}` } }
@@ -104,13 +117,20 @@ const aipGuard = (options = { requireLogin: true }) => {
104
117
 
105
118
  req.user = authRes.data.user;
106
119
 
107
- // 🛡️ THE FIX: Native Node.js Fallback for Set-Cookie (Framework Agnostic)
120
+ // 🛡️ Native Node.js Fallback for Set-Cookie (Framework Agnostic)
108
121
  const isProd = process.env.NODE_ENV === 'production';
122
+
109
123
  if (typeof res.cookie === 'function') {
124
+ // Express.js environment
110
125
  res.cookie('aip_session', token, {
111
- httpOnly: true, secure: isProd, sameSite: isProd ? 'none' : 'lax', path: '/', maxAge: 12 * 60 * 60 * 1000
126
+ httpOnly: true,
127
+ secure: isProd,
128
+ sameSite: isProd ? 'none' : 'lax',
129
+ path: '/',
130
+ maxAge: 12 * 60 * 60 * 1000
112
131
  });
113
132
  } else {
133
+ // Raw Node.js or alternate framework environment
114
134
  const cookieStr = `aip_session=${token}; HttpOnly; ${isProd ? 'Secure; SameSite=None' : 'SameSite=Lax'}; Path=/; Max-Age=43200`;
115
135
  res.setHeader('Set-Cookie', cookieStr);
116
136
  }
@@ -118,11 +138,15 @@ const aipGuard = (options = { requireLogin: true }) => {
118
138
  } catch (error) {
119
139
  const status = error.response?.status || 500;
120
140
  const errMsg = error.response?.data?.error || "AIP Identity Blocked: Invalid Token.";
121
-
122
141
  const isProd = process.env.NODE_ENV === 'production';
142
+
123
143
  if (status === 401) {
124
144
  if (typeof res.clearCookie === 'function') {
125
- res.clearCookie('aip_session', { path: '/', sameSite: isProd ? 'none' : 'lax', secure: isProd });
145
+ res.clearCookie('aip_session', {
146
+ path: '/',
147
+ sameSite: isProd ? 'none' : 'lax',
148
+ secure: isProd
149
+ });
126
150
  } else {
127
151
  res.setHeader('Set-Cookie', `aip_session=; HttpOnly; ${isProd ? 'Secure; SameSite=None' : 'SameSite=Lax'}; Path=/; Max-Age=0`);
128
152
  }
@@ -136,16 +160,28 @@ const aipGuard = (options = { requireLogin: true }) => {
136
160
  // 🛡️ LAYER 3: WAF THREAT SCAN
137
161
  // ==========================================
138
162
  try {
139
- const payloadData = JSON.stringify({ body: req.body || {}, query: req.query || {} });
163
+ const payloadData = JSON.stringify({
164
+ body: req.body || {},
165
+ query: req.query || {}
166
+ });
167
+
140
168
  const forwardedFor = req.headers['x-forwarded-for'];
141
169
  let clientIp = req.ip || (req.connection && req.connection.remoteAddress) || "0.0.0.0";
170
+
142
171
  if (forwardedFor) {
143
- clientIp = Array.isArray(forwardedFor) ? forwardedFor[0].trim() : forwardedFor.split(',')[0].trim();
172
+ clientIp = Array.isArray(forwardedFor)
173
+ ? forwardedFor[0].trim()
174
+ : forwardedFor.split(',')[0].trim();
144
175
  }
145
176
 
146
177
  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'] || "",
178
+ api_key: API_KEY,
179
+ ip: clientIp,
180
+ method: req.method,
181
+ url: req.originalUrl,
182
+ payload: payloadData,
183
+ origin: req.headers.origin || "",
184
+ user_agent: req.headers['user-agent'] || "",
149
185
  session_token: token || ""
150
186
  });
151
187
 
@@ -156,26 +192,64 @@ const aipGuard = (options = { requireLogin: true }) => {
156
192
  } catch (error) {
157
193
  return res.status(500).json({ error: "Security verification failed." });
158
194
  }
195
+
159
196
  next();
160
197
  };
161
198
  };
162
199
 
200
+ // ==========================================
201
+ // 🛡️ Admin Client SDK
202
+ // ==========================================
163
203
  class AipAdminClient {
164
204
  constructor(apiKey = process.env.AIP_MASTER_API_KEY, masterUrl = process.env.AIP_MASTER_URL) {
165
205
  if (!apiKey) throw new Error("AIP Admin Client requires an API Key");
166
206
  this.apiKey = apiKey;
167
207
  this.baseUrl = getSafeMasterUrl(masterUrl);
168
- this.client = axios.create({ baseURL: this.baseUrl, headers: { 'Authorization': `Bearer ${this.apiKey}` } });
208
+ this.client = axios.create({
209
+ baseURL: this.baseUrl,
210
+ headers: { 'Authorization': `Bearer ${this.apiKey}` }
211
+ });
212
+ }
213
+
214
+ async getUsers() {
215
+ const res = await this.client.get('/sdk/admin/users');
216
+ return res.data;
217
+ }
218
+
219
+ async createUser(data) {
220
+ const res = await this.client.post('/sdk/admin/users', data);
221
+ return res.data;
222
+ }
223
+
224
+ async updateUser(userId, data) {
225
+ const res = await this.client.put(`/sdk/admin/users/${userId}`, data);
226
+ return res.data;
227
+ }
228
+
229
+ async updateRBAC(userId, permissions) {
230
+ const res = await this.client.put(`/sdk/admin/users/${userId}/rbac`, { permissions });
231
+ return res.data;
232
+ }
233
+
234
+ async deleteUser(userId) {
235
+ const res = await this.client.delete(`/sdk/admin/users/${userId}`);
236
+ return res.data;
237
+ }
238
+
239
+ async getRoles() {
240
+ const res = await this.client.get('/sdk/admin/roles');
241
+ return res.data;
242
+ }
243
+
244
+ async createRole(data) {
245
+ const res = await this.client.post('/sdk/admin/roles', data);
246
+ return res.data;
169
247
  }
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
248
  }
178
249
 
250
+ // ==========================================
251
+ // 🛡️ End-User IAM Client SDK
252
+ // ==========================================
179
253
  class AipIAMClient {
180
254
  constructor(apiKey = process.env.AIP_MASTER_API_KEY, masterUrl = process.env.AIP_MASTER_URL) {
181
255
  if (!apiKey) throw new Error("AIP IAM Client requires an API Key");
@@ -183,33 +257,58 @@ class AipIAMClient {
183
257
  this.baseUrl = getSafeMasterUrl(masterUrl);
184
258
  this.client = axios.create({ baseURL: this.baseUrl });
185
259
  }
260
+
186
261
  async loginUser(req, res, credentials) {
187
262
  let clientIp = req.ip || (req.connection && req.connection.remoteAddress) || "0.0.0.0";
188
263
  const forwardedFor = req.headers['x-forwarded-for'];
189
- if (forwardedFor) clientIp = Array.isArray(forwardedFor) ? forwardedFor[0].trim() : forwardedFor.split(',')[0].trim();
264
+
265
+ if (forwardedFor) {
266
+ clientIp = Array.isArray(forwardedFor)
267
+ ? forwardedFor[0].trim()
268
+ : forwardedFor.split(',')[0].trim();
269
+ }
190
270
 
191
- const authRes = await this.client.post('/iam/auth', { api_key: this.apiKey, ip: clientIp, ...credentials });
271
+ const authRes = await this.client.post('/iam/auth', {
272
+ api_key: this.apiKey,
273
+ ip: clientIp,
274
+ ...credentials
275
+ });
192
276
 
193
277
  if (authRes.data.session_token) {
194
278
  const isProd = process.env.NODE_ENV === 'production';
279
+
195
280
  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 });
281
+ res.cookie('aip_session', authRes.data.session_token, {
282
+ httpOnly: true,
283
+ secure: isProd,
284
+ sameSite: isProd ? 'none' : 'lax',
285
+ path: '/',
286
+ maxAge: 12 * 60 * 60 * 1000
287
+ });
197
288
  } else {
198
289
  res.setHeader('Set-Cookie', `aip_session=${authRes.data.session_token}; HttpOnly; ${isProd ? 'Secure; SameSite=None' : 'SameSite=Lax'}; Path=/; Max-Age=43200`);
199
290
  }
200
291
  }
201
292
  return authRes.data;
202
293
  }
294
+
203
295
  logoutUser(res) {
204
296
  const isProd = process.env.NODE_ENV === 'production';
297
+
205
298
  if (typeof res.clearCookie === 'function') {
206
- res.clearCookie('aip_session', { path: '/', sameSite: isProd ? 'none' : 'lax', secure: isProd });
299
+ res.clearCookie('aip_session', {
300
+ path: '/',
301
+ sameSite: isProd ? 'none' : 'lax',
302
+ secure: isProd
303
+ });
207
304
  } else {
208
305
  res.setHeader('Set-Cookie', `aip_session=; HttpOnly; ${isProd ? 'Secure; SameSite=None' : 'SameSite=Lax'}; Path=/; Max-Age=0`);
209
306
  }
210
307
  }
211
308
  }
212
309
 
310
+ // Attach the clients directly to the Guard export for easy importing
213
311
  aipGuard.AipAdminClient = AipAdminClient;
214
312
  aipGuard.AipIAMClient = AipIAMClient;
313
+
215
314
  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.11",
4
4
  "description": "Enterprise-grade WAF and IAM security middleware for Node.js.",
5
5
  "main": "index.js",
6
6
  "scripts": {