Skip to content

Conversation

@rnels
Copy link

@rnels rnels commented Jul 17, 2024

Motivation

Currently, it isn't possible to authenticate a steam user with a backend web server. Adding the function GetAuthTicketForWebApi and its associated callback handler GetTicketForWebApiResponse_t allows us to do so.

Changes

  • Added init_user and shutdown_user to core.cpp
    • Seems like these were omitted by mistake, preventing the user.cpp callback handlers from registering
  • Added user.getAuthTicketForWebApi method and OnTicketForWebApiResponse callback handler

This is my first time writing C++, so please let me know if you have any suggestions for improving my code. Also let me know if there are any additional requirements for contribution I've missed.

Copy link
Collaborator

@yancouto yancouto left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for contributing!

Nice to notice user init and shutdown was missing, I fixed this on master now!

In order to accept this, I would need:

  1. Documentation for the added function
  2. Explain how this was tested. Did you use it in a game?

(I know this PR is old, sorry about it, I wasn't active here but now I'm back.)

@rnels
Copy link
Author

rnels commented Dec 15, 2025

Hi @yancouto, welcome back. Could you elaborate more on the documentation you're looking for? I've included the Steamworks docs in the description. Though if you're looking for code comments, I could add those.

I have used a patched build of luasteam which includes my contribution on a personal project which adds a multiplayer leaderboard to the game Balatro. My use case was for authenticating requests sent by a Steam user to a web API, which uses AuthenticateUserTicket as a middleware to authenticate requests as being from the Steam account making the request. In my project, this ensured that users could not spoof their leaderboard scores as being from a different Steam account.

My use case worked without issue using my patched version. My project that includes the server + mod code is a private repo, but if you want to see just the code for the mod (which includes the patched luasteam), you can find it here.

@yancouto
Copy link
Collaborator

yancouto commented Dec 17, 2025

Hi! I mean documentation in the docs/ directory.

All our documentation is Read The Docs, and to add to it you need to add to the docs/ directory. In CONTRIBUTING.md, there is some explanation of what to do when you add a function, mentioning to add documentation and how to do that, take a look and let me know if you have any doubts!

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds support for authenticating Steam users with backend web servers by implementing the GetAuthTicketForWebApi Steamworks API function. The implementation includes both the API function wrapper and its associated callback handler to receive ticket responses asynchronously.

  • Added user.getAuthTicketForWebApi Lua-accessible function to request web API authentication tickets
  • Added OnTicketForWebApiResponse callback handler to process ticket generation responses
  • Registered new STEAM_CALLBACK in CallbackListener class for the web API ticket response

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

return;
}
lua_rawgeti(L, LUA_REGISTRYINDEX, user_ref);
lua_getfield(L, -1, "OnTicketForWebApiResponse");
Copy link

Copilot AI Dec 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The callback name uses PascalCase "OnTicketForWebApiResponse" while the existing callback "onAuthSessionTicketResponse" on line 41 uses camelCase. For consistency with the existing codebase, this should be "onTicketForWebApiResponse" (with lowercase 'o').

Copilot uses AI. Check for mistakes.
EResult result = data->m_eResult;
uint32 handle = data->m_hAuthTicket;
uint8 *authTicket = data->m_rgubTicket;
const char *hexTicket = bufferToHex(authTicket, 2560).c_str();
Copy link

Copilot AI Dec 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using c_str() on a temporary string returned by bufferToHex() creates a dangling pointer. The temporary string object is destroyed at the end of the statement, making hexTicket point to deallocated memory. Store the result in a std::string variable first, similar to how it's done in luasteam_getAuthSessionTicket on line 110.

Copilot uses AI. Check for mistakes.
EResult result = data->m_eResult;
uint32 handle = data->m_hAuthTicket;
uint8 *authTicket = data->m_rgubTicket;
const char *hexTicket = bufferToHex(authTicket, 2560).c_str();
Copy link

Copilot AI Dec 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The buffer size 2560 is hardcoded and appears to be a magic number. According to Steamworks documentation, GetTicketForWebApiResponse_t contains m_rgubTicket with size defined by k_cubAuthTicketForWebApiSizeMax (typically 512 bytes). The actual size of the ticket should be obtained from the m_cubTicket field in the callback data structure. Using a hardcoded value of 2560 will read beyond the actual buffer bounds, potentially causing memory safety issues.

Copilot uses AI. Check for mistakes.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot hallucinated here a little about the constant name that doesn't exist.

I see where you're coming, it returns an array with maximum size 2560. However, it does seem the data has a m_cubTicket with the actual length, can't you use that?

if (ticket != k_HAuthTicketInvalid) {
lua_newtable(L);
lua_pushinteger(L, ticket);
lua_setfield(L, -2, "handle");
Copy link

Copilot AI Dec 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The return field is named "handle" but in the similar function luasteam_getAuthSessionTicket (line 113), the ticket is returned as "ticket". For API consistency, consider using "ticket" as the field name here as well, or if "handle" is intentionally different, ensure this naming difference is clearly documented.

Copilot uses AI. Check for mistakes.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are you returning a table with a single field? https://partner.steamgames.com/doc/api/ISteamUser#GetAuthTicketForWebApi just returns an integer.

Well it seems our luasteam_getAuthSessionTicket also breaks convention and returns a table with ticket and hexTicket (not sure why). At least follow that.

uint8 *authTicket = data->m_rgubTicket;
const char *hexTicket = bufferToHex(authTicket, 2560).c_str();

lua_createtable(L, 0, 1);
Copy link

Copilot AI Dec 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The table is created with a hint of 1 field (lua_createtable(L, 0, 1)) but actually stores 3 fields (handle, hexTicket, result). The second parameter should be 3 to properly hint the table size to the Lua allocator for better performance.

Copilot uses AI. Check for mistakes.
@yancouto
Copy link
Collaborator

Ignore the CI failures, the job is not working properly on Pull Requests.

The Copilot review seems legit, you are using c_str() from a std::string that may be deleted. (Unforturnately, C++ let's you make those mistakes easily...)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants