Server Impersonation

Server Impersonation & Token Exchange

In this How-To we'll look at how to do a request through the LootLocker Game API on a server, acting as a specific user. The server impersonation feature can be used to do something as a player of your game. This can be useful for many reasons such as for example:

  • Offloading logic from client to server

  • Moving authoritative calls from the client to the server. In general we will always recommend keeping all write operations in the server. This would be things like submitting scores, rewarding currency, and adding points to a progression.

  • Delayed actions. Say your idle game loop allows players to perform actions that will take effect after a certain amount of time, while they're not online. Now you can take those actions from your game server.

About Impersonation using Token Exchange

Impersonation on the server can be done in one of two ways. The first we'll discuss is done using a player session token. With this method a token from a Game API session can be used to identify a player. This can should be used in cases where you want to use the Game API in your game client but perform some player actions in the secure context of the server and not allow them on the client.

Prefer token exchange if:

  • Your player is online when you make actions on behalf of them

  • The calls you make are triggered by player actions

About Impersonation using Player ULIDs

The other way of impersonating a player is through the use of their ULID. With this method, the server can get an active Game API session token without the user authenticating. This can be used in cases where you either do not want the client to talk directly with LootLocker at all, or if you need to perform actions on behalf of a player disconnected from them being online to authenticate.

Prefer ULID impersonation if:

  • You need to perform actions on behalf of a player disconnected from their online status. For example:

    • Delayed actions

    • Actions triggered from outside of the game such as sending a friend request from a web-page for example

  • You want to perform mass actions on lists of players

  • You want to add user moderation actions to the game server

  • You want to help support recover accounts or in other ways inspect player information

Prerequisites

Implementation

With Token Exchange

1. Start a server session

Refer to the Start Server documentation.

2. Client Code

First you'll need to authenticate your player in the game client and sync the received session token to the server. In this example we will use guest sessions in the interest of simplicity.

ULootLockerSDKManager::GuestLogin(FLootLockerSessionResponse::CreateLambda([](const FLootLockerAuthenticationResponse& sessionResponse) {
    if (!sessionResponse.success)
    {
        // Handle the error case
        UE_LOG(<LogCategory>, Error, TEXT("Failed to login: %s"), *sessionResponse.ErrorData.Message);
        return;
    }

    // Replace with your way of syncing the session token to the server
    SyncPlayerTokenToGameServer(sessionResponse.session_token);
}));

3. Server Token Exchange

When you get the token from the client you need to exchange the client session token for an impersonated token using the token exchange method. When you have done that you need to add this new session to your copy of the Game SDK.

void OnTokenReceivedFromGameClient(const FString& ClientSessionToken) {
    ULootLockerServerForCpp::GameApiTokenExchange(ClientSessionToken, 
        FLootLockerServerTokenExchangeResponseDelegate::CreateLambda([](const FLootLockerServerTokenExchangeResponse& TokenExchangeResponse)
        {
            if (!TokenExchangeResponse.Success)
            {
                // Handle the error case
                UE_LOG(<LogCategory>, Error, TEXT("Failed to exchange game client token with error %s"), *TokenExchangeResponse.ErrorData.Message);
                return;
            }

            // Successfully exchanged the token, now add game api session to local copy of game SDK
            FLootLockerPlayerData PlayerData;
            PlayerData.PlayerUlid = TokenExchangeResponse.Subject_ulid;
            PlayerData.Token = TokenExchangeResponse.Access_token;
            
            ULootLockerSDKManager::StartSessionManual(PlayerData);

            // Make sure to save the player ulid as that will be used for subsequent requests
            TokenExchangeResponse.Subject_ulid;
        })
    );
}

4. Secure Calls on players behalf

Now that you have the impersonated user in the server, that session can be used to call any of the functions in the Unreal Engine Game SDK, increasing the functionality beyond what the Server SDK is capable of. Here is an example of submitting scores in the game server once an imaginary match finishes on behalf of the impersonated players.

void OnMatchFinished(const FMatchData& MatchData) {
	for(const FMatchPlayer& Player : MatchData.Players) {
		ULootLockerSDKManager::SubmitScore("", "match_highscore_" + MatchData.MatchId, Player.MatchScore, Player.MatchComment, FLootLockerSubmitScoreResponseDelegate::CreateLambda([](const FLootLockerSubmitScoreResponse& SubmitScoreResponse)
		{
			if (!SubmitScoreResponse.success)
			{
				UE_LOG(<LogCategory>, Warning, TEXT("Failed to submit score for player %s with error %s"), *SubmitScoreResponse.PlayerName, *SubmitScoreResponse.ErrorData.Message);
			}

			else
			{
				UE_LOG(<LogCategory>, Display, TEXT("Successfully submitted score for player %s with score %d"), *SubmitScoreResponse.PlayerName, SubmitScoreResponse.Score);
			}
		}), /*IMPORTANT: Make the request execute on behalf of *this* player:*/ Player.PlayerUlid);
	}
}

With Player ULID Impersonation

1. Start a Server Session

Refer to the Start Server documentation.

2. Impersonate Using Player ULID

When you want to take actions on behalf of a player, you will need that player's ULID. In this example we will start impersonation from an imaginary server command.

void OnCommandImpersonatePlayer(const FString& PlayerUlid) {
    ULootLockerServerForCpp::GameApiUserImpersonation(PlayerUlid, 
        FLootLockerServerTokenExchangeResponseDelegate::CreateLambda([](const FLootLockerServerTokenExchangeResponse& TokenExchangeResponse)
        {
            if (!TokenExchangeResponse.Success)
            {
                // Handle the error case
                UE_LOG(<LogCategory>, Error, TEXT("Failed to impersonate player with error %s"), *TokenExchangeResponse.ErrorData.Message);
                return;
            }

            // Successfully impersonated the player, now add game api session to local copy of game SDK
            FLootLockerPlayerData PlayerData;
            PlayerData.PlayerUlid = TokenExchangeResponse.Subject_ulid;
            PlayerData.Token = TokenExchangeResponse.Access_token;
            
            ULootLockerSDKManager::StartSessionManual(PlayerData);

            // Make sure to save the player ulid as that will be used for subsequent requests
            TokenExchangeResponse.Subject_ulid;
        })
    );
}

4. Secure Calls on Player's Behalf

This logic will be the same as for the token exchange example in that you simply call the Client SDK methods like you would on the client and make sure to supply the "forPlayerWithUlid" parameter with the ulid you want to execute the request for.

Conclusion

In this How-to we’ve gone through two different ways of allowing the Server to do actions as a player. With the Server API and Game API working hand in hand on your server, you can minimize the risk of tampering from the Game Client as you can reduce the ways that your players can interact with LootLocker. The full Unreal Game SDK and Unreal Server SDK is now at your disposal to be used for trusted and secure operations from your Game Server.

Last updated