@naman_deep_singh/security 1.3.2 → 1.4.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.
Files changed (52) hide show
  1. package/README.md +153 -355
  2. package/dist/cjs/core/crypto/cryptoManager.d.ts +5 -5
  3. package/dist/cjs/core/crypto/cryptoManager.js +42 -25
  4. package/dist/cjs/core/jwt/decode.js +4 -1
  5. package/dist/cjs/core/jwt/generateTokens.d.ts +1 -1
  6. package/dist/cjs/core/jwt/generateTokens.js +7 -4
  7. package/dist/cjs/core/jwt/jwtManager.d.ts +19 -43
  8. package/dist/cjs/core/jwt/jwtManager.js +72 -202
  9. package/dist/cjs/core/jwt/parseDuration.js +3 -2
  10. package/dist/cjs/core/jwt/signToken.js +2 -1
  11. package/dist/cjs/core/jwt/validateToken.d.ts +10 -7
  12. package/dist/cjs/core/jwt/validateToken.js +14 -11
  13. package/dist/cjs/core/jwt/verify.d.ts +9 -10
  14. package/dist/cjs/core/jwt/verify.js +57 -14
  15. package/dist/cjs/core/password/hash.js +4 -4
  16. package/dist/cjs/core/password/passwordManager.d.ts +2 -2
  17. package/dist/cjs/core/password/passwordManager.js +43 -82
  18. package/dist/cjs/core/password/strength.js +5 -5
  19. package/dist/cjs/core/password/utils.d.ts +12 -0
  20. package/dist/cjs/core/password/utils.js +16 -1
  21. package/dist/cjs/core/password/verify.js +5 -5
  22. package/dist/cjs/index.d.ts +2 -7
  23. package/dist/esm/core/crypto/cryptoManager.d.ts +5 -5
  24. package/dist/esm/core/crypto/cryptoManager.js +42 -25
  25. package/dist/esm/core/jwt/decode.js +4 -1
  26. package/dist/esm/core/jwt/generateTokens.d.ts +1 -1
  27. package/dist/esm/core/jwt/generateTokens.js +7 -4
  28. package/dist/esm/core/jwt/jwtManager.d.ts +19 -43
  29. package/dist/esm/core/jwt/jwtManager.js +73 -203
  30. package/dist/esm/core/jwt/parseDuration.js +3 -2
  31. package/dist/esm/core/jwt/signToken.js +2 -1
  32. package/dist/esm/core/jwt/validateToken.d.ts +10 -7
  33. package/dist/esm/core/jwt/validateToken.js +14 -11
  34. package/dist/esm/core/jwt/verify.d.ts +9 -10
  35. package/dist/esm/core/jwt/verify.js +55 -12
  36. package/dist/esm/core/password/hash.js +4 -4
  37. package/dist/esm/core/password/passwordManager.d.ts +2 -2
  38. package/dist/esm/core/password/passwordManager.js +43 -82
  39. package/dist/esm/core/password/strength.js +5 -5
  40. package/dist/esm/core/password/utils.d.ts +12 -0
  41. package/dist/esm/core/password/utils.js +16 -1
  42. package/dist/esm/core/password/verify.js +5 -5
  43. package/dist/esm/index.d.ts +2 -7
  44. package/dist/types/core/crypto/cryptoManager.d.ts +5 -5
  45. package/dist/types/core/jwt/generateTokens.d.ts +1 -1
  46. package/dist/types/core/jwt/jwtManager.d.ts +19 -43
  47. package/dist/types/core/jwt/validateToken.d.ts +10 -7
  48. package/dist/types/core/jwt/verify.d.ts +9 -10
  49. package/dist/types/core/password/passwordManager.d.ts +2 -2
  50. package/dist/types/core/password/utils.d.ts +12 -0
  51. package/dist/types/index.d.ts +2 -7
  52. package/package.json +2 -2
@@ -1,5 +1,5 @@
1
1
  import { type JwtPayload, type Secret } from 'jsonwebtoken';
