From 216a7339cb5a6ec539a51a89a32132030c915915 Mon Sep 17 00:00:00 2001 From: Sebastian Tauch Date: Sat, 6 May 2023 21:34:27 +0900 Subject: [PATCH] Implement and fix auth ticket related code --- docs/gameServer.rst | 52 +++++++++++++++++++++++++++++++++- docs/user.rst | 23 ++++++++++----- src/gameServer.cpp | 69 ++++++++++++++++++++++++++++++++++++++++++++- src/user.cpp | 33 +++++++++++++++++++--- 4 files changed, 164 insertions(+), 13 deletions(-) diff --git a/docs/gameServer.rst b/docs/gameServer.rst index 2a4bac6..a255c22 100644 --- a/docs/gameServer.rst +++ b/docs/gameServer.rst @@ -36,9 +36,12 @@ List of Functions * :func:`gameServer.bSecure` * :func:`gameServer.getSteamID` * :func:`gameServer.setDedicatedServer` +* :func:`gameServer.beginAuthSession` +* :func:`gameServer.endAuthSession` List of Callbacks ----------------- +* :func:`gameServer.onValidateAuthTicketResponse` * :func:`gameServer.onSteamServersConnected` * :func:`gameServer.onSteamServersDisconnected` * :func:`gameServer.onSteamServerConnectFailure` @@ -188,6 +191,34 @@ Function Reference Steam.gameServer.setDedicatedServer(true) +.. function:: gameServer.beginAuthSession() + + :param string authTicket: The auth ticket in hexadeximal string representation + :param uint64 steamID: The steam id of the user to verify an auth ticket for. Needs to be the same user that originally created the authTicket. + :returns: nothing + :SteamWorks: `BeginAuthSession `_ + + Use this to validate an auth ticket created with :func:`user.getAuthSessionTicket`. Read `User Authentication and Ownership `_ for more information. + + Triggers the callback :func:`gameServer.onValidateAuthTicketResponse` + + +**Example**:: + + Steam.gameServer.beginAuthSession(authTicket, steamID) + +.. function:: gameServer.endAuthSession() + + :param uint64 steamID: The steam id of the user for which a verification is in progress. + :returns: nothing + :SteamWorks: `EndAuthSession `_ + + Cancel the auth session. Must be called for any calls to :func:`gameServer.beginAuthSession` when the connection is ended or before the server shutdown. Read `User Authentication and Ownership `_ for more information. + +**Example**:: + + Steam.gameServer.endAuthSession() + Callbacks Reference ------------------- @@ -197,13 +228,32 @@ Callbacks Reference Also, you **must** constantly call ``Steam.gameServer.runCallbacks()`` (preferably in your game loop) in order for your callbacks to be called. +.. function:: gameServer.onValidateAuthTicketResponse() + + :param table data: A result table + + * **data.steam_id** (`uint64`) The steam id of the account requesting validation for the given game context + * **data.owner_steam_id** (`uint64`) The steam id of the owner of the game for the given game context. E.g. for example when the game is played through family sharing, the owner_steam_id will differ. + * **data.response** (`string`) A result string. **OK** if the validation was successful, otherwise an error string. See `EAuthSessionResponse _` for details. + + :returns: nothing + :SteamWorks: `ValidateAuthTicketResponse_t `_ + + Called when steam has validated an auth ticket on the steam server side. If **OK**, you know that's a registered, verified steam account that owns a valid license for the app in question. + +**Example**:: + + function Steam.gameServer.onValidateAuthTicketResponse(data) + print("Auth ticket validated", data.steam_id, data.response) + end + .. function:: gameServer.onSteamServersConnected() :returns: nothing :SteamWorks: `SteamServersConnected_t `_ Called when a connections to the Steam back-end has been established. This is in response to a call to :func:`networkingSockets.LogOn` or :func:`networkingSockets.LogOnAnonymous` or after loosing a connection. - + This means the Steam client now has a working connection to the Steam servers. **Example**:: diff --git a/docs/user.rst b/docs/user.rst index ea6d29c..458cf01 100644 --- a/docs/user.rst +++ b/docs/user.rst @@ -50,26 +50,35 @@ Function Reference .. function:: user.getAuthSessionTicket () - :returns: (`number`) The handle for the auth session ticket or 0 if the call failed + :returns: (`data`) A result table if successful, otherwise nil + + * **data.ticket** (`number`) The handle for the auth session ticket (only has meaning on the client on where you called this, is NOT an auth ticket) + * **data.hexTicket** (`string`) The ticket data as a hexadeximal formatted string. This is the ticket that you need to send to the server/authenticating instance. + :SteamWorks: `GetAuthSessionTicket `_ - Retrieve an authentication ticket from steam. You can use this to authenticate yourself with a third party server. This call just creates a request for a ticket. Once it has been issued by the Steam servers, the callback :func:`user.onAuthSessionTicketResponse` will be called. When you're done with the ticket **you must call** :func:`user.cancelAuthTicket` on the handle. + Retrieve an authentication ticket from steam. You can use this to authenticate yourself with a third party server. You should wait for a successful callback to :func:`user.onAuthSessionTicketResponse` (indicating that Steam has accepted your request for a ticket) before using this ticket. + + When you're done with the ticket **you must call** :func:`user.cancelAuthTicket` on the handle. **Example**:: - local ticketHandle = Steam.user.getAuthSessionTicket() -- keep the ticket handle around + local data = Steam.user.getAuthSessionTicket() -- keep the ticket handle around + if data then + local ticketHandle = data.ticket + local ticketData = data.hexTicket + -- Now do something with it + end .. function:: user.cancelAuthTicket () - :param number ticketHandle: The ticket handle to cancel the auth ticket for. You need to call this once you are done using a requested or scheduled ticket. Make sure to also call this when quitting your application for any open ticket handles. + :param number ticketHandle: The ticket handle to cancel the auth ticket for. You need to call this once you are done using a requested or scheduled ticket. Make sure to call this for any open ticket handles when quitting your application. :returns: nothing :SteamWorks: `CancelAuthTicket `_ - Retrieve an authentication ticket from steam. You can use this to authenticate yourself with a third party server. This call just creates a request for a ticket. Once it has been issued by the Steam servers, the callback :func:`user.onAuthSessionTicketResponse` will be called. When you're done with the ticket **you must call** :func:`user.cancelAuthTicket` on the handle. - **Example**:: - local ticketHandle = Steam.user.getAuthSessionTicket() -- keep the ticket handle around + Steam.user.cancelAuthTicket(ticketHandle) Callbacks Reference ------------------- diff --git a/src/gameServer.cpp b/src/gameServer.cpp index 0246544..565986c 100644 --- a/src/gameServer.cpp +++ b/src/gameServer.cpp @@ -4,11 +4,29 @@ #include "networkingSockets.hpp" #include "networkingUtils.hpp" #include "const.hpp" +#include +#include // =============================== // ======= SteamGameServer ======= // =============================== +static const char *steam_auth_session_response[] = { + "OK", "UserNotConnectedToSteam", "NoLicenseOrExpired", "VACBanned", "LoggedInElseWhere", "VACCheckTimedOut", "AuthTicketCanceled", "AuthTicketInvalidAlreadyUsed", "AuthTicketInvalid", "PublisherIssuedBan", nullptr, +}; + +std::vector hexToBuffer(const std::string &hexString) { + std::vector buffer; + buffer.reserve(hexString.size() / 2); + for (size_t i = 0; i < hexString.size(); i += 2) { + unsigned int byte; + std::istringstream iss(hexString.substr(i, 2)); + iss >> std::hex >> byte; + buffer.push_back(static_cast(byte)); + } + return buffer; +} + using luasteam::CallResultListener; namespace { @@ -19,11 +37,38 @@ int gameServer_ref = LUA_NOREF; class CallbackListener { private: + STEAM_GAMESERVER_CALLBACK(CallbackListener, OnValidateAuthTicketResponse, ValidateAuthTicketResponse_t); STEAM_GAMESERVER_CALLBACK(CallbackListener, OnSteamServersConnected, SteamServersConnected_t); STEAM_GAMESERVER_CALLBACK(CallbackListener, OnSteamServersDisconnected, SteamServersDisconnected_t); STEAM_GAMESERVER_CALLBACK(CallbackListener, OnSteamServerConnectFailure, SteamServerConnectFailure_t); }; +// void SetValidateAuthTicketResponseCallback( function callback ) +void CallbackListener::OnValidateAuthTicketResponse(ValidateAuthTicketResponse_t *data) { + if (data == nullptr) { + return; + } + lua_State *L = luasteam::global_lua_state; + if (!lua_checkstack(L, 4)) { + return; + } + lua_rawgeti(L, LUA_REGISTRYINDEX, gameServer_ref); + lua_getfield(L, -1, "onValidateAuthTicketResponse"); + if (lua_isnil(L, -1)) { + lua_pop(L, 2); + } else { + lua_createtable(L, 0, 3); + luasteam::pushuint64(L, data->m_SteamID.ConvertToUint64()); + lua_setfield(L, -2, "steam_id"); + luasteam::pushuint64(L, data->m_OwnerSteamID.ConvertToUint64()); + lua_setfield(L, -2, "owner_steam_id"); + lua_pushstring(L, steam_auth_session_response[data->m_eAuthSessionResponse]); + lua_setfield(L, -2, "response"); + lua_call(L, 1, 0); + lua_pop(L, 1); + } +} + void CallbackListener::OnSteamServersConnected(SteamServersConnected_t *data) { if (data == nullptr) { return; @@ -176,6 +221,26 @@ EXTERN int luasteam_server_setDedicatedServer(lua_State *L) { return 0; } +// EBeginAuthSessionResult BeginAuthSession( const void *pAuthTicket, int cbAuthTicket, CSteamID steamID ) +EXTERN int luasteam_server_beginAuthSession(lua_State *L) { + const char *hexTicket = luaL_checkstring(L, 1); + std::vector authTicketBuffer = hexToBuffer(hexTicket); + const void *authTicket = authTicketBuffer.data(); + int cbAuthTicket = authTicketBuffer.size(); + + CSteamID steamID(luasteam::checkuint64(L, 2)); + EBeginAuthSessionResult result = SteamGameServer()->BeginAuthSession(authTicket, cbAuthTicket, steamID); + lua_pushinteger(L, result); + return 1; +} + +// void EndAuthSession( CSteamID steamID ); +EXTERN int luasteam_server_endAuthSession(lua_State *L) { + CSteamID steamID(luasteam::checkuint64(L, 1)); + SteamGameServer()->EndAuthSession(steamID); + return 0; +} + void add_gameserver_constants(lua_State *L) { lua_createtable(L, 0, 3); lua_pushnumber(L, EServerMode::eServerModeNoAuthentication); @@ -190,7 +255,7 @@ void add_gameserver_constants(lua_State *L) { namespace luasteam { void add_gameServer(lua_State *L) { - lua_createtable(L, 0, 10); + lua_createtable(L, 0, 12); add_func(L, "init", luasteam_init_server); add_func(L, "shutdown", luasteam_shutdown_server); add_func(L, "runCallbacks", luasteam_runCallbacks_server); @@ -201,6 +266,8 @@ void add_gameServer(lua_State *L) { add_func(L, "bSecure", luasteam_server_bSecure); add_func(L, "getSteamID", luasteam_server_getSteamID); add_func(L, "setDedicatedServer", luasteam_server_setDedicatedServer); + add_func(L, "beginAuthSession", luasteam_server_beginAuthSession); + add_func(L, "endAuthSession", luasteam_server_endAuthSession); add_gameserver_constants(L); lua_pushvalue(L, -1); gameServer_ref = luaL_ref(L, LUA_REGISTRYINDEX); diff --git a/src/user.cpp b/src/user.cpp index 36a6577..6d08eb3 100644 --- a/src/user.cpp +++ b/src/user.cpp @@ -1,10 +1,22 @@ #include "user.hpp" #include "const.hpp" +#include +#include // ============================ // ======== SteamUser ========= // ============================ +std::string bufferToHex(const void *buffer, size_t bufferLength) { + const unsigned char *pBuffer = static_cast(buffer); + std::ostringstream oss; + oss << std::hex << std::setfill('0'); + for (size_t i = 0; i < bufferLength; ++i) { + oss << std::setw(2) << static_cast(pBuffer[i]); + } + return oss.str(); +} + namespace { class CallbackListener; @@ -55,11 +67,24 @@ EXTERN int luasteam_getSteamID(lua_State *L) { //HAuthTicket GetAuthSessionTicket( void *pTicket, int cbMaxTicket, uint32 *pcbTicket ) EXTERN int luasteam_getAuthSessionTicket(lua_State *L) { - uint32 *pcbTicket = nullptr; - void *pTicket = nullptr; - HAuthTicket ticket = SteamUser()->GetAuthSessionTicket(pTicket, 1024, pcbTicket); + uint32 pcbTicket = 0; + void *pTicket = malloc(1024); + + HAuthTicket ticket = SteamUser()->GetAuthSessionTicket(pTicket, 1024, &pcbTicket); + + if (ticket != k_HAuthTicketInvalid) { + std::string hexTicket = bufferToHex(pTicket, pcbTicket); + lua_newtable(L); + lua_pushinteger(L, ticket); + lua_setfield(L, -2, "ticket"); + lua_pushstring(L, hexTicket.c_str()); + lua_setfield(L, -2, "hexTicket"); + free(pTicket); + return 1; + } - lua_pushinteger(L, ticket); + free(pTicket); + lua_pushnil(L); return 1; }