@starklabs/backend-core 1.0.0 → 1.1.1

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/README.md ADDED
@@ -0,0 +1,477 @@
1
+ # @starklabs/backend-core
2
+
3
+ A comprehensive backend authentication library featuring MongoDB integration, JWT-based authentication, encryption/decryption using libsodium, and utilities for error handling and logging. Supports both ES modules and CommonJS.
4
+
5
+ ## Features
6
+
7
+ - **MongoDB Integration**: Seamless MongoDB connection management with Mongoose
8
+ - **JWT Authentication**: Sign and verify JWT tokens with configurable expiry
9
+ - **Encryption/Decryption**: Secure data encryption using libsodium (libsodium-wrappers-sumo)
10
+ - **Error Handling**: Custom AppError class for consistent error management
11
+ - **Structured Logging**: AppLog utility for emoji-tagged console logging
12
+ - **Async Handler**: Express middleware wrapper for async route handlers
13
+ - **Dual Module Support**: Works with both ES modules and CommonJS
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ npm install @starklabs/backend-core
19
+ ```
20
+
21
+ ## Environment Variables
22
+
23
+ The following environment variables are required:
24
+
25
+ ```env
26
+ MONGODB_URI=mongodb://localhost:27017
27
+ DB_NAME=your_database_name
28
+ MASTER_KEY=your_base64_encoded_32_byte_key
29
+ JWT_SECRET=your_jwt_secret_string
30
+ JWT_EXPIRY=1d
31
+ ```
32
+
33
+ ### Generating MASTER_KEY and JWT_SECRET
34
+
35
+ Generate both keys using the crypto utilities:
36
+
37
+ ```javascript
38
+ import { crypto } from '@starklabs/backend-core';
39
+
40
+ // Generate 32-byte master key for encryption
41
+ const masterKey = await crypto.generateMasterKey();
42
+ console.log(masterKey); // Use this in .env as MASTER_KEY
43
+
44
+ // Generate 64-byte JWT secret for token signing
45
+ const jwtSecret = await crypto.generateJWTSecret();
46
+ console.log(jwtSecret); // Use this in .env as JWT_SECRET
47
+ ```
48
+
49
+ Store the generated values in your `.env` file:
50
+
51
+ ```env
52
+ MASTER_KEY=eZkZDoy2s+DvGe44QGa7AdU41nKhglEaIjFsxfQCKao=
53
+ JWT_SECRET=your_generated_jwt_secret_base64_string
54
+ ```
55
+
56
+ ## Usage
57
+
58
+ ### ES Modules
59
+
60
+ ```javascript
61
+ import starkAuth, { crypto, AppError, AppLog, asyncHandler } from '@starklabs/backend-core';
62
+
63
+ // Initialize
64
+ const auth = await starkAuth.create({
65
+ MONGODB_URI: process.env.MONGODB_URI,
66
+ DB_NAME: process.env.DB_NAME,
67
+ MASTER_KEY: process.env.MASTER_KEY,
68
+ JWT_SECRET: process.env.JWT_SECRET,
69
+ JWT_EXPIRY: process.env.JWT_EXPIRY || '1d'
70
+ });
71
+
72
+ // Sign JWT
73
+ const token = auth.signJWT({ userId: '123', email: 'user@example.com' });
74
+
75
+ // Verify JWT
76
+ const payload = auth.verifyJWT(token);
77
+ console.log(payload); // { userId: '123', email: 'user@example.com', iat: ..., exp: ... }
78
+
79
+ // Encrypt data
80
+ const encrypted = await auth.encrypt('sensitive data');
81
+ // Returns: { str, nonce, publicKey, securedPrivateKey }
82
+
83
+ // Decrypt data
84
+ const decrypted = await auth.decrypt(
85
+ encrypted.str,
86
+ encrypted.nonce,
87
+ encrypted.publicKey,
88
+ encrypted.securedPrivateKey
89
+ );
90
+ console.log(decrypted); // 'sensitive data'
91
+ ```
92
+
93
+ ### CommonJS
94
+
95
+ ```javascript
96
+ const starkAuth = require('@starklabs/backend-core');
97
+
98
+ // Initialize
99
+ const auth = await starkAuth.create({
100
+ MONGODB_URI: process.env.MONGODB_URI,
101
+ DB_NAME: process.env.DB_NAME,
102
+ MASTER_KEY: process.env.MASTER_KEY,
103
+ JWT_SECRET: process.env.JWT_SECRET,
104
+ JWT_EXPIRY: process.env.JWT_EXPIRY || '1d'
105
+ });
106
+
107
+ // Same API as ES modules
108
+ const token = auth.signJWT({ userId: '123' });
109
+ const payload = auth.verifyJWT(token);
110
+ ```
111
+
112
+ ## API Documentation
113
+
114
+ ### StarkAuth Class
115
+
116
+ #### `static async create(config)`
117
+
118
+ Initializes database connection and returns a new StarkAuth instance.
119
+
120
+ **Parameters:**
121
+ - `config.MONGODB_URI` (string): MongoDB connection URI
122
+ - `config.DB_NAME` (string): Database name
123
+ - `config.MASTER_KEY` (string): Base64-encoded 32-byte encryption key
124
+ - `config.JWT_SECRET` (string): Secret for JWT signing
125
+ - `config.JWT_EXPIRY` (string): JWT expiry time (e.g., "1d", "24h", "7d")
126
+
127
+ **Returns:** `Promise<StarkAuth>`
128
+
129
+ ```javascript
130
+ const auth = await starkAuth.create({
131
+ MONGODB_URI: 'mongodb://localhost:27017',
132
+ DB_NAME: 'myapp',
133
+ MASTER_KEY: 'eZkZDoy2s+DvGe44QGa7AdU41nKhglEaIjFsxfQCKao=',
134
+ JWT_SECRET: 'your-secret-key',
135
+ JWT_EXPIRY: '7d'
136
+ });
137
+ ```
138
+
139
+ #### `async encrypt(str)`
140
+
141
+ Encrypts a string using the master key.
142
+
143
+ **Parameters:**
144
+ - `str` (string): String to encrypt
145
+
146
+ **Returns:** `Promise<{ str, nonce, publicKey, securedPrivateKey }>`
147
+
148
+ ```javascript
149
+ const encrypted = await auth.encrypt('credit card number');
150
+ // Store all fields: str, nonce, publicKey, securedPrivateKey
151
+ ```
152
+
153
+ #### `async decrypt(str, nonce, publicKey, securedPrivateKey)`
154
+
155
+ Decrypts an encrypted string.
156
+
157
+ **Parameters:**
158
+ - `str` (string): Encrypted data (base64)
159
+ - `nonce` (string): Nonce (base64)
160
+ - `publicKey` (string): Public key (base64)
161
+ - `securedPrivateKey` (string): Secured private key (base64)
162
+
163
+ **Returns:** `Promise<string>`
164
+
165
+ ```javascript
166
+ const original = await auth.decrypt(
167
+ encrypted.str,
168
+ encrypted.nonce,
169
+ encrypted.publicKey,
170
+ encrypted.securedPrivateKey
171
+ );
172
+ ```
173
+
174
+ #### `signJWT(payload)`
175
+
176
+ Signs a JWT token.
177
+
178
+ **Parameters:**
179
+ - `payload` (object): Data to encode in token
180
+
181
+ **Returns:** `string` (JWT token)
182
+
183
+ **Supported Expiry Values:**
184
+ - `"2m"`, `"10m"`, `"1h"`, `"6h"`, `"12h"`, `"1d"`, `"3d"`, `"7d"`, `"14d"`, `"30d"`
185
+
186
+ ```javascript
187
+ const token = auth.signJWT({
188
+ userId: '507f1f77bcf86cd799439011',
189
+ role: 'admin',
190
+ email: 'user@example.com'
191
+ });
192
+ ```
193
+
194
+ #### `verifyJWT(token)`
195
+
196
+ Verifies and decodes a JWT token.
197
+
198
+ **Parameters:**
199
+ - `token` (string): JWT token to verify
200
+
201
+ **Returns:** `object` (decoded payload)
202
+
203
+ **Throws:** `AppError` if token is invalid or expired
204
+
205
+ ```javascript
206
+ try {
207
+ const payload = auth.verifyJWT(token);
208
+ console.log(payload.userId);
209
+ } catch (error) {
210
+ console.error('Invalid token:', error.message);
211
+ }
212
+ ```
213
+
214
+ ### Crypto Utilities
215
+
216
+ #### `crypto.generateMasterKey()`
217
+
218
+ Generates a secure 32-byte master key encoded in base64.
219
+
220
+ **Returns:** `Promise<string>`
221
+
222
+ ```javascript
223
+ const key = await crypto.generateMasterKey();
224
+ console.log(key); // "eZkZDoy2s+DvGe44QGa7AdU41nKhglEaIjFsxfQCKao="
225
+ ```
226
+
227
+ #### `crypto.generateJWTSecret()`
228
+
229
+ Generates a secure 64-byte JWT secret encoded in base64.
230
+
231
+ **Returns:** `Promise<string>`
232
+
233
+ ```javascript
234
+ const secret = await crypto.generateJWTSecret();
235
+ console.log(secret); // "long_base64_encoded_string_suitable_for_jwt_signing"
236
+ ```
237
+
238
+ #### `crypto.hash(password)`
239
+
240
+ Hashes a password using argon2i algorithm.
241
+
242
+ **Parameters:**
243
+ - `password` (string): Password to hash
244
+
245
+ **Returns:** `Promise<string>` (salt + hash in base64)
246
+
247
+ ```javascript
248
+ const hashedPassword = await crypto.hash('myPassword123');
249
+ // Store hashedPassword in database
250
+ ```
251
+
252
+ #### `crypto.verifyHash(password, storedHash)`
253
+
254
+ Verifies a password against a stored hash.
255
+
256
+ **Parameters:**
257
+ - `password` (string): Password to verify
258
+ - `storedHash` (string): Hash from database
259
+
260
+ **Returns:** `Promise<boolean>`
261
+
262
+ ```javascript
263
+ const isValid = await crypto.verifyHash('myPassword123', storedHash);
264
+ if (isValid) {
265
+ console.log('Password correct');
266
+ } else {
267
+ console.log('Password incorrect');
268
+ }
269
+ ```
270
+
271
+ ### Utility Exports
272
+
273
+ #### `AppError`
274
+
275
+ Custom error class for application errors.
276
+
277
+ ```javascript
278
+ import { AppError } from '@starklabs/backend-core';
279
+
280
+ throw new AppError('User not found', 404);
281
+ ```
282
+
283
+ **Constructor:** `new AppError(message, statusCode)`
284
+
285
+ #### `AppLog`
286
+
287
+ Structured logging utility with emoji tags.
288
+
289
+ ```javascript
290
+ import { AppLog } from '@starklabs/backend-core';
291
+
292
+ AppLog('check', 'auth.js', 'User authenticated successfully');
293
+ // Output: ✅ [auth.js] User authenticated successfully
294
+
295
+ AppLog('X', 'auth.js', 'Authentication failed');
296
+ // Output: ❌ [auth.js] Authentication failed
297
+ ```
298
+
299
+ **Parameters:** `AppLog(emoji, file, message)`
300
+ - `emoji` (string): `'check'` or `'X'`
301
+ - `file` (string): File name for context
302
+ - `message` (string): Log message
303
+
304
+ #### `asyncHandler`
305
+
306
+ Express middleware wrapper for handling async errors.
307
+
308
+ ```javascript
309
+ import { asyncHandler } from '@starklabs/backend-core';
310
+ import express from 'express';
311
+
312
+ const app = express();
313
+
314
+ app.post('/login', asyncHandler(async (req, res, next) => {
315
+ // Any thrown errors will be passed to next(error)
316
+ const user = await findUser(req.body.email);
317
+ res.json({ user });
318
+ }));
319
+ ```
320
+
321
+ ## Complete Example
322
+
323
+ ### Express Server with Authentication
324
+
325
+ ```javascript
326
+ import express from 'express';
327
+ import starkAuth, { asyncHandler, AppLog, AppError } from '@starklabs/backend-core';
328
+
329
+ const app = express();
330
+ app.use(express.json());
331
+
332
+ let auth;
333
+
334
+ // Initialize auth middleware
335
+ app.use(async (req, res, next) => {
336
+ if (!auth) {
337
+ auth = await starkAuth.create({
338
+ MONGODB_URI: process.env.MONGODB_URI,
339
+ DB_NAME: process.env.DB_NAME,
340
+ MASTER_KEY: process.env.MASTER_KEY,
341
+ JWT_SECRET: process.env.JWT_SECRET,
342
+ JWT_EXPIRY: '7d'
343
+ });
344
+ }
345
+ req.auth = auth;
346
+ next();
347
+ });
348
+
349
+ // Login endpoint
350
+ app.post('/login', asyncHandler(async (req, res) => {
351
+ const { email, password } = req.body;
352
+
353
+ if (!email || !password) {
354
+ throw new AppError('Email and password required', 400);
355
+ }
356
+
357
+ // Verify credentials (pseudo code)
358
+ const user = { id: '123', email };
359
+
360
+ const token = req.auth.signJWT({
361
+ userId: user.id,
362
+ email: user.email
363
+ });
364
+
365
+ AppLog('check', 'login', `User ${email} logged in`);
366
+ res.json({ token });
367
+ }));
368
+
369
+ // Protected endpoint with JWT verification
370
+ app.get('/profile', asyncHandler(async (req, res) => {
371
+ const token = req.headers.authorization?.split(' ')[1];
372
+
373
+ if (!token) {
374
+ throw new AppError('Token required', 401);
375
+ }
376
+
377
+ const payload = req.auth.verifyJWT(token);
378
+ res.json({ userId: payload.userId, email: payload.email });
379
+ }));
380
+
381
+ // Error handler
382
+ app.use((err, req, res, next) => {
383
+ const statusCode = err.statusCode || 500;
384
+ const message = err.message || 'Internal Server Error';
385
+ AppLog('X', 'server', message);
386
+ res.status(statusCode).json({ error: message });
387
+ });
388
+
389
+ app.listen(3000, () => {
390
+ AppLog('check', 'server', 'Server running on port 3000');
391
+ });
392
+ ```
393
+
394
+ ## Error Handling
395
+
396
+ All methods throw `AppError` on failure:
397
+
398
+ ```javascript
399
+ import { AppError } from '@starklabs/backend-core';
400
+
401
+ try {
402
+ const payload = auth.verifyJWT(invalidToken);
403
+ } catch (error) {
404
+ if (error instanceof AppError) {
405
+ console.log(error.message); // "Invalid or expired token"
406
+ console.log(error.statusCode); // 401
407
+ }
408
+ }
409
+ ```
410
+
411
+ ## Testing
412
+
413
+ Run the test suite:
414
+
415
+ ```bash
416
+ npm test
417
+ ```
418
+
419
+ Tests verify:
420
+ - Instance creation and method existence
421
+ - JWT signing/verification
422
+ - Encryption/decryption
423
+ - Cross-module compatibility (ES ↔ CJS)
424
+ - Error handling
425
+
426
+ ## Supported Environments
427
+
428
+ - **Node.js**: v14+
429
+ - **Module Systems**: ES Modules (`.mjs`, `"type": "module"`) and CommonJS (`.cjs`, `.js` with `"type": "commonjs"`)
430
+ - **Databases**: MongoDB 4.0+
431
+
432
+ ## Dependencies
433
+
434
+ - `mongoose`: ^9.6.1 - MongoDB ODM
435
+ - `jsonwebtoken`: ^9.0.3 - JWT signing/verification
436
+ - `libsodium-wrappers-sumo`: ^0.8.4 - Encryption/hashing
437
+ - `dotenv`: ^17.4.2 - Environment variable management
438
+ - `express`: ^5.2.1 - Web framework (optional)
439
+ - `cors`: ^2.8.6 - CORS middleware (optional)
440
+ - `helmet`: ^8.1.0 - Security headers (optional)
441
+
442
+ ## Security Considerations
443
+
444
+ 1. **MASTER_KEY**: Generate a strong key using `crypto.generateMasterKey()` and store securely in `.env`
445
+ 2. **JWT_SECRET**: Generate using `crypto.generateJWTSecret()` - this must be kept confidential and should never be shared or committed to version control
446
+ 3. **Database**: Ensure MongoDB is protected with authentication and firewall rules
447
+ 4. **HTTPS**: Always use HTTPS in production
448
+ 5. **Token Storage**: Store JWT tokens securely (httpOnly cookies recommended, not localStorage)
449
+ 6. **Key Rotation**: Consider rotating MASTER_KEY and JWT_SECRET periodically for enhanced security
450
+
451
+ ## Common Issues
452
+
453
+ ### "Invalid expiry key"
454
+ Ensure `JWT_EXPIRY` is one of the supported values: `"2m"`, `"10m"`, `"1h"`, `"6h"`, `"12h"`, `"1d"`, `"3d"`, `"7d"`, `"14d"`, `"30d"`
455
+
456
+ ### "Error while connecting!"
457
+ Check that:
458
+ - MongoDB is running and accessible
459
+ - `MONGODB_URI` is correct
460
+ - Network/firewall allows connection
461
+
462
+ ### "Invalid or expired token"
463
+ - Token may have expired (check `JWT_EXPIRY`)
464
+ - `JWT_SECRET` may have changed
465
+ - Token may be malformed or tampered
466
+
467
+ ## License
468
+
469
+ ISC
470
+
471
+ ## Author
472
+
473
+ Musa (@starklabs)
474
+
475
+ ## Contributing
476
+
477
+ Contributions are welcome! Please feel free to submit a Pull Request.
@@ -1,5 +1,5 @@
1
1
  const connectDB = require("./db.cjs");
2
- const { seal, unSeal, generateMasterKey, hash, verifyHash } = require("./utils/libsodium.cjs");
2
+ const { seal, unSeal, generateMasterKey, generateJWTSecret, hash, verifyHash } = require("./utils/libsodium.cjs");
3
3
  const { signJWT, verifyJWT } = require("./utils/jwt.cjs");
4
4
 
5
5
  const privateKeys = new WeakMap();
@@ -14,7 +14,7 @@ class StarkAuth {
14
14
  }
15
15
 
16
16
  static async create(config) {
17
- await connectDB(config.MONGO_URI, config.DB_NAME);
17
+ await connectDB(config.MONGODB_URI, config.DB_NAME);
18
18
  return new StarkAuth(config);
19
19
  }
20
20
 
@@ -43,6 +43,7 @@ module.exports = StarkAuth;
43
43
 
44
44
  const crypto = {
45
45
  generateMasterKey,
46
+ generateJWTSecret,
46
47
  hash,
47
48
  verifyHash,
48
49
  };
@@ -52,11 +52,22 @@ const generateMasterKey = async () => {
52
52
  sodium.base64_variants.ORIGINAL
53
53
  );
54
54
 
55
-
55
+
56
56
  console.log(base64Key);
57
57
  return base64Key
58
58
  };
59
59
 
60
+ // generate JWT secret
61
+ const generateJWTSecret = async () => {
62
+ await sodium.ready;
63
+
64
+ const secret = sodium.randombytes_buf(64);
65
+
66
+ const base64Secret = sodium.to_base64(secret, sodium.base64_variants.ORIGINAL);
67
+ console.log(base64Secret);
68
+ return base64Secret;
69
+ };
70
+
60
71
  // seal
61
72
  const seal = async (string, masterKey) => {
62
73
  try {
@@ -128,6 +139,7 @@ module.exports = {
128
139
  hash,
129
140
  verifyHash,
130
141
  generateMasterKey,
142
+ generateJWTSecret,
131
143
  seal,
132
144
  unSeal,
133
145
  };
package/dist/js/index.js CHANGED
@@ -19,7 +19,7 @@ class StarkAuth {
19
19
 
20
20
  // create - initializes DB and auth instance
21
21
  static async create(config) {
22
- await connectDB(config.MONGO_URI, config.DB_NAME);
22
+ await connectDB(config.MONGODB_URI, config.DB_NAME);
23
23
  return new StarkAuth(config);
24
24
  }
25
25
 
@@ -47,10 +47,11 @@ class StarkAuth {
47
47
  export default StarkAuth;
48
48
 
49
49
  // crypto - exports hashing utilities
50
- import { generateMasterKey, hash, verifyHash } from "./utils/libsodium.js";
50
+ import { generateMasterKey, generateJWTSecret, hash, verifyHash } from "./utils/libsodium.js";
51
51
 
52
52
  export const crypto = {
53
53
  generateMasterKey,
54
+ generateJWTSecret,
54
55
  hash,
55
56
  verifyHash,
56
57
  };
@@ -55,6 +55,19 @@ export const generateMasterKey = async () => {
55
55
  return base64Key;
56
56
  };
57
57
 
58
+ // generate JWT secret
59
+ export const generateJWTSecret = async () => {
60
+ await sodium.ready;
61
+
62
+ // Generate a secure 64-byte random key for JWT
63
+ const secret = sodium.randombytes_buf(64);
64
+
65
+ // Convert to base64 for .env storage
66
+ const base64Secret = sodium.to_base64(secret, sodium.base64_variants.ORIGINAL);
67
+ console.log(base64Secret);
68
+ return base64Secret;
69
+ };
70
+
58
71
  // seal
59
72
  export const seal = async (string, masterKey) => {
60
73
  try {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@starklabs/backend-core",
3
- "version": "1.0.0",
4
- "description": "A comprehensive backend authentication library featuring MongoDB integration, JWT-based authentication, encryption/decryption using libsodium, and utilities for error handling and logging. Supports both ES modules and CommonJS. Requires MONGO_URI, DB_NAME, MASTER_KEY, and JWT_SECRET environment variables.",
3
+ "version": "1.1.1",
4
+ "description": "A comprehensive backend authentication library featuring MongoDB integration, JWT-based authentication, encryption/decryption using libsodium, and utilities for error handling and logging. Supports both ES modules and CommonJS. Requires MONGODB_URI, DB_NAME, MASTER_KEY, and JWT_SECRET environment variables.",
5
5
  "keywords": [
6
6
  "auth-mongo",
7
7
  "backend-core"