@naman_deep_singh/security 1.1.0 → 1.3.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 +358 -175
- package/dist/cjs/core/crypto/cryptoManager.d.ts +111 -0
- package/dist/cjs/core/crypto/cryptoManager.js +185 -0
- package/dist/cjs/core/crypto/index.d.ts +5 -4
- package/dist/cjs/core/crypto/index.js +12 -4
- package/dist/cjs/core/jwt/extractToken.d.ts +2 -2
- package/dist/cjs/core/jwt/extractToken.js +12 -7
- package/dist/cjs/core/jwt/generateTokens.d.ts +3 -6
- package/dist/cjs/core/jwt/generateTokens.js +10 -3
- package/dist/cjs/core/jwt/index.d.ts +1 -0
- package/dist/cjs/core/jwt/index.js +1 -0
- package/dist/cjs/core/jwt/jwtManager.d.ts +66 -0
- package/dist/cjs/core/jwt/jwtManager.js +319 -0
- package/dist/cjs/core/jwt/signToken.d.ts +1 -1
- package/dist/cjs/core/jwt/types.d.ts +22 -0
- package/dist/cjs/core/jwt/types.js +2 -0
- package/dist/cjs/core/jwt/validateToken.d.ts +1 -1
- package/dist/cjs/core/jwt/verify.d.ts +12 -7
- package/dist/cjs/core/jwt/verify.js +23 -3
- package/dist/cjs/core/password/passwordManager.d.ts +29 -0
- package/dist/cjs/core/password/passwordManager.js +242 -0
- package/dist/cjs/index.d.ts +11 -9
- package/dist/cjs/interfaces/jwt.interface.d.ts +47 -0
- package/dist/cjs/interfaces/jwt.interface.js +2 -0
- package/dist/cjs/interfaces/password.interface.d.ts +60 -0
- package/dist/cjs/interfaces/password.interface.js +2 -0
- package/dist/esm/core/crypto/cryptoManager.d.ts +111 -0
- package/dist/esm/core/crypto/cryptoManager.js +180 -0
- package/dist/esm/core/crypto/index.d.ts +5 -4
- package/dist/esm/core/crypto/index.js +5 -4
- package/dist/esm/core/jwt/extractToken.d.ts +2 -2
- package/dist/esm/core/jwt/extractToken.js +12 -7
- package/dist/esm/core/jwt/generateTokens.d.ts +3 -6
- package/dist/esm/core/jwt/generateTokens.js +10 -3
- package/dist/esm/core/jwt/index.d.ts +1 -0
- package/dist/esm/core/jwt/index.js +1 -0
- package/dist/esm/core/jwt/jwtManager.d.ts +66 -0
- package/dist/esm/core/jwt/jwtManager.js +312 -0
- package/dist/esm/core/jwt/signToken.d.ts +1 -1
- package/dist/esm/core/jwt/types.d.ts +22 -0
- package/dist/esm/core/jwt/types.js +1 -0
- package/dist/esm/core/jwt/validateToken.d.ts +1 -1
- package/dist/esm/core/jwt/verify.d.ts +12 -7
- package/dist/esm/core/jwt/verify.js +20 -2
- package/dist/esm/core/password/passwordManager.d.ts +29 -0
- package/dist/esm/core/password/passwordManager.js +235 -0
- package/dist/esm/index.d.ts +11 -9
- package/dist/esm/interfaces/jwt.interface.d.ts +47 -0
- package/dist/esm/interfaces/jwt.interface.js +1 -0
- package/dist/esm/interfaces/password.interface.d.ts +60 -0
- package/dist/esm/interfaces/password.interface.js +1 -0
- package/dist/types/core/crypto/cryptoManager.d.ts +111 -0
- package/dist/types/core/crypto/index.d.ts +5 -4
- package/dist/types/core/jwt/extractToken.d.ts +2 -2
- package/dist/types/core/jwt/generateTokens.d.ts +3 -6
- package/dist/types/core/jwt/index.d.ts +1 -0
- package/dist/types/core/jwt/jwtManager.d.ts +66 -0
- package/dist/types/core/jwt/signToken.d.ts +1 -1
- package/dist/types/core/jwt/types.d.ts +22 -0
- package/dist/types/core/jwt/validateToken.d.ts +1 -1
- package/dist/types/core/jwt/verify.d.ts +12 -7
- package/dist/types/core/password/passwordManager.d.ts +29 -0
- package/dist/types/index.d.ts +11 -9
- package/dist/types/interfaces/jwt.interface.d.ts +47 -0
- package/dist/types/interfaces/password.interface.d.ts +60 -0
- package/package.json +1 -1
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.JWTManager = void 0;
|
|
7
|
+
const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
|
|
8
|
+
const signToken_1 = require("./signToken");
|
|
9
|
+
const verify_1 = require("./verify");
|
|
10
|
+
const errors_utils_1 = require("@naman_deep_singh/errors-utils");
|
|
11
|
+
// Simple LRU cache for token validation
|
|
12
|
+
class TokenCache {
|
|
13
|
+
constructor(maxSize = 100, ttl = 5 * 60 * 1000) {
|
|
14
|
+
this.cache = new Map();
|
|
15
|
+
this.maxSize = maxSize;
|
|
16
|
+
this.ttl = ttl;
|
|
17
|
+
}
|
|
18
|
+
get(key) {
|
|
19
|
+
const entry = this.cache.get(key);
|
|
20
|
+
if (!entry)
|
|
21
|
+
return null;
|
|
22
|
+
if (Date.now() - entry.timestamp > this.ttl) {
|
|
23
|
+
this.cache.delete(key);
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
// Move to end (most recently used)
|
|
27
|
+
this.cache.delete(key);
|
|
28
|
+
this.cache.set(key, entry);
|
|
29
|
+
return { valid: entry.valid, payload: entry.payload };
|
|
30
|
+
}
|
|
31
|
+
set(key, value) {
|
|
32
|
+
if (this.cache.has(key)) {
|
|
33
|
+
this.cache.delete(key);
|
|
34
|
+
}
|
|
35
|
+
else if (this.cache.size >= this.maxSize) {
|
|
36
|
+
// Remove least recently used (first item)
|
|
37
|
+
const firstKey = this.cache.keys().next().value;
|
|
38
|
+
if (firstKey !== undefined) {
|
|
39
|
+
this.cache.delete(firstKey);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
this.cache.set(key, { ...value, timestamp: Date.now() });
|
|
43
|
+
}
|
|
44
|
+
clear() {
|
|
45
|
+
this.cache.clear();
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
class JWTManager {
|
|
49
|
+
constructor(config) {
|
|
50
|
+
this.accessSecret = config.accessSecret;
|
|
51
|
+
this.refreshSecret = config.refreshSecret;
|
|
52
|
+
this.accessExpiry = config.accessExpiry || "15m";
|
|
53
|
+
this.refreshExpiry = config.refreshExpiry || "7d";
|
|
54
|
+
if (config.enableCaching) {
|
|
55
|
+
this.cache = new TokenCache(config.maxCacheSize || 100);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Generate both access and refresh tokens
|
|
60
|
+
*/
|
|
61
|
+
async generateTokens(payload) {
|
|
62
|
+
try {
|
|
63
|
+
this.validatePayload(payload);
|
|
64
|
+
const accessToken = await this.generateAccessToken(payload);
|
|
65
|
+
const refreshToken = await this.generateRefreshToken(payload);
|
|
66
|
+
return {
|
|
67
|
+
accessToken,
|
|
68
|
+
refreshToken,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
if (error instanceof errors_utils_1.BadRequestError || error instanceof errors_utils_1.ValidationError) {
|
|
73
|
+
throw error;
|
|
74
|
+
}
|
|
75
|
+
throw new errors_utils_1.BadRequestError("Failed to generate tokens");
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Generate access token
|
|
80
|
+
*/
|
|
81
|
+
async generateAccessToken(payload) {
|
|
82
|
+
try {
|
|
83
|
+
this.validatePayload(payload);
|
|
84
|
+
const token = (0, signToken_1.signToken)(payload, this.accessSecret, this.accessExpiry, { algorithm: "HS256" });
|
|
85
|
+
return token;
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
if (error instanceof errors_utils_1.BadRequestError || error instanceof errors_utils_1.ValidationError) {
|
|
89
|
+
throw error;
|
|
90
|
+
}
|
|
91
|
+
throw new errors_utils_1.BadRequestError("Failed to generate access token");
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Generate refresh token
|
|
96
|
+
*/
|
|
97
|
+
async generateRefreshToken(payload) {
|
|
98
|
+
try {
|
|
99
|
+
this.validatePayload(payload);
|
|
100
|
+
const token = (0, signToken_1.signToken)(payload, this.refreshSecret, this.refreshExpiry, { algorithm: "HS256" });
|
|
101
|
+
return token;
|
|
102
|
+
}
|
|
103
|
+
catch (error) {
|
|
104
|
+
if (error instanceof errors_utils_1.BadRequestError || error instanceof errors_utils_1.ValidationError) {
|
|
105
|
+
throw error;
|
|
106
|
+
}
|
|
107
|
+
throw new errors_utils_1.BadRequestError("Failed to generate refresh token");
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Verify access token
|
|
112
|
+
*/
|
|
113
|
+
async verifyAccessToken(token) {
|
|
114
|
+
try {
|
|
115
|
+
if (!token || typeof token !== "string") {
|
|
116
|
+
throw new errors_utils_1.ValidationError("Access token must be a non-empty string");
|
|
117
|
+
}
|
|
118
|
+
const cacheKey = `access_${token}`;
|
|
119
|
+
if (this.cache) {
|
|
120
|
+
const cached = this.cache.get(cacheKey);
|
|
121
|
+
if (cached) {
|
|
122
|
+
if (!cached.valid) {
|
|
123
|
+
throw new errors_utils_1.UnauthorizedError("Access token is invalid or expired");
|
|
124
|
+
}
|
|
125
|
+
return cached.payload;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
const decoded = (0, verify_1.verifyToken)(token, this.accessSecret);
|
|
129
|
+
if (this.cache) {
|
|
130
|
+
this.cache.set(cacheKey, { valid: true, payload: decoded });
|
|
131
|
+
}
|
|
132
|
+
return decoded;
|
|
133
|
+
}
|
|
134
|
+
catch (error) {
|
|
135
|
+
if (error instanceof errors_utils_1.ValidationError || error instanceof errors_utils_1.UnauthorizedError) {
|
|
136
|
+
throw error;
|
|
137
|
+
}
|
|
138
|
+
if (error instanceof Error && error.name === "TokenExpiredError") {
|
|
139
|
+
throw new errors_utils_1.UnauthorizedError("Access token has expired");
|
|
140
|
+
}
|
|
141
|
+
if (error instanceof Error && error.name === "JsonWebTokenError") {
|
|
142
|
+
throw new errors_utils_1.UnauthorizedError("Access token is invalid");
|
|
143
|
+
}
|
|
144
|
+
throw new errors_utils_1.UnauthorizedError("Failed to verify access token");
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Verify refresh token
|
|
149
|
+
*/
|
|
150
|
+
async verifyRefreshToken(token) {
|
|
151
|
+
try {
|
|
152
|
+
if (!token || typeof token !== "string") {
|
|
153
|
+
throw new errors_utils_1.ValidationError("Refresh token must be a non-empty string");
|
|
154
|
+
}
|
|
155
|
+
const cacheKey = `refresh_${token}`;
|
|
156
|
+
if (this.cache) {
|
|
157
|
+
const cached = this.cache.get(cacheKey);
|
|
158
|
+
if (cached) {
|
|
159
|
+
if (!cached.valid) {
|
|
160
|
+
throw new errors_utils_1.UnauthorizedError("Refresh token is invalid or expired");
|
|
161
|
+
}
|
|
162
|
+
return cached.payload;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
const decoded = (0, verify_1.verifyToken)(token, this.refreshSecret);
|
|
166
|
+
if (this.cache) {
|
|
167
|
+
this.cache.set(cacheKey, { valid: true, payload: decoded });
|
|
168
|
+
}
|
|
169
|
+
return decoded;
|
|
170
|
+
}
|
|
171
|
+
catch (error) {
|
|
172
|
+
if (error instanceof errors_utils_1.ValidationError || error instanceof errors_utils_1.UnauthorizedError) {
|
|
173
|
+
throw error;
|
|
174
|
+
}
|
|
175
|
+
if (error instanceof Error && error.name === "TokenExpiredError") {
|
|
176
|
+
throw new errors_utils_1.UnauthorizedError("Refresh token has expired");
|
|
177
|
+
}
|
|
178
|
+
if (error instanceof Error && error.name === "JsonWebTokenError") {
|
|
179
|
+
throw new errors_utils_1.UnauthorizedError("Refresh token is invalid");
|
|
180
|
+
}
|
|
181
|
+
throw new errors_utils_1.UnauthorizedError("Failed to verify refresh token");
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Decode token without verification
|
|
186
|
+
*/
|
|
187
|
+
decodeToken(token, complete = false) {
|
|
188
|
+
try {
|
|
189
|
+
if (!token || typeof token !== "string") {
|
|
190
|
+
throw new errors_utils_1.ValidationError("Token must be a non-empty string");
|
|
191
|
+
}
|
|
192
|
+
return jsonwebtoken_1.default.decode(token, { complete });
|
|
193
|
+
}
|
|
194
|
+
catch (error) {
|
|
195
|
+
if (error instanceof errors_utils_1.ValidationError) {
|
|
196
|
+
throw error;
|
|
197
|
+
}
|
|
198
|
+
return null;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Extract token from Authorization header
|
|
203
|
+
*/
|
|
204
|
+
extractTokenFromHeader(authHeader) {
|
|
205
|
+
try {
|
|
206
|
+
if (!authHeader || typeof authHeader !== "string") {
|
|
207
|
+
return null;
|
|
208
|
+
}
|
|
209
|
+
const parts = authHeader.split(" ");
|
|
210
|
+
if (parts.length !== 2 || parts[0] !== "Bearer") {
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
return parts[1];
|
|
214
|
+
}
|
|
215
|
+
catch {
|
|
216
|
+
return null;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Validate token without throwing exceptions
|
|
221
|
+
*/
|
|
222
|
+
validateToken(token, secret, options = {}) {
|
|
223
|
+
try {
|
|
224
|
+
if (!token || typeof token !== "string") {
|
|
225
|
+
return false;
|
|
226
|
+
}
|
|
227
|
+
const result = (0, verify_1.safeVerifyToken)(token, secret);
|
|
228
|
+
return result.valid;
|
|
229
|
+
}
|
|
230
|
+
catch {
|
|
231
|
+
return false;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Rotate refresh token
|
|
236
|
+
*/
|
|
237
|
+
async rotateRefreshToken(oldToken) {
|
|
238
|
+
try {
|
|
239
|
+
if (!oldToken || typeof oldToken !== "string") {
|
|
240
|
+
throw new errors_utils_1.ValidationError("Old refresh token must be a non-empty string");
|
|
241
|
+
}
|
|
242
|
+
const decoded = await this.verifyRefreshToken(oldToken);
|
|
243
|
+
if (typeof decoded === "string") {
|
|
244
|
+
throw new errors_utils_1.ValidationError("Invalid token payload — expected JWT payload object");
|
|
245
|
+
}
|
|
246
|
+
// Create new payload without issued/expired timestamps
|
|
247
|
+
const payload = { ...decoded };
|
|
248
|
+
delete payload.iat;
|
|
249
|
+
delete payload.exp;
|
|
250
|
+
// Generate new refresh token
|
|
251
|
+
const newToken = (0, signToken_1.signToken)(payload, this.refreshSecret, this.refreshExpiry);
|
|
252
|
+
return newToken;
|
|
253
|
+
}
|
|
254
|
+
catch (error) {
|
|
255
|
+
if (error instanceof errors_utils_1.ValidationError || error instanceof errors_utils_1.UnauthorizedError) {
|
|
256
|
+
throw error;
|
|
257
|
+
}
|
|
258
|
+
throw new errors_utils_1.BadRequestError("Failed to rotate refresh token");
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Check if token is expired
|
|
263
|
+
*/
|
|
264
|
+
isTokenExpired(token) {
|
|
265
|
+
try {
|
|
266
|
+
const decoded = this.decodeToken(token);
|
|
267
|
+
if (!decoded || !decoded.exp) {
|
|
268
|
+
return true;
|
|
269
|
+
}
|
|
270
|
+
const currentTime = Math.floor(Date.now() / 1000);
|
|
271
|
+
return decoded.exp < currentTime;
|
|
272
|
+
}
|
|
273
|
+
catch {
|
|
274
|
+
return true;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Get token expiration date
|
|
279
|
+
*/
|
|
280
|
+
getTokenExpiration(token) {
|
|
281
|
+
try {
|
|
282
|
+
const decoded = this.decodeToken(token);
|
|
283
|
+
if (!decoded || !decoded.exp) {
|
|
284
|
+
return null;
|
|
285
|
+
}
|
|
286
|
+
return new Date(decoded.exp * 1000);
|
|
287
|
+
}
|
|
288
|
+
catch {
|
|
289
|
+
return null;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Clear token cache
|
|
294
|
+
*/
|
|
295
|
+
clearCache() {
|
|
296
|
+
this.cache?.clear();
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Get cache statistics
|
|
300
|
+
*/
|
|
301
|
+
getCacheStats() {
|
|
302
|
+
if (!this.cache)
|
|
303
|
+
return null;
|
|
304
|
+
return {
|
|
305
|
+
size: this.cache.cache.size,
|
|
306
|
+
maxSize: this.cache.maxSize
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
// Private helper methods
|
|
310
|
+
validatePayload(payload) {
|
|
311
|
+
if (!payload || typeof payload !== "object") {
|
|
312
|
+
throw new errors_utils_1.ValidationError("Payload must be a non-null object");
|
|
313
|
+
}
|
|
314
|
+
if (Object.keys(payload).length === 0) {
|
|
315
|
+
throw new errors_utils_1.ValidationError("Payload cannot be empty");
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
exports.JWTManager = JWTManager;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { Secret, SignOptions } from "jsonwebtoken";
|
|
2
|
-
export declare const signToken: (payload: Record<string,
|
|
2
|
+
export declare const signToken: (payload: Record<string, unknown>, secret: Secret, expiresIn?: string | number, options?: SignOptions) => string;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { JwtPayload } from "jsonwebtoken";
|
|
2
|
+
export interface AccessTokenBrand {
|
|
3
|
+
readonly access: unique symbol;
|
|
4
|
+
}
|
|
5
|
+
export interface RefreshTokenBrand {
|
|
6
|
+
readonly refresh: unique symbol;
|
|
7
|
+
}
|
|
8
|
+
export type AccessToken = string & AccessTokenBrand;
|
|
9
|
+
export type RefreshToken = string & RefreshTokenBrand;
|
|
10
|
+
export interface TokenPair {
|
|
11
|
+
accessToken: AccessToken;
|
|
12
|
+
refreshToken: RefreshToken;
|
|
13
|
+
}
|
|
14
|
+
export interface VerificationResult<T = JwtPayload> {
|
|
15
|
+
valid: boolean;
|
|
16
|
+
payload?: T | string;
|
|
17
|
+
error?: Error;
|
|
18
|
+
}
|
|
19
|
+
export interface TokenValidationOptions {
|
|
20
|
+
ignoreExpiration?: boolean;
|
|
21
|
+
ignoreIssuedAt?: boolean;
|
|
22
|
+
}
|
|
@@ -4,7 +4,7 @@ export interface TokenRequirements {
|
|
|
4
4
|
forbiddenFields?: string[];
|
|
5
5
|
validateTypes?: Record<string, "string" | "number" | "boolean">;
|
|
6
6
|
}
|
|
7
|
-
export declare function validateTokenPayload(payload: Record<string,
|
|
7
|
+
export declare function validateTokenPayload(payload: Record<string, unknown>, rules?: TokenRequirements): {
|
|
8
8
|
valid: true;
|
|
9
9
|
} | {
|
|
10
10
|
valid: false;
|
|
@@ -1,13 +1,18 @@
|
|
|
1
|
-
import { Secret, JwtPayload } from "jsonwebtoken";
|
|
1
|
+
import jwt, { Secret, JwtPayload } from "jsonwebtoken";
|
|
2
|
+
import { VerificationResult } from "./types";
|
|
2
3
|
/**
|
|
3
4
|
* Verify token (throws if invalid or expired)
|
|
4
5
|
*/
|
|
5
6
|
export declare const verifyToken: (token: string, secret: Secret) => string | JwtPayload;
|
|
6
7
|
/**
|
|
7
|
-
* Safe verify — never throws, returns
|
|
8
|
+
* Safe verify — never throws, returns structured result
|
|
8
9
|
*/
|
|
9
|
-
export declare const safeVerifyToken: (token: string, secret: Secret) =>
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
export declare const safeVerifyToken: (token: string, secret: Secret) => VerificationResult;
|
|
11
|
+
/**
|
|
12
|
+
* Verify token with validation options
|
|
13
|
+
*/
|
|
14
|
+
export declare const verifyTokenWithOptions: (token: string, secret: Secret, options?: jwt.VerifyOptions) => string | JwtPayload;
|
|
15
|
+
/**
|
|
16
|
+
* Safe verify with validation options
|
|
17
|
+
*/
|
|
18
|
+
export declare const safeVerifyTokenWithOptions: (token: string, secret: Secret, options?: jwt.VerifyOptions) => VerificationResult;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.safeVerifyToken = exports.verifyToken = void 0;
|
|
3
|
+
exports.safeVerifyTokenWithOptions = exports.verifyTokenWithOptions = exports.safeVerifyToken = exports.verifyToken = void 0;
|
|
4
4
|
const jsonwebtoken_1 = require("jsonwebtoken");
|
|
5
5
|
/**
|
|
6
6
|
* Verify token (throws if invalid or expired)
|
|
@@ -10,7 +10,7 @@ const verifyToken = (token, secret) => {
|
|
|
10
10
|
};
|
|
11
11
|
exports.verifyToken = verifyToken;
|
|
12
12
|
/**
|
|
13
|
-
* Safe verify — never throws, returns
|
|
13
|
+
* Safe verify — never throws, returns structured result
|
|
14
14
|
*/
|
|
15
15
|
const safeVerifyToken = (token, secret) => {
|
|
16
16
|
try {
|
|
@@ -18,7 +18,27 @@ const safeVerifyToken = (token, secret) => {
|
|
|
18
18
|
return { valid: true, payload: decoded };
|
|
19
19
|
}
|
|
20
20
|
catch (error) {
|
|
21
|
-
return { valid: false, error };
|
|
21
|
+
return { valid: false, error: error };
|
|
22
22
|
}
|
|
23
23
|
};
|
|
24
24
|
exports.safeVerifyToken = safeVerifyToken;
|
|
25
|
+
/**
|
|
26
|
+
* Verify token with validation options
|
|
27
|
+
*/
|
|
28
|
+
const verifyTokenWithOptions = (token, secret, options = {}) => {
|
|
29
|
+
return (0, jsonwebtoken_1.verify)(token, secret, options);
|
|
30
|
+
};
|
|
31
|
+
exports.verifyTokenWithOptions = verifyTokenWithOptions;
|
|
32
|
+
/**
|
|
33
|
+
* Safe verify with validation options
|
|
34
|
+
*/
|
|
35
|
+
const safeVerifyTokenWithOptions = (token, secret, options = {}) => {
|
|
36
|
+
try {
|
|
37
|
+
const decoded = (0, jsonwebtoken_1.verify)(token, secret, options);
|
|
38
|
+
return { valid: true, payload: decoded };
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
return { valid: false, error: error };
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
exports.safeVerifyTokenWithOptions = safeVerifyTokenWithOptions;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { IPasswordManager, PasswordConfig, PasswordValidationResult, HashedPassword, PasswordStrength } from "../../interfaces/password.interface";
|
|
2
|
+
export declare class PasswordManager implements IPasswordManager {
|
|
3
|
+
private defaultConfig;
|
|
4
|
+
constructor(config?: PasswordConfig);
|
|
5
|
+
/**
|
|
6
|
+
* Hash a password asynchronously using bcrypt
|
|
7
|
+
*/
|
|
8
|
+
hash(password: string, salt?: string): Promise<HashedPassword>;
|
|
9
|
+
/**
|
|
10
|
+
* Verify password against hash and salt
|
|
11
|
+
*/
|
|
12
|
+
verify(password: string, hash: string, salt: string): Promise<boolean>;
|
|
13
|
+
/**
|
|
14
|
+
* Generate a random password
|
|
15
|
+
*/
|
|
16
|
+
generate(length?: number, options?: PasswordConfig): string;
|
|
17
|
+
/**
|
|
18
|
+
* Validate password against configuration
|
|
19
|
+
*/
|
|
20
|
+
validate(password: string, config?: PasswordConfig): PasswordValidationResult;
|
|
21
|
+
/**
|
|
22
|
+
* Check password strength
|
|
23
|
+
*/
|
|
24
|
+
checkStrength(password: string): PasswordStrength;
|
|
25
|
+
/**
|
|
26
|
+
* Check if password hash needs upgrade (different salt rounds)
|
|
27
|
+
*/
|
|
28
|
+
needsUpgrade(hash: string, currentConfig: PasswordConfig): boolean;
|
|
29
|
+
}
|