v1.3.0.1 (Epic Store release)
This commit is contained in:
+65
@@ -0,0 +1,65 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Steam;
|
||||
|
||||
namespace Barotrauma.Networking;
|
||||
|
||||
public enum AuthenticationTicketKind
|
||||
{
|
||||
SteamAuthTicketForSteamHost = 0,
|
||||
SteamAuthTicketForEosHost = 1,
|
||||
EgsOwnershipToken = 2
|
||||
}
|
||||
|
||||
[NetworkSerialize(ArrayMaxSize = UInt16.MaxValue)]
|
||||
readonly record struct AuthenticationTicket(
|
||||
AuthenticationTicketKind Kind,
|
||||
ImmutableArray<byte> Data) : INetSerializableStruct
|
||||
{
|
||||
public static async Task<Option<AuthenticationTicket>> Create(Endpoint serverEndpoint)
|
||||
{
|
||||
if (SteamManager.IsInitialized && SteamManager.GetSteamId().TryUnwrap(out var steamId))
|
||||
{
|
||||
if (serverEndpoint is EosP2PEndpoint)
|
||||
{
|
||||
var authTicket = await SteamManager.GetAuthTicketForEosHostAuth();
|
||||
return authTicket
|
||||
.Bind(t => t.Data != null ? Option.Some(t.Data) : Option.None)
|
||||
.Select(data => new AuthenticationTicket(AuthenticationTicketKind.SteamAuthTicketForEosHost, data.ToImmutableArray()));
|
||||
}
|
||||
else
|
||||
{
|
||||
var authTicket = SteamManager.GetAuthSessionTicketForSteamHost(serverEndpoint);
|
||||
var steamIdBytes = BitConverter.GetBytes(steamId.Value);
|
||||
return authTicket
|
||||
.Bind(t => t.Data != null ? Option.Some(t.Data) : Option.None)
|
||||
.Select(data => new AuthenticationTicket(
|
||||
AuthenticationTicketKind.SteamAuthTicketForSteamHost,
|
||||
steamIdBytes.Concat(data).ToImmutableArray()));
|
||||
}
|
||||
}
|
||||
|
||||
if (EosInterface.IdQueries.GetLoggedInPuids() is { Length: > 0 } puids)
|
||||
{
|
||||
var externalAccountIdsResult = await EosInterface.IdQueries.GetSelfExternalAccountIds(puids[0]);
|
||||
if (externalAccountIdsResult.TryUnwrapSuccess(out var externalAccountIds))
|
||||
{
|
||||
var epicAccountIdOption = externalAccountIds.OfType<EpicAccountId>().FirstOrNone();
|
||||
if (epicAccountIdOption.TryUnwrap(out var epicAccountId))
|
||||
{
|
||||
return (await EosInterface.Ownership.GetGameOwnershipToken(epicAccountId))
|
||||
.Select(t => new AuthenticationTicket(
|
||||
AuthenticationTicketKind.EgsOwnershipToken,
|
||||
Encoding.UTF8.GetBytes(t.Jwt.ToString()).ToImmutableArray()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Option.None;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
#nullable enable
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Threading.Tasks;
|
||||
using Barotrauma.Steam;
|
||||
|
||||
namespace Barotrauma.Networking;
|
||||
|
||||
abstract class Authenticator
|
||||
{
|
||||
public abstract Task<AccountInfo> VerifyTicket(AuthenticationTicket ticket);
|
||||
public abstract void EndAuthSession(AccountId accountId);
|
||||
|
||||
public static ImmutableDictionary<AuthenticationTicketKind, Authenticator> GetAuthenticatorsForHost(Option<Endpoint> ownerEndpointOption)
|
||||
{
|
||||
var authenticators = new Dictionary<AuthenticationTicketKind, Authenticator>();
|
||||
|
||||
if (EosInterface.Core.IsInitialized)
|
||||
{
|
||||
// Every kind of host should be able to do EOS ID Token authentication if they have EOS enabled
|
||||
authenticators.Add(AuthenticationTicketKind.EgsOwnershipToken, new EgsOwnershipTokenAuthenticator());
|
||||
|
||||
if (ownerEndpointOption.TryUnwrap(out var ownerEndpoint) && ownerEndpoint is EosP2PEndpoint)
|
||||
{
|
||||
// EOS P2P hosts do not have access to Steamworks
|
||||
authenticators.Add(AuthenticationTicketKind.SteamAuthTicketForEosHost, new SteamAuthTicketForEosHostAuthenticator());
|
||||
}
|
||||
}
|
||||
|
||||
if (!(ownerEndpointOption.TryUnwrap(out var ownerEndpoint2) && ownerEndpoint2 is EosP2PEndpoint) && SteamManager.IsInitialized)
|
||||
{
|
||||
// Steam P2P hosts and dedicated servers have access to Steamworks
|
||||
authenticators.Add(AuthenticationTicketKind.SteamAuthTicketForSteamHost, new SteamAuthTicketForSteamHostAuthenticator());
|
||||
}
|
||||
|
||||
return authenticators.ToImmutableDictionary();
|
||||
}
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Barotrauma.Networking;
|
||||
|
||||
sealed class EgsOwnershipTokenAuthenticator : Authenticator
|
||||
{
|
||||
public override async Task<AccountInfo> VerifyTicket(AuthenticationTicket ticket)
|
||||
{
|
||||
var jwtOption = JsonWebToken.Parse(Encoding.UTF8.GetString(ticket.Data.AsSpan()));
|
||||
|
||||
if (!jwtOption.TryUnwrap(out var jwt)) { return AccountInfo.None; }
|
||||
var ownershipToken = new EosInterface.Ownership.Token(jwt);
|
||||
var accountIdOption = await ownershipToken.Verify();
|
||||
|
||||
if (!accountIdOption.TryUnwrap(out var accountId)) { return AccountInfo.None; }
|
||||
return new AccountInfo(accountId);
|
||||
}
|
||||
|
||||
public override void EndAuthSession(AccountId accountId) { /* do nothing */ }
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json;
|
||||
using System.Threading.Tasks;
|
||||
using RestSharp;
|
||||
|
||||
namespace Barotrauma.Networking;
|
||||
|
||||
sealed class SteamAuthTicketForEosHostAuthenticator : Authenticator
|
||||
{
|
||||
#warning FIXME change URL back to the non-experimental one once this passes QA
|
||||
private const string consentServerUrl = "https://barotraumagame.com/baromaster/experimental/";
|
||||
private const string consentServerFile = "getOwnerSteamId.php";
|
||||
private const int RemoteRequestVersion = 1;
|
||||
|
||||
public override async Task<AccountInfo> VerifyTicket(AuthenticationTicket ticket)
|
||||
{
|
||||
string ticketData = ToolBoxCore.ByteArrayToHexString(ticket.Data);
|
||||
|
||||
var client = new RestClient(consentServerUrl);
|
||||
|
||||
var request = new RestRequest(consentServerFile, Method.GET);
|
||||
request.AddParameter("authticket", ticketData);
|
||||
request.AddParameter("request_version", RemoteRequestVersion);
|
||||
|
||||
var response = await client.ExecuteAsync(request, Method.GET);
|
||||
if (!response.IsSuccessful) { return AccountInfo.None; }
|
||||
|
||||
try
|
||||
{
|
||||
var jsonDoc = JsonDocument.Parse(response.Content);
|
||||
Option<SteamId> steamId = Option.None;
|
||||
Option<SteamId> ownerSteamId = Option.None;
|
||||
foreach (var property in jsonDoc.RootElement.EnumerateObject())
|
||||
{
|
||||
if (!property.Name.ToIdentifier().Contains("SteamId")) { continue; }
|
||||
var accountIdOption = SteamId.Parse(property.Value.GetString() ?? "");
|
||||
if (accountIdOption.IsNone()) { continue; }
|
||||
if (property.Name.ToIdentifier() == "SteamId")
|
||||
{
|
||||
steamId = accountIdOption;
|
||||
}
|
||||
else if (property.Name.ToIdentifier() == "OwnerSteamId")
|
||||
{
|
||||
ownerSteamId = accountIdOption;
|
||||
}
|
||||
}
|
||||
var otherIds = ownerSteamId.TryUnwrap(out var id) ? new AccountId[] { id } : Array.Empty<AccountId>();
|
||||
return new AccountInfo(steamId.Select(static id => (AccountId)id), otherIds);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return AccountInfo.None;
|
||||
}
|
||||
}
|
||||
|
||||
public override void EndAuthSession(AccountId accountId) { /* do nothing */ }
|
||||
}
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Barotrauma.Steam;
|
||||
|
||||
namespace Barotrauma.Networking;
|
||||
|
||||
#if CLIENT
|
||||
using SteamAuthSessionInterface = Steamworks.SteamUser;
|
||||
#else
|
||||
using SteamAuthSessionInterface = Steamworks.SteamServer;
|
||||
#endif
|
||||
|
||||
sealed class SteamAuthTicketForSteamHostAuthenticator : Authenticator
|
||||
{
|
||||
private static Steamworks.BeginAuthResult BeginAuthSession(Steamworks.AuthTicket authTicket, SteamId clientSteamId)
|
||||
{
|
||||
if (!SteamManager.IsInitialized) { return Steamworks.BeginAuthResult.ServerNotConnectedToSteam; }
|
||||
if (authTicket.Data is null) { return Steamworks.BeginAuthResult.InvalidTicket; }
|
||||
|
||||
DebugConsole.Log("Authenticating Steam client " + clientSteamId);
|
||||
Steamworks.BeginAuthResult startResult = SteamAuthSessionInterface.BeginAuthSession(authTicket.Data, clientSteamId.Value);
|
||||
if (startResult != Steamworks.BeginAuthResult.OK)
|
||||
{
|
||||
DebugConsole.Log($"Steam authentication failed: failed to start auth session ({startResult})");
|
||||
}
|
||||
|
||||
return startResult;
|
||||
}
|
||||
|
||||
private static void EndAuthSession(SteamId clientSteamId)
|
||||
{
|
||||
if (!SteamManager.IsInitialized) { return; }
|
||||
|
||||
DebugConsole.Log($"Ending auth session with Steam client {clientSteamId}");
|
||||
SteamAuthSessionInterface.EndAuthSession(clientSteamId.Value);
|
||||
}
|
||||
|
||||
public override async Task<AccountInfo> VerifyTicket(AuthenticationTicket ticket)
|
||||
{
|
||||
if (ticket.Data.Length < 8) { return AccountInfo.None; }
|
||||
|
||||
var ticketData = ticket.Data.ToArray();
|
||||
var steamAuthTicket = new Steamworks.AuthTicket { Data = ticketData[8..] };
|
||||
var steamId = new SteamId(BitConverter.ToUInt64(ticketData.AsSpan()[..8]));
|
||||
|
||||
using var janitor = Janitor.Start();
|
||||
|
||||
(Steamworks.AuthResponse AuthResponse, SteamId OwnerSteamId)? authResult = null;
|
||||
void onValidateAuthTicketResponse(Steamworks.SteamId clientId, Steamworks.SteamId ownerClientId, Steamworks.AuthResponse response)
|
||||
{
|
||||
if (clientId != steamId.Value) { response = Steamworks.AuthResponse.AuthTicketInvalid; }
|
||||
authResult = (response, new SteamId(ownerClientId));
|
||||
}
|
||||
|
||||
SteamAuthSessionInterface.OnValidateAuthTicketResponse += onValidateAuthTicketResponse;
|
||||
janitor.AddAction(() => SteamAuthSessionInterface.OnValidateAuthTicketResponse -= onValidateAuthTicketResponse);
|
||||
var beginAuthSessionResult = BeginAuthSession(steamAuthTicket, steamId);
|
||||
|
||||
if (beginAuthSessionResult != Steamworks.BeginAuthResult.OK) { return AccountInfo.None; }
|
||||
|
||||
while (authResult is null)
|
||||
{
|
||||
await Task.Delay(32);
|
||||
}
|
||||
if (authResult.Value.AuthResponse != Steamworks.AuthResponse.OK) { return AccountInfo.None; }
|
||||
|
||||
return new AccountInfo(steamId, authResult.Value.OwnerSteamId);
|
||||
}
|
||||
|
||||
public override void EndAuthSession(AccountId accountId)
|
||||
{
|
||||
if (accountId is not SteamId steamId) { return; }
|
||||
SteamAuthSessionInterface.EndAuthSession(steamId.Value);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user