@carecard/auth-util 3.0.10 → 3.1.12

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/index.d.ts CHANGED
@@ -1,174 +1,279 @@
1
+ /**
2
+ * Utility functions for authentication and authorization in the CareCard ecosystem.
3
+ */
4
+
5
+ import {Request} from 'express';
6
+
1
7
  /**
2
8
  * Represents the standard JWT header structure.
3
9
  */
4
10
  export interface JwtHeader {
5
- /** The cryptographic algorithm used to secure the JWT. */
6
- alg?: string;
7
- /** The media type of the JWT. Defaults to 'JWT'. */
8
- typ?: string;
9
- /** Any other custom header fields. */
10
- [key: string]: any;
11
+ /** The cryptographic algorithm used to secure the JWT. */
12
+ alg?: string;
13
+ /** The media type of the JWT. Defaults to 'JWT'. */
14
+ typ?: string;
15
+
16
+ /** Any other custom header fields. */
17
+ [key: string]: any;
11
18
  }
12
19
 
13
20
  /**
14
21
  * Represents the standard JWT payload (claims) structure.
15
22
  */
16
23
  export interface JwtPayload {
17
- /** Issued at time, in seconds since the epoch. */
18
- iat?: number;
19
- /** Expiration time, in seconds since the epoch. */
20
- exp?: number;
21
- /** Not before time, in seconds since the epoch. */
22
- nbf?: number;
23
- /** Authentication time, in seconds since the epoch. */
24
- auth_time?: number;
25
- /** Any other custom payload fields. */
26
- [key: string]: any;
24
+ /** Issued at time, in seconds since the epoch. */
25
+ iat?: number;
26
+ /** Expiration time, in seconds since the epoch. */
27
+ exp?: number;
28
+ /** Not before time, in seconds since the epoch. */
29
+ nbf?: number;
30
+ /** Authentication time, in seconds since the epoch. */
31
+ auth_time?: number;
32
+ /** Subject (usually the client ID). */
33
+ sub?: string;
34
+ /** Roles assigned to the user. */
35
+ roles?: string[];
36
+
37
+ /** Any other custom payload fields. */
38
+ [key: string]: any;
27
39
  }
28
40
 
29
41
  /**
30
42
  * Container for the decoded header and payload of a JWT.
31
43
  */
32
44
  export interface JwtParts {
33
- /** Decoded JWT header. */
34
- header: JwtHeader;
35
- /** Decoded JWT payload. */
36
- payload: JwtPayload;
45
+ /** Decoded JWT header. */
46
+ header: JwtHeader;
47
+ /** Decoded JWT payload. */
48
+ payload: JwtPayload;
49
+ }
50
+
51
+ /**
52
+ * Structure of the JWT object attached to the request.
53
+ */
54
+ export interface JwtRequestObject {
55
+ header: JwtHeader;
56
+ payload: JwtPayload;
57
+ age?: number;
58
+ jwtClientId: (req?: any) => string | undefined;
59
+ doesJwtUserHasRole: (role: string) => boolean;
60
+ isJwtExpired: (jwtValiditySeconds?: number) => boolean;
61
+ jwtAgeInSeconds: (req?: any) => number;
62
+ }
63
+
64
+ /**
65
+ * Structure of the visitor object attached to the request.
66
+ */
67
+ export interface VisitorRequestObject {
68
+ header: JwtHeader;
69
+ payload: JwtPayload;
70
+ visitorClientId: (req?: any) => string | undefined;
71
+ }
72
+
73
+ /**
74
+ * Extended Express Request to include jwt and visitor objects.
75
+ */
76
+ export interface AuthenticatedRequest extends Request {
77
+ jwt?: JwtRequestObject | null;
78
+ visitor?: VisitorRequestObject | null;
37
79
  }
38
80
 
39
81
  /**
40
82
  * Represents the decomposed parts of a stored password hash.
83
+ * @deprecated Use native Buffer methods or other modern alternatives.
41
84
  */
42
85
  export interface PasswordParts {
43
- /** Version indicator of the hashing format. */
44
- version: string;
45
- /** Base64 encoded algorithm name. */
46
- alg: string;
47
- /** Base64 encoded password hash. */
48
- hash: string;
49
- /** Base64 encoded random salt. */
50
- salt: string;
86
+ /** Version indicator of the hashing format. */
87
+ version: string;
88
+ /** Base64 encoded algorithm name. */
89
+ alg: string;
90
+ /** Base64 encoded password hash. */
91
+ hash: string;
92
+ /** Base64 encoded random salt. */
93
+ salt: string;
51
94
  }
52
95
 
53
96
  /**
54
97
  * Contains a pair of public and private cryptographic keys.
55
98
  */
56
99
  export interface KeyPair {
57
- /** PEM formatted public key string. */
58
- publicKey: string;
59
- /** PEM formatted private key string. */
60
- privateKey: string;
100
+ /** PEM formatted public key string. */
101
+ publicKey: string;
102
+ /** PEM formatted private key string. */
103
+ privateKey: string;
61
104
  }
62
105
 
63
106
  /**
64
107
  * Utility functions for creating, verifying, and parsing JSON Web Tokens (JWT).
108
+ * @deprecated use direct import of the new functions.
65
109
  */
