How To Create One-Click Sign in using Nami for Cardano

Nicholas Maselli
7 min readAug 18, 2022

--

Cardano is one of the most popular cryptocurrencies in the world currently sitting at a $18 billion market cap at the time of writing along with a huge social media following.

However, while trying to create my Cardano NFT minting platform Saturn, I needed to read technical articles about Ethereum’s Metamask such as this one to learn how to create a one-click crypto login system and then apply that knowledge to Cardano. This knowledge did not always transfer cleanly, so I’m writing this article to help those building Cardano Apps and Dapps to create a login system with Cardano using the Nami Wallet!

Nami Cardano Wallet

Note: This method applies to all other Cardano Wallets as well such as Eternl, Flint, Gero, Typhoon, Nufi, and others.

To begin, we first need a quick primer traditional login systems works. Traditional login systems will ask for a username and password to register and also login. The username and password will be sent over a secure https connection and reach the backend. Once at the backend, the system will look up the username in the database hash the sent password with the appropriate salt that is found in the database entry and compare the calculated password hash to the hash stored in the database.

Traditional Login Steps:

  1. Ask for username and password
  2. Send username and password to server over https
  3. Get username data from the database (password hash and salt)
  4. Hash the given password with the salt and compare to the password hash stored in the database
  5. If they are a match, send down a JWT / Refresh token to the user to login
Traditional login with password Hashing using bcrypt

Traditional login systems have served us well for a long time but come with some major drawbacks.

Drawbacks to Traditional Login Systems:

  1. We often need to register with a new email / username / password for every website
  2. We often forget which password goes to which site
  3. Some sites may not hash passwords correctly (or at all!) which could be hacked
  4. If we use the same password for many different sites, we could have our password
  5. If our password is too easy to guess, we could be open to a rainbow table attack

Google and single sign on systems have helped but they involve giving all your data to those companies. With these drawbacks in mind we want to use a new login system.

Cardano Logo

Crypto Login Systems:

Crypto login systems work in a similar way to traditional login systems, but use the wallet’s public address as the username and a message signed by the address’s private key as the password.

We don’t want to use the public and private keys from the wallet directly as the private key should never leave the wallet.

To do this, when a user attempts to use a crypto login and sends their public address, we need to generate a nonce on the backend. A nonce is a 1 time password that we will ask the user to sign so that we know they own the private key associated with the public address. (Note: a user will get a new nonce each time they attempt to login)

We respond to the client’s request with the nonce, the client’s crypto wallet signs the nonce with the private key, and the client sends this signed message along with their public address and nonce back to the server. Finally our backend checks that the nonce is correct for this user and verifies that the signed message was in fact signed by the user’s public key.

Crypto Login Steps:

  1. Ask the user to enter the app (simple button click)
  2. Crypto wallet sends public address to server
  3. Server stores and sends back a nonce
  4. Client crypto wallet signs the nonce with their private key and sends it to the server
  5. Server checks the nonce and the signed message to ensure it was signed by the wallet’s private key
  6. Server sends back JWT / Refresh tokens
Nami Login

As you can see, a crypto login system comes with NONE of the drawbacks of a traditional login system. The only drawback is the user needs to own a wallet.

How To Build A Crypto Login System With Nami:

Now that we have the technical details sorted out its time to actually build a crypto login system with Nami.

What we will need:

  1. A front end
  2. A back end
  3. A database
  4. A Nami Wallet (browser extension)

You can use NextJS (which supports backend api routes) and PostgresSQL. For this article I am only going to be showing code for the Nami Wallet / Cardano Wallets as articles about Nextjs / Postgres exist in plenty.

NextJS Javascript Framework

Connecting to Nami:

First, we need to connect our Nami wallet when a user clicks a button. Here is a simple function used to connect our Cardano.

export const connect = async () => {
const cardano = window.cardano;
const api = await cardano.nami.enable();
return api;
};

