It's occurred to me that we have to have password-based authentication, but we don't have an SSL certificate on heroku. That means that if we do this the usual way, everyone's password will be sent in cleartext over the wire, which is obviously suboptimal.
My solution is to arrange a system whereby no authentication information sent over the wire will be useful for more than 10 minutes. The idea is that we send hashes over the wire, but they are salted using one-time use random numbers, meaning that as soon as they are read by the server, they can never be used again.
Here's a more detailed example.
A client C wants to log in. C tells the server S that C wants to log in using username U. The server generates a random number N and sends N, along with the salt of U's password, back to C. Recall that the user has just typed their password, so C has the password P and U. Now it also has the password's salt on the server and a random number. C hashes P with the salt, and then hashes the result of that with N, yielding a new hash H that will never be seen again, as N will never be generated again. C sends H to S, which then takes the salted password hash from the database and hashes it again with N just like C did before. If the server's result matches the result that C sent, S knows that C has the real password, but the password was never explicitly shared. S sends a session key K back to C, stores K in the database, and deletes N from the database. K will expire in a short time (say 10 minutes), after which time S will tell C to authenticate again. If there is an attacker reading the wire, all he has is H, which is now worthless, since it was hashed with N which will never be used again, and K, which will only be useful for the next 10 minutes. The attacker has the salt of U's password, which isn't that valuable, and U itself, which he could get easily anyway.
"But wait," you're probably asking, "won't this make the site a huge pain to code?" No. At least, hopefully not. I have a plan for that too.
On the JavaScript side, I will have an object (not a React Component, mind you) called an Authentication
. An Authentication
will contain the salted hashed password, a session key, and the username of the current user. It will have methods for POST requests and GET requests that will abstract away the need to worry about any authentication problems. If will automatically include the session key in the payload of the requests if required, and if the key expires, it will automatically re-authenticate and retry the request. In theory, you'll have a Login
React Component that will accept login information and the return an Authentication
object to the App
Component through a callback (hopefully putting it in App.state
). This means that the check for whether someone is logged in on the client side will just be App.state.auth !== null
.
On the server side, you can just use a decorator (eg, @session_required
) to turn route functions into authenticated route functions. All that decorator will do is look at the Flask request
object and find the session key in the payload. If the session key is still good, it will set a global variable user
with the primary key of the User
object that is calling the API, otherwise it will return an error saying that the session does not exist. This will happen often as sessions expire after a short period of time.
Note also that the User
object will be global to match the request
object in Flask. I assume if they're doing it, it must be a pretty good idea. They have a method for dealing with multithreaded web servers, which I will steal to implement this.
Having said all that, this is a request for comments. I think this will work, and it'll be cool, and it will be secure, but it will probably also be somewhat complicated. I think I've come up with abstractions that will make it simple to use, but this remains to be seen in practice. Any thoughts?