66
110
  export const jwtUtilAuth: {
67
- /**
68
- * Creates a signed JWT from header and payload objects.
69
- * Automatically normalizes header (sets alg/typ) and payload (sets iat/exp/etc. in seconds).
70
- * @param headerObject - Header data for the JWT.
71
- * @param payloadObject - Payload data for the JWT.
72
- * @param privateKey - PEM formatted private key to sign the token.
73
- * @returns Signed JWT string or null if an error occurs.
74
- */
75
- createSignedJwtFromObject: (headerObject: JwtHeader, payloadObject: JwtPayload, privateKey: string) => string | null;
76
- /**
77
- * Verifies the signature of a JWT using a public key.
78
- * @param jwt - The JWT string to verify.
79
- * @param publicKey - PEM formatted public key.
80
- * @returns True if the signature is valid, false otherwise.
81
- */
82
- verifyJwtSignature: (jwt: string, publicKey: string) => boolean;
83
- /**
84
- * Decodes a JWT and returns its header and payload as objects.
85
- * Note: This does NOT verify the signature.
86
- * @param jwt - The JWT string to parse.
87
- * @returns An object containing the header and payload, or null if parsing fails.
88
- */
89
- getHeaderPayloadFromJwt: (jwt: string) => JwtParts | null;
111
+ /**
112
+ * Creates a signed JWT from header and payload objects.
113
+ * Automatically normalizes header (sets alg/typ) and payload (sets iat/exp/etc. in seconds).
114
+ * @param headerObject - Header data for the JWT.
115
+ * @param payloadObject - Payload data for the JWT.
116
+ * @param privateKey - PEM formatted private key to sign the token.
117
+ * @returns Signed JWT string or null if an error occurs.
118
+ */
119
+ createSignedJwtFromObject: (headerObject: JwtHeader, payloadObject: JwtPayload, privateKey: string) => string | null;
120
+ /**
121
+ * Verifies the signature of a JWT using a public key.
122
+ * @param jwt - The JWT string to verify.
123
+ * @param publicKey - PEM formatted public key.
124
+ * @returns True if the signature is valid, false otherwise.
125
+ */
126
+ verifyJwtSignature: (jwt: string, publicKey: string) => boolean;
127
+ /**
128
+ * Decodes a JWT and returns its header and payload as objects.
129
+ * Note: This does NOT verify the signature.
130
+ * @param jwt - The JWT string to parse.
131
+ * @returns An object containing the header and payload, or null if parsing fails.
132
+ */
133
+ getHeaderPayloadFromJwt: (jwt: string) => JwtParts | null;
90
134
  };
91
135
 
92
136
  /**
93
137
  * Utility functions for password hashing and verification.
138
+ * @deprecated use direct imports of the new functions.
94
139
  */
95
140
  export const pwdUtilAuth: {
96
- /**
97
- * Generates a password hash using a random salt and specified algorithm.
98
- * @param password - The plain-text password to hash.
99
- * @param secret - A pepper/secret key to combine with the password.
100
- * @param algorithm - The hashing algorithm to use (e.g., 'sha256').
101
- * @returns A string containing the formatted hash with metadata ($1$alg$hash$salt$).
102
- */
103
- createPasswordHashWithRandomSalt: (password: string, secret: string, algorithm: string) => string;
104
- /**
105
- * Generates a password hash using the same algorithm and salt from a previously saved hash.
106
- * Useful for verifying a password against a stored hash.
107
- * @param password - The plain-text password to verify.
108
- * @param savedPasswordHash - The full stored hash string (including salt and metadata).
109
- * @param secret - The pepper/secret key used for hashing.
110
- * @returns A hash string that should match the saved hash if the password is correct.
111
- */
112
- createPasswordHashBasedOnSavedAlgorithmSalt: (password: string, savedPasswordHash: string, secret: string) => string;
141
+ /**
142
+ * Generates a password hash using a random salt and specified algorithm.
143
+ * @param password - The plain-text password to hash.
144
+ * @param secret - A pepper/secret key to combine with the password.
145
+ * @param algorithm - The hashing algorithm to use (e.g., 'sha256').
146
+ * @returns A string containing the formatted hash with metadata ($1$alg$hash$salt$) or null if an error occurs.
147
+ */
148
+ createPasswordHashWithRandomSalt: (password: string, secret: string, algorithm: string) => string | null;
149
+ /**
150
+ * Generates a password hash using the same algorithm and salt from a previously saved hash.
151
+ * Useful for verifying a password against a stored hash.
152
+ * @param password - The plain-text password to verify.
153
+ * @param savedPasswordHash - The full stored hash string (including salt and metadata).
154
+ * @param secret - The pepper/secret key used for hashing.
155
+ * @returns A hash string that should match the saved hash if the password is correct, or null if an error occurs.
156
+ */
157
+ createPasswordHashBasedOnSavedAlgorithmSalt: (password: string, savedPasswordHash: string, secret: string) => string | null;
113
158
  };
114
159
 
