patentul patentul - 11 months ago 68
Node.js Question

Security time to live token in nodejs

I have implemented in nodejs a security token for an API that communicates only over ajax requests.

Upon each request a security token is sent, if this token exists, a new one is generated and sent in the response via header.

If it does not exist, the response ends with a 403, this is if there are no login credentials passed.

The problem is when I start two requests at the same time (let's say A and B), both of them send the same security token, A ends before B, thus updating the token before B, therefore the token that was passed to B is in this case invalid, because it was invalidated by A.

How can I overcome this issue?

How can I reimplement it?

Answer Source

You can use a token that is built up of two parts: one shared between the client and the server and the other one based on some external parameter.
You don't even have to renew it.

As an example, imagine that client and server share a numeric token named t and they agree on that token during the first call (login, user creation, whatever you want).
When they create the token, they also set a counter c to 0 and create a couple p = <t, c> that is the current token (or better, the last seen).
They must never send the token or the counter explicitly in any request.

When the client wants to send a message to the server, steps are:

  • Create the new value v = t*c
  • Updated p as <t,c+1>
  • Send the request using the generated token v

Each request will have an unique actual token.

To verify the token on the server, steps are:

  • Compute a set of accepted tokens aN as it follows:
    • a0 = v*(c-N/2) (where c' = (c-N/2))
    • a1 = v*(c-N/2+1)
    • ...
    • aN = v*(c+N/2-1)
  • If there exists aM that is equal to the received token, then accept the request
  • If the request has been accepted, update the counter server side accordingly if the c' used to create the chosen aM is greater than the current c

This way, concurrent requests are nicely handled and you don't have to renew the token explicitly.

Whenever a request fails, the client can decide to reset the shared token t and the counter c somehow with a dedicated endpoint.
The server can simply reject a request for which there doesn't exist a valid aM in the computed set and that's all.

Of course, you must be able to store those tokens somehow, somewhere.