0

I am building a loan dapp. The functionality is not easy to build but relatively straightforward. My question is though does anyone have any suggestions of how to build in user logins and security? The contracts are written in Solidity and I am using a Web3JS HTML front-end.

I would really appreciate any pointers you can give me.

Thanks

0

does anyone have any suggestions of how to build in user logins and security

One of the hard paradigm-shifts for newcomers to get over is the idea that users don't register at all, at least they shouldn't.

The most secure thing you can do is rely upon msg.sender to authenticate the user who has signed with a private key. You can collect and record attributes about the users, as appropriate, but understand that every user who interacts is authenticated without any help from us.

Client-side key management, the formation of committees and quorums that control those keys are external concerns, possibly even handled by other contracts. Generally, those innovations are of zero concern in smart contract design.

It's possible to generate addresses and key pairs, client-side (you should _never_ want to know a user's signing key) with JavaScript libraries like Eth-Crypto. It's even possible to relieve users of the burden of paying for gas (with alternative payment schemes that do show up in contracts).

TL;DR;

Everyone is already registered.

Some users may have memberships or privileges described in whitelists, but no one needs to register.

Servers

If you are building DApp with a server as well as blockchain then the logical topology is usually triangular - server, blockchain/contracts and user. In this context, the server is also a "client" from the contract's perspective, and it too signs with its own key. A server listen to the blockchain for transactions users committed without going through the server. It can use wallet addresses as unique identifiers and it can confirm conclusively that a web user is in control of an address by having the user sign an off-chain message and send it through at no cost. The user experience can be quite good without compromising user keys.

Hope it helps.

UPDATE

Picking up from Philip's comment. Some hints at implementation.

The flows are a little different from what you might expect. It's not like a user => server => database where the user goes through the server to storage. The user goes:

  • user => Ethereum => contract

and

  • user => browser => server

Also, the server goes:

  • Server => Ethereum => contract

The user, the server and the contract are three points of a triangle where each "talks" to the other two, some of the time.

The contract will have access control based on msg.sender which is always an authenticated transaction signer. It will use that to sort out permission.

The server will identify users by the same key, their address. Users will sign a message (JavaScript) and the server will verify the signature (JavaScript) to prove the message was signed by a certain address. You can scaffold this in a simplified approximation by having the user browser inspect the user accounts const accounts = await web3.eth.getAccounts();. Let the user select one of their accounts (it's an array) and pass it as form data. The server can trust that the client isn't lying about it and carry on. It is not secure. Later, you can tighten it up by making the user prove it by signing a message which the server will verify.

You have to decide on roles. Let us say there will be specialUsers, superUsers and everyone else. The general pool of unprivileged users doesn't need any special attention but you are free to record attributes about them as you go, in the contract (expensive) or on the server. Use their address as a Uid.

Set whomever deployed the contract as superUser. Let us further decide that the server will be the superUser, therefore in the deployment ceremony, the server will attend to it.

contract Groups  {

  address public superUser;

  constructor() public {
    superUser = msg.sender;
  }
}

We will guard certain functions. Before the constructor, insert:

modifier onlySuperUser {
  require(msg.sender == userUser, "403 - not allowed.");
  _;
}

SpecialUsers will be a whitelist and only the SuperUser can maintain it:

mapping(address => bool) public specialUsers;

Create a guard:

modifier onlySpecialUser {
  require(specialUser[msg.sender[, "403 - not allowed.";
  _;
}

Create maintenance:

function setSpecialUser(address user, bool isAllowed) public onlySuperUser {
  specialUser[user] = isAllowed;
}

That shows usage of the guards (modifier onlySuperUser).

There is a popular pattern,Ownable.sol (openzeppelin), everyone should start with. You can copy that and tweak it to create multiple roles. There is also a pattern called RBAC - role-based access control.

  • Dear Rob, thank-you for your thoughtful answer. It does help. Let me explain my thinking - I get what you are saying about no need to register. I also understand that if the user has a private key, then why do we need to also have a password? However, I need to have some control over WHO can use the system and in WHAT capacity. What I am thinking of is that I need some kind of list or array of Users with their associated rights but I have no idea how I would implement it. Have you ever done anything like that? – Philip Rutovitz Oct 25 at 3:13

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.