@starklabs/backend-core 1.0.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.
- package/dist/cjs/db.cjs +17 -0
- package/dist/cjs/index.cjs +58 -0
- package/dist/cjs/utils/AppError.cjs +13 -0
- package/dist/cjs/utils/AppLog.cjs +13 -0
- package/dist/cjs/utils/asyncHandler.cjs +6 -0
- package/dist/cjs/utils/jwt.cjs +38 -0
- package/dist/cjs/utils/libsodium.cjs +133 -0
- package/dist/cjs/utils/successResponse.cjs +13 -0
- package/dist/js/db.js +19 -0
- package/dist/js/index.js +65 -0
- package/dist/js/utils/AppError.js +13 -0
- package/dist/js/utils/AppLog.js +19 -0
- package/dist/js/utils/asyncHandler.js +5 -0
- package/dist/js/utils/index.js +7 -0
- package/dist/js/utils/jwt.js +38 -0
- package/dist/js/utils/libsodium.js +123 -0
- package/dist/js/utils/successResponse.js +13 -0
- package/package.json +28 -0
package/dist/cjs/db.cjs
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
const mongoose = require("mongoose");
|
|
2
|
+
const AppLog = require("./utils/AppLog.cjs");
|
|
3
|
+
|
|
4
|
+
// connecting to db
|
|
5
|
+
const connectDB = async (uri, db) => {
|
|
6
|
+
const mongodbUri = uri || "mongodb://localhost:27017";
|
|
7
|
+
try {
|
|
8
|
+
await mongoose.connect(`${mongodbUri}/${db || "test"}`);
|
|
9
|
+
AppLog("check", "db.js", "Connected successfully!");
|
|
10
|
+
} catch (error) {
|
|
11
|
+
AppLog("X", "db.js", "Error while connecting!");
|
|
12
|
+
AppLog("X", "db.js", error.message);
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
// export
|
|
17
|
+
module.exports = connectDB;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
const connectDB = require("./db.cjs");
|
|
2
|
+
const { seal, unSeal, generateMasterKey, hash, verifyHash } = require("./utils/libsodium.cjs");
|
|
3
|
+
const { signJWT, verifyJWT } = require("./utils/jwt.cjs");
|
|
4
|
+
|
|
5
|
+
const privateKeys = new WeakMap();
|
|
6
|
+
|
|
7
|
+
class StarkAuth {
|
|
8
|
+
constructor(config) {
|
|
9
|
+
privateKeys.set(this, {
|
|
10
|
+
_masterKey: config.MASTER_KEY,
|
|
11
|
+
_JWT_SECRET: config.JWT_SECRET,
|
|
12
|
+
_JWT_EXPIRY: config.JWT_EXPIRY,
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
static async create(config) {
|
|
17
|
+
await connectDB(config.MONGO_URI, config.DB_NAME);
|
|
18
|
+
return new StarkAuth(config);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async encrypt(str) {
|
|
22
|
+
const { _masterKey } = privateKeys.get(this);
|
|
23
|
+
return seal(str, _masterKey);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async decrypt(str, nonce, publicKey, securedPrivateKey) {
|
|
27
|
+
const { _masterKey } = privateKeys.get(this);
|
|
28
|
+
return unSeal(str, nonce, publicKey, securedPrivateKey, _masterKey);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
signJWT(payLoad) {
|
|
32
|
+
const { _JWT_SECRET, _JWT_EXPIRY } = privateKeys.get(this);
|
|
33
|
+
return signJWT(payLoad, _JWT_SECRET, _JWT_EXPIRY);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
verifyJWT(token) {
|
|
37
|
+
const { _JWT_SECRET } = privateKeys.get(this);
|
|
38
|
+
return verifyJWT(token, _JWT_SECRET);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
module.exports = StarkAuth;
|
|
43
|
+
|
|
44
|
+
const crypto = {
|
|
45
|
+
generateMasterKey,
|
|
46
|
+
hash,
|
|
47
|
+
verifyHash,
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const AppError = require("./utils/AppError.cjs");
|
|
51
|
+
const AppLog = require("./utils/AppLog.cjs");
|
|
52
|
+
const asyncHandler = require("./utils/asyncHandler.cjs");
|
|
53
|
+
|
|
54
|
+
module.exports = StarkAuth;
|
|
55
|
+
module.exports.crypto = crypto;
|
|
56
|
+
module.exports.AppError = AppError;
|
|
57
|
+
module.exports.AppLog = AppLog;
|
|
58
|
+
module.exports.asyncHandler = asyncHandler;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// custom error class
|
|
2
|
+
class AppError extends Error {
|
|
3
|
+
// constructor
|
|
4
|
+
constructor(message, statusCode) {
|
|
5
|
+
// calling parent constructor
|
|
6
|
+
super(message);
|
|
7
|
+
|
|
8
|
+
this.statusCode = statusCode;
|
|
9
|
+
this.isOperational = true;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
module.exports = AppError;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
const jwt = require("jsonwebtoken");
|
|
2
|
+
const AppError = require("./AppError.cjs");
|
|
3
|
+
|
|
4
|
+
const EXPIRY = {
|
|
5
|
+
"2m": 1000 * 60 * 2,
|
|
6
|
+
"10m": 1000 * 60 * 10,
|
|
7
|
+
"1h": 1000 * 60 * 60,
|
|
8
|
+
"6h": 1000 * 60 * 60 * 6,
|
|
9
|
+
"12h": 1000 * 60 * 60 * 12,
|
|
10
|
+
"1d": 1000 * 60 * 60 * 24,
|
|
11
|
+
"3d": 1000 * 60 * 60 * 24 * 3,
|
|
12
|
+
"7d": 1000 * 60 * 60 * 24 * 7,
|
|
13
|
+
"14d": 1000 * 60 * 60 * 24 * 14,
|
|
14
|
+
"30d": 1000 * 60 * 60 * 24 * 30,
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const getExpiry = (key) => {
|
|
18
|
+
if (!EXPIRY[key]) throw new Error("Invalid expiry key");
|
|
19
|
+
return EXPIRY[key];
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const signJWT = (payLoad, JWT_SECRET, JWT_EXPIRY) => {
|
|
23
|
+
try {
|
|
24
|
+
return jwt.sign(payLoad, JWT_SECRET, { expiresIn: JWT_EXPIRY });
|
|
25
|
+
} catch (error) {
|
|
26
|
+
throw new AppError(error.message);
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const verifyJWT = (token, JWT_SECRET) => {
|
|
31
|
+
try {
|
|
32
|
+
return jwt.verify(token, JWT_SECRET);
|
|
33
|
+
} catch (error) {
|
|
34
|
+
throw new AppError("Invalid or expired token", 401);
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
module.exports = { EXPIRY, signJWT, verifyJWT };
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
const sodium = require("libsodium-wrappers-sumo");
|
|
2
|
+
const AppError = require("./AppError.cjs");
|
|
3
|
+
|
|
4
|
+
// Hash password
|
|
5
|
+
const hash = async (password) => {
|
|
6
|
+
await sodium.ready;
|
|
7
|
+
|
|
8
|
+
const salt = sodium.randombytes_buf(sodium.crypto_pwhash_SALTBYTES);
|
|
9
|
+
|
|
10
|
+
const hashStr = sodium.crypto_pwhash(
|
|
11
|
+
32,
|
|
12
|
+
password,
|
|
13
|
+
salt,
|
|
14
|
+
sodium.crypto_pwhash_OPSLIMIT_INTERACTIVE,
|
|
15
|
+
sodium.crypto_pwhash_MEMLIMIT_INTERACTIVE,
|
|
16
|
+
sodium.crypto_pwhash_ALG_DEFAULT
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
// store salt + hashStr together
|
|
20
|
+
return sodium.to_base64(salt) + ":" + sodium.to_base64(hashStr);
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
// verifyPassword
|
|
24
|
+
const verifyHash = async (password, storedHash) => {
|
|
25
|
+
await sodium.ready;
|
|
26
|
+
|
|
27
|
+
const [saltB64, hashB64] = storedHash.split(":");
|
|
28
|
+
|
|
29
|
+
const salt = sodium.from_base64(saltB64);
|
|
30
|
+
const originalHash = sodium.from_base64(hashB64);
|
|
31
|
+
|
|
32
|
+
const testHash = sodium.crypto_pwhash(
|
|
33
|
+
32,
|
|
34
|
+
password,
|
|
35
|
+
salt,
|
|
36
|
+
sodium.crypto_pwhash_OPSLIMIT_INTERACTIVE,
|
|
37
|
+
sodium.crypto_pwhash_MEMLIMIT_INTERACTIVE,
|
|
38
|
+
sodium.crypto_pwhash_ALG_DEFAULT
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
return sodium.memcmp(originalHash, testHash);
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
// generate master key
|
|
45
|
+
const generateMasterKey = async () => {
|
|
46
|
+
await sodium.ready;
|
|
47
|
+
|
|
48
|
+
const key = sodium.crypto_secretbox_keygen();
|
|
49
|
+
|
|
50
|
+
const base64Key = sodium.to_base64(
|
|
51
|
+
key,
|
|
52
|
+
sodium.base64_variants.ORIGINAL
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
console.log(base64Key);
|
|
57
|
+
return base64Key
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
// seal
|
|
61
|
+
const seal = async (string, masterKey) => {
|
|
62
|
+
try {
|
|
63
|
+
await sodium.ready;
|
|
64
|
+
|
|
65
|
+
const keyPair = sodium.crypto_box_keypair();
|
|
66
|
+
const strBytes = sodium.from_string(string);
|
|
67
|
+
|
|
68
|
+
const encrypted = sodium.crypto_box_seal(strBytes, keyPair.publicKey);
|
|
69
|
+
const str = sodium.to_base64(encrypted);
|
|
70
|
+
|
|
71
|
+
masterKey = sodium.from_base64(masterKey, sodium.base64_variants.ORIGINAL);
|
|
72
|
+
let nonce = sodium.randombytes_buf(24);
|
|
73
|
+
|
|
74
|
+
const pk = sodium.crypto_secretbox_easy(
|
|
75
|
+
keyPair.privateKey,
|
|
76
|
+
nonce,
|
|
77
|
+
masterKey
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
nonce = sodium.to_base64(nonce);
|
|
81
|
+
|
|
82
|
+
const securedPrivateKey = sodium.to_base64(pk);
|
|
83
|
+
const publicKey = sodium.to_base64(keyPair.publicKey);
|
|
84
|
+
|
|
85
|
+
return {
|
|
86
|
+
str,
|
|
87
|
+
nonce,
|
|
88
|
+
publicKey,
|
|
89
|
+
securedPrivateKey,
|
|
90
|
+
};
|
|
91
|
+
} catch (error) {
|
|
92
|
+
console.log(error.message);
|
|
93
|
+
throw new AppError("Error while encrypting");
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
// unSeal
|
|
98
|
+
const unSeal = async (str, nonce, publicKey, securedPrivateKey, masterKey) => {
|
|
99
|
+
try {
|
|
100
|
+
if (!str || !nonce || !publicKey || !securedPrivateKey)
|
|
101
|
+
throw new AppError(
|
|
102
|
+
"Str, nonce, public key and secured private key are required"
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
await sodium.ready;
|
|
106
|
+
|
|
107
|
+
masterKey = sodium.from_base64(masterKey, sodium.base64_variants.ORIGINAL);
|
|
108
|
+
nonce = sodium.from_base64(nonce);
|
|
109
|
+
securedPrivateKey = sodium.from_base64(securedPrivateKey);
|
|
110
|
+
publicKey = sodium.from_base64(publicKey);
|
|
111
|
+
str = sodium.from_base64(str);
|
|
112
|
+
|
|
113
|
+
const privateKey = sodium.crypto_secretbox_open_easy(
|
|
114
|
+
securedPrivateKey,
|
|
115
|
+
nonce,
|
|
116
|
+
masterKey
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
const decrypted = sodium.crypto_box_seal_open(str, publicKey, privateKey);
|
|
120
|
+
return sodium.to_string(decrypted);
|
|
121
|
+
} catch (error) {
|
|
122
|
+
console.log(error.message);
|
|
123
|
+
throw new AppError("Error while decrypting");
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
module.exports = {
|
|
128
|
+
hash,
|
|
129
|
+
verifyHash,
|
|
130
|
+
generateMasterKey,
|
|
131
|
+
seal,
|
|
132
|
+
unSeal,
|
|
133
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// success response to client
|
|
2
|
+
const successResponse = (res, data, status) => {
|
|
3
|
+
const response = { success: true };
|
|
4
|
+
|
|
5
|
+
if (data) {
|
|
6
|
+
response.data = data;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
res.status(status).json(response);
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
// export
|
|
13
|
+
module.exports = successResponse;
|
package/dist/js/db.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// module imports
|
|
2
|
+
import mongoose from "mongoose";
|
|
3
|
+
|
|
4
|
+
import AppLog from "./utils/AppLog.js";
|
|
5
|
+
|
|
6
|
+
// connecting to db
|
|
7
|
+
const connectDB = async (uri, db) => {
|
|
8
|
+
const mongodbUri = uri || "mongodb://localhost:27017";
|
|
9
|
+
try {
|
|
10
|
+
AppLog("check", "db.js", "Connected successfully!");
|
|
11
|
+
return await mongoose.connect(`${mongodbUri}/${db || "test"}`);
|
|
12
|
+
} catch (error) {
|
|
13
|
+
AppLog("X", "db.js", "Error while connecting!");
|
|
14
|
+
AppLog("X", "db.js", error.message);
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
// export
|
|
19
|
+
export default connectDB;
|
package/dist/js/index.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// connectDB - connects MongoDB database
|
|
2
|
+
import connectDB from "./db.js";
|
|
3
|
+
|
|
4
|
+
// seal/unSeal - encrypt and decrypt data
|
|
5
|
+
import { seal, unSeal } from "./utils/libsodium.js";
|
|
6
|
+
import { signJWT, verifyJWT } from "./utils/jwt.js";
|
|
7
|
+
|
|
8
|
+
class StarkAuth {
|
|
9
|
+
#_masterKey;
|
|
10
|
+
#_JWT_SECRET;
|
|
11
|
+
#_JWT_EXPIRY;
|
|
12
|
+
|
|
13
|
+
// constructor - store keys etc.
|
|
14
|
+
constructor(config) {
|
|
15
|
+
this.#_masterKey = config.MASTER_KEY;
|
|
16
|
+
this.#_JWT_SECRET = config.JWT_SECRET;
|
|
17
|
+
this.#_JWT_EXPIRY = config.JWT_EXPIRY;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// create - initializes DB and auth instance
|
|
21
|
+
static async create(config) {
|
|
22
|
+
await connectDB(config.MONGO_URI, config.DB_NAME);
|
|
23
|
+
return new StarkAuth(config);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// encrypt - encrypts string using master key
|
|
27
|
+
async encrypt(str) {
|
|
28
|
+
return seal(str, this.#_masterKey);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// decrypt - decrypts string using master key
|
|
32
|
+
async decrypt(str, nonce, publicKey, securedPrivateKey) {
|
|
33
|
+
return unSeal(str, nonce, publicKey, securedPrivateKey, this.#_masterKey);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// signJWT
|
|
37
|
+
signJWT(payLoad) {
|
|
38
|
+
return signJWT(payLoad, this.#_JWT_SECRET, this.#_JWT_EXPIRY);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// verifyJWT
|
|
42
|
+
verifyJWT(token) {
|
|
43
|
+
return verifyJWT(token, this.#_JWT_SECRET);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export default StarkAuth;
|
|
48
|
+
|
|
49
|
+
// crypto - exports hashing utilities
|
|
50
|
+
import { generateMasterKey, hash, verifyHash } from "./utils/libsodium.js";
|
|
51
|
+
|
|
52
|
+
export const crypto = {
|
|
53
|
+
generateMasterKey,
|
|
54
|
+
hash,
|
|
55
|
+
verifyHash,
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
// AppError - custom application error class
|
|
59
|
+
export { default as AppError } from "./utils/AppError.js";
|
|
60
|
+
|
|
61
|
+
// AppLog - structured logging utility
|
|
62
|
+
export { default as AppLog } from "./utils/AppLog.js";
|
|
63
|
+
|
|
64
|
+
// async handler - AppError in async functions
|
|
65
|
+
export { default as asyncHandler } from "./utils/asyncHandler.js";
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// custom error class
|
|
2
|
+
class AppError extends Error {
|
|
3
|
+
// constructor
|
|
4
|
+
constructor(message, statusCode) {
|
|
5
|
+
// calling parent constructor
|
|
6
|
+
super(message);
|
|
7
|
+
|
|
8
|
+
this.statusCode = statusCode;
|
|
9
|
+
this.isOperational = true;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export default AppError;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// emojis object
|
|
2
|
+
const emojis = {
|
|
3
|
+
check: "✅",
|
|
4
|
+
X: "❌",
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
// console logs
|
|
8
|
+
/**
|
|
9
|
+
*
|
|
10
|
+
* @param {check | X} emoji
|
|
11
|
+
* @param {string} file
|
|
12
|
+
* @param {string} message
|
|
13
|
+
*/
|
|
14
|
+
const AppLog = (emoji, file, message) => {
|
|
15
|
+
console.log(`${emojis[emoji]} [${file}] ${message}`);
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
// export
|
|
19
|
+
export default AppLog;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// exports for this directory
|
|
2
|
+
export { default as AppLog } from "./AppLog.js";
|
|
3
|
+
export { default as successResponse } from "./successResponse.js";
|
|
4
|
+
export { default as asyncHandler } from "./asyncHandler.js";
|
|
5
|
+
export { default as AppError } from "./AppError.js";
|
|
6
|
+
export { signJWT, verifyJWT } from "./jwt.js";
|
|
7
|
+
export { hashPassword, verifyPassword } from "./libsodium.js";
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import jwt from "jsonwebtoken";
|
|
2
|
+
import AppError from "./AppError.js";
|
|
3
|
+
|
|
4
|
+
export const EXPIRY = {
|
|
5
|
+
"2m": 1000 * 60 * 2,
|
|
6
|
+
"10m": 1000 * 60 * 10,
|
|
7
|
+
"1h": 1000 * 60 * 60,
|
|
8
|
+
"6h": 1000 * 60 * 60 * 6,
|
|
9
|
+
"12h": 1000 * 60 * 60 * 12,
|
|
10
|
+
"1d": 1000 * 60 * 60 * 24,
|
|
11
|
+
"3d": 1000 * 60 * 60 * 24 * 3,
|
|
12
|
+
"7d": 1000 * 60 * 60 * 24 * 7,
|
|
13
|
+
"14d": 1000 * 60 * 60 * 24 * 14,
|
|
14
|
+
"30d": 1000 * 60 * 60 * 24 * 30,
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const getExpiry = (key) => {
|
|
18
|
+
if (!EXPIRY[key]) throw new Error("Invalid expiry key");
|
|
19
|
+
return EXPIRY[key];
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const signJWT = (payLoad, JWT_SECRET, JWT_EXPIRY) => {
|
|
23
|
+
try {
|
|
24
|
+
return jwt.sign(payLoad, JWT_SECRET, { expiresIn: JWT_EXPIRY });
|
|
25
|
+
} catch (error) {
|
|
26
|
+
throw new AppError(error.message);
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const verifyJWT = (token, JWT_SECRET) => {
|
|
31
|
+
try {
|
|
32
|
+
return jwt.verify(token, JWT_SECRET);
|
|
33
|
+
} catch (error) {
|
|
34
|
+
throw new AppError("Invalid or expired token", 401);
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export { signJWT, verifyJWT };
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import sodium from "libsodium-wrappers-sumo";
|
|
2
|
+
import AppError from "./AppError.js";
|
|
3
|
+
// import envs from "../config/envs.js";
|
|
4
|
+
|
|
5
|
+
// Hash password
|
|
6
|
+
export const hash = async (password) => {
|
|
7
|
+
await sodium.ready;
|
|
8
|
+
|
|
9
|
+
const salt = sodium.randombytes_buf(sodium.crypto_pwhash_SALTBYTES);
|
|
10
|
+
|
|
11
|
+
const hashStr = sodium.crypto_pwhash(
|
|
12
|
+
32,
|
|
13
|
+
password,
|
|
14
|
+
salt,
|
|
15
|
+
sodium.crypto_pwhash_OPSLIMIT_INTERACTIVE,
|
|
16
|
+
sodium.crypto_pwhash_MEMLIMIT_INTERACTIVE,
|
|
17
|
+
sodium.crypto_pwhash_ALG_DEFAULT,
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
// store salt + hashStr together
|
|
21
|
+
return sodium.to_base64(salt) + ":" + sodium.to_base64(hashStr);
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
// verifyPassword
|
|
25
|
+
export const verifyHash = async (password, storedHash) => {
|
|
26
|
+
await sodium.ready;
|
|
27
|
+
|
|
28
|
+
const [saltB64, hashB64] = storedHash.split(":");
|
|
29
|
+
|
|
30
|
+
const salt = sodium.from_base64(saltB64);
|
|
31
|
+
const originalHash = sodium.from_base64(hashB64);
|
|
32
|
+
|
|
33
|
+
const testHash = sodium.crypto_pwhash(
|
|
34
|
+
32,
|
|
35
|
+
password,
|
|
36
|
+
salt,
|
|
37
|
+
sodium.crypto_pwhash_OPSLIMIT_INTERACTIVE,
|
|
38
|
+
sodium.crypto_pwhash_MEMLIMIT_INTERACTIVE,
|
|
39
|
+
sodium.crypto_pwhash_ALG_DEFAULT,
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
return sodium.memcmp(originalHash, testHash);
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
// generate master key
|
|
46
|
+
export const generateMasterKey = async () => {
|
|
47
|
+
await sodium.ready;
|
|
48
|
+
|
|
49
|
+
// Generate a secure 32-byte key
|
|
50
|
+
const key = sodium.crypto_secretbox_keygen();
|
|
51
|
+
|
|
52
|
+
// Convert to Base64 for .env storage
|
|
53
|
+
const base64Key = sodium.to_base64(key, sodium.base64_variants.ORIGINAL);
|
|
54
|
+
console.log(base64Key);
|
|
55
|
+
return base64Key;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
// seal
|
|
59
|
+
export const seal = async (string, masterKey) => {
|
|
60
|
+
try {
|
|
61
|
+
await sodium.ready;
|
|
62
|
+
|
|
63
|
+
const keyPair = sodium.crypto_box_keypair();
|
|
64
|
+
const strBytes = sodium.from_string(string);
|
|
65
|
+
|
|
66
|
+
const encrypted = sodium.crypto_box_seal(strBytes, keyPair.publicKey);
|
|
67
|
+
const str = sodium.to_base64(encrypted);
|
|
68
|
+
|
|
69
|
+
masterKey = sodium.from_base64(masterKey, sodium.base64_variants.ORIGINAL);
|
|
70
|
+
let nonce = sodium.randombytes_buf(24);
|
|
71
|
+
|
|
72
|
+
const pk = sodium.crypto_secretbox_easy(
|
|
73
|
+
keyPair.privateKey,
|
|
74
|
+
nonce,
|
|
75
|
+
masterKey,
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
nonce = sodium.to_base64(nonce);
|
|
79
|
+
|
|
80
|
+
const securedPrivateKey = sodium.to_base64(pk);
|
|
81
|
+
const publicKey = sodium.to_base64(keyPair.publicKey);
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
str,
|
|
85
|
+
nonce,
|
|
86
|
+
publicKey,
|
|
87
|
+
securedPrivateKey,
|
|
88
|
+
};
|
|
89
|
+
} catch (error) {
|
|
90
|
+
console.log(error.message);
|
|
91
|
+
throw new AppError("Error while encrypting");
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
// unSeal
|
|
96
|
+
export const unSeal = async (str, nonce, publicKey, securedPrivateKey, masterKey) => {
|
|
97
|
+
try {
|
|
98
|
+
if (!str || !nonce || !publicKey || !securedPrivateKey)
|
|
99
|
+
throw new AppError(
|
|
100
|
+
"Str, nonce, public key and secured private key are required",
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
await sodium.ready;
|
|
104
|
+
|
|
105
|
+
masterKey = sodium.from_base64(masterKey, sodium.base64_variants.ORIGINAL);
|
|
106
|
+
nonce = sodium.from_base64(nonce);
|
|
107
|
+
securedPrivateKey = sodium.from_base64(securedPrivateKey);
|
|
108
|
+
publicKey = sodium.from_base64(publicKey);
|
|
109
|
+
str = sodium.from_base64(str);
|
|
110
|
+
|
|
111
|
+
const privateKey = sodium.crypto_secretbox_open_easy(
|
|
112
|
+
securedPrivateKey,
|
|
113
|
+
nonce,
|
|
114
|
+
masterKey,
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
const decrypted = sodium.crypto_box_seal_open(str, publicKey, privateKey);
|
|
118
|
+
return sodium.to_string(decrypted);
|
|
119
|
+
} catch (error) {
|
|
120
|
+
console.log(error.message);
|
|
121
|
+
throw new AppError("Error while decrypting");
|
|
122
|
+
}
|
|
123
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// success response to client
|
|
2
|
+
const successResponse = (res, data, status) => {
|
|
3
|
+
const response = { success: true };
|
|
4
|
+
|
|
5
|
+
if (data) {
|
|
6
|
+
response.data = data;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
res.status(status).json(response);
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
// export
|
|
13
|
+
export default successResponse;
|
package/package.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
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.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"auth-mongo",
|
|
7
|
+
"backend-core"
|
|
8
|
+
],
|
|
9
|
+
"license": "ISC",
|
|
10
|
+
"author": "Musa",
|
|
11
|
+
"type": "module",
|
|
12
|
+
"main": "index.js",
|
|
13
|
+
"scripts": {
|
|
14
|
+
"test": "node test.js"
|
|
15
|
+
},
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"cookie-parser": "^1.4.7",
|
|
18
|
+
"cors": "^2.8.6",
|
|
19
|
+
"dotenv": "^17.4.2",
|
|
20
|
+
"express": "^5.2.1",
|
|
21
|
+
"helmet": "^8.1.0",
|
|
22
|
+
"jsonwebtoken": "^9.0.3",
|
|
23
|
+
"libsodium-wrappers-sumo": "^0.8.4",
|
|
24
|
+
"mongoose": "^9.6.1",
|
|
25
|
+
"resend": "^6.12.2",
|
|
26
|
+
"zod": "^4.4.1"
|
|
27
|
+
}
|
|
28
|
+
}
|