Skip to content

Web3, Authentication, Web3Identity & Signatures

Nicolas Lorusso edited this page Jan 8, 2024 · 9 revisions

dApp authentication process

Allows the user authentication in the application via web3 wallet signing.

The class: DappWeb3Authenticator : IWeb3VerifiedAuthenticator.

The LoginAsync process consists in:

  1. Connects via web socket (Socket.IO lib) to the auth-server https://auth-api.decentraland.zone/https://auth-api.decentraland.org
  2. Request a signature for an ephemeral login message
  3. The server responds with a requestId
  4. The client opens a new browser tab through explorer-website to let the user sign through a custom installed wallet using the requestId
  5. After the user signs the payload, the server sends a message to the client with the resultant auth-chain information

Identities expires after 1 week.

Identity cache management & local storage

After the user successfully authenticates, the identity is stored in two sources, via ProxyWeb3Authenticator:

  • PlayerPrefs PlayerPrefsIdentityProvider, so the identity can be used in the following sessions without the need of the login process
  • Memory MemoryWeb3IdentityCache, so it can be accessed anywhere without performance implications

How to get the user address

It is required that the user is already authenticated, through IWeb3Authenticator.LoginAsync(..) during the application flow.

Then you can access the address by: IWeb3IdentityCache.Identity.Address. Example:

public class MyClass
{
    private readonly IWeb3IdentityCache web3IdentityCache;

    public MyClass(IWeb3IdentityCache web3IdentityCache)
    {
        this.web3IdentityCache = web3IdentityCache;
    }

    public void DoSomething()
    {
        Web3Address ownUserId = web3IdentityCache.Identity!.Address;
        Debug.Log($"My address is: {ownUserId}");
    }
}

How to authorize a request with the auth-chain

Some systems in Decentraland requires a user authorization to access different kind of resources, like saving your profile.

Here is how you can get an AuthChain:

using AuthChain authChain = IWeb3IdentityCache.Identity!.Sign("your payload information");

Depends on the required request structure, it may vary how you can fill the parameters, but here is an example:

private readonly IWeb3IdentityCache web3IdentityProvider;

private void SignRequest(UnityWebRequest unityWebRequest)
{
    using AuthChain authChain = web3IdentityProvider.Identity!.Sign("your payload information");

    var i = 0;

    foreach (AuthLink link in authChain)
    {
        unityWebRequest.SetRequestHeader($"x-identity-auth-chain-{i}", link.ToJson());
        i++;
    }
}

Signing messages into the blockchain

Web3 operations requires to be signed by a wallet (ie: Metamask). For example an SDK scene wants to allow the user to make an ERC20 transaction of XX tokens, or get the current ERC20 balance, or get the gas price, etc.

Based on Metamask JSON RPC Api here is how you can execute personal_sign or wallet_getPermissions methods:

GetPermissionResponse[] permissions = await SendAsync<GetPermissionResponse[]>(new EthApiRequest
{
    method = "wallet_getPermissions",
    @params = Array.Empty<object>(),
}, ct);

string signature = await SendAsync<string>(new EthApiRequest
{
    method = "personal_sign",
    @params = new object[] { "0x00", identityCache.Identity!.Address.ToString() },
}, ct);

[Serializable]
public struct GetPermissionResponse
{
    public string id;
    public string parentCapability;
    public string invoker;
    public long date;
    public Caveats[] caveats;

    [Serializable]
    public struct Caveats
    {
        public string type;
        public string[] value;
    }
}

public class MyClass
{
    private readonly IEthereumApi ethereumApi;
    private readonly IWeb3IdentityCache identityCache;

    public MyClass(IEthereumApi ethereumApi,
        IWeb3IdentityCache identityCache)
    {
        this.ethereumApi = ethereumApi;
        this.identityCache = identityCache;
    }

    public async UniTask MakePersonalSign(CancellationToken ct)
    {
        string signature = await ethereumApi.SendAsync<string>(new EthApiRequest
        {
            method = "personal_sign",
            @params = new object[] { "0x00", identityCache.Identity!.Address.ToString() },
        }, ct);

        Debug.Log($"My signature is: {signature}");
    }

    public async UniTask GetPermissions(CancellationToken ct)
    {
        GetPermissionResponse[] permissions = await SendAsync<GetPermissionResponse[]>(new EthApiRequest
        {
            method = "wallet_getPermissions",
            @params = Array.Empty<object>(),
        }, ct);

        Debug.Log($"List of permissions is: {JsonUtility.ToJson(permissions)}");
    }
}

Clone this wiki locally