I'm building a long-lived session system which requires that users verify their authentication before performing "dangerous" operations, such as changing critical settings, deleting their account, etc. This requirement is analogous to Facebook requiring you re-enter your password before changing your email address, for example. I would like to allow the user to make dangerous changes within some period of time
T after verifying their password.
The system will scale horizontally, so remembering "who validated when" in memory isn't possible and I'd like to avoid storing the information in the database, and looking it up on each "dangerous" action. I believe that leaves generating some secure time-sensitive
token as my best alternative.
Here's my approach to generating and verifying this token, please let me know if you think I've overlooked anything, or am making some foolish security error. All client-server communication is over TLS/SSL.
1. Alice wants to perform some "dangerous" operation (client side). 2. Alice is presented with a "verify password" modal or similar UI element where she enters her password. 3. The client-side system sends the password to the server. 4. The server checks if the password matches Alice's stored password. 5. If the passwords match, a token is generated as follows: a. Let `id` be Alice's user ID (UUID), `t` be a fixed length string representation of the current timestamp, and `secret` be a secret key appropriately protected on the server. b. Generate the secure part as `k = (string) HMAC(secret, id + t)` (Assume `k` to be typecasted or converted to a string representation.) c. Finally, let `token = k + t` where `+` is the string concatenation operator 6. Send the token to Alice. 7. For each "dangerous" operation, Alice includes `token` with the request. 8. The server can verify the `token` by splitting `k` and `t` and validating `k` as in step 5.b above, and verify that time `T` hasn't elapsed since Alice verified her password.
I believe this approach solves the problem. Suggestions? Am I missing something?