For the past few weeks, I’ve been creating a Discord bot using Discordia and Luvit that interfaces with Roblox’s API via coro-http. When it came to authenticating an account, however, I couldn’t find anything that used coro-http. I only figured it out by reading through @likeajumpingpro’s Rbx.lua code, which, although helpful, didn’t offer a clear explanation.
This tutorial assumes you’re using Chrome, as well as the above software. A lot of these need setup, but documentation and tutorials for those already exist.
Getting Your Cookie
-
Log into Roblox
-
Click the lock icon to the left of the address bar
-
Click
Cookies
-
Open the
roblox.com
menu, as well as theCookies
folder
-
Scroll down and select the
.ROBLOSECURITY
cookie
-
Copy the
Content
field. Do not share this information with anyone you don’t trust. Notice how I blacked mine out.
Everything, including the warning at the front, is your cookie.
Using Your Cookie to Authenticate
Now that we have our cookie, we can authenticate with Roblox’s API. This tutorial will attempt to set an authenticated user’s primary group as an example.
First, we need to get coro-http.
local HTTP = require("coro-http")
As the name implies, we must run coro-http inside coroutines. For reasons I’ll discuss later, we need to be able to call our function again. So, the majority of our code will go in a regular function. To ensure coro-http still works, we can use coroutine.wrap()
.
local HTTP = require("coro-http")
local function POST_groups_v1_user_groups_primary()
end
local WrappedFunction = coroutine.wrap(POST_groups_v1_user_groups_primary)
WrappedFunction()
Then, we need to send our request. request
returns two variables.
local HTTP = require("coro-http")
local function POST_groups_v1_user_groups_primary()
local Request, Body = HTTP.request()
end
local WrappedFunction = coroutine.wrap(POST_groups_v1_user_groups_primary)
WrappedFunction()
To set our authenticated user’s primary group, we need to include some information:
- The method (first argument, our method, a string)
- The URL (second argument, our endpoint, a string)
- Our headers (third argument, a series of arrays that follow the {“header-name”, “header-value”} pattern, how we want it returned + our cookie)
- Our payload (the group we’re setting to our primary, fourth argument, a string)
- Our options (fifth argument, a table, or a number, in this case, our cookie again)
We do this by passing this information as arguments through HTTP.request()
local HTTP = require("coro-http")
local function POST_groups_v1_user_groups_primary()
local Request, Body = HTTP.request("POST", "https://groups.roblox.com/v1/user/groups/primary", {{"Content-Type", "text/json"}, {"Cookie", ".ROBLOSECURITY=YOUR_COOKIE_GOES_HERE_AFTER_THE_EQUAL_SIGN"}}, "{\"groupId\": GROUP_ID_NUMBER}", {"ALSO_PUT_YOUR_COOKIE_HERE"})
end
local WrappedFunction = coroutine.wrap(POST_groups_v1_user_groups_primary)
WrappedFunction()
If you were to fill in all the details and run this code, you would get an error like this:
{"errors":[{"code":0,"message":"Token Validation Failed"}]}
This error happens because we’re missing one last detail, an X-CSRF-token. You can read more about these and their purpose here.
To get this token, we need to send the request without a token. Roblox will return the error, along with a valid token. We can then retry the request with this token. This missing token is also why our request needed to happen in a regular function.
To fix it, we can check the status of our request. If we get this error, we can re-call the function along with the returned X-CSRF-token. To pass this information along, we can define it as a argument for our function, and include it as another header, along with our Content-Type
and Cookie
.
local HTTP = require("coro-http")
local function POST_groups_v1_user_groups_primary(X_CSRF_TOKEN)
local Request, Body = HTTP.request("POST", "https://groups.roblox.com/v1/user/groups/primary", {{"Content-Type", "text/json"}, {"X-CSRF-TOKEN", X_CSRF_TOKEN}, {"Cookie", ".ROBLOSECURITY=YOUR_COOKIE_GOES_HERE_AFTER_THE_EQUAL_SIGN"}}, "{\"groupId\": GROUP_ID_NUMBER}", {"ALSO_PUT_YOUR_COOKIE_HERE"})
if Request.reason == "Token Validation Failed" then
X_CSRF_TOKEN = Request[6][2] -- Request, along with some other information, returns a series of arrays with two elements, a returned header name and its associated value. Roblox returns the X-CSRF-TOKEN as the sixth value.
POST_groups_v1_user_groups_primary(X_CSRF_TOKEN)
end
end
local WrappedFunction = coroutine.wrap(POST_groups_v1_user_groups_primary)
WrappedFunction(X_CSRF_TOKEN)
And there you have it! This technique also works with any other Roblox endpoint. Of course, you’ll have to change the method, URL, headers, and options depending on what you’re doing. Some don’t require authentication at all.