Now that we have updated our existing tests, it's time to update our implementation to make the tests pass again. Let's start with updating the Create User JSON schema, replacing the password property with the digest property:
{
"properties": {
"email": { ... },
"digest": { "type": "string" },
"profile": { ... }
},
"required": ["email", "digest"],
}
However, it is not enough to simply validate the data type of the digest property; we need to check that the digest string is a legitimate bcrypt digest. Fortunately, all bcrypt digests have the same general structure:

Therefore, we can use the following regular expression to match valid digests:
^\$2[aby]?\$\d{1,2}\$[.\/A-Za-z0-9]{53}$
To explain this regular expression, let's break it down:
- \$2[aby]?\$: This matches the algorithm that's used. Valid values are 2, 2a, 2y, and 2b.
- \d{1,2}\$: This matches the cost, or the number of rounds, which is an integer between 4 and 31 (inclusive).
- [.\/A-Za-z0-9]{53}: This matches the salt and the hash, with the salt making up the first 22 characters and the hashed password making up the last 31.
So, let's update our digest sub-schema to include this pattern:
"digest": {
"type": "string",
"pattern": "^\\$2[aby]?\\$\\d{1,2}\\$[.\\/A-Za-z0-9]{53}$"
}
Now, if a client-provided password digest does not match against this pattern, the Create User validator would return a ValidationError object where the keyword property is set to "pattern". We can use this fact to return a custom message to inform the client that the provided digest is invalid.
Add the following lines to src/validators/errors/messages/index.js:
if (error.keyword === 'pattern') {
return `The '${pathPrefix}${error.dataPath}' field should be a valid bcrypt digest`;
}
Lastly, don't forget to write unit tests that cover this new logical branch:
it('should return the correct string when error.keyword is "pattern"', function () {
const errors = [{
keyword: 'pattern',
dataPath: '.test.path',
}];
const actualErrorMessage = generateValidationErrorMessage(errors);
const expectedErrorMessage = "The '.test.path' field should be a valid bcrypt digest";
assert.equal(actualErrorMessage, expectedErrorMessage);
});
Then, in our Retrieve User and Search User engines (defined in src/engines/users/), make sure we are excluding the digest field when querying for the User object, for example:
db.get({
index: process.env.ELASTICSEARCH_INDEX,
type: 'user',
id: req.params.userId,
_sourceExclude: 'digest',
})
Now, run the E2E tests again and confirm that they are passing. Once that's done, update the unit and integration tests so they'll pass as well. Lastly, commit the changes to a new branch called authentication/main, push that branch to GitHub, and check the results on the Travis and Jenkins CI servers.