2
- import type { AccessToken, ITokenManager, JWTConfig, RefreshToken, TokenPair, TokenValidationOptions } from '../../interfaces/jwt.interface';
2
+ import type { AccessToken, ITokenManager, JWTConfig, RefreshToken, TokenPair } from '../../interfaces/jwt.interface';
3
3
  export declare class JWTManager implements ITokenManager {
4
4
  private accessSecret;
5
5
  private refreshSecret;
@@ -8,60 +8,36 @@ export declare class JWTManager implements ITokenManager {
8
8
  private cache?;
9
9
  private cacheTTL;
10
10
  constructor(config: JWTConfig);
11
- /**
12
- * Generate both access and refresh tokens
13
- */
11
+ /** Generate both access and refresh tokens */
14
12
  generateTokens(payload: Record<string, unknown>): Promise<TokenPair>;
15
- /**
16
- * Generate access token
17
- */
13
+ /** Generate access token */
18
14
  generateAccessToken(payload: Record<string, unknown>): Promise<AccessToken>;
19
- /**
20
- * Generate refresh token
21
- */
15
+ /** Generate refresh token */
22
16
  generateRefreshToken(payload: Record<string, unknown>): Promise<RefreshToken>;
23
- /**
24
- * Verify access token
25
- */
26
- verifyAccessToken(token: string): Promise<JwtPayload | string>;
27
- /**
28
- * Verify refresh token
29
- */
30
- verifyRefreshToken(token: string): Promise<JwtPayload | string>;
31
- /**
32
- * Decode token without verification
33
- */
17
+ /** Verify access token */
18
+ verifyAccessToken(token: string): Promise<JwtPayload>;
19
+ /** Verify refresh token */
20
+ verifyRefreshToken(token: string): Promise<JwtPayload>;
21
+ /** Decode token without verification */
34
22
  decodeToken(token: string, complete?: boolean): JwtPayload | string | null;
35
- /**
36
- * Extract token from Authorization header
37
- */
23
+ /** Extract token from Authorization header */
38
24
  extractTokenFromHeader(authHeader: string): string | null;
39
- /**
40
- * Validate token without throwing exceptions
41
- */
42
- validateToken(token: string, secret: Secret, options?: TokenValidationOptions): boolean;
43
- /**
44
- * Rotate refresh token
45
- */
25
+ /** Validate token without throwing exceptions */
26
+ validateToken(token: string, secret: Secret): boolean;
27
+ /** Rotate refresh token */
46
28
  rotateRefreshToken(oldToken: string): Promise<RefreshToken>;
47
- /**
48
- * Check if token is expired
49
- */
29
+ /** Check if token is expired */
50
30
  isTokenExpired(token: string): boolean;
51
- /**
52
- * Get token expiration date
53
- */
31
+ /** Get token expiration date */
54
32
  getTokenExpiration(token: string): Date | null;
55
- /**
56
- * Clear token cache
57
- */
33
+ /** Clear token cache */
58
34
  clearCache(): void;
59
- /**
60
- * Get cache statistics
61
- */
35
+ /** Get cache statistics */
62
36
  getCacheStats(): {
63
37
  size: number;
64
38
  maxSize: number;
65
39
  } | null;
40
+ /** Private helper methods */
66
41
  private validatePayload;
42
+ private verifyTokenWithCache;
67
43
  }
@@ -1,6 +1,6 @@
1
1
  import jwt from 'jsonwebtoken';
2
2
  import { signToken } from './signToken';
3
- import { safeVerifyToken, verifyToken } from './verify';
3
+ import { safeVerifyToken } from './verify';
4
4
  import { BadRequestError, UnauthorizedError, ValidationError, } from '@naman_deep_singh/errors-utils';
5
5
  import { LRUCache } from '@naman_deep_singh/js-extensions';
6
6
  export class JWTManager {
@@ -9,284 +9,154 @@ export class JWTManager {
9
9
  this.refreshSecret = config.refreshSecret;
10
10
  this.accessExpiry = config.accessExpiry || '15m';
11
11
  this.refreshExpiry = config.refreshExpiry || '7d';
12
- this.cacheTTL = 5 * 60 * 1000; // 5 minutes default TTL
12
+ this.cacheTTL = 5 * 60 * 1000; // 5 minutes
13
13
  if (config.enableCaching) {
14
14
  this.cache = new LRUCache(config.maxCacheSize || 100);
15
15
  }
16
16
  }
17
- /**
18
- * Generate both access and refresh tokens
19
- */
17
+ /** Generate both access and refresh tokens */
20
18
  async generateTokens(payload) {
21
19
  try {
22
20
  this.validatePayload(payload);
23
21
  const accessToken = await this.generateAccessToken(payload);
24
22
  const refreshToken = await this.generateRefreshToken(payload);
25
- return {
26
- accessToken,
27
- refreshToken,
28
- };
23
+ return { accessToken, refreshToken };
29
24
  }
30
25
  catch (error) {
31
- if (error instanceof BadRequestError ||
32
- error instanceof ValidationError) {
26
+ if (error instanceof BadRequestError || error instanceof ValidationError)
33
27
  throw error;
34
- }
35
- throw new BadRequestError('Failed to generate tokens');
28
+ throw new BadRequestError({ message: 'Failed to generate tokens' }, error instanceof Error ? error : undefined);
36
29
  }
37
30
  }
38
- /**
39
- * Generate access token
40
- */
31
+ /** Generate access token */
41
32
  async generateAccessToken(payload) {
42
33
  try {
43
34
  this.validatePayload(payload);
44
- const token = signToken(payload, this.accessSecret, this.accessExpiry, {
45
- algorithm: 'HS256',
46
- });
35
+ const token = signToken(payload, this.accessSecret, this.accessExpiry, { algorithm: 'HS256' });
47
36
  return token;
48
37
  }
49
38
  catch (error) {
50
- if (error instanceof BadRequestError ||
51
- error instanceof ValidationError) {
39
+ if (error instanceof BadRequestError || error instanceof ValidationError)
52
40
  throw error;
53
- }
54
- throw new BadRequestError('Failed to generate access token');
41
+ throw new BadRequestError({ message: 'Failed to generate access token' }, error instanceof Error ? error : undefined);
55
42
  }
56
43
  }
57
- /**
58
- * Generate refresh token
59
- */
44
+ /** Generate refresh token */
60
45
  async generateRefreshToken(payload) {
61
46
  try {
62
47
  this.validatePayload(payload);
63
- const token = signToken(payload, this.refreshSecret, this.refreshExpiry, {
64
- algorithm: 'HS256',
65
- });
48
+ const token = signToken(payload, this.refreshSecret, this.refreshExpiry, { algorithm: 'HS256' });
66
49
  return token;
67
50
  }
68
51
  catch (error) {
69
- if (error instanceof BadRequestError ||
70
- error instanceof ValidationError) {
52
+ if (error instanceof BadRequestError || error instanceof ValidationError)
71
53
  throw error;
72
- }
73
- throw new BadRequestError('Failed to generate refresh token');
54
+ throw new BadRequestError({ message: 'Failed to generate refresh token' }, error instanceof Error ? error : undefined);
74
55
  }
75
56
  }
76
- /**
77
- * Verify access token
78
- */
57
+ /** Verify access token */
79
58
  async verifyAccessToken(token) {
80
- try {
81
- if (!token || typeof token !== 'string') {
82
- throw new ValidationError('Access token must be a non-empty string');
83
- }
84
- const cacheKey = `access_${token}`;
85
- if (this.cache) {
86
- const cached = this.cache.get(cacheKey);
87
- if (cached && Date.now() - cached.timestamp <= this.cacheTTL) {
88
- if (!cached.valid) {
89
- throw new UnauthorizedError('Access token is invalid or expired');
90
- }
91
- return cached.payload;
92
- }
93
- }
94
- const decoded = verifyToken(token, this.accessSecret);
95
- if (this.cache) {
96
- this.cache.set(cacheKey, {
97
- valid: true,
98
- payload: decoded,
99
- timestamp: Date.now(),
100
- });
101
- }
102
- return decoded;
103
- }
104
- catch (error) {
105
- if (error instanceof ValidationError ||
106
- error instanceof UnauthorizedError) {
107
- throw error;
108
- }
109
- if (error instanceof Error && error.name === 'TokenExpiredError') {
110
- throw new UnauthorizedError('Access token has expired');
111
- }
112
- if (error instanceof Error && error.name === 'JsonWebTokenError') {
113
- throw new UnauthorizedError('Access token is invalid');
114
- }
115
- throw new UnauthorizedError('Failed to verify access token');
116
- }
59
+ return this.verifyTokenWithCache(token, this.accessSecret, 'access');
117
60
  }
118
- /**
119
- * Verify refresh token
120
- */
61
+ /** Verify refresh token */
121
62
  async verifyRefreshToken(token) {
122
- try {
123
- if (!token || typeof token !== 'string') {
124
- throw new ValidationError('Refresh token must be a non-empty string');
125
- }
126
- const cacheKey = `refresh_${token}`;
127
- if (this.cache) {
128
- const cached = this.cache.get(cacheKey);
129
- if (cached) {
130
- if (!cached.valid) {
131
- throw new UnauthorizedError('Refresh token is invalid or expired');
132
- }
133
- return cached.payload;
134
- }
135
- }
136
- const decoded = verifyToken(token, this.refreshSecret);
137
- if (this.cache) {
138
- this.cache.set(cacheKey, { valid: true, payload: decoded, timestamp: Date.now() });
139
- }
140
- return decoded;
141
- }
142
- catch (error) {
143
- if (error instanceof ValidationError ||
144
- error instanceof UnauthorizedError) {
145
- throw error;
146
- }
147
- if (error instanceof Error && error.name === 'TokenExpiredError') {
148
- throw new UnauthorizedError('Refresh token has expired');
149
- }
150
- if (error instanceof Error && error.name === 'JsonWebTokenError') {
151
- throw new UnauthorizedError('Refresh token is invalid');
152
- }
153
- throw new UnauthorizedError('Failed to verify refresh token');
154
- }
63
+ return this.verifyTokenWithCache(token, this.refreshSecret, 'refresh');
155
64
  }
156
- /**
157
- * Decode token without verification
158
- */
65
+ /** Decode token without verification */
159
66
  decodeToken(token, complete = false) {
160
- try {
161
- if (!token || typeof token !== 'string') {
162
- throw new ValidationError('Token must be a non-empty string');
163
- }
164
- return jwt.decode(token, { complete });
165
- }
166
- catch (error) {
167
- if (error instanceof ValidationError) {
168
- throw error;
169
- }
67
+ if (!token || typeof token !== 'string')
170
68
  return null;
171
- }
69
+ return jwt.decode(token, { complete });
172
70
  }
173
- /**
174
- * Extract token from Authorization header
175
- */
71
+ /** Extract token from Authorization header */
176
72
  extractTokenFromHeader(authHeader) {
177
- try {
178
- if (!authHeader || typeof authHeader !== 'string') {
179
- return null;
180
- }
181
- const parts = authHeader.split(' ');
182
- if (parts.length !== 2 || parts[0] !== 'Bearer') {
183
- return null;
184
- }
185
- return parts[1];
186
- }
187
- catch {
73
+ if (!authHeader || typeof authHeader !== 'string')
188
74
  return null;
189
- }
75
+ const parts = authHeader.split(' ');
76
+ if (parts.length !== 2 || parts[0] !== 'Bearer')
77
+ return null;
78
+ return parts[1];
190
79
  }
191
- /**
192
- * Validate token without throwing exceptions
193
- */
194
- validateToken(token, secret, options = {}) {
195
- try {
196
- if (!token || typeof token !== 'string') {
197
- return false;
198
- }
199
- const result = safeVerifyToken(token, secret);
200
- return result.valid;
201
- }
202
- catch {
80
+ /** Validate token without throwing exceptions */
81
+ validateToken(token, secret) {
82
+ if (!token || typeof token !== 'string')
203
83
  return false;
204
- }
84
+ return safeVerifyToken(token, secret).valid;
205
85
  }
206
- /**
207
- * Rotate refresh token
208
- */
86
+ /** Rotate refresh token */
209
87
  async rotateRefreshToken(oldToken) {
210
- try {
211
- if (!oldToken || typeof oldToken !== 'string') {
212
- throw new ValidationError('Old refresh token must be a non-empty string');
213
- }
214
- const decoded = await this.verifyRefreshToken(oldToken);
215
- if (typeof decoded === 'string') {
216
- throw new ValidationError('Invalid token payload — expected JWT payload object');
217
- }
218
- // Create new payload without issued/expired timestamps
219
- const payload = { ...decoded };
220
- delete payload.iat;
221
- delete payload.exp;
222
- // Generate new refresh token
223
- const newToken = signToken(payload, this.refreshSecret, this.refreshExpiry);
224
- return newToken;
225
- }
226
- catch (error) {
227
- if (error instanceof ValidationError ||
228
- error instanceof UnauthorizedError) {
229
- throw error;
230
- }
231
- throw new BadRequestError('Failed to rotate refresh token');
232
- }
88
+ if (!oldToken || typeof oldToken !== 'string') {
89
+ throw new ValidationError({ message: 'Old refresh token must be a non-empty string' });
90
+ }
91
+ const decoded = await this.verifyRefreshToken(oldToken);
92
+ const payload = { ...decoded };
93
+ delete payload.iat;
94
+ delete payload.exp;
95
+ const newToken = signToken(payload, this.refreshSecret, this.refreshExpiry);
96
+ return newToken;
233
97
  }
234
- /**
235
- * Check if token is expired
236
- */
98
+ /** Check if token is expired */
237
99
  isTokenExpired(token) {
238
100
  try {
239
101
  const decoded = this.decodeToken(token);
240
- if (!decoded || !decoded.exp) {
102
+ if (!decoded || !decoded.exp)
241
103
  return true;
242
- }
243
- const currentTime = Math.floor(Date.now() / 1000);
244
- return decoded.exp < currentTime;
104
+ return decoded.exp < Math.floor(Date.now() / 1000);
245
105
  }
246
106
  catch {
247
107
  return true;
248
108
  }
249
109
  }
250
- /**
251
- * Get token expiration date
252
- */
110
+ /** Get token expiration date */
253
111
  getTokenExpiration(token) {
254
112
  try {
255
113
  const decoded = this.decodeToken(token);
256
- if (!decoded || !decoded.exp) {
114
+ if (!decoded || !decoded.exp)
257
115
  return null;
258
- }
259
116
  return new Date(decoded.exp * 1000);
260
117
  }
261
118
  catch {
262
119
  return null;
263
120
  }
264
121
  }
265
- /**
266
- * Clear token cache
267
- */
122
+ /** Clear token cache */
268
123
  clearCache() {
269
124
  this.cache?.clear();
270
125
  }
271
- /**
272
- * Get cache statistics
273
- */
126
+ /** Get cache statistics */
274
127
  getCacheStats() {
275
128
  if (!this.cache)
276
129
  return null;
277
- // Note: LRUCache doesn't expose internal size, so we return maxSize only
278
- return {
279
- size: -1, // Size not available from LRUCache
280
- maxSize: this.cache.maxSize,
281
- };
130
+ return { size: -1, maxSize: this.cache.maxSize };
282
131
  }
283
- // Private helper methods
132
+ /** Private helper methods */
284
133
  validatePayload(payload) {
285
134
  if (!payload || typeof payload !== 'object') {
286
- throw new ValidationError('Payload must be a non-null object');
135
+ throw new ValidationError({ message: 'Payload must be a non-null object' });
287
136
  }
288
137
  if (Object.keys(payload).length === 0) {
289
- throw new ValidationError('Payload cannot be empty');
138
+ throw new ValidationError({ message: 'Payload cannot be empty' });
290
139
  }
291
140
  }
141
+ async verifyTokenWithCache(token, secret, type) {
142
+ if (!token || typeof token !== 'string') {
143
+ throw new ValidationError({ message: `${type} token must be a non-empty string` });
144
+ }
145
+ const cacheKey = `${type}_${token}`;
146
+ if (this.cache) {
147
+ const cached = this.cache.get(cacheKey);
148
+ if (cached && Date.now() - cached.timestamp <= this.cacheTTL) {
149
+ if (!cached.valid)
150
+ throw new UnauthorizedError({ message: `${type} token is invalid or expired` });
151
+ return cached.payload;
152
+ }
153
+ }
154
+ const { valid, payload, error } = safeVerifyToken(token, secret);
155
+ if (!valid || !payload || typeof payload === 'string') {
156
+ this.cache?.set(cacheKey, { valid: false, payload: {}, timestamp: Date.now() });
157
+ throw new UnauthorizedError({ message: `${type} token is invalid or expired`, cause: error });
158
+ }
159
+ this.cache?.set(cacheKey, { valid: true, payload, timestamp: Date.now() });
160
+ return payload;
161
+ }
292
162
  }
@@ -1,3 +1,4 @@
1
+ import { ValidationError } from "@naman_deep_singh/errors-utils";
1
2
  const TIME_UNITS = {
2
3
  s: 1,
3
4
  m: 60,
@@ -15,12 +16,12 @@ export function parseDuration(input) {
15
16
  const value = Number.parseInt(match[1], 10);
16
17
  const unit = match[2].toLowerCase();
17
18
  if (!TIME_UNITS[unit]) {
18
- throw new Error(`Invalid time unit: ${unit}`);
19
+ throw new ValidationError({ message: `Invalid time unit: ${unit}` });
19
20
  }
20
21
  totalSeconds += value * TIME_UNITS[unit];
21
22
  }
22
23
  if (totalSeconds === 0) {
23
- throw new Error(`Invalid expiry format: "${input}"`);
24
+ throw new ValidationError({ message: `Invalid expiry format: "${input}"` });
24
25
  }
25
26
  return totalSeconds;
26
27
  }
@@ -1,12 +1,13 @@
1
1
  import { sign } from 'jsonwebtoken';
2
2
  import { parseDuration } from './parseDuration';
3
+ import { ValidationError } from '@naman_deep_singh/errors-utils';
3
4
  function getExpiryTimestamp(seconds) {
4
5
  return Math.floor(Date.now() / 1000) + seconds;
5
6
  }
6
7
  export const signToken = (payload, secret, expiresIn = '1h', options = {}) => {
7
8
  const seconds = parseDuration(expiresIn);
8
9
  if (!seconds || seconds < 10) {
9
- throw new Error('Token expiry too small');
10
+ throw new ValidationError({ message: 'Token expiry too small' });
10
11
  }
11
12
  const tokenPayload = {
12
13
  ...payload,
@@ -1,13 +1,16 @@
1
- import type { JwtPayload } from 'node_modules/@types/jsonwebtoken';
1
+ import type { JwtPayload } from 'jsonwebtoken';
2
2
  export interface TokenRequirements {
3
3
  requiredFields?: string[];
4
4
  forbiddenFields?: string[];
5
5
  validateTypes?: Record<string, 'string' | 'number' | 'boolean'>;
6
6
  }
7
- export declare function validateTokenPayload(payload: Record<string, unknown>, rules?: TokenRequirements): {
8
- valid: true;
9
- } | {
10
- valid: false;
11
- error: string;
12
- };
7
+ /**
8
+ * Validates a JWT payload according to the provided rules.
9
+ * Throws ValidationError if validation fails.
10
+ */
11
+ export declare function validateTokenPayload(payload: Record<string, unknown>, rules?: TokenRequirements): void;
12
+ /**
13
+ * Checks if a JWT payload is expired.
14
+ * Returns true if expired or missing 'exp'.
15
+ */
13
16
  export declare function isTokenExpired(payload: JwtPayload): boolean;
@@ -1,31 +1,34 @@
1
- export function validateTokenPayload(payload, rules = {
2
- requiredFields: ['exp', 'iat'],
3
- }) {
4
- const { requiredFields = [], forbiddenFields = [], validateTypes = {}, } = rules;
1
+ import { ValidationError } from '@naman_deep_singh/errors-utils';
2
+ /**
3
+ * Validates a JWT payload according to the provided rules.
4
+ * Throws ValidationError if validation fails.
5
+ */
6
+ export function validateTokenPayload(payload, rules = { requiredFields: ['exp', 'iat'] }) {
7
+ const { requiredFields = [], forbiddenFields = [], validateTypes = {} } = rules;
5
8
  // 1. Required fields
6
9
  for (const field of requiredFields) {
7
10
  if (!(field in payload)) {
8
- return { valid: false, error: `Missing required field: ${field}` };
11
+ throw new ValidationError(`Missing required field: ${field}`);
9
12
  }
10
13
  }
11
14
  // 2. Forbidden fields
12
15
  for (const field of forbiddenFields) {
13
16
  if (field in payload) {
14
- return { valid: false, error: `Forbidden field in token: ${field}` };
17
+ throw new ValidationError(`Forbidden field in token: ${field}`);
15
18
  }
16
19
  }
17
20
  // 3. Type validation
18
21
  for (const key in validateTypes) {
19
22
  const expectedType = validateTypes[key];
20
23
  if (key in payload && typeof payload[key] !== expectedType) {
21
- return {
22
- valid: false,
23
- error: `Invalid type for ${key}. Expected ${expectedType}.`,
24
- };
24
+ throw new ValidationError(`Invalid type for ${key}. Expected ${expectedType}, got ${typeof payload[key]}`);
25
25
  }
26
26
  }
27
- return { valid: true };
28
27
  }
28
+ /**
29
+ * Checks if a JWT payload is expired.
30
+ * Returns true if expired or missing 'exp'.
31
+ */
29
32
  export function isTokenExpired(payload) {
30
33
  if (!payload.exp)
31
34
  return true;
@@ -1,19 +1,18 @@
1
- import type jwt from 'jsonwebtoken';
2
- import { type JwtPayload, type Secret } from 'jsonwebtoken';
3
- import type { VerificationResult } from './types';
1
+ import { type JwtPayload, type Secret, VerifyOptions } from 'jsonwebtoken';
2
+ import { VerificationResult } from './types';
4
3
  /**
5
- * Verify token (throws if invalid or expired)
4
+ * Verify token (throws UnauthorizedError if invalid or expired)
6
5
  */
7
6
  export declare const verifyToken: (token: string, secret: Secret) => string | JwtPayload;
8
7
  /**
9
- * Safe verify never throws, returns structured result
8
+ * Verify token with options
10
9
  */
11
- export declare const safeVerifyToken: (token: string, secret: Secret) => VerificationResult;
10
+ export declare const verifyTokenWithOptions: (token: string, secret: Secret, options?: VerifyOptions) => string | JwtPayload;
12
11
  /**
13
- * Verify token with validation options
12
+ * Safe verify — never throws, returns structured result with UnauthorizedError on failure
14
13
  */
15
- export declare const verifyTokenWithOptions: (token: string, secret: Secret, options?: jwt.VerifyOptions) => string | JwtPayload;
14
+ export declare const safeVerifyToken: (token: string, secret: Secret) => VerificationResult;
16
15
  /**
17
- * Safe verify with validation options
16
+ * Safe verify with options — never throws, returns structured result with UnauthorizedError on failure
18
17
  */
19
- export declare const safeVerifyTokenWithOptions: (token: string, secret: Secret, options?: jwt.VerifyOptions) => VerificationResult;
18
+ export declare const safeVerifyTokenWithOptions: (token: string, secret: Secret, options?: VerifyOptions) => VerificationResult;