@naman_deep_singh/security 1.0.3 → 1.1.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/README.md +329 -24
- package/dist/cjs/core/crypto/decrypt.d.ts +1 -0
- package/dist/cjs/core/crypto/decrypt.js +21 -0
- package/dist/cjs/core/crypto/encrypt.d.ts +1 -0
- package/dist/cjs/core/crypto/encrypt.js +16 -0
- package/dist/cjs/core/crypto/hmac.d.ts +8 -0
- package/dist/cjs/core/crypto/hmac.js +24 -0
- package/dist/cjs/core/crypto/index.d.ts +4 -0
- package/dist/cjs/core/crypto/index.js +20 -0
- package/dist/cjs/core/crypto/random.d.ts +8 -0
- package/dist/cjs/core/crypto/random.js +21 -0
- package/dist/cjs/core/jwt/decode.d.ts +12 -0
- package/dist/cjs/core/jwt/decode.js +25 -0
- package/dist/cjs/core/jwt/extractToken.d.ts +11 -0
- package/dist/cjs/core/jwt/extractToken.js +49 -0
- package/dist/cjs/core/jwt/generateTokens.d.ts +7 -0
- package/dist/cjs/core/jwt/generateTokens.js +23 -0
- package/dist/cjs/core/jwt/index.d.ts +7 -0
- package/dist/cjs/core/jwt/index.js +23 -0
- package/dist/cjs/core/jwt/parseDuration.d.ts +1 -0
- package/dist/cjs/core/jwt/parseDuration.js +29 -0
- package/dist/cjs/core/jwt/signToken.d.ts +2 -0
- package/dist/cjs/core/jwt/signToken.js +26 -0
- package/dist/cjs/core/jwt/validateToken.d.ts +13 -0
- package/dist/cjs/core/jwt/validateToken.js +37 -0
- package/dist/cjs/core/jwt/verify.d.ts +13 -0
- package/dist/cjs/core/jwt/verify.js +24 -0
- package/dist/cjs/core/password/hash.d.ts +10 -0
- package/dist/cjs/core/password/hash.js +45 -0
- package/dist/cjs/core/password/index.d.ts +3 -0
- package/dist/cjs/core/password/index.js +19 -0
- package/dist/cjs/core/password/strength.d.ts +2 -0
- package/dist/cjs/core/password/strength.js +21 -0
- package/dist/cjs/core/password/types.d.ts +7 -0
- package/dist/cjs/core/password/types.js +2 -0
- package/dist/cjs/core/password/utils.d.ts +4 -0
- package/dist/cjs/core/password/utils.js +38 -0
- package/dist/cjs/core/password/verify.d.ts +10 -0
- package/dist/cjs/core/password/verify.js +46 -0
- package/dist/cjs/index.d.ts +43 -0
- package/dist/cjs/index.js +56 -0
- package/dist/esm/core/crypto/decrypt.d.ts +1 -0
- package/dist/esm/core/crypto/decrypt.js +14 -0
- package/dist/esm/core/crypto/encrypt.d.ts +1 -0
- package/dist/esm/core/crypto/encrypt.js +9 -0
- package/dist/esm/core/crypto/hmac.d.ts +8 -0
- package/dist/esm/core/crypto/hmac.js +16 -0
- package/dist/esm/core/crypto/index.d.ts +4 -0
- package/dist/esm/core/crypto/index.js +4 -0
- package/dist/esm/core/crypto/random.d.ts +8 -0
- package/dist/esm/core/crypto/random.js +13 -0
- package/dist/esm/core/jwt/decode.d.ts +12 -0
- package/dist/esm/core/jwt/decode.js +21 -0
- package/dist/esm/core/jwt/extractToken.d.ts +11 -0
- package/dist/esm/core/jwt/extractToken.js +46 -0
- package/dist/esm/core/jwt/generateTokens.d.ts +7 -0
- package/dist/esm/core/jwt/generateTokens.js +18 -0
- package/dist/esm/core/jwt/index.d.ts +7 -0
- package/dist/esm/core/jwt/index.js +7 -0
- package/dist/esm/core/jwt/parseDuration.d.ts +1 -0
- package/dist/esm/core/jwt/parseDuration.js +26 -0
- package/dist/esm/core/jwt/signToken.d.ts +2 -0
- package/dist/esm/core/jwt/signToken.js +22 -0
- package/dist/esm/core/jwt/validateToken.d.ts +13 -0
- package/dist/esm/core/jwt/validateToken.js +33 -0
- package/dist/esm/core/jwt/verify.d.ts +13 -0
- package/dist/esm/core/jwt/verify.js +19 -0
- package/dist/esm/core/password/hash.d.ts +10 -0
- package/dist/esm/core/password/hash.js +35 -0
- package/dist/esm/core/password/index.d.ts +3 -0
- package/dist/esm/core/password/index.js +3 -0
- package/dist/esm/core/password/strength.d.ts +2 -0
- package/dist/esm/core/password/strength.js +17 -0
- package/dist/esm/core/password/types.d.ts +7 -0
- package/dist/esm/core/password/types.js +1 -0
- package/dist/esm/core/password/utils.d.ts +4 -0
- package/dist/esm/core/password/utils.js +29 -0
- package/dist/esm/core/password/verify.d.ts +10 -0
- package/dist/esm/core/password/verify.js +36 -0
- package/dist/esm/index.d.ts +43 -0
- package/dist/esm/index.js +13 -0
- package/dist/types/core/crypto/decrypt.d.ts +1 -0
- package/dist/types/core/crypto/encrypt.d.ts +1 -0
- package/dist/types/core/crypto/hmac.d.ts +8 -0
- package/dist/types/core/crypto/index.d.ts +4 -0
- package/dist/types/core/crypto/random.d.ts +8 -0
- package/dist/types/core/jwt/decode.d.ts +12 -0
- package/dist/types/core/jwt/extractToken.d.ts +11 -0
- package/dist/types/core/jwt/generateTokens.d.ts +7 -0
- package/dist/types/core/jwt/index.d.ts +7 -0
- package/dist/types/core/jwt/parseDuration.d.ts +1 -0
- package/dist/types/core/jwt/signToken.d.ts +2 -0
- package/dist/types/core/jwt/validateToken.d.ts +13 -0
- package/dist/types/core/jwt/verify.d.ts +13 -0
- package/dist/types/core/password/hash.d.ts +10 -0
- package/dist/types/core/password/index.d.ts +3 -0
- package/dist/types/core/password/strength.d.ts +2 -0
- package/dist/types/core/password/types.d.ts +7 -0
- package/dist/types/core/password/utils.d.ts +4 -0
- package/dist/types/core/password/verify.d.ts +10 -0
- package/dist/types/index.d.ts +43 -0
- package/package.json +34 -10
- package/dist/index.d.ts +0 -5
- package/dist/index.js +0 -27
- package/src/index.ts +0 -21
- package/tsconfig.json +0 -20
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Universal token extractor
|
|
3
|
+
*/
|
|
4
|
+
export function extractToken(sources) {
|
|
5
|
+
const { header, cookies, query, body, wsMessage } = sources;
|
|
6
|
+
// 1. Authorization: Bearer <token>
|
|
7
|
+
if (header) {
|
|
8
|
+
const parts = header.split(" ");
|
|
9
|
+
if (parts.length === 2 && parts[0] === "Bearer")
|
|
10
|
+
return parts[1];
|
|
11
|
+
}
|
|
12
|
+
// 2. Cookies: token / accessToken
|
|
13
|
+
if (cookies) {
|
|
14
|
+
if (cookies["token"])
|
|
15
|
+
return cookies["token"];
|
|
16
|
+
if (cookies["accessToken"])
|
|
17
|
+
return cookies["accessToken"];
|
|
18
|
+
}
|
|
19
|
+
// 3. Query params: ?token=xxx
|
|
20
|
+
if (query?.token)
|
|
21
|
+
return query.token;
|
|
22
|
+
// 4. Body: { token: "" }
|
|
23
|
+
if (body?.token)
|
|
24
|
+
return body.token;
|
|
25
|
+
// 5. WebSocket message extraction (NEW)
|
|
26
|
+
if (wsMessage) {
|
|
27
|
+
try {
|
|
28
|
+
let msg = wsMessage;
|
|
29
|
+
// If it's a JSON string → parse safely
|
|
30
|
+
if (typeof wsMessage === "string") {
|
|
31
|
+
msg = JSON.parse(wsMessage);
|
|
32
|
+
}
|
|
33
|
+
// Direct token
|
|
34
|
+
if (typeof msg.token === "string")
|
|
35
|
+
return msg.token;
|
|
36
|
+
// Nested token: { auth: { token: "" } }
|
|
37
|
+
if (msg.auth && typeof msg.auth.token === "string") {
|
|
38
|
+
return msg.auth.token;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
// Ignore parse errors gracefully
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Secret } from "jsonwebtoken";
|
|
2
|
+
export interface TokenPair {
|
|
3
|
+
accessToken: string;
|
|
4
|
+
refreshToken: string;
|
|
5
|
+
}
|
|
6
|
+
export declare const generateTokens: (payload: object, accessSecret: Secret, refreshSecret: Secret, accessExpiry?: string | number, refreshExpiry?: string | number) => TokenPair;
|
|
7
|
+
export declare function rotateRefreshToken(oldToken: string, secret: Secret): string;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { signToken } from "./signToken";
|
|
2
|
+
import { verifyToken } from "./verify";
|
|
3
|
+
export const generateTokens = (payload, accessSecret, refreshSecret, accessExpiry = "15m", refreshExpiry = "7d") => {
|
|
4
|
+
return {
|
|
5
|
+
accessToken: signToken(payload, accessSecret, accessExpiry, { algorithm: "HS256" }),
|
|
6
|
+
refreshToken: signToken(payload, refreshSecret, refreshExpiry, { algorithm: "HS256" }),
|
|
7
|
+
};
|
|
8
|
+
};
|
|
9
|
+
export function rotateRefreshToken(oldToken, secret) {
|
|
10
|
+
const decoded = verifyToken(oldToken, secret);
|
|
11
|
+
if (typeof decoded === "string") {
|
|
12
|
+
throw new Error("Invalid token payload — expected JWT payload object");
|
|
13
|
+
}
|
|
14
|
+
const payload = { ...decoded };
|
|
15
|
+
delete payload.iat;
|
|
16
|
+
delete payload.exp;
|
|
17
|
+
return signToken(payload, secret, "7d");
|
|
18
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function parseDuration(input: string | number): number;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
const TIME_UNITS = {
|
|
2
|
+
s: 1,
|
|
3
|
+
m: 60,
|
|
4
|
+
h: 3600,
|
|
5
|
+
d: 86400,
|
|
6
|
+
w: 604800
|
|
7
|
+
};
|
|
8
|
+
export function parseDuration(input) {
|
|
9
|
+
if (typeof input === "number")
|
|
10
|
+
return input;
|
|
11
|
+
const regex = /(\d+)\s*(s|m|h|d|w)/gi;
|
|
12
|
+
let totalSeconds = 0;
|
|
13
|
+
let match;
|
|
14
|
+
while ((match = regex.exec(input)) !== null) {
|
|
15
|
+
const value = parseInt(match[1], 10);
|
|
16
|
+
const unit = match[2].toLowerCase();
|
|
17
|
+
if (!TIME_UNITS[unit]) {
|
|
18
|
+
throw new Error(`Invalid time unit: ${unit}`);
|
|
19
|
+
}
|
|
20
|
+
totalSeconds += value * TIME_UNITS[unit];
|
|
21
|
+
}
|
|
22
|
+
if (totalSeconds === 0) {
|
|
23
|
+
throw new Error(`Invalid expiry format: "${input}"`);
|
|
24
|
+
}
|
|
25
|
+
return totalSeconds;
|
|
26
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { sign } from "jsonwebtoken";
|
|
2
|
+
import { parseDuration } from "./parseDuration";
|
|
3
|
+
function getExpiryTimestamp(seconds) {
|
|
4
|
+
return Math.floor(Date.now() / 1000) + seconds;
|
|
5
|
+
}
|
|
6
|
+
export const signToken = (payload, secret, expiresIn = "1h", options = {}) => {
|
|
7
|
+
const seconds = parseDuration(expiresIn);
|
|
8
|
+
if (!seconds || seconds < 10) {
|
|
9
|
+
throw new Error("Token expiry too small");
|
|
10
|
+
}
|
|
11
|
+
const tokenPayload = {
|
|
12
|
+
...payload
|
|
13
|
+
};
|
|
14
|
+
if (!("exp" in payload))
|
|
15
|
+
tokenPayload.exp = getExpiryTimestamp(seconds);
|
|
16
|
+
if (!("iat" in payload))
|
|
17
|
+
tokenPayload.iat = Math.floor(Date.now() / 1000);
|
|
18
|
+
return sign(tokenPayload, secret, {
|
|
19
|
+
algorithm: "HS256",
|
|
20
|
+
...options
|
|
21
|
+
});
|
|
22
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { JwtPayload } from "node_modules/@types/jsonwebtoken";
|
|
2
|
+
export interface TokenRequirements {
|
|
3
|
+
requiredFields?: string[];
|
|
4
|
+
forbiddenFields?: string[];
|
|
5
|
+
validateTypes?: Record<string, "string" | "number" | "boolean">;
|
|
6
|
+
}
|
|
7
|
+
export declare function validateTokenPayload(payload: Record<string, any>, rules?: TokenRequirements): {
|
|
8
|
+
valid: true;
|
|
9
|
+
} | {
|
|
10
|
+
valid: false;
|
|
11
|
+
error: string;
|
|
12
|
+
};
|
|
13
|
+
export declare function isTokenExpired(payload: JwtPayload): boolean;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export function validateTokenPayload(payload, rules = {
|
|
2
|
+
requiredFields: ["exp", "iat"]
|
|
3
|
+
}) {
|
|
4
|
+
const { requiredFields = [], forbiddenFields = [], validateTypes = {} } = rules;
|
|
5
|
+
// 1. Required fields
|
|
6
|
+
for (const field of requiredFields) {
|
|
7
|
+
if (!(field in payload)) {
|
|
8
|
+
return { valid: false, error: `Missing required field: ${field}` };
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
// 2. Forbidden fields
|
|
12
|
+
for (const field of forbiddenFields) {
|
|
13
|
+
if (field in payload) {
|
|
14
|
+
return { valid: false, error: `Forbidden field in token: ${field}` };
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
// 3. Type validation
|
|
18
|
+
for (const key in validateTypes) {
|
|
19
|
+
const expectedType = validateTypes[key];
|
|
20
|
+
if (key in payload && typeof payload[key] !== expectedType) {
|
|
21
|
+
return {
|
|
22
|
+
valid: false,
|
|
23
|
+
error: `Invalid type for ${key}. Expected ${expectedType}.`
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return { valid: true };
|
|
28
|
+
}
|
|
29
|
+
export function isTokenExpired(payload) {
|
|
30
|
+
if (!payload.exp)
|
|
31
|
+
return true;
|
|
32
|
+
return Date.now() >= payload.exp * 1000;
|
|
33
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Secret, JwtPayload } from "jsonwebtoken";
|
|
2
|
+
/**
|
|
3
|
+
* Verify token (throws if invalid or expired)
|
|
4
|
+
*/
|
|
5
|
+
export declare const verifyToken: (token: string, secret: Secret) => string | JwtPayload;
|
|
6
|
+
/**
|
|
7
|
+
* Safe verify — never throws, returns { valid, payload?, error? }
|
|
8
|
+
*/
|
|
9
|
+
export declare const safeVerifyToken: (token: string, secret: Secret) => {
|
|
10
|
+
valid: boolean;
|
|
11
|
+
payload?: string | JwtPayload;
|
|
12
|
+
error?: unknown;
|
|
13
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { verify } from "jsonwebtoken";
|
|
2
|
+
/**
|
|
3
|
+
* Verify token (throws if invalid or expired)
|
|
4
|
+
*/
|
|
5
|
+
export const verifyToken = (token, secret) => {
|
|
6
|
+
return verify(token, secret);
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Safe verify — never throws, returns { valid, payload?, error? }
|
|
10
|
+
*/
|
|
11
|
+
export const safeVerifyToken = (token, secret) => {
|
|
12
|
+
try {
|
|
13
|
+
const decoded = verify(token, secret);
|
|
14
|
+
return { valid: true, payload: decoded };
|
|
15
|
+
}
|
|
16
|
+
catch (error) {
|
|
17
|
+
return { valid: false, error };
|
|
18
|
+
}
|
|
19
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hash a password asynchronously using bcrypt.
|
|
3
|
+
*/
|
|
4
|
+
export declare const hashPassword: (password: string, saltRounds?: number) => Promise<string>;
|
|
5
|
+
export declare function hashPasswordWithPepper(password: string, pepper: string): Promise<string>;
|
|
6
|
+
/**
|
|
7
|
+
* Hash a password synchronously using bcrypt.
|
|
8
|
+
*/
|
|
9
|
+
export declare const hashPasswordSync: (password: string, saltRounds?: number) => string;
|
|
10
|
+
export declare function hashPasswordWithPepperSync(password: string, pepper: string): string;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import bcrypt from "bcryptjs";
|
|
2
|
+
import { ensureValidPassword } from "./utils";
|
|
3
|
+
import { InternalServerError } from "@naman_deep_singh/errors-utils";
|
|
4
|
+
/**
|
|
5
|
+
* Hash a password asynchronously using bcrypt.
|
|
6
|
+
*/
|
|
7
|
+
export const hashPassword = async (password, saltRounds = 10) => {
|
|
8
|
+
try {
|
|
9
|
+
ensureValidPassword(password);
|
|
10
|
+
const salt = await bcrypt.genSalt(saltRounds);
|
|
11
|
+
return bcrypt.hash(password, salt);
|
|
12
|
+
}
|
|
13
|
+
catch (err) {
|
|
14
|
+
throw new InternalServerError('Password hashing failed');
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
export function hashPasswordWithPepper(password, pepper) {
|
|
18
|
+
return hashPassword(password + pepper);
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Hash a password synchronously using bcrypt.
|
|
22
|
+
*/
|
|
23
|
+
export const hashPasswordSync = (password, saltRounds = 10) => {
|
|
24
|
+
try {
|
|
25
|
+
ensureValidPassword(password);
|
|
26
|
+
const salt = bcrypt.genSaltSync(saltRounds);
|
|
27
|
+
return bcrypt.hashSync(password, salt);
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
throw new InternalServerError('Password hashing failed');
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
export function hashPasswordWithPepperSync(password, pepper) {
|
|
34
|
+
return hashPasswordSync(password + pepper);
|
|
35
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { BadRequestError, ValidationError } from "@naman_deep_singh/errors-utils";
|
|
2
|
+
export const isPasswordStrong = (password, options = {}) => {
|
|
3
|
+
if (!password)
|
|
4
|
+
throw new BadRequestError('Invalid password provided');
|
|
5
|
+
const { minLength = 8, requireUppercase = true, requireLowercase = true, requireNumbers = true, requireSymbols = false, } = options;
|
|
6
|
+
if (password.length < minLength)
|
|
7
|
+
throw new ValidationError(`Password must be at least ${minLength} characters`);
|
|
8
|
+
if (requireUppercase && !/[A-Z]/.test(password))
|
|
9
|
+
throw new ValidationError("Password must include uppercase letters");
|
|
10
|
+
if (requireLowercase && !/[a-z]/.test(password))
|
|
11
|
+
throw new ValidationError("Password must include lowercase letters");
|
|
12
|
+
if (requireNumbers && !/[0-9]/.test(password))
|
|
13
|
+
throw new ValidationError("Password must include numbers");
|
|
14
|
+
if (requireSymbols && !/[^A-Za-z0-9]/.test(password))
|
|
15
|
+
throw new ValidationError("Password must include symbols");
|
|
16
|
+
return true;
|
|
17
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare function ensureValidPassword(password: string): void;
|
|
2
|
+
export declare function safeCompare(a: string, b: string): boolean;
|
|
3
|
+
export declare function estimatePasswordEntropy(password: string): number;
|
|
4
|
+
export declare function normalizePassword(password: string): string;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import crypto from "crypto";
|
|
2
|
+
import { BadRequestError } from "@naman_deep_singh/errors-utils";
|
|
3
|
+
export function ensureValidPassword(password) {
|
|
4
|
+
if (!password || typeof password !== "string") {
|
|
5
|
+
throw new BadRequestError('Invalid password provided');
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
export function safeCompare(a, b) {
|
|
9
|
+
const bufA = Buffer.from(a);
|
|
10
|
+
const bufB = Buffer.from(b);
|
|
11
|
+
if (bufA.length !== bufB.length)
|
|
12
|
+
return false;
|
|
13
|
+
return crypto.timingSafeEqual(bufA, bufB);
|
|
14
|
+
}
|
|
15
|
+
export function estimatePasswordEntropy(password) {
|
|
16
|
+
let pool = 0;
|
|
17
|
+
if (/[a-z]/.test(password))
|
|
18
|
+
pool += 26;
|
|
19
|
+
if (/[A-Z]/.test(password))
|
|
20
|
+
pool += 26;
|
|
21
|
+
if (/[0-9]/.test(password))
|
|
22
|
+
pool += 10;
|
|
23
|
+
if (/[^A-Za-z0-9]/.test(password))
|
|
24
|
+
pool += 32;
|
|
25
|
+
return password.length * Math.log2(pool);
|
|
26
|
+
}
|
|
27
|
+
export function normalizePassword(password) {
|
|
28
|
+
return password.normalize("NFKC");
|
|
29
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compare a password with a stored hash asynchronously.
|
|
3
|
+
*/
|
|
4
|
+
export declare const verifyPassword: (password: string, hash: string) => Promise<boolean>;
|
|
5
|
+
export declare function verifyPasswordWithPepper(password: string, pepper: string, hash: string): Promise<boolean>;
|
|
6
|
+
/**
|
|
7
|
+
* Compare a password with a stored hash synchronously.
|
|
8
|
+
*/
|
|
9
|
+
export declare const verifyPasswordSync: (password: string, hash: string) => boolean;
|
|
10
|
+
export declare function verifyPasswordWithPepperSync(password: string, pepper: string, hash: string): Promise<boolean>;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import bcrypt from "bcryptjs";
|
|
2
|
+
import { UnauthorizedError } from "@naman_deep_singh/errors-utils";
|
|
3
|
+
/**
|
|
4
|
+
* Compare a password with a stored hash asynchronously.
|
|
5
|
+
*/
|
|
6
|
+
export const verifyPassword = async (password, hash) => {
|
|
7
|
+
try {
|
|
8
|
+
const result = await bcrypt.compare(password, hash);
|
|
9
|
+
if (!result)
|
|
10
|
+
throw new UnauthorizedError('Password verification failed');
|
|
11
|
+
return result;
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
throw new UnauthorizedError('Password verification failed');
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
export async function verifyPasswordWithPepper(password, pepper, hash) {
|
|
18
|
+
return verifyPassword(password + pepper, hash);
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Compare a password with a stored hash synchronously.
|
|
22
|
+
*/
|
|
23
|
+
export const verifyPasswordSync = (password, hash) => {
|
|
24
|
+
try {
|
|
25
|
+
const result = bcrypt.compareSync(password, hash);
|
|
26
|
+
if (!result)
|
|
27
|
+
throw new UnauthorizedError('Password verification failed');
|
|
28
|
+
return result;
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
throw new UnauthorizedError('Password verification failed');
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
export async function verifyPasswordWithPepperSync(password, pepper, hash) {
|
|
35
|
+
return verifyPasswordSync(password + pepper, hash);
|
|
36
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export * from "./core/password";
|
|
2
|
+
export * from "./core/jwt";
|
|
3
|
+
export * from "./core/crypto";
|
|
4
|
+
export { BadRequestError, UnauthorizedError, ValidationError, InternalServerError } from "@naman_deep_singh/errors-utils";
|
|
5
|
+
import * as JWTUtils from "./core/jwt";
|
|
6
|
+
declare const _default: {
|
|
7
|
+
decrypt: (data: string, secret: string) => string;
|
|
8
|
+
encrypt: (text: string, secret: string) => string;
|
|
9
|
+
hmacSign: (message: string, secret: string) => string;
|
|
10
|
+
hmacVerify: (message: string, secret: string, signature: string) => boolean;
|
|
11
|
+
randomToken: (length?: number) => string;
|
|
12
|
+
generateStrongPassword: (length?: number) => string;
|
|
13
|
+
decodeToken(token: string): null | string | import("node_modules/@types/jsonwebtoken").JwtPayload;
|
|
14
|
+
decodeTokenStrict(token: string): import("node_modules/@types/jsonwebtoken").JwtPayload;
|
|
15
|
+
extractToken(sources: JWTUtils.TokenSources): string | null;
|
|
16
|
+
rotateRefreshToken(oldToken: string, secret: import("node_modules/@types/jsonwebtoken").Secret): string;
|
|
17
|
+
generateTokens: (payload: object, accessSecret: import("node_modules/@types/jsonwebtoken").Secret, refreshSecret: import("node_modules/@types/jsonwebtoken").Secret, accessExpiry?: string | number, refreshExpiry?: string | number) => JWTUtils.TokenPair;
|
|
18
|
+
parseDuration(input: string | number): number;
|
|
19
|
+
signToken: (payload: Record<string, any>, secret: import("node_modules/@types/jsonwebtoken").Secret, expiresIn?: string | number, options?: import("node_modules/@types/jsonwebtoken").SignOptions) => string;
|
|
20
|
+
validateTokenPayload(payload: Record<string, any>, rules?: JWTUtils.TokenRequirements): {
|
|
21
|
+
valid: true;
|
|
22
|
+
} | {
|
|
23
|
+
valid: false;
|
|
24
|
+
error: string;
|
|
25
|
+
};
|
|
26
|
+
isTokenExpired(payload: import("node_modules/@types/jsonwebtoken").JwtPayload): boolean;
|
|
27
|
+
verifyToken: (token: string, secret: import("node_modules/@types/jsonwebtoken").Secret) => string | import("node_modules/@types/jsonwebtoken").JwtPayload;
|
|
28
|
+
safeVerifyToken: (token: string, secret: import("node_modules/@types/jsonwebtoken").Secret) => {
|
|
29
|
+
valid: boolean;
|
|
30
|
+
payload?: string | import("node_modules/@types/jsonwebtoken").JwtPayload;
|
|
31
|
+
error?: unknown;
|
|
32
|
+
};
|
|
33
|
+
hashPasswordWithPepper(password: string, pepper: string): Promise<string>;
|
|
34
|
+
hashPasswordWithPepperSync(password: string, pepper: string): string;
|
|
35
|
+
hashPassword: (password: string, saltRounds?: number) => Promise<string>;
|
|
36
|
+
hashPasswordSync: (password: string, saltRounds?: number) => string;
|
|
37
|
+
isPasswordStrong: (password: string, options?: import("./core/password/types").PasswordStrengthOptions) => boolean;
|
|
38
|
+
verifyPasswordWithPepper(password: string, pepper: string, hash: string): Promise<boolean>;
|
|
39
|
+
verifyPasswordWithPepperSync(password: string, pepper: string, hash: string): Promise<boolean>;
|
|
40
|
+
verifyPassword: (password: string, hash: string) => Promise<boolean>;
|
|
41
|
+
verifyPasswordSync: (password: string, hash: string) => boolean;
|
|
42
|
+
};
|
|
43
|
+
export default _default;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export * from "./core/password";
|
|
2
|
+
export * from "./core/jwt";
|
|
3
|
+
export * from "./core/crypto";
|
|
4
|
+
// Re-export common errors for convenience
|
|
5
|
+
export { BadRequestError, UnauthorizedError, ValidationError, InternalServerError } from "@naman_deep_singh/errors-utils";
|
|
6
|
+
import * as PasswordUtils from "./core/password";
|
|
7
|
+
import * as JWTUtils from "./core/jwt";
|
|
8
|
+
import * as CryptoUtils from "./core/crypto";
|
|
9
|
+
export default {
|
|
10
|
+
...PasswordUtils,
|
|
11
|
+
...JWTUtils,
|
|
12
|
+
...CryptoUtils,
|
|
13
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const decrypt: (data: string, secret: string) => string;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const encrypt: (text: string, secret: string) => string;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { JwtPayload } from "jsonwebtoken";
|
|
2
|
+
/**
|
|
3
|
+
* Flexible decode
|
|
4
|
+
* Returns: null | string | JwtPayload
|
|
5
|
+
* Mirrors jsonwebtoken.decode()
|
|
6
|
+
*/
|
|
7
|
+
export declare function decodeToken(token: string): null | string | JwtPayload;
|
|
8
|
+
/**
|
|
9
|
+
* Strict decode
|
|
10
|
+
* Always returns JwtPayload or throws error
|
|
11
|
+
*/
|
|
12
|
+
export declare function decodeTokenStrict(token: string): JwtPayload;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export interface TokenSources {
|
|
2
|
+
header?: string | undefined | null;
|
|
3
|
+
cookies?: Record<string, string> | undefined;
|
|
4
|
+
query?: Record<string, string | undefined> | undefined;
|
|
5
|
+
body?: Record<string, any> | undefined;
|
|
6
|
+
wsMessage?: string | Record<string, any> | undefined;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Universal token extractor
|
|
10
|
+
*/
|
|
11
|
+
export declare function extractToken(sources: TokenSources): string | null;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Secret } from "jsonwebtoken";
|
|
2
|
+
export interface TokenPair {
|
|
3
|
+
accessToken: string;
|
|
4
|
+
refreshToken: string;
|
|
5
|
+
}
|
|
6
|
+
export declare const generateTokens: (payload: object, accessSecret: Secret, refreshSecret: Secret, accessExpiry?: string | number, refreshExpiry?: string | number) => TokenPair;
|
|
7
|
+
export declare function rotateRefreshToken(oldToken: string, secret: Secret): string;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function parseDuration(input: string | number): number;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { JwtPayload } from "node_modules/@types/jsonwebtoken";
|
|
2
|
+
export interface TokenRequirements {
|
|
3
|
+
requiredFields?: string[];
|
|
4
|
+
forbiddenFields?: string[];
|
|
5
|
+
validateTypes?: Record<string, "string" | "number" | "boolean">;
|
|
6
|
+
}
|
|
7
|
+
export declare function validateTokenPayload(payload: Record<string, any>, rules?: TokenRequirements): {
|
|
8
|
+
valid: true;
|
|
9
|
+
} | {
|
|
10
|
+
valid: false;
|
|
11
|
+
error: string;
|
|
12
|
+
};
|
|
13
|
+
export declare function isTokenExpired(payload: JwtPayload): boolean;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Secret, JwtPayload } from "jsonwebtoken";
|
|
2
|
+
/**
|
|
3
|
+
* Verify token (throws if invalid or expired)
|
|
4
|
+
*/
|
|
5
|
+
export declare const verifyToken: (token: string, secret: Secret) => string | JwtPayload;
|
|
6
|
+
/**
|
|
7
|
+
* Safe verify — never throws, returns { valid, payload?, error? }
|
|
8
|
+
*/
|
|
9
|
+
export declare const safeVerifyToken: (token: string, secret: Secret) => {
|
|
10
|
+
valid: boolean;
|
|
11
|
+
payload?: string | JwtPayload;
|
|
12
|
+
error?: unknown;
|
|
13
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hash a password asynchronously using bcrypt.
|
|
3
|
+
*/
|
|
4
|
+
export declare const hashPassword: (password: string, saltRounds?: number) => Promise<string>;
|
|
5
|
+
export declare function hashPasswordWithPepper(password: string, pepper: string): Promise<string>;
|
|
6
|
+
/**
|
|
7
|
+
* Hash a password synchronously using bcrypt.
|
|
8
|
+
*/
|
|
9
|
+
export declare const hashPasswordSync: (password: string, saltRounds?: number) => string;
|
|
10
|
+
export declare function hashPasswordWithPepperSync(password: string, pepper: string): string;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare function ensureValidPassword(password: string): void;
|
|
2
|
+
export declare function safeCompare(a: string, b: string): boolean;
|
|
3
|
+
export declare function estimatePasswordEntropy(password: string): number;
|
|
4
|
+
export declare function normalizePassword(password: string): string;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compare a password with a stored hash asynchronously.
|
|
3
|
+
*/
|
|
4
|
+
export declare const verifyPassword: (password: string, hash: string) => Promise<boolean>;
|
|
5
|
+
export declare function verifyPasswordWithPepper(password: string, pepper: string, hash: string): Promise<boolean>;
|
|
6
|
+
/**
|
|
7
|
+
* Compare a password with a stored hash synchronously.
|
|
8
|
+
*/
|
|
9
|
+
export declare const verifyPasswordSync: (password: string, hash: string) => boolean;
|
|
10
|
+
export declare function verifyPasswordWithPepperSync(password: string, pepper: string, hash: string): Promise<boolean>;
|