Authenticate requests made from Roblox servers

As a developer on Roblox, it is currently too difficult to authenticate the origin and authenticity of HTTP requests made by Roblox games. At both Islands and BedWars, we maintain several (and have plans for more) business-critical applications. Some of these include Island’s public Island discovery list and a few internal analytics platforms, as well as a future moderation platform. I also maintain a group ranking API for SizzleBurger. While not all these applications are business-critical, many are, and unauthorized use could leave devastating (and potentially irreversible) damage to the games and/or groups.

I have been maintaining a couple of different packages to accomplish this for a few years now (RoCheck and its rework Rodentify). However, they both work on the same method and therefore suffer the same downsides.

The method works by sending an initial request GET to Roblox’s PlaceLauncher API (see source above). This API takes the place and job ID of the server and returns a joinScriptUrl. Sending another GET request to this joinScriptUrl will return a lot of information, including the machineAddress of the game server you’re trying to join. You can compare this address to the IP which made the API request and know if it is from Roblox. For anybody reading this post, no, this does not count as a join to the game and does not inflate visits.

While this method does work in a use few cases, it falls apart for the majority because:

  • The game server must be available to the Roblox account making the API request. This means that it cannot be full or a reserved/VIP server, making this the biggest blocker of the approach for people and the reason we haven’t been able to adopt it in both Islands and BedWars.

  • The method relies on the uptime/usage/availability of an internal Roblox API. Over the years I’ve used it, there have been several cases where this API becomes unavailable (for whatever reason), which takes everything else down with it. Something as business-critical as this should never rely on the availability of any single API (and not even getting into the issues of rate-limiting here).

  • It eats up bandwidth. In total, this method makes two requests every time it’s used (one to get the joinScriptUrl and another to make a request to that joinScriptUrl). Both of these APIs return a lot of data, and when you’re handling millions of requests a day (like we have to), this eats through bandwidth limits and costs quickly.

  • It’s slow! Utilizing this method means that the response times of our own APIs are directly dependent on the response time of Roblox’s. If Roblox is having API issues, that means we are as well.

As it stands, secrets are not an effective solution to this problem because Roblox provides no proper way of securely storing them. The “gamejoin method” works because it doesn’t rely on the security of a single secret and makes Roblox the source of truth as to whether or not a request is really authentic. With a secret, you have no way of knowing if the request is really from Roblox or not and have to place all trust in a secret value that hasn’t been stored properly to begin with.

I would love the opportunity to discuss a feature like this further if given the chance. Due to the security nature of the request, I’m unable to give some private implementation and use case details in a public post like this.