in src/auth/user-import-builder.ts [550:726]
private populateOptions(
options: UserImportOptions | undefined, requiresHashOptions: boolean): UploadAccountOptions {
let populatedOptions: UploadAccountOptions;
if (!requiresHashOptions) {
return {};
}
if (!validator.isNonNullObject(options)) {
throw new FirebaseAuthError(
AuthClientErrorCode.INVALID_ARGUMENT,
'"UserImportOptions" are required when importing users with passwords.',
);
}
if (!validator.isNonNullObject(options.hash)) {
throw new FirebaseAuthError(
AuthClientErrorCode.MISSING_HASH_ALGORITHM,
'"hash.algorithm" is missing from the provided "UserImportOptions".',
);
}
if (typeof options.hash.algorithm === 'undefined' ||
!validator.isNonEmptyString(options.hash.algorithm)) {
throw new FirebaseAuthError(
AuthClientErrorCode.INVALID_HASH_ALGORITHM,
'"hash.algorithm" must be a string matching the list of supported algorithms.',
);
}
let rounds: number;
switch (options.hash.algorithm) {
case 'HMAC_SHA512':
case 'HMAC_SHA256':
case 'HMAC_SHA1':
case 'HMAC_MD5':
if (!validator.isBuffer(options.hash.key)) {
throw new FirebaseAuthError(
AuthClientErrorCode.INVALID_HASH_KEY,
'A non-empty "hash.key" byte buffer must be provided for ' +
`hash algorithm ${options.hash.algorithm}.`,
);
}
populatedOptions = {
hashAlgorithm: options.hash.algorithm,
signerKey: utils.toWebSafeBase64(options.hash.key),
};
break;
case 'MD5':
case 'SHA1':
case 'SHA256':
case 'SHA512': {
// MD5 is [0,8192] but SHA1, SHA256, and SHA512 are [1,8192]
rounds = getNumberField(options.hash, 'rounds');
const minRounds = options.hash.algorithm === 'MD5' ? 0 : 1;
if (isNaN(rounds) || rounds < minRounds || rounds > 8192) {
throw new FirebaseAuthError(
AuthClientErrorCode.INVALID_HASH_ROUNDS,
`A valid "hash.rounds" number between ${minRounds} and 8192 must be provided for ` +
`hash algorithm ${options.hash.algorithm}.`,
);
}
populatedOptions = {
hashAlgorithm: options.hash.algorithm,
rounds,
};
break;
}
case 'PBKDF_SHA1':
case 'PBKDF2_SHA256':
rounds = getNumberField(options.hash, 'rounds');
if (isNaN(rounds) || rounds < 0 || rounds > 120000) {
throw new FirebaseAuthError(
AuthClientErrorCode.INVALID_HASH_ROUNDS,
'A valid "hash.rounds" number between 0 and 120000 must be provided for ' +
`hash algorithm ${options.hash.algorithm}.`,
);
}
populatedOptions = {
hashAlgorithm: options.hash.algorithm,
rounds,
};
break;
case 'SCRYPT': {
if (!validator.isBuffer(options.hash.key)) {
throw new FirebaseAuthError(
AuthClientErrorCode.INVALID_HASH_KEY,
'A "hash.key" byte buffer must be provided for ' +
`hash algorithm ${options.hash.algorithm}.`,
);
}
rounds = getNumberField(options.hash, 'rounds');
if (isNaN(rounds) || rounds <= 0 || rounds > 8) {
throw new FirebaseAuthError(
AuthClientErrorCode.INVALID_HASH_ROUNDS,
'A valid "hash.rounds" number between 1 and 8 must be provided for ' +
`hash algorithm ${options.hash.algorithm}.`,
);
}
const memoryCost = getNumberField(options.hash, 'memoryCost');
if (isNaN(memoryCost) || memoryCost <= 0 || memoryCost > 14) {
throw new FirebaseAuthError(
AuthClientErrorCode.INVALID_HASH_MEMORY_COST,
'A valid "hash.memoryCost" number between 1 and 14 must be provided for ' +
`hash algorithm ${options.hash.algorithm}.`,
);
}
if (typeof options.hash.saltSeparator !== 'undefined' &&
!validator.isBuffer(options.hash.saltSeparator)) {
throw new FirebaseAuthError(
AuthClientErrorCode.INVALID_HASH_SALT_SEPARATOR,
'"hash.saltSeparator" must be a byte buffer.',
);
}
populatedOptions = {
hashAlgorithm: options.hash.algorithm,
signerKey: utils.toWebSafeBase64(options.hash.key),
rounds,
memoryCost,
saltSeparator: utils.toWebSafeBase64(options.hash.saltSeparator || Buffer.from('')),
};
break;
}
case 'BCRYPT':
populatedOptions = {
hashAlgorithm: options.hash.algorithm,
};
break;
case 'STANDARD_SCRYPT': {
const cpuMemCost = getNumberField(options.hash, 'memoryCost');
if (isNaN(cpuMemCost)) {
throw new FirebaseAuthError(
AuthClientErrorCode.INVALID_HASH_MEMORY_COST,
'A valid "hash.memoryCost" number must be provided for ' +
`hash algorithm ${options.hash.algorithm}.`,
);
}
const parallelization = getNumberField(options.hash, 'parallelization');
if (isNaN(parallelization)) {
throw new FirebaseAuthError(
AuthClientErrorCode.INVALID_HASH_PARALLELIZATION,
'A valid "hash.parallelization" number must be provided for ' +
`hash algorithm ${options.hash.algorithm}.`,
);
}
const blockSize = getNumberField(options.hash, 'blockSize');
if (isNaN(blockSize)) {
throw new FirebaseAuthError(
AuthClientErrorCode.INVALID_HASH_BLOCK_SIZE,
'A valid "hash.blockSize" number must be provided for ' +
`hash algorithm ${options.hash.algorithm}.`,
);
}
const dkLen = getNumberField(options.hash, 'derivedKeyLength');
if (isNaN(dkLen)) {
throw new FirebaseAuthError(
AuthClientErrorCode.INVALID_HASH_DERIVED_KEY_LENGTH,
'A valid "hash.derivedKeyLength" number must be provided for ' +
`hash algorithm ${options.hash.algorithm}.`,
);
}
populatedOptions = {
hashAlgorithm: options.hash.algorithm,
cpuMemCost,
parallelization,
blockSize,
dkLen,
};
break;
}
default:
throw new FirebaseAuthError(
AuthClientErrorCode.INVALID_HASH_ALGORITHM,
`Unsupported hash algorithm provider "${options.hash.algorithm}".`,
);
}
return populatedOptions;
}