Rate limit based on API session token - not on source IP


#1

Hello,

We maintain an API at https://go.tallyfy.com/api served via Cloudflare.

In order to use our API, clients need to do an OAuth 2.0 authentication and they receive a session-id.

That unique session ID is used for all subsequent API calls - it’s a token, and has an expiration date.

We want to write a Cloudflare worker which reads a session ID and rate-limits any calls by that session ID to 10 calls/second.

Note that normal rate-limiting only rate-limits by IP - whereas we want to rate-limit by session token ID.

How would we do it, assuming we need to store the “current calls/second” somewhere as well?

Amit


#2

You could use globals to save the token and a counter with a timer to rate-limit.

See here for globals example: https://cloudflareworkers.com/#179e7c3a2ab2c3ced734f1e016048ed3:https://tutorial.cloudflareworkers.com


#3

Thanks Thomas. Can you have say - 100,000 globals for 100,000 API tokens currently active, and not expired yet? Also - how would I expire API session tokens automatically i.e. on the expiry date, so that the CF worker knows to just deny the request, as the token is past expiry?


#4

Do you have the ability to determine if a user has exceeded the rate limit you want on the origin and return a different response code? If so, you could just add the tokens which exceed the threshold along with a period of time the token should be retained (for banning the user).


#5

The script can be max 1MB large, if you put the list in a variable in JSON it should make it under the 50ms limit for the worker CPU consumption when selecting the keys. So far I’ve seen libraries that are 500KB large, for example the jQuery-like DOM-like XML parser Cheerio - it consumes “just” about too much to be over 10ms limit. So in this case, the 50ms limit should be more than enough for your use case.

For expiration - you can use the date function, though I have not yet tested where it fetches the date from (Could be local time on the current edge server) or if it’s a specific time-zone.


#6

The common way to do this is hash the expiration date as a part of the session token. You can use the JWT standard to do this, or do it yourself. For example, if your token is:

Expiration + ":" + HMAC(Expiration, Secret)

Then you can look at the expiration to know when to make the token invalid, and you can trust that no one without your secret will be able to forge a token. When you are using the token, just recompute that HMAC and compare it to the value at the beginning of the token to confirm it hasn’t been tampered with.


#7

Hi,

You could use a bloom filter. You can store the filter as a global variable and increment the array values each time a request is made from the same API Key.

Periodically, you can reset the filter to zero again. (e.g. every 10s or each second). You can implement this using a simple date / time check when the request is made.

It will provide some false positives, although with a big enough filter array and the reset each second it should be okay.

The only downside is that there may be multiple such instances of a global variable as the script runs concurrently, so some of the requests will slip through - test this first what the threshold value of the bloom filter should be.

Hope that helps. Kind Regards,
Simon