Skip to content

Commit

Permalink
Implement and fix auth ticket related code
Browse files Browse the repository at this point in the history
  • Loading branch information
zorfmorf committed May 6, 2023
1 parent cc9e204 commit 216a733
Show file tree
Hide file tree
Showing 4 changed files with 164 additions and 13 deletions.
52 changes: 51 additions & 1 deletion docs/gameServer.rst
Expand Up @@ -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`
Expand Down Expand Up @@ -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 <https://partner.steamgames.com/doc/api/ISteamGameServer#BeginAuthSession>`_

Use this to validate an auth ticket created with :func:`user.getAuthSessionTicket`. Read `User Authentication and Ownership <https://partner.steamgames.com/doc/features/auth>`_ 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 <https://partner.steamgames.com/doc/api/ISteamGameServer#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 <https://partner.steamgames.com/doc/features/auth>`_ for more information.

**Example**::

Steam.gameServer.endAuthSession()

Callbacks Reference
-------------------

Expand All @@ -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 <https://partner.steamgames.com/doc/api/steam_api#EAuthSessionResponse>_` for details.

:returns: nothing
:SteamWorks: `ValidateAuthTicketResponse_t <https://partner.steamgames.com/doc/api/ISteamUser#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 <https://partner.steamgames.com/doc/api/ISteamUser#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**::
Expand Down
23 changes: 16 additions & 7 deletions docs/user.rst
Expand Up @@ -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 <https://partner.steamgames.com/doc/api/ISteamUser#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 <https://partner.steamgames.com/doc/api/ISteamUser#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
-------------------
Expand Down
69 changes: 68 additions & 1 deletion src/gameServer.cpp
Expand Up @@ -4,11 +4,29 @@
#include "networkingSockets.hpp"
#include "networkingUtils.hpp"
#include "const.hpp"
#include <vector>
#include <sstream>

// ===============================
// ======= SteamGameServer =======
// ===============================

static const char *steam_auth_session_response[] = {
"OK", "UserNotConnectedToSteam", "NoLicenseOrExpired", "VACBanned", "LoggedInElseWhere", "VACCheckTimedOut", "AuthTicketCanceled", "AuthTicketInvalidAlreadyUsed", "AuthTicketInvalid", "PublisherIssuedBan", nullptr,
};

std::vector<unsigned char> hexToBuffer(const std::string &hexString) {
std::vector<unsigned char> 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<unsigned char>(byte));
}
return buffer;
}

using luasteam::CallResultListener;

namespace {
Expand All @@ -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;
Expand Down Expand Up @@ -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<unsigned char> 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);
Expand All @@ -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);
Expand All @@ -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);
Expand Down
33 changes: 29 additions & 4 deletions src/user.cpp
@@ -1,10 +1,22 @@
#include "user.hpp"
#include "const.hpp"
#include <sstream>
#include <iomanip>

// ============================
// ======== SteamUser =========
// ============================

std::string bufferToHex(const void *buffer, size_t bufferLength) {
const unsigned char *pBuffer = static_cast<const unsigned char *>(buffer);
std::ostringstream oss;
oss << std::hex << std::setfill('0');
for (size_t i = 0; i < bufferLength; ++i) {
oss << std::setw(2) << static_cast<int>(pBuffer[i]);
}
return oss.str();
}

namespace {

class CallbackListener;
Expand Down Expand Up @@ -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;
}

Expand Down

0 comments on commit 216a733

Please sign in to comment.