@carecard/auth-util 3.23.0 → 3.25.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/.agents/skills/pkg-auth-util-auth-crypto-library/SKILL.md +27 -16
- package/.nvmrc +1 -0
- package/index.d.ts +12 -72
- package/index.js +2 -10
- package/lib/pwdUtilAuth.js +96 -53
- package/lib/stringUtilAuth.js +0 -20
- package/package.json +6 -3
- package/readme.md +24 -14
|
@@ -107,10 +107,10 @@ depend on those folders being present.
|
|
|
107
107
|
- `lib/cryptoUtilAuth.js` wraps Node.js `crypto` primitives for signing,
|
|
108
108
|
verifying, generating HMACs, and salts.
|
|
109
109
|
- `lib/keyGen.js` owns Ed25519 and RSA key generation.
|
|
110
|
-
- `lib/stringUtilAuth.js` owns legacy base64, base64-url-safe, JWT
|
|
111
|
-
|
|
112
|
-
- Keep direct exports
|
|
113
|
-
|
|
110
|
+
- `lib/stringUtilAuth.js` owns legacy base64, base64-url-safe, and JWT string
|
|
111
|
+
helpers. Password-hash parsing stays private to `lib/pwdUtilAuth.js`.
|
|
112
|
+
- Keep direct exports as the public package API. Do not add password aliases,
|
|
113
|
+
nested password objects, or compatibility parsers.
|
|
114
114
|
- Preserve CommonJS exports unless the repository intentionally migrates module
|
|
115
115
|
systems.
|
|
116
116
|
|
|
@@ -133,27 +133,38 @@ depend on those folders being present.
|
|
|
133
133
|
|
|
134
134
|
## Password And Crypto Layer
|
|
135
135
|
|
|
136
|
-
-
|
|
137
|
-
|
|
136
|
+
- Create password credentials with Node.js `crypto.argon2` using Argon2id,
|
|
137
|
+
version 19, 19,456 KiB memory, two passes, one degree of parallelism, a
|
|
138
|
+
unique 16-byte random salt, a 32-byte tag, and a pepper of at least 32 UTF-8
|
|
139
|
+
bytes.
|
|
140
|
+
- Persist only the strict unpadded PHC string:
|
|
138
141
|
|
|
139
142
|
```text
|
|
140
|
-
$1$base64(
|
|
143
|
+
$argon2id$v=19$m=19456,t=2,p=1$base64(salt)$base64(tag)
|
|
141
144
|
```
|
|
142
145
|
|
|
143
|
-
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
-
|
|
147
|
-
|
|
146
|
+
- `createPasswordHash(password, pepper)` and
|
|
147
|
+
`verifyPassword(password, savedHash, pepper)` are the only public password
|
|
148
|
+
credential APIs; both are asynchronous.
|
|
149
|
+
- Generate salts internally for every credential. Never accept caller-selected
|
|
150
|
+
salts and never persist or log the pepper.
|
|
151
|
+
- Normalize well-formed Unicode passwords to NFC before deriving the tag, but
|
|
152
|
+
do not trim or impose password policy here. `@carecard/validate` owns policy,
|
|
153
|
+
while login intentionally permits existing passwords of any length.
|
|
154
|
+
- Return `false` for malformed, non-canonical, differently parameterized, or
|
|
155
|
+
legacy saved hashes. Throw for programmer/configuration errors such as an
|
|
156
|
+
invalid pepper.
|
|
157
|
+
- Do not add legacy HMAC verification. Seeded accounts must be rehashed before
|
|
158
|
+
the Argon2id-only service starts.
|
|
159
|
+
- Prefer Node.js `crypto` primitives over new dependencies.
|
|
148
160
|
|
|
149
161
|
## String Utilities
|
|
150
162
|
|
|
151
163
|
- Use `stringUtilAuth` for base64 and URL-safe string transformations to keep
|
|
152
164
|
parsing and serialization consistent.
|
|
153
|
-
-
|
|
154
|
-
- Cover edge cases for malformed JWT strings,
|
|
155
|
-
|
|
156
|
-
change.
|
|
165
|
+
- Do not expose password-hash parsing through `stringUtilAuth`.
|
|
166
|
+
- Cover edge cases for malformed JWT strings, bad base64 input, missing
|
|
167
|
+
segments, and unsupported algorithms when these paths change.
|
|
157
168
|
|
|
158
169
|
## Security And Error Handling
|
|
159
170
|
|
package/.nvmrc
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
24.20.0
|
package/index.d.ts
CHANGED
|
@@ -112,21 +112,6 @@ export interface AuthenticatedRequest extends Request {
|
|
|
112
112
|
visitor?: VisitorRequestObject | null;
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
-
/**
|
|
116
|
-
* Represents the decomposed parts of a stored password hash.
|
|
117
|
-
* @deprecated Use native Buffer methods or other modern alternatives.
|
|
118
|
-
*/
|
|
119
|
-
export interface PasswordParts {
|
|
120
|
-
/** Version indicator of the hashing format. */
|
|
121
|
-
version: string;
|
|
122
|
-
/** Base64 encoded algorithm name. */
|
|
123
|
-
alg: string;
|
|
124
|
-
/** Base64 encoded password hash. */
|
|
125
|
-
hash: string;
|
|
126
|
-
/** Base64 encoded random salt. */
|
|
127
|
-
salt: string;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
115
|
/**
|
|
131
116
|
* Contains a pair of public and private cryptographic keys.
|
|
132
117
|
*/
|
|
@@ -171,38 +156,6 @@ export const jwtUtilAuth: {
|
|
|
171
156
|
getHeaderPayloadFromJwt: (jwt: string) => JwtParts | null;
|
|
172
157
|
};
|
|
173
158
|
|
|
174
|
-
/**
|
|
175
|
-
* Utility functions for password hashing and verification.
|
|
176
|
-
* @deprecated use direct imports of the new functions.
|
|
177
|
-
*/
|
|
178
|
-
export const pwdUtilAuth: {
|
|
179
|
-
/**
|
|
180
|
-
* Generates a password hash using a random salt and specified algorithm.
|
|
181
|
-
* @param password - The plain-text password to hash.
|
|
182
|
-
* @param secret - A pepper/secret key to combine with the password.
|
|
183
|
-
* @param algorithm - The hashing algorithm to use (e.g., 'sha256').
|
|
184
|
-
* @returns A string containing the formatted hash with metadata ($1$alg$hash$salt$) or null if an error occurs.
|
|
185
|
-
*/
|
|
186
|
-
createPasswordHashWithRandomSalt: (
|
|
187
|
-
password: string,
|
|
188
|
-
secret: string,
|
|
189
|
-
algorithm: string,
|
|
190
|
-
) => string | null;
|
|
191
|
-
/**
|
|
192
|
-
* Generates a password hash using the same algorithm and salt from a previously saved hash.
|
|
193
|
-
* Useful for verifying a password against a stored hash.
|
|
194
|
-
* @param password - The plain-text password to verify.
|
|
195
|
-
* @param savedPasswordHash - The full stored hash string (including salt and metadata).
|
|
196
|
-
* @param secret - The pepper/secret key used for hashing.
|
|
197
|
-
* @returns A hash string that should match the saved hash if the password is correct, or null if an error occurs.
|
|
198
|
-
*/
|
|
199
|
-
createPasswordHashBasedOnSavedAlgorithmSalt: (
|
|
200
|
-
password: string,
|
|
201
|
-
savedPasswordHash: string,
|
|
202
|
-
secret: string,
|
|
203
|
-
) => string | null;
|
|
204
|
-
};
|
|
205
|
-
|
|
206
159
|
/**
|
|
207
160
|
* Utility functions for string manipulation, base64 encoding, and parsing auth-related strings.
|
|
208
161
|
* @deprecated Use native Buffer methods or other modern alternatives.
|
|
@@ -236,13 +189,6 @@ export const stringUtilAuth: {
|
|
|
236
189
|
* @returns Decoded plain-text string.
|
|
237
190
|
*/
|
|
238
191
|
base64ToAscii: (codedString: string) => string;
|
|
239
|
-
/**
|
|
240
|
-
* Parses a stored password hash string into its constituent parts.
|
|
241
|
-
* @deprecated Use native Buffer methods or other modern alternatives.
|
|
242
|
-
* @param passwordHash - The formatted hash string ($1$alg$hash$salt$).
|
|
243
|
-
* @returns A PasswordParts object or null if the format is invalid.
|
|
244
|
-
*/
|
|
245
|
-
dollarSignConnectedStringToAlgorithmHashSalt: (passwordHash: string) => PasswordParts | null;
|
|
246
192
|
/**
|
|
247
193
|
* Splits a JWT into its three base64-encoded string parts (header, payload, signature).
|
|
248
194
|
* @deprecated Use native Buffer methods or other modern alternatives.
|
|
@@ -322,28 +268,22 @@ export function jwtVerifySignedToken(jwt: string, publicKey: string): boolean;
|
|
|
322
268
|
export function jwtGetHeaderPayload(jwt: string): JwtParts | null;
|
|
323
269
|
|
|
324
270
|
/**
|
|
325
|
-
*
|
|
271
|
+
* Creates an Argon2id PHC password hash with a unique random salt.
|
|
326
272
|
* @param password - The plain-text password to hash.
|
|
327
|
-
* @param
|
|
328
|
-
* @
|
|
329
|
-
* @returns A string containing the formatted hash with metadata ($1$alg$hash$salt$) or null if an error occurs.
|
|
273
|
+
* @param pepper - A secret pepper containing at least 32 UTF-8 bytes.
|
|
274
|
+
* @returns The strict Argon2id PHC string.
|
|
330
275
|
*/
|
|
331
|
-
export function
|
|
332
|
-
password: string,
|
|
333
|
-
secret: string,
|
|
334
|
-
algorithm: string,
|
|
335
|
-
): string | null;
|
|
276
|
+
export function createPasswordHash(password: string, pepper: string): Promise<string>;
|
|
336
277
|
|
|
337
278
|
/**
|
|
338
|
-
*
|
|
339
|
-
* Useful for verifying a password against a stored hash.
|
|
279
|
+
* Verifies a password against a strict CareCard Argon2id PHC hash.
|
|
340
280
|
* @param password - The plain-text password to verify.
|
|
341
|
-
* @param
|
|
342
|
-
* @param
|
|
343
|
-
* @returns
|
|
281
|
+
* @param savedHash - The stored Argon2id PHC hash.
|
|
282
|
+
* @param pepper - The same secret pepper used when creating the hash.
|
|
283
|
+
* @returns False for a mismatch or malformed/unsupported saved hash.
|
|
344
284
|
*/
|
|
345
|
-
export function
|
|
285
|
+
export function verifyPassword(
|
|
346
286
|
password: string,
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
):
|
|
287
|
+
savedHash: string,
|
|
288
|
+
pepper: string,
|
|
289
|
+
): Promise<boolean>;
|
package/index.js
CHANGED
|
@@ -10,8 +10,8 @@ module.exports = {
|
|
|
10
10
|
jwtCreateServiceAuthorizationHeader: jwtUtilAuth.createServiceAuthorizationHeader,
|
|
11
11
|
jwtVerifySignedToken: jwtUtilAuth.verifyJwtSignature,
|
|
12
12
|
jwtGetHeaderPayload: jwtUtilAuth.getHeaderPayloadFromJwt,
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
createPasswordHash: pwdUtilAuth.createPasswordHash,
|
|
14
|
+
verifyPassword: pwdUtilAuth.verifyPassword,
|
|
15
15
|
|
|
16
16
|
// Deprecated
|
|
17
17
|
/**
|
|
@@ -22,14 +22,6 @@ module.exports = {
|
|
|
22
22
|
verifyJwtSignature: jwtUtilAuth.verifyJwtSignature,
|
|
23
23
|
getHeaderPayloadFromJwt: jwtUtilAuth.getHeaderPayloadFromJwt,
|
|
24
24
|
},
|
|
25
|
-
/**
|
|
26
|
-
* @deprecated use direct import of the new functions.
|
|
27
|
-
*/
|
|
28
|
-
pwdUtilAuth: {
|
|
29
|
-
createPasswordHashWithRandomSalt: pwdUtilAuth.createPasswordHashWithRandomSalt,
|
|
30
|
-
createPasswordHashBasedOnSavedAlgorithmSalt:
|
|
31
|
-
pwdUtilAuth.createPasswordHashBasedOnSavedAlgorithmSalt,
|
|
32
|
-
},
|
|
33
25
|
/**
|
|
34
26
|
* @deprecated Use native Buffer methods or other modern alternatives.
|
|
35
27
|
*/
|
package/lib/pwdUtilAuth.js
CHANGED
|
@@ -1,70 +1,113 @@
|
|
|
1
|
-
|
|
1
|
+
'use strict';
|
|
2
2
|
|
|
3
|
-
const
|
|
3
|
+
const { argon2, randomBytes, timingSafeEqual } = require('node:crypto');
|
|
4
|
+
const { promisify } = require('node:util');
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
6
|
+
const deriveArgon2Tag = promisify(argon2);
|
|
7
|
+
const ARGON2_ALGORITHM = 'argon2id';
|
|
8
|
+
const ARGON2_VERSION = 19;
|
|
9
|
+
const ARGON2_MEMORY_KIB = 19456;
|
|
10
|
+
const ARGON2_PASSES = 2;
|
|
11
|
+
const ARGON2_PARALLELISM = 1;
|
|
12
|
+
const PASSWORD_SALT_BYTES = 16;
|
|
13
|
+
const PASSWORD_TAG_BYTES = 32;
|
|
14
|
+
const MINIMUM_PEPPER_BYTES = 32;
|
|
15
|
+
const PASSWORD_HASH_PATTERN =
|
|
16
|
+
/^\$argon2id\$v=19\$m=19456,t=2,p=1\$([A-Za-z0-9+/]{22})\$([A-Za-z0-9+/]{43})$/;
|
|
13
17
|
|
|
14
|
-
// Pattern:
|
|
15
|
-
function
|
|
16
|
-
|
|
18
|
+
// Pattern: Configuration Guard - converts only a sufficiently strong string pepper to bytes.
|
|
19
|
+
function createPepperBuffer(pepper) {
|
|
20
|
+
if (typeof pepper !== 'string') {
|
|
21
|
+
throw new TypeError('Password pepper must be a string.');
|
|
22
|
+
}
|
|
23
|
+
const pepperBuffer = Buffer.from(pepper, 'utf8');
|
|
24
|
+
if (pepperBuffer.length < MINIMUM_PEPPER_BYTES) {
|
|
25
|
+
throw new RangeError('Password pepper must be at least 32 bytes.');
|
|
26
|
+
}
|
|
27
|
+
return pepperBuffer;
|
|
17
28
|
}
|
|
18
29
|
|
|
19
|
-
// Pattern:
|
|
20
|
-
function
|
|
21
|
-
|
|
30
|
+
// Pattern: Canonicalization - converts well-formed NFC password text to stable UTF-8 bytes.
|
|
31
|
+
function createPasswordBuffer(password) {
|
|
32
|
+
if (typeof password !== 'string' || !password.isWellFormed()) {
|
|
33
|
+
throw new TypeError('Password must be a well-formed Unicode string.');
|
|
34
|
+
}
|
|
35
|
+
return Buffer.from(password.normalize('NFC'), 'utf8');
|
|
22
36
|
}
|
|
23
37
|
|
|
24
|
-
// Pattern:
|
|
25
|
-
function
|
|
26
|
-
|
|
27
|
-
const savedBuffer = Buffer.from(savedPasswordHash);
|
|
28
|
-
return (
|
|
29
|
-
candidateBuffer.length === savedBuffer.length &&
|
|
30
|
-
crypto.timingSafeEqual(candidateBuffer, savedBuffer)
|
|
31
|
-
);
|
|
38
|
+
// Pattern: Serializer Helper - emits canonical unpadded PHC Base64 text.
|
|
39
|
+
function encodePhcBase64(value) {
|
|
40
|
+
return value.toString('base64').replace(/=+$/, '');
|
|
32
41
|
}
|
|
33
42
|
|
|
34
|
-
// Pattern:
|
|
35
|
-
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
const hashBase64 = createSaltedPasswordDigest(password, secret, algorithm, salt);
|
|
39
|
-
return serializePasswordHash(algorithmBase64, hashBase64, salt);
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
// Pattern: Compatibility Verification - verifies salted hashes while retaining existing credentials.
|
|
43
|
-
const createPasswordHashBasedOnSavedAlgorithmSalt = (password, savedPasswordHash, secret) => {
|
|
44
|
-
if (typeof savedPasswordHash !== 'string') {
|
|
43
|
+
// Pattern: Parser Guard - accepts only canonical PHC Base64 with the required byte length.
|
|
44
|
+
function decodePhcBase64(value, expectedLength) {
|
|
45
|
+
const decodedValue = Buffer.from(value, 'base64');
|
|
46
|
+
if (decodedValue.length !== expectedLength || encodePhcBase64(decodedValue) !== value) {
|
|
45
47
|
return null;
|
|
46
48
|
}
|
|
49
|
+
return decodedValue;
|
|
50
|
+
}
|
|
47
51
|
|
|
48
|
-
|
|
49
|
-
|
|
52
|
+
// Pattern: Strict Parser - accepts only the exact CareCard Argon2id storage contract.
|
|
53
|
+
function parsePasswordHash(savedHash) {
|
|
54
|
+
if (typeof savedHash !== 'string') {
|
|
50
55
|
return null;
|
|
51
56
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
const algBase64 = splitStringArray[2];
|
|
55
|
-
const salt = splitStringArray[4];
|
|
56
|
-
if (version !== PASSWORD_HASH_VERSION || !algBase64 || !salt) {
|
|
57
|
+
const match = savedHash.match(PASSWORD_HASH_PATTERN);
|
|
58
|
+
if (!match) {
|
|
57
59
|
return null;
|
|
58
60
|
}
|
|
59
|
-
const
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
61
|
+
const salt = decodePhcBase64(match[1], PASSWORD_SALT_BYTES);
|
|
62
|
+
const tag = decodePhcBase64(match[2], PASSWORD_TAG_BYTES);
|
|
63
|
+
return salt && tag ? { salt, tag } : null;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Pattern: Parameter Object - centralizes the fixed Argon2id security profile.
|
|
67
|
+
function createArgon2Parameters(passwordBuffer, salt, pepperBuffer) {
|
|
68
|
+
return {
|
|
69
|
+
message: passwordBuffer,
|
|
70
|
+
nonce: salt,
|
|
71
|
+
parallelism: ARGON2_PARALLELISM,
|
|
72
|
+
tagLength: PASSWORD_TAG_BYTES,
|
|
73
|
+
memory: ARGON2_MEMORY_KIB,
|
|
74
|
+
passes: ARGON2_PASSES,
|
|
75
|
+
secret: pepperBuffer,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Pattern: Cryptographic Adapter - derives one tag through Node's asynchronous Argon2 boundary.
|
|
80
|
+
async function derivePasswordTag(password, salt, pepperBuffer) {
|
|
81
|
+
const parameters = createArgon2Parameters(createPasswordBuffer(password), salt, pepperBuffer);
|
|
82
|
+
return deriveArgon2Tag(ARGON2_ALGORITHM, parameters);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Pattern: Serializer - records the fixed parameters, salt, and tag in strict PHC form.
|
|
86
|
+
function serializePasswordHash(salt, tag) {
|
|
87
|
+
const parameters = `m=${ARGON2_MEMORY_KIB},t=${ARGON2_PASSES},p=${ARGON2_PARALLELISM}`;
|
|
88
|
+
return `$${ARGON2_ALGORITHM}$v=${ARGON2_VERSION}$${parameters}$${encodePhcBase64(salt)}$${encodePhcBase64(tag)}`;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Pattern: Credential Factory - creates an independently salted Argon2id password hash.
|
|
92
|
+
async function createPasswordHash(password, pepper) {
|
|
93
|
+
const pepperBuffer = createPepperBuffer(pepper);
|
|
94
|
+
const salt = randomBytes(PASSWORD_SALT_BYTES);
|
|
95
|
+
const tag = await derivePasswordTag(password, salt, pepperBuffer);
|
|
96
|
+
return serializePasswordHash(salt, tag);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Pattern: Constant-Time Verification - derives and compares a strict Argon2id credential tag.
|
|
100
|
+
async function verifyPassword(password, savedHash, pepper) {
|
|
101
|
+
const pepperBuffer = createPepperBuffer(pepper);
|
|
102
|
+
if (typeof password !== 'string' || !password.isWellFormed()) {
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
const parsedHash = parsePasswordHash(savedHash);
|
|
106
|
+
if (!parsedHash) {
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
const candidateTag = await derivePasswordTag(password, parsedHash.salt, pepperBuffer);
|
|
110
|
+
return timingSafeEqual(candidateTag, parsedHash.tag);
|
|
111
|
+
}
|
|
66
112
|
|
|
67
|
-
module.exports = {
|
|
68
|
-
createPasswordHashWithRandomSalt,
|
|
69
|
-
createPasswordHashBasedOnSavedAlgorithmSalt,
|
|
70
|
-
};
|
|
113
|
+
module.exports = { createPasswordHash, verifyPassword };
|
package/lib/stringUtilAuth.js
CHANGED
|
@@ -52,25 +52,6 @@ const base64ToAscii = codedString => {
|
|
|
52
52
|
return Buffer.from(codedString, 'base64').toString('utf8');
|
|
53
53
|
};
|
|
54
54
|
|
|
55
|
-
/**
|
|
56
|
-
* Decompose $ connected string and return an object
|
|
57
|
-
* return null if error
|
|
58
|
-
* @deprecated Use native Buffer methods or other modern alternatives.
|
|
59
|
-
* @param passwordHash
|
|
60
|
-
*/
|
|
61
|
-
const dollarSignConnectedStringToAlgorithmHashSalt = passwordHash => {
|
|
62
|
-
const splitStringArray = passwordHash.split('$');
|
|
63
|
-
if (splitStringArray.length !== 6) {
|
|
64
|
-
return null;
|
|
65
|
-
}
|
|
66
|
-
return {
|
|
67
|
-
version: splitStringArray[1],
|
|
68
|
-
alg: splitStringArray[2],
|
|
69
|
-
hash: splitStringArray[3],
|
|
70
|
-
salt: splitStringArray[4],
|
|
71
|
-
};
|
|
72
|
-
};
|
|
73
|
-
|
|
74
55
|
/**
|
|
75
56
|
* Decompose . connected string and return an object with
|
|
76
57
|
* {header: 'string', payload: 'string', signature: 'string'}
|
|
@@ -117,7 +98,6 @@ module.exports = {
|
|
|
117
98
|
reverseStringUrlSafe,
|
|
118
99
|
asciiToBase64,
|
|
119
100
|
base64ToAscii,
|
|
120
|
-
dollarSignConnectedStringToAlgorithmHashSalt,
|
|
121
101
|
dotConnectedStringToHeaderPayloadSignature,
|
|
122
102
|
objectToBase64UrlSafeString,
|
|
123
103
|
urlSafeBase64ToObject,
|
package/package.json
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@carecard/auth-util",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.25.0",
|
|
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
|
+
"engines": {
|
|
9
|
+
"node": ">=24.20.0 <25"
|
|
10
|
+
},
|
|
8
11
|
"scripts": {
|
|
9
12
|
"test": "node scripts/runPackageTask.mjs test",
|
|
10
13
|
"test:order": "node --test scripts/testOrder/randomizeTestOrder.test.mjs scripts/testParallel/runIndexedMochaTests.test.mjs scripts/packageTaskRunner.test.mjs scripts/canonicalTestCommand.test.mjs",
|
|
@@ -47,8 +50,8 @@
|
|
|
47
50
|
"typescript": "6.0.3"
|
|
48
51
|
},
|
|
49
52
|
"dependencies": {
|
|
50
|
-
"@carecard/common-util": "3.
|
|
51
|
-
"@carecard/validate": "3.
|
|
53
|
+
"@carecard/common-util": "3.25.0",
|
|
54
|
+
"@carecard/validate": "3.25.0",
|
|
52
55
|
"@types/express": "5.0.6"
|
|
53
56
|
},
|
|
54
57
|
"overrides": {
|
package/readme.md
CHANGED
|
@@ -26,7 +26,7 @@ Non-negotiable code organization rule: Functions with the same or equivalent beh
|
|
|
26
26
|
## Features
|
|
27
27
|
|
|
28
28
|
- **JWT Utilities**: Create, verify, and parse JSON Web Tokens with support for EdDSA (Ed25519) and RSA.
|
|
29
|
-
- **Password Utilities**:
|
|
29
|
+
- **Password Utilities**: Asynchronous Argon2id password hashing through Node.js `crypto`, with a unique random salt and application pepper.
|
|
30
30
|
- **Key Generation**: Generate Ed25519 and RSA key pairs for JWT signing.
|
|
31
31
|
- **Crypto Utilities**: Low-level cryptographic primitives for signing, verification, and hashing.
|
|
32
32
|
- **String Utilities**: Base64 and Base64UrlSafe encoding/decoding, and custom string parsing.
|
|
@@ -42,7 +42,11 @@ npm install @carecard/auth-util
|
|
|
42
42
|
### JWT Utilities (`jwtUtilAuth`)
|
|
43
43
|
|
|
44
44
|
```javascript
|
|
45
|
-
const {
|
|
45
|
+
const {
|
|
46
|
+
jwtCreateSignedToken,
|
|
47
|
+
jwtGetHeaderPayload,
|
|
48
|
+
jwtVerifySignedToken,
|
|
49
|
+
} = require('@carecard/auth-util');
|
|
46
50
|
|
|
47
51
|
const header = { alg: 'EdDSA', typ: 'JWT' };
|
|
48
52
|
const payload = { sub: '1234567890', name: 'John Doe' };
|
|
@@ -62,7 +66,10 @@ const { header: decodedHeader, payload: decodedPayload } = jwtGetHeaderPayload(t
|
|
|
62
66
|
### Service-To-Service JWT Creation
|
|
63
67
|
|
|
64
68
|
```javascript
|
|
65
|
-
const {
|
|
69
|
+
const {
|
|
70
|
+
jwtCreateServiceAuthorizationHeader,
|
|
71
|
+
jwtCreateServiceToken,
|
|
72
|
+
} = require('@carecard/auth-util');
|
|
66
73
|
|
|
67
74
|
const token = jwtCreateServiceToken({
|
|
68
75
|
issuer: 'ms-institutions',
|
|
@@ -77,23 +84,26 @@ const authorization = jwtCreateServiceAuthorizationHeader({
|
|
|
77
84
|
});
|
|
78
85
|
```
|
|
79
86
|
|
|
80
|
-
### Password Utilities
|
|
87
|
+
### Password Utilities
|
|
81
88
|
|
|
82
89
|
```javascript
|
|
83
|
-
const {
|
|
90
|
+
const { createPasswordHash, verifyPassword } = require('@carecard/auth-util');
|
|
84
91
|
|
|
85
|
-
const password = '
|
|
86
|
-
const
|
|
87
|
-
const algorithm = 'sha512';
|
|
92
|
+
const password = 'correct horse battery staple';
|
|
93
|
+
const pepper = process.env.MS_AUTH_HASH_KEY; // At least 32 UTF-8 bytes.
|
|
88
94
|
|
|
89
|
-
// Create a
|
|
90
|
-
const
|
|
91
|
-
// Resulting format: $1$base64(algorithm)$base64(hash)$base64(salt)$
|
|
95
|
+
// Create a strict Argon2id PHC credential with a unique random salt.
|
|
96
|
+
const savedHash = await createPasswordHash(password, pepper);
|
|
92
97
|
|
|
93
|
-
// Verify
|
|
94
|
-
const isCorrect =
|
|
98
|
+
// Verify through the package boundary. Malformed and legacy credentials return false.
|
|
99
|
+
const isCorrect = await verifyPassword(password, savedHash, pepper);
|
|
95
100
|
```
|
|
96
101
|
|
|
102
|
+
Both functions preserve password spaces and normalize well-formed Unicode to
|
|
103
|
+
NFC. Password-policy checks belong to `@carecard/validate`; this package hashes
|
|
104
|
+
and compares the supplied value without enforcing password length or
|
|
105
|
+
composition. Keep the pepper outside persisted credentials and logs.
|
|
106
|
+
|
|
97
107
|
### Key Generation
|
|
98
108
|
|
|
99
109
|
```javascript
|
|
@@ -150,7 +160,7 @@ npm run test:types
|
|
|
150
160
|
The package is organized into several modules:
|
|
151
161
|
|
|
152
162
|
- `jwtUtilAuth`: Manages the JWT lifecycle.
|
|
153
|
-
- `pwdUtilAuth`:
|
|
163
|
+
- `pwdUtilAuth`: Implements asynchronous Argon2id password hashing and verification behind the direct package exports.
|
|
154
164
|
- `keyGen`: Utility for generating cryptographic keys.
|
|
155
165
|
- `cryptoUtilAuth`: Core cryptographic operations using Node.js `crypto` module.
|
|
156
166
|
- `stringUtilAuth`: String manipulation and format conversions.
|