Timing
An answer can read the same and still tell the caller which case it was, by how long it took. A password is stored as a bcrypt hash, and at Laravel's default cost of 12 rounds one hash takes in the order of a quarter of a second. Whichever answer pays for a hash that the other one does not is that much slower, and a single request shows it.
Registration
Creating an account hashes its password, and finding a taken address does not. Without the package a taken address is answered in a fraction of the time a free one takes.
The package has both outcomes pay the same hash. On Fortify's route your CreatesNewUsers action
hashes the password of a new account, and for a taken address the package hashes the same password
with the same hasher. The registrar hashes the
password once, before the address is looked up, and hands the hash to the code that creates the
account.
Both outcomes also send one mail: the verification mail of a new account, or the notice to the holder of a taken address. Send them the same way. Laravel sends the verification mail at once, and so does the package's notice; an application that queues the one queues the other, as shown in Registration.
The minimum duration
The password routes work the other way round. For an address with an account the broker creates a token, which costs a hash, and sends the link; for an address without one it does neither. Laravel holds these requests, and a failed sign-in, to a minimum duration called a timebox, so that the short case waits until the long one would be done. Its default is 200 milliseconds, which is less than the hash alone, so the case with an account still takes longer.
The package raises that minimum to one second by setting auth.timebox_duration, unless your
application sets that key itself. A failed sign-in, every reset-link request and a failed reset
then take one second, with an account or without. A successful sign-in and a successful reset still
return at once.
| Setting | Default |
|---|---|
timebox.microseconds | 1000000, one second |
FORTIFY_HARDENING_TIMEBOX_MICROSECONDS | the same, from the environment |
Set it to 0 to leave Laravel's own minimum in place. A value of your own in
auth.timebox_duration always wins over the package's.
The minimum only hides work that finishes inside it. Queue the reset-link notification, so that sending the mail is not part of the request, and keep the minimum above the time your slowest server needs for one hash.