- add hotp_secret to user model
- view to create backup codes in user prefs
- check backup code if otp doesn't work
- increment hotp count if used
- show correct errors if code wrong
- Instead of passing the user as a hidden form element, we use a session variable.
- Introduces a 60 second limit on completing the login, and an exponentially increasing delay to attempt to login with 2FA if the code is entered incorrectly.
- use proper Django form error when incorrect otp value entered
Each readthrough should represent a unique instance of reading a book,
and so it should have a single status that represents that status of
that readthrough. This will be used to place the book on the appropriate
"shelves", and should allow for there to be a single active readthrough
if a book is in progress. That maeks the `is_active` field unnecessary.
Since the status of the readthrough is unambiguous, defining "finished
date" and "stopped date" separately is no longer necessary.
I've sketched out some database constraints but haven't implemented them
yet, because the conflicts might get gnarly and let's take this one step
at a time.
The migration logic is complex, so this is an untested draft and I'll
write unit tests for it in a subsequent commit.