160
+
161
+ /**
162
+ * Utility functions for string manipulation, base64 encoding, and parsing auth-related strings.
163
+ * @deprecated Use native Buffer methods or other modern alternatives.
164
+ */
165
+ export const stringUtilAuth: {
166
+ /**
167
+ * Converts a base64 string to be URL-safe (replaces + with -, / with _, and removes =).
168
+ * @deprecated Use native Buffer methods or other modern alternatives.
169
+ * @param urlUnsafeString - The string to convert.
170
+ * @returns URL-safe string.
171
+ */
172
+ makeStringUrlSafe: (urlUnsafeString?: string) => string;
173
+ /**
174
+ * Reverses URL-safe conversion and restores standard base64 characters and padding.
175
+ * @deprecated Use native Buffer methods or other modern alternatives.
176
+ * @param urlSafeString - The URL-safe string to restore.
177
+ * @returns Standard base64 string.
178
+ */
179
+ reverseStringUrlSafe: (urlSafeString?: string) => string;
180
+ /**
181
+ * Encodes a plain-text string to base64.
182
+ * @deprecated Use native Buffer methods or other modern alternatives.
183
+ * @param unCodedString - Plain-text string.
184
+ * @returns Base64 encoded string.
185
+ */
186
+ asciiToBase64: (unCodedString: string) => string;
187
+ /**
188
+ * Decodes a base64 string to UTF-8 plain-text.
189
+ * @deprecated Use native Buffer methods or other modern alternatives.
190
+ * @param codedString - Base64 encoded string.
191
+ * @returns Decoded plain-text string.
192
+ */
193
+ base64ToAscii: (codedString: string) => string;
194
+ /**
195
+ * Parses a stored password hash string into its constituent parts.
196
+ * @deprecated Use native Buffer methods or other modern alternatives.
197
+ * @param passwordHash - The formatted hash string ($1$alg$hash$salt$).
198
+ * @returns A PasswordParts object or null if the format is invalid.
199
+ */
200
+ dollarSignConnectedStringToAlgorithmHashSalt: (passwordHash: string) => PasswordParts | null;
201
+ /**
202
+ * Splits a JWT into its three base64-encoded string parts (header, payload, signature).
203
+ * @deprecated Use native Buffer methods or other modern alternatives.
204
+ * @param jwt - The full JWT string.
205
+ * @returns An object with the three raw parts, or null if the format is invalid.
206
+ */
207
+ dotConnectedStringToHeaderPayloadSignature: (jwt: string) => {
208
+ header: string,
209
+ payload: string,
210
+ signature: string
211
+ } | null;
212
+ /**
213
+ * Serializes an object into a URL-safe base64 string.
214
+ * @deprecated Use native Buffer methods or other modern alternatives.
215
+ * @param object - The object to serialize.
216
+ * @returns URL-safe base64 string.
217
+ */
218
+ objectToBase64UrlSafeString: (object: any) => string;
219
+ /**
220
+ * Parses a URL-safe base64 string into an object.
221
+ * @deprecated Use native Buffer methods or other modern alternatives.
222
+ * @param urlSafeBase64String - URL-safe base64 string.
223
+ * @returns The parsed object.
224
+ */
225
+ urlSafeBase64ToObject: (urlSafeBase64String: string) => any;
226
+ };
227
+
228
+
115
229
  /**
116
230
  * Generates a public/private key pair for JWT signing.
117
231
  * @param algorithm - The algorithm to use ('ed25519' or 'rsa'). Defaults to 'ed25519'.
118
232
  * @returns A KeyPair object containing PEM formatted keys.
119
233
  */
120
- export const createKeys: (algorithm?: 'ed25519' | 'rsa' | string) => KeyPair;
234
+ export function generateKeyPair(algorithm?: 'ed25519' | 'rsa' | string): KeyPair;
121
235
 
122
236
  /**
123
- * Utility functions for string manipulation, base64 encoding, and parsing auth-related strings.
237
+ * Creates a signed JWT from header and payload objects.
238
+ * Automatically normalizes header (sets alg/typ) and payload (sets iat/exp/etc. in seconds).
239
+ * @param headerObject - Header data for the JWT.
240
+ * @param payloadObject - Payload data for the JWT.
241
+ * @param privateKey - PEM formatted private key to sign the token.
242
+ * @returns Signed JWT string or null if an error occurs.
124
243
  */
125
- export const stringUtilAuth: {
126
- /**
127
- * Converts a base64 string to be URL-safe (replaces + with -, / with _, and removes =).
128
- * @param urlUnsafeString - The string to convert.
129
- * @returns URL-safe string.
130
- */
131
- makeStringUrlSafe: (urlUnsafeString?: string) => string;
132
- /**
133
- * Reverses URL-safe conversion and restores standard base64 characters and padding.
134
- * @param urlSafeString - The URL-safe string to restore.
135
- * @returns Standard base64 string.
136
- */
137
- reverseStringUrlSafe: (urlSafeString?: string) => string;
138
- /**
139
- * Encodes a plain-text string to base64.
140
- * @param unCodedString - Plain-text string.
141
- * @returns Base64 encoded string.
142
- */
143
- asciiToBase64: (unCodedString: string) => string;
144
- /**
145
- * Decodes a base64 string to UTF-8 plain-text.
146
- * @param codedString - Base64 encoded string.
147
- * @returns Decoded plain-text string.
148
- */
149
- base64ToAscii: (codedString: string) => string;
150
- /**
151
- * Parses a stored password hash string into its constituent parts.
152
- * @param passwordHash - The formatted hash string ($1$alg$hash$salt$).
153
- * @returns A PasswordParts object or null if the format is invalid.
154
- */
155
- dollarSignConnectedStringToAlgorithmHashSalt: (passwordHash: string) => PasswordParts | null;
156
- /**
157
- * Splits a JWT into its three base64-encoded string parts (header, payload, signature).
158
- * @param jwt - The full JWT string.
159
- * @returns An object with the three raw parts, or null if the format is invalid.
160
- */
161
- dotConnectedStringToHeaderPayloadSignature: (jwt: string) => { header: string, payload: string, signature: string } | null;
162
- /**
163
- * Serializes an object into a URL-safe base64 string.
164
- * @param object - The object to serialize.
165
- * @returns URL-safe base64 string.
166
- */
167
- objectToBase64UrlSafeString: (object: any) => string;
168
- /**
169
- * Parses a URL-safe base64 string into an object.
170
- * @param urlSafeBase64String - URL-safe base64 string.
171
- * @returns The parsed object.
172
- */
173
- urlSafeBase64ToObject: (urlSafeBase64String: string) => any;
174
- };
244
+ export function jwtCreateSignedToken(headerObject: JwtHeader, payloadObject: JwtPayload, privateKey: string): string | null;
245
+
246
+ /**
247
+ * Verifies the signature of a JWT using a public key.
248
+ * @param jwt - The JWT string to verify.
249
+ * @param publicKey - PEM formatted public key.
250
+ * @returns True if the signature is valid, false otherwise.
251
+ */
252
+ export function jwtVerifySignedToken(jwt: string, publicKey: string): boolean;
253
+
254
+ /**
255
+ * Decodes a JWT and returns its header and payload as objects.
256
+ * Note: This does NOT verify the signature.
257
+ * @param jwt - The JWT string to parse.
258
+ * @returns An object containing the header and payload, or null if parsing fails.
259
+ */
260
+ export function jwtGetHeaderPayload(jwt: string): JwtParts | null;
261
+
262
+ /**
263
+ * Generates a password hash using a random salt and specified algorithm.
264
+ * @param password - The plain-text password to hash.
265
+ * @param secret - A pepper/secret key to combine with the password.
266
+ * @param algorithm - The hashing algorithm to use (e.g., 'sha256').
267
+ * @returns A string containing the formatted hash with metadata ($1$alg$hash$salt$) or null if an error occurs.
268
+ */
269
+ export function passwordCreateHashWithRandomSalt(password: string, secret: string, algorithm: string): string | null;
270
+
271
+ /**
272
+ * Generates a password hash using the same algorithm and salt from a previously saved hash.
273
+ * Useful for verifying a password against a stored hash.
274
+ * @param password - The plain-text password to verify.
275
+ * @param savedPasswordHash - The full stored hash string (including salt and metadata).
276
+ * @param secret - The pepper/secret key used for hashing.
277
+ * @returns A hash string that should match the saved hash if the password is correct, or null if an error occurs.
278
+ */
279
+ export function passwordCreateHashFromSavedHash(password: string, savedPasswordHash: string, secret: string): string | null;
package/index.js CHANGED
@@ -1,21 +1,35 @@
1
- const pwdUtilAuth = require( './lib/pwdUtilAuth' );
2
- const jwtUtilAuth = require( './lib/jwtUtilAuth' );
3
- const keyGen = require( './lib/keyGen' );
1
+ const pwdUtilAuth = require('./lib/pwdUtilAuth');
2
+ const jwtUtilAuth = require('./lib/jwtUtilAuth');
3
+ const keyGen = require('./lib/keyGen');
4
4
 
5
5
  module.exports = {
6
+ // New
7
+ generateKeyPair: keyGen.generateKeyPair,
8
+ jwtCreateSignedToken: jwtUtilAuth.createSignedJwtFromObject,
9
+ jwtVerifySignedToken: jwtUtilAuth.verifyJwtSignature,
10
+ jwtGetHeaderPayload: jwtUtilAuth.getHeaderPayloadFromJwt,
11
+ passwordCreateHashWithRandomSalt: pwdUtilAuth.createPasswordHashWithRandomSalt,
12
+ passwordCreateHashFromSavedHash: pwdUtilAuth.createPasswordHashBasedOnSavedAlgorithmSalt,
13
+
14
+ // Deprecated
15
+ /**
16
+ * @deprecated use direct import of the new functions.
17
+ */
6
18
  jwtUtilAuth: {
7
19
  createSignedJwtFromObject: jwtUtilAuth.createSignedJwtFromObject,
8
20
  verifyJwtSignature: jwtUtilAuth.verifyJwtSignature,
9
21
  getHeaderPayloadFromJwt: jwtUtilAuth.getHeaderPayloadFromJwt
10
22
  },
11
-
23
+ /**
24
+ * @deprecated use direct import of the new functions.
25
+ */
12
26
  pwdUtilAuth: {
13
27
  createPasswordHashWithRandomSalt: pwdUtilAuth.createPasswordHashWithRandomSalt,
14
28
  createPasswordHashBasedOnSavedAlgorithmSalt: pwdUtilAuth.createPasswordHashBasedOnSavedAlgorithmSalt
15
29
  },
16
-
17
- createKeys: keyGen.generateKeyPair,
18
-
19
- stringUtilAuth: require( './lib/stringUtilAuth' ),
30
+ /**
31
+ * @deprecated Use native Buffer methods or other modern alternatives.
32
+ */
33
+ stringUtilAuth: require('./lib/stringUtilAuth'),
20
34
  };
21
35
 
@@ -1,35 +1,6 @@
1
- const stringUtilAuth = require( './stringUtilAuth' );
2
- const cryptoUtilAuth = require( './cryptoUtilAuth' );
1
+ const crypto = require( 'crypto' );
3
2
 
4
- /**
5
- * User supplied header, payload and signature create jwt.
6
- * @returns {string|null}
7
- * @param headerBase64
8
- * @param payloadBase64
9
- * @param signatureBase64
10
- */
11
- const _assembleJwt = ( headerBase64, payloadBase64, signatureBase64 ) => {
12
- return headerBase64 + "." + payloadBase64 + "." + signatureBase64;
13
- };
14
-
15
- /**
16
- * Normalizes jwt header
17
- * @param headerObject
18
- * @returns {{alg: string, typ: string}}
19
- */
20
- const normalizeHeader = ( headerObject ) => {
21
- const header = { ...headerObject };
22
- header.alg = header.alg || 'EdDSA';
23
- header.typ = 'JWT';
24
- return header;
25
- };
26
-
27
- /**
28
- * Normalizes jwt payload time-related fields
29
- * @param payloadObject
30
- * @returns {*}
31
- */
32
- const normalizePayload = ( payloadObject ) => {
3
+ const _normalizePayload = ( payloadObject ) => {
33
4
  const payload = { ...payloadObject };
34
5
  const now = Math.floor( Date.now() / 1000 );
35
6
  const fieldsToNormalize = [ 'iat', 'exp', 'nbf', 'auth_time' ];
@@ -46,22 +17,39 @@ const normalizePayload = ( payloadObject ) => {
46
17
  } );
47
18
 
48
19
  if ( !payload.exp ) {
49
- // Default 1 hour expiration if not provided
50
20
  payload.exp = payload.iat + 3600;
51
- } else if ( payload.exp > msThreshold ) {
52
- payload.exp = Math.floor( payload.exp / 1000 );
53
21
  }
54
-
55
22
  return payload;
56
23
  };
57
24
 
58
- /**
59
- * User supplied header, payload and signature create jwt.
60
- * @returns {{payload: *, signature: *, header: *}}
61
- * @param jwt
62
- */
63
- const _splitJwtInToHeaderPayloadSignature = ( jwt ) => {
64
- return stringUtilAuth.dotConnectedStringToHeaderPayloadSignature( jwt );
25
+ const _encode = ( obj ) => {
26
+ return Buffer.from( JSON.stringify( obj ) ).toString( 'base64url' );
27
+ };
28
+
29
+ const _decode = ( str ) => {
30
+ return JSON.parse( Buffer.from( str, 'base64url' ).toString( 'utf8' ) );
31
+ };
32
+
33
+ const _sign = ( token, alg, privateKey ) => {
34
+ if ( alg === 'EdDSA' || alg === 'Ed25519' ) {
35
+ return crypto.sign( null, Buffer.from( token ), privateKey ).toString( 'base64url' );
36
+ }
37
+
38
+ const sign = crypto.createSign( alg );
39
+ sign.write( token );
40
+ sign.end();
41
+ return sign.sign( privateKey, 'base64url' );
42
+ };
43
+
44
+ const _verify = ( token, signature, alg, publicKey ) => {
45
+ if ( alg === 'EdDSA' || alg === 'Ed25519' ) {
46
+ return crypto.verify( null, Buffer.from( token ), publicKey, Buffer.from( signature, 'base64url' ) );
47
+ }
48
+
49
+ const verify = crypto.createVerify( alg );
50
+ verify.update( token );
51
+ verify.end();
52
+ return verify.verify( publicKey, signature, 'base64url' );
65
53
  };
66
54
 
67
55
  /**
@@ -75,17 +63,19 @@ const createSignedJwtFromObject = ( headerObject, payloadObject, privateKey ) =>
75
63
  try {
76
64
  if ( !privateKey ) return null;
77
65
 
78
- const header = normalizeHeader( headerObject );
79
- const payload = normalizePayload( payloadObject );
66
+ const header = { ...headerObject };
67
+ header.alg = header.alg || 'EdDSA';
68
+ header.typ = 'JWT';
80
69
 
81
- const headerBase64UrlSafe = stringUtilAuth.objectToBase64UrlSafeString( header );
82
- const payloadBase64UrlSafe = stringUtilAuth.objectToBase64UrlSafeString( payload );
70
+ const payload = _normalizePayload( payloadObject );
71
+
72
+ const headerBase64UrlSafe = _encode( header );
73
+ const payloadBase64UrlSafe = _encode( payload );
83
74
  const token = headerBase64UrlSafe + "." + payloadBase64UrlSafe;
84
75
 
85
- const signature = cryptoUtilAuth.createBase64SignatureOfToken( token, privateKey, header.alg );
86
- const urlSafeSignature = stringUtilAuth.makeStringUrlSafe( signature );
76
+ const signature = _sign( token, header.alg, privateKey );
87
77
 
88
- return _assembleJwt( headerBase64UrlSafe, payloadBase64UrlSafe, urlSafeSignature );
78
+ return token + "." + signature;
89
79
  } catch ( error ) {
90
80
  return null;
91
81
  }
@@ -100,13 +90,14 @@ const createSignedJwtFromObject = ( headerObject, payloadObject, privateKey ) =>
100
90
  const verifyJwtSignature = ( jwt, publicKey ) => {
101
91
  try {
102
92
  if ( !jwt || !publicKey ) return false;
103
- const parts = _splitJwtInToHeaderPayloadSignature( jwt );
104
- if ( !parts ) return false;
105
- const { header, signature } = parts;
106
- const token = header + "." + parts.payload;
107
- const headerObject = stringUtilAuth.urlSafeBase64ToObject( header );
108
- const signatureBase64 = stringUtilAuth.reverseStringUrlSafe( signature );
109
- return cryptoUtilAuth.verifyBase64SignatureOfToken( token, signatureBase64, publicKey, headerObject.alg )
93
+ const splitJWT = jwt.split( '.' );
94
+ if ( splitJWT.length !== 3 ) return false;
95
+
96
+ const [ header, payload, signature ] = splitJWT;
97
+ const token = header + "." + payload;
98
+ const headerObject = _decode( header );
99
+
100
+ return _verify( token, signature, headerObject.alg, publicKey );
110
101
  } catch ( error ) {
111
102
  return false;
112
103
  }
@@ -115,28 +106,30 @@ const verifyJwtSignature = ( jwt, publicKey ) => {
115
106
  /**
116
107
  * Returns header and payload object for jwt.
117
108
  * @param jwt
118
- * @return {{payload: any, header: any}}
109
+ * @return {{payload: *, header: *}|null}
119
110
  */
120
111
  const getHeaderPayloadFromJwt = jwt => {
121
112
  try {
122
- const parts = _splitJwtInToHeaderPayloadSignature( jwt );
123
- if ( !parts ) return null;
124
- const { header, payload } = parts;
125
- let headerObject = stringUtilAuth.urlSafeBase64ToObject( header );
126
- let payloadObject = stringUtilAuth.urlSafeBase64ToObject( payload );
113
+ if ( typeof jwt !== 'string' ) return null;
114
+ const splitJWT = jwt.split( '.' );
115
+ if ( splitJWT.length !== 3 ) return null;
116
+
117
+ const headerObject = _decode( splitJWT[ 0 ] );
118
+ const payloadObject = _decode( splitJWT[ 1 ] );
119
+
127
120
  return { header: headerObject, payload: payloadObject }
128
121
  } catch ( e ) {
129
122
  return null;
130
123
  }
131
124
  };
132
125
 
133
-
134
126
  module.exports = {
135
- _assembleJwt,
136
- _splitJwtInToHeaderPayloadSignature,
137
- normalizeHeader,
138
- normalizePayload,
139
127
  createSignedJwtFromObject,
140
128
  verifyJwtSignature,
141
- getHeaderPayloadFromJwt
129
+ getHeaderPayloadFromJwt,
130
+ _normalizePayload,
131
+ _encode,
132
+ _decode,
133
+ _sign,
134
+ _verify
142
135
  };
@@ -1,40 +1,4 @@
1
- const cryptoUtilAuth = require( './cryptoUtilAuth' );
2
- const stringUtilAuth = require( './stringUtilAuth' );
3
-
4
- /**
5
- * Just assemble password together
6
- * @param algorithmBase64
7
- * @param hashBase64
8
- * @param saltBase64
9
- * @return {string}
10
- */
11
- const _assemblePasswordHash = ( algorithmBase64, hashBase64, saltBase64 ) => {
12
- return "$1$" + algorithmBase64 + "$" + hashBase64 + "$" + saltBase64 + "$";
13
- };
14
-
15
- /**
16
- * Break password into its parts does not reverse base64 encoding.
17
- * @param passwordHashStored
18
- * @return {{salt: *, version: *, alg: *, hash: *}}
19
- */
20
- const _disassemblePasswordHash = passwordHashStored => {
21
- return stringUtilAuth.dollarSignConnectedStringToAlgorithmHashSalt( passwordHashStored );
22
- };
23
-
24
-
25
- /**
26
- * Creates password hash ready to be saved in database.
27
- * @param password
28
- * @param secret
29
- * @param salt
30
- * @param algorithm
31
- * @return {string}
32
- */
33
- const _createPasswordHash = ( password, secret, salt, algorithm ) => {
34
- const algorithmBase64 = stringUtilAuth.asciiToBase64( algorithm );
35
- const hashBase64 = cryptoUtilAuth.createHmacBase64( password, secret, algorithm );
36
- return _assemblePasswordHash( algorithmBase64, hashBase64, salt );
37
- };
1
+ const crypto = require( 'crypto' );
38
2
 
39
3
  /**
40
4
  * Automatically adds random salt.
@@ -44,8 +8,14 @@ const _createPasswordHash = ( password, secret, salt, algorithm ) => {
44
8
  * @return {string}
45
9
  */
46
10
  const createPasswordHashWithRandomSalt = ( password, secret, algorithm ) => {
47
- const salt = cryptoUtilAuth.createSaltBase64();
48
- return _createPasswordHash( password, secret, salt, algorithm );
11
+ try {
12
+ const salt = crypto.randomBytes( 32 ).toString( 'base64' );
13
+ const algorithmBase64 = Buffer.from( algorithm ).toString( 'base64' );
14
+ const hashBase64 = crypto.createHmac( algorithm, secret ).update( password ).digest( 'base64' );
15
+ return "$1$" + algorithmBase64 + "$" + hashBase64 + "$" + salt + "$";
16
+ } catch ( e ) {
17
+ return null;
18
+ }
49
19
  };
50
20
 
51
21
  /**
@@ -56,16 +26,22 @@ const createPasswordHashWithRandomSalt = ( password, secret, algorithm ) => {
56
26
  * @return {string}
57
27
  */
58
28
  const createPasswordHashBasedOnSavedAlgorithmSalt = ( password, savedPasswordHash, secret ) => {
59
- const { version, alg, hash, salt } = _disassemblePasswordHash( savedPasswordHash );
60
- const algorithm = stringUtilAuth.base64ToAscii( alg );
61
- return _createPasswordHash( password, secret, salt, algorithm );
29
+ try {
30
+ const splitStringArray = savedPasswordHash.split( '$' );
31
+ if ( splitStringArray.length !== 6 ) return null;
32
+
33
+ const algBase64 = splitStringArray[ 2 ];
34
+ const salt = splitStringArray[ 4 ];
35
+ const algorithm = Buffer.from( algBase64, 'base64' ).toString( 'utf8' );
36
+
37
+ const hashBase64 = crypto.createHmac( algorithm, secret ).update( password ).digest( 'base64' );
38
+ return "$1$" + algBase64 + "$" + hashBase64 + "$" + salt + "$";
39
+ } catch ( e ) {
40
+ return null;
41
+ }
62
42
  };
63
43
 
64
-
65
44
  module.exports = {
66
- _assemblePasswordHash,
67
- _disassemblePasswordHash,
68
- _createPasswordHash,
69
45
  createPasswordHashWithRandomSalt,
70
46
  createPasswordHashBasedOnSavedAlgorithmSalt
71
47
  }
@@ -6,6 +6,7 @@
6
6
 
7
7
  /**
8
8
  * Adjusts padding of base64String
9
+ * @deprecated Use native Buffer methods or other modern alternatives.
9
10
  * @param base64String
10
11
  * @return {*}
11
12
  */
@@ -15,6 +16,7 @@ const adjustBase64Padding = base64String => {
15
16
 
16
17
  /**
17
18
  * Removes /, + and = from the string
19
+ * @deprecated Use native Buffer methods or other modern alternatives.
18
20
  * @returns {string}
19
21
  */
20
22
  const makeStringUrlSafe = ( urlUnsafeString = '' ) => {
@@ -26,6 +28,7 @@ const makeStringUrlSafe = ( urlUnsafeString = '' ) => {
26
28
 
27
29
  /**
28
30
  * Put back /, + and = into the string
31
+ * @deprecated Use native Buffer methods or other modern alternatives.
29
32
  * @returns {string}
30
33
  */
31
34
  const reverseStringUrlSafe = ( urlSafeString = '' ) => {
@@ -37,6 +40,7 @@ const reverseStringUrlSafe = ( urlSafeString = '' ) => {
37
40
 
38
41
  /**
39
42
  * Encode string to base64 string
43
+ * @deprecated Use native Buffer methods or other modern alternatives.
40
44
  * @param unCodedString
41
45
  * @returns {string}
42
46
  */
@@ -45,6 +49,7 @@ const asciiToBase64 = ( unCodedString ) => {
45
49
  }
46
50
 
47
51
  /** Decode string from base64
52
+ * @deprecated Use native Buffer methods or other modern alternatives.
48
53
  * @param codedString
49
54
  * @returns {string}
50
55
  */
@@ -55,6 +60,7 @@ const base64ToAscii = ( codedString ) => {
55
60
  /**
56
61
  * Decompose $ connected string and return an object
57
62
  * return null if error
63
+ * @deprecated Use native Buffer methods or other modern alternatives.
58
64
  * @param passwordHash
59
65
  */
60
66
  const dollarSignConnectedStringToAlgorithmHashSalt = ( passwordHash ) => {
@@ -72,6 +78,7 @@ const dollarSignConnectedStringToAlgorithmHashSalt = ( passwordHash ) => {
72
78
  * Decompose . connected string and return an object with
73
79
  * {header: 'string', payload: 'string', signature: 'string'}
74
80
  * return null if error
81
+ * @deprecated Use native Buffer methods or other modern alternatives.
75
82
  */
76
83
  const dotConnectedStringToHeaderPayloadSignature = ( jwt ) => {
77
84
  if ( typeof jwt !== 'string' ) return null;
@@ -87,6 +94,7 @@ const dotConnectedStringToHeaderPayloadSignature = ( jwt ) => {
87
94
 
88
95
  /**
89
96
  * Turns object into url safe string
97
+ * @deprecated Use native Buffer methods or other modern alternatives.
90
98
  * @param object
91
99
  * @return {string}
92
100
  */
@@ -96,6 +104,7 @@ const objectToBase64UrlSafeString = object => {
96
104
 
97
105
  /**
98
106
  * Turns base64 into object
107
+ * @deprecated Use native Buffer methods or other modern alternatives.
99
108
  * @param urlSafeBase64String
100
109
  * @return {any}
101
110
  */
package/package.json CHANGED
@@ -1,26 +1,36 @@
1
1
  {
2
2
  "name": "@carecard/auth-util",
3
- "version": "3.0.10",
3
+ "version": "3.1.12",
4
4
  "repository": "https://github.com/CareCard-ca/pkg-auth-util.git",
5
5
  "description": "Auth utility functions",
6
6
  "main": "index.js",
7
7
  "types": "index.d.ts",
8
8
  "scripts": {
9
9
  "test": "export NODE_ENV=test && mocha --recursive",
10
- "test:types": "tsc --noEmit && mocha -r ts-node/register test/types.test.ts"
10
+ "test:types": "tsc --noEmit && mocha -r ts-node/register test/types.test.ts",
11
+ "test:coverage": "tsc --noEmit && export NODE_ENV=test && nyc mocha --recursive -r ts-node/register 'test/**/*.{js,ts}'",
12
+ "prepare": "husky"
11
13
  },
12
14
  "keywords": [
13
15
  "auth",
14
16
  "utility",
15
- "cryptology"
17
+ "cryptology",
18
+ "jwt"
16
19
  ],
17
20
  "author": "CareCard team",
18
21
  "license": "ISC",
19
22
  "devDependencies": {
23
+ "@types/express": "5.0.6",
20
24
  "@types/mocha": "10.0.10",
21
25
  "@types/node": "25.5.0",
26
+ "husky": "^9.1.7",
22
27
  "mocha": "11.7.5",
28
+ "nyc": "^18.0.0",
23
29
  "ts-node": "10.9.2",
24
30
  "typescript": "5.9.3"
31
+ },
32
+ "dependencies": {
33
+ "@carecard/common-util": "3.1.11",
34
+ "@carecard/validate": "3.0.11"
25
35
  }
26
36
  }
package/readme.md CHANGED
@@ -1,51 +1,108 @@
1
- # Auth utilities class
1
+ # @carecard/auth-util
2
2
 
3
- ## Main Functionality
3
+ ![Tests Passing](https://github.com/CareCard-ca/pkg-auth-util/actions/workflows/ci.yml/badge.svg)
4
+ ![Coverage](https://img.shields.io/badge/Coverage-97%25-green)
4
5
 
5
- This is a collection of utility functions for use in authentication
6
+ Utility package for authentication and authorization in the CareCard ecosystem.
6
7
 
7
- ### Main functions
8
+ ## Features
8
9
 
9
- ```js
10
- const { jwtUtilAuth, pwdUtilAuth } = require( '@chatpta/auth-util' );
10
+ - **JWT Utilities**: Create, verify, and parse JSON Web Tokens with support for EdDSA (Ed25519) and RSA.
11
+ - **Password Utilities**: Secure password hashing using HMAC with random salt and a custom string format for easy storage.
12
+ - **Key Generation**: Generate Ed25519 and RSA key pairs for JWT signing.
13
+ - **Crypto Utilities**: Low-level cryptographic primitives for signing, verification, and hashing.
14
+ - **String Utilities**: Base64 and Base64UrlSafe encoding/decoding, and custom string parsing.
15
+
16
+ ## Installation
17
+
18
+ ```bash
19
+ npm install @carecard/auth-util
20
+ ```
21
+
22
+ ## Usage
23
+
24
+ ### JWT Utilities (`jwtUtilAuth`)
25
+
26
+ ```javascript
27
+ const { jwtUtilAuth } = require('@carecard/auth-util');
28
+
29
+ const header = { alg: 'EdDSA', typ: 'JWT' };
30
+ const payload = { sub: '1234567890', name: 'John Doe' };
31
+ const privateKey = '...'; // Your private PEM key
32
+
33
+ // Create a signed JWT
34
+ const token = jwtUtilAuth.createSignedJwtFromObject(header, payload, privateKey);
35
+
36
+ // Verify a JWT signature
37
+ const publicKey = '...'; // Your public PEM key
38
+ const isValid = jwtUtilAuth.verifyJwtSignature(token, publicKey);
39
+
40
+ // Get header and payload from a JWT
41
+ const { header: decodedHeader, payload: decodedPayload } = jwtUtilAuth.getHeaderPayloadFromJwt(token);
11
42
  ```
12
43
 
13
- Create jwt
44
+ ### Password Utilities (`pwdUtilAuth`)
45
+
46
+ ```javascript
47
+ const { pwdUtilAuth } = require('@carecard/auth-util');
14
48
 
15
- ```js
16
- const headerObject = {
17
- alg: "SHA256", // Mendatory acceptable algorithm
18
- ...
19
- };
49
+ const password = 'mySecretPassword';
50
+ const secret = 'application-wide-secret';
51
+ const algorithm = 'sha512';
20
52
 
21
- const payloadObject = {
22
- ...
23
- };
53
+ // Create a new password hash with a random salt
54
+ const hash = pwdUtilAuth.createPasswordHashWithRandomSalt(password, secret, algorithm);
55
+ // Resulting format: $1$base64(algorithm)$base64(hash)$base64(salt)$
24
56
 
25
- const jwt = jwtUtilAuth.createSignedJwtFromObject( headerObject, payloadObject, privateKey );
57
+ // Verify a password against a saved hash
58
+ const isCorrect = (pwdUtilAuth.createPasswordHashBasedOnSavedAlgorithmSalt(password, hash, secret) === hash);
26
59
  ```
27
60
 
28
- Verify jwt signature returns ```true``` or ```false```
61
+ ### Key Generation
62
+
63
+ ```javascript
64
+ const { generateKeyPair } = require('@carecard/auth-util');
29
65
 
30
- ```js
31
- const isVerified = jwtUtilAuth.verifyJwtSignature( jwt, publicKey );
66
+ // Generate Ed25519 keys (default)
67
+ const { publicKey, privateKey } = generateKeyPair();
68
+
69
+ // Generate RSA keys
70
+ const rsaKeys = generateKeyPair('rsa');
32
71
  ```
33
72
 
34
- Get header and payload object from jwt.
73
+ ### String Utilities (`stringUtilAuth`)
74
+
75
+ ```javascript
76
+ const { stringUtilAuth } = require('@carecard/auth-util');
35
77
 
36
- ```js
37
- const { header, payload } = jwtUtilAuth.getHeaderPayloadFromJwt( jwt );
78
+ const base64 = stringUtilAuth.asciiToBase64('Hello World');
79
+ const original = stringUtilAuth.base64ToAscii(base64);
80
+
81
+ const urlSafe = stringUtilAuth.makeStringUrlSafe('a+b/c==');
82
+ // Result: a-b_c
38
83
  ```
39
84
 
40
- Create password hash to save in database
85
+ ## Testing
86
+
87
+ Run tests using:
41
88
 
42
- ```js
43
- const hash = pwdUtilAuth.createPasswordHashWithRandomSalt( password, secret, algorithm );
89
+ ```bash
90
+ npm test
44
91
  ```
45
92
 
46
- Create another password hash based on saved hash to compare.
93
+ To run type tests:
47
94
 
48
- ```js
49
- const hashForLogin = pwdUtilAuth.createPasswordHashBasedOnSavedAlgorithmSalt( passwordForLogin, savedPasswordHash, secret );
95
+ ```bash
96
+ npm run test:types
50
97
  ```
51
98
 
99
+ ## Architecture
100
+
101
+ The package is organized into several modules:
102
+ - `jwtUtilAuth`: Manages the JWT lifecycle.
103
+ - `pwdUtilAuth`: Handles password hashing and verification.
104
+ - `keyGen`: Utility for generating cryptographic keys.
105
+ - `cryptoUtilAuth`: Core cryptographic operations using Node.js `crypto` module.
106
+ - `stringUtilAuth`: String manipulation and format conversions.
107
+
108
+ All modules are exported through the main `index.js`.
@@ -1,71 +0,0 @@
1
- const crypto = require( "crypto" );
2
-
3
-
4
- /**
5
- * Signs a token returns signature string
6
- * @param token
7
- * @param privateKey
8
- * @param signingAlgorithm
9
- * @returns {string}
10
- */
11
- const createBase64SignatureOfToken = function ( token = '', privateKey, signingAlgorithm = 'EdDSA' ) {
12
- if ( signingAlgorithm === 'EdDSA' || signingAlgorithm === 'Ed25519' ) {
13
- return crypto.sign( null, Buffer.from( token ), privateKey ).toString( 'base64' );
14
- }
15
-
16
- const sign = crypto.createSign( signingAlgorithm );
17
- sign.write( token );
18
- sign.end();
19
- return sign.sign( privateKey, 'base64' );
20
- };
21
-
22
- /**
23
- * Verifies the signature returns true or false
24
- * @param token
25
- * @param signature
26
- * @param publicKey
27
- * @param signingAlgorithm
28
- * @returns {boolean}
29
- */
30
- const verifyBase64SignatureOfToken = function ( token = '', signature, publicKey, signingAlgorithm = 'EdDSA' ) {
31
- try {
32
- if ( signingAlgorithm === 'EdDSA' || signingAlgorithm === 'Ed25519' ) {
33
- return crypto.verify( null, Buffer.from( token ), publicKey, Buffer.from( signature, 'base64' ) );
34
- }
35
-
36
- const verify = crypto.createVerify( signingAlgorithm );
37
- verify.update( token );
38
- verify.end();
39
- return verify.verify( publicKey, signature, 'base64' );
40
- } catch ( e ) {
41
- return false;
42
- }
43
- };
44
-
45
- /**
46
- * Creates the hash of given string
47
- * @param string
48
- * @param secret
49
- * @param algorithm
50
- * @returns {string}
51
- */
52
- const createHmacBase64 = function ( string = '', secret, algorithm ) {
53
- const hmac = crypto.createHmac( algorithm, secret );
54
- hmac.update( string );
55
- return hmac.digest( 'base64' );
56
- };
57
-
58
- /**
59
- * Create random salt
60
- * @returns {string}
61
- */
62
- const createSaltBase64 = ( size = 32 ) => {
63
- return crypto.randomBytes( size ).toString( 'base64' );
64
- };
65
-
66
- module.exports = {
67
- createBase64SignatureOfToken,
68
- verifyBase64SignatureOfToken,
69
- createHmacBase64,
70
- createSaltBase64
71
- };