This is a very, very simple function. The function we use for our NFT minting platform Saturn is more complex with error handling and network detection, but the code above gets the job done for now. If you are using another wallet like Flint, replace cardano.nami.enable()with cardano.flint.enable() and a similar pattern for the other Cardano wallets.

Note: For live examples of how to interface with the Nami wallet. I have created the open source repo for ADA Blobs which is the first Cardano smart contract auction NFT project released in 2021.

Now that we have connected our Nami wallet, we need to create an API that has our backend create a nonce, store it in the database, and send it back to the client.

Signing the Nonce with Nami:

When the client receives the Nonce, we need to sign it with our Cardano wallet like this:

export const signData = async (api, nonce) => {
const hexAddresses = await api.getUsedAddresses();
const messageToSign = toHex(nonce);
const signedMessage = await api.signData(hexAddresses[0], messageToSign);
return signedMessage;
};
export const toHex = (bytes) => Buffer.from(bytes).toString('hex');

Note: “api” is the api we returned from our previous connect function. Here I am directly passing in the nonce but you can also pass in the nonce as a string with some additional data like I have done with Saturn. Now we need to send data to the server to verify!

Verifying the Signature On The Server:

We need to now pass our address, nonce, and signed nonce back to the server. We need to send all 3 as the server must check if the nonce you signed is actually the nonce they sent you.

Now here is where the fun begins. In order to verify this signature we need this code.

export const verifySignature = async (address: string, payload: string, signature: any) => {const coseSign1Hex = signature.signature;
const coseKey = signature.key;
const coseSign1 = Message.COSESign1.from_bytes(Buffer.from(coseSign1Hex, 'hex'));
const payloadCose = coseSign1.payload();
// Verify Payload hereconst protectedHeaders = coseSign1.headers().protected().deserialized_headers();const addressCose = Cardano.Address.from_bytes(protectedHeaders.header(Message.Label.new_text('address')).as_bytes());const addressCose = Cardano.Address.from_bytes(protectedHeaders.header(APILoader.Message.Label.new_text('address')).as_bytes());
const key = Message.COSEKey.from_bytes(Buffer.from(coseKey, 'hex'));
const publicKeyBytes = key.header(Message.Label.new_int(APILoader.Message.Int.new_negative(Message.BigNum.from_str('2')))).as_bytes();const publicKeyCose = Cardano.PublicKey.from_bytes(publicKeyBytes);// Verify Address hereconst ed25519Signature = Cardano.Ed25519Signature.from_bytes(coseSign1.signature());
const data = coseSign1.signed_data().to_bytes();
const verified = publicKeyCose.verify(data, ed25519Signature);
return verified;

Oh boy, where to begin. Above is the code required to verify message signatures. As of 8/18/2022, one of my primary criticisms of Cardano is the lack of simple message signing libraries as you can see.

In the above code Message refers to the message signing library (Github below) and Cardano refers to the serialization library (Github also below)

The code is using the signature key and to obtain the signed payload and compare it to the given payload (our nonce) the code then checks to ensure the address we sent them is the same the address on the signature. The Ed25519 signatures you see are Elliptic curve encryption

The libraries we are using here are:

  1. The emurgo message signing library https://github.com/Emurgo/message-signing
  2. The cardano serialization library https://github.com/Emurgo/cardano-serialization-lib
  3. Nami https://github.com/Berry-Pool/nami-wallet

I would highly recommend looking at the documentation for these libraries. The code for verifying the address which I have commented in the above code is particularly long but exists in the Nami open source repo.

Public Private Key Message Signing

Finishing Up:

After we have verified that the user signed a message with their private key and that the message was the nonce we sent them, we can log the user in by sending them a JWT token and a refresh token. This works exactly the same way as a traditional login system so you can use the many resources that exist for this part.

I hope you enjoyed this read on creating a wallet login with Cardano wallets! To see it action, check out https://saturnnft.io/ or if you have any questions send me a tweet https://twitter.com/TheAvatarNick!

--

--