Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

update session validation logic #97

Merged
merged 1 commit into from
Aug 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 71 additions & 28 deletions Assets/Treasure/TDK/Runtime/Identity/TDK.Identity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,34 +76,59 @@ private async Task<string> SignLoginPayload(AuthPayload payload)
return await TDKServiceLocator.GetService<TDKThirdwebService>().Wallet.Sign(payloadToSign);
}

private async Task CreateSessionKey(string backendWallet, List<string> callTargets)
private async Task CreateSessionKey(string backendWallet, List<string> callTargets, BigInteger nativeTokenLimitPerTransaction)
{
var permissionStartTimestamp = (decimal)Utils.GetUnixTimeStampNow() - 60 * 60;
var permissionEndTimestamp = (decimal)(Utils.GetUnixTimeStampNow() + TDK.Instance.AppConfig.SessionLengthSeconds);
var permissionEndTimestamp = (decimal)(Utils.GetUnixTimeStampNow() + TDK.Instance.AppConfig.SessionDurationSec);
await TDKServiceLocator.GetService<TDKThirdwebService>().Wallet.CreateSessionKey(
signerAddress: backendWallet,
approvedTargets: callTargets,
nativeTokenLimitPerTransactionInWei: "0",
nativeTokenLimitPerTransactionInWei: nativeTokenLimitPerTransaction.ToString(),
permissionStartTimestamp: permissionStartTimestamp.ToString(),
permissionEndTimestamp: permissionEndTimestamp.ToString(),
reqValidityStartTimestamp: permissionStartTimestamp.ToString(),
reqValidityEndTimestamp: permissionEndTimestamp.ToString()
);
}

private bool ValidateActiveSigner(string backendWallet, List<string> callTargets, string signer, IEnumerable<string> approvedTargets, string endTimestamp)
private async Task<List<User.Signer>> GetActiveSigners()
{
var signerApprovedTargets = approvedTargets.Select(approvedTarget => approvedTarget.ToLowerInvariant());
var expirationDate = BigInteger.Parse(endTimestamp);
var activeSigners = await TDKServiceLocator.GetService<TDKThirdwebService>().Wallet.GetAllActiveSigners();
return activeSigners.Select(activeSigner => new User.Signer
{
isAdmin = activeSigner.isAdmin ?? false,
signer = activeSigner.signer,
approvedTargets = activeSigner.permissions.approvedCallTargets.ToArray(),
nativeTokenLimitPerTransaction = activeSigner.permissions.nativeTokenLimitPerTransaction,
startTimestamp = activeSigner.permissions.startDate,
endTimestamp = activeSigner.permissions.expirationDate
}).ToList();
}

private bool ValidateActiveSigner(string backendWallet, List<string> callTargets, BigInteger nativeTokenLimitPerTransaction, User.Signer signer)
{
var requestedCallTargets = callTargets.Select(callTarget => callTarget.ToLowerInvariant());
var signerApprovedTargets = signer.approvedTargets.Select(approvedTarget => approvedTarget.ToLowerInvariant());
var nowDate = Utils.GetUnixTimeStampNow();
var minEndDate = Utils.GetUnixTimeStampNow() + TDK.Instance.AppConfig.SessionMinDurationLeftSec;
var maxEndDate = Utils.GetUnixTimeStampIn10Years();
return
// Expiration date is at least 1 hour in the future
expirationDate > Utils.GetUnixTimeStampNow() + 60 * 60 &&
// Expiration date is not too far in the future
expirationDate <= Utils.GetUnixTimeStampIn10Years() &&
// Expected backend wallet is signer
signer.ToLowerInvariant() == backendWallet &&
// All requested call targets are approved
callTargets.All(callTarget => signerApprovedTargets.Contains(callTarget));
signer.signer.ToLowerInvariant() == backendWallet.ToLowerInvariant() &&
// If this signer is an admin, they always have the required permissions
(signer.isAdmin ||
// Start date has passed
(long.Parse(signer.startTimestamp) < nowDate &&
// Expiration date meets minimum time requirements
long.Parse(signer.endTimestamp) >= minEndDate &&
// Expiration date is not too far in the future (10 years because Thirdweb uses this for admins)
// This check is to prevent sessions from being created with timestamps in milliseconds
long.Parse(signer.endTimestamp) <= maxEndDate &&
// All requested targets are approved
requestedCallTargets.All(callTarget => signerApprovedTargets.Contains(callTarget)) &&
// Native token limit per transaction is approved
BigInteger.Parse(signer.nativeTokenLimitPerTransaction) >= nativeTokenLimitPerTransaction)
);
}
#endregion

Expand All @@ -123,16 +148,27 @@ private bool ValidateActiveSigner(string backendWallet, List<string> callTargets

var backendWallet = await TDK.Instance.AppConfig.GetBackendWallet();
var callTargets = await TDK.Instance.AppConfig.GetCallTargets();
// Check if any active signers match the call targets
var hasActiveSession = user.allActiveSigners.Any((signer) =>
var requiresSession = !string.IsNullOrEmpty(backendWallet) && callTargets.Count > 0;
if (requiresSession)
{
return ValidateActiveSigner(backendWallet, callTargets, signer.signer, signer.approvedTargets, signer.endTimestamp);
});
var nativeTokenLimitPerTransaction = await TDK.Instance.AppConfig.GetNativeTokenLimitPerTransaction();

if (!hasActiveSession)
{
TDKLogger.Log("Existing user session does not have required permissions. User must start a new session.");
return null;
// Check if any active signers match the call targets
var hasActiveSession = user.allActiveSigners.Any((signer) =>
{
return ValidateActiveSigner(
backendWallet,
callTargets,
nativeTokenLimitPerTransaction,
signer
);
});

if (!hasActiveSession)
{
TDKLogger.Log("Existing user session does not have required permissions. User must start a new session.");
return null;
}
}

_address = user.smartAccountAddress;
Expand Down Expand Up @@ -181,13 +217,15 @@ public async Task<string> StartUserSession(ChainId sessionChainId = ChainId.Unkn

var backendWallet = await TDK.Instance.AppConfig.GetBackendWallet();
var callTargets = await TDK.Instance.AppConfig.GetCallTargets();
var nativeTokenLimitPerTransaction = await TDK.Instance.AppConfig.GetNativeTokenLimitPerTransaction();
var requiresSession = !string.IsNullOrEmpty(backendWallet) && callTargets.Count > 0;
var didCreateSession = false;

// If smart wallet isn't deployed yet, create a new session to bundle the two txs
if (!await TDKServiceLocator.GetService<TDKThirdwebService>().Wallet.IsDeployed())
if (!await TDKServiceLocator.GetService<TDKThirdwebService>().Wallet.IsDeployed() && requiresSession)
{
TDKLogger.Log("Deploying smart wallet and creating session key");
await CreateSessionKey(backendWallet, callTargets);
await CreateSessionKey(backendWallet, callTargets, nativeTokenLimitPerTransaction);
didCreateSession = true;
}

Expand All @@ -202,13 +240,13 @@ public async Task<string> StartUserSession(ChainId sessionChainId = ChainId.Unkn
_authToken = await TDK.API.LogIn(payload, signature);

// Smart wallet was already deployed, so check for existing sessions
if (!didCreateSession)
if (!didCreateSession && requiresSession)
{
var hasActiveSession = false;
List<SignerWithPermissions> activeSigners = null;
List<User.Signer> activeSigners = null;
try
{
activeSigners = await TDKServiceLocator.GetService<TDKThirdwebService>().Wallet.GetAllActiveSigners();
activeSigners = await GetActiveSigners();
}
catch (Exception e)
{
Expand All @@ -222,14 +260,19 @@ public async Task<string> StartUserSession(ChainId sessionChainId = ChainId.Unkn
// Check if any active signers match the call targets
hasActiveSession = activeSigners.Any((signer) =>
{
return ValidateActiveSigner(backendWallet, callTargets, signer.signer, signer.permissions.approvedCallTargets, signer.permissions.expirationDate);
return ValidateActiveSigner(
backendWallet,
callTargets,
nativeTokenLimitPerTransaction,
signer
);
});
}

if (!hasActiveSession)
{
TDKLogger.Log("Creating new session key");
await CreateSessionKey(backendWallet, callTargets);
await CreateSessionKey(backendWallet, callTargets, nativeTokenLimitPerTransaction);
}
else
{
Expand Down
21 changes: 14 additions & 7 deletions Assets/Treasure/TDK/Runtime/TDKConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Numerics;
using Thirdweb;

namespace Treasure
{
Expand Down Expand Up @@ -78,7 +80,7 @@ public Env Environment
}

public string CartridgeTag => _general._cartridgeTag;
public string CartridgeName =>_general. _cartridgeName;
public string CartridgeName => _general._cartridgeName;
public Sprite CartridgeIcon => _general._cartridgeIcon;

public string TDKApiUrl => Environment == Env.DEV ? _general._devApiUrl : _general._prodApiUrl;
Expand All @@ -89,7 +91,8 @@ public Env Environment
public ChainId DefaultChainId =>
Environment == Env.DEV ? _connect._devDefaultChainId : _connect._prodDefaultChainId;

public int SessionLengthSeconds => _connect._sessionDurationSec;
public int SessionDurationSec => _connect._sessionDurationSec;
public int SessionMinDurationLeftSec => _connect._sessionMinDurationLeftSec;

public string AnalyticsApiUrl => Environment == Env.DEV ? _analytics._devApiUrl : _analytics._prodApiUrl;

Expand Down Expand Up @@ -118,11 +121,12 @@ public async Task<List<string>> GetCallTargets()
return option != null ? option.callTargets.ConvertAll(ct => ct.ToLowerInvariant()) : new List<string>();
}

public async Task<double> GetNativeTokenLimitPerTransaction()
public async Task<BigInteger> GetNativeTokenLimitPerTransaction()
{
var chainId = await TDK.Connect.GetChainId();
var option = _connect._sessionOptions.Find(d => d.chainId == chainId);
return option?.nativeTokenLimitPerTransaction ?? 0;
var value = option?.nativeTokenLimitPerTransaction ?? 0;
return BigInteger.Parse(Utils.ToWei(value.ToString()));
}

public T GetModuleConfig<T>()
Expand Down Expand Up @@ -152,7 +156,8 @@ public static TDKConfig LoadFromResources()
public void SetConfig(SerializedTDKConfig config)
{
// General
_general = new GeneralConfig {
_general = new GeneralConfig
{
_cartridgeTag = config.general.cartridgeTag,
_cartridgeName = config.general.cartridgeName,
_devApiUrl = config.general.devApiUrl,
Expand All @@ -164,7 +169,8 @@ public void SetConfig(SerializedTDKConfig config)
};

// Connect
_connect = new ConnectConfig {
_connect = new ConnectConfig
{
_factoryAddress = config.connect.factoryAddress,
_devDefaultChainId = Constants.NameToChainId.GetValueOrDefault(
config.connect.devDefaultChainIdentifier ?? "",
Expand Down Expand Up @@ -194,7 +200,8 @@ public void SetConfig(SerializedTDKConfig config)
}

// Analytics
_analytics = new AnalyticsConfig {
_analytics = new AnalyticsConfig
{
_devApiUrl = config.analytics.devApiUrl,
_prodApiUrl = config.analytics.prodApiUrl,
};
Expand Down