vys
vys

Reputation: 45

Why does Roblox’s Quick Login API return a 403 error when checking login status?

When I scan the qr from the quick-login code, and successfuly login, then send a post to the status endpoint, the response reflects that I'm not logged in.

code:

// sending post to create endpoint to generate qr code and login code
app.post('/login/create', async (req, res) => {
    console.log('Creating login session...');
    try {
        const response = await axios.post('https://apis.roblox.com/auth-token-service/v1/login/create', {}, {
            headers: {
                'accept': 'application/json, text/plain, */*',
                'accept-language': 'en-US,en;q=0.9',
                'cookie': req.headers.cookie,
                'x-csrf-token': req.headers['x-csrf-token'],
            }
        });

        const { code, imagePath, privateKey } = response.data;
        const qrCodeUrl = `https://apis.roblox.com/auth-token-service${imagePath}`;

        console.log('Login session created successfully:', { code, qrCodeUrl, privateKey });
        res.json({ code, qrCodeUrl, privateKey });
    } catch (error) {
        console.error('Error creating login session:', error.message);
        res.status(500).json({ error: error.message });
    }
});
//login status once I scan the qr code and approve the login
app.post('/login/status', async (req, res) => {
    const { code, privateKey } = req.body;
    console.log('Checking login status for code:', code);
    try {
        const response = await axios.post('https://apis.roblox.com/auth-token-service/v1/login/status', {
            code,
            privateKey
        }, {
            headers: {
                'accept': 'application/json, text/plain, */*',
                'accept-language': 'en-US,en;q=0.9',
                'content-type': 'application/json;charset=UTF-8',
                'x-csrf-token': req.headers['x-csrf-token'],
            }
        });
        console.log('Login status response:', response.data);
        res.json(response.data);
    } catch (error) {
        console.error('Error checking login status:', error.message);
        res.status(500).json({ error: error.message });
    }
});

But it still returns:

Checking login status for code: B8VHPW
Error checking login status: Request failed with status code 403

What am I missing?

Upvotes: 2

Views: 96

Answers (1)

Tiago Peres
Tiago Peres

Reputation: 15602

As per this guide explaining the full process of authenticating an account through Quick Login in Roblox

These APIs requires a special header called x-csrf-token, this can be fetched by observing a failed response for the same header, if it’s present, you should attach the x-csrf-token from the response headers, and attach it to the request headers.

This means that in the case you're getting 403, very likely your x-csrf-token is either not there or has expired / is invalid.

When you call Roblox without a valid CSRF token, it returns 403 and provides a fresh x-csrf-token in the response headers.

So, you can adjust the code to retry the request using that new token like so

Visual of the new flow

app.post('/login/status', async (req, res) => {
    const { code, privateKey } = req.body;
    console.log('Checking login status for code:', code);

    const postStatus = async (csrfToken = '') => {
        return axios.post(
            'https://apis.roblox.com/auth-token-service/v1/login/status',
            { code, privateKey },
            {
                headers: {
                    'Accept': 'application/json, text/plain, */*',
                    'Accept-Language': 'en-US,en;q=0.9',
                    'Content-Type': 'application/json;charset=UTF-8',
                    'x-csrf-token': csrfToken,
                },
            }
        );
    };

    try {
        // 1) First attempt using whatever x-csrf-token was in the request (may be empty)
        const response = await postStatus(req.headers['x-csrf-token'] || '');
        console.log('Login status response:', response.data);
        return res.json(response.data);
    } catch (error) {
        // 2) If Roblox returns 403 with a new x-csrf-token, grab it and retry.
        if (
            error.response &&
            error.response.status === 403 &&
            error.response.headers['x-csrf-token']
        ) {
            const newToken = error.response.headers['x-csrf-token'];
            console.log('Received new x-csrf-token from 403:', newToken);

            try {
                const retryResponse = await postStatus(newToken);
                console.log('Login status response (retry):', retryResponse.data);
                return res.json(retryResponse.data);
            } catch (retryError) {
                console.error('Error on retry with new CSRF token:', retryError.message);
                return res.status(500).json({ error: retryError.message });
            }
        }

        console.error('Error checking login status:', error.message);
        return res.status(500).json({ error: error.message });
    }
});

Upvotes: 2

Related Questions