How to Use Steem Keychain Login in Your Node JS Website/API

avatar

cover.png

Steem Keychain is a Steem wallet plugin for browsers that enables users to securely store your private keys and sign transactions using their key(s) securely.

At 2020 Steem Keychain should be the preferred way to log into any Steem dApps. In this post, I am going to write about how can we implement Steem Keychain authentication (also authorization) into our Node JS website or API.

Goal

Our goal is to verify a user is a user they are claimed to be. So, we are going to verify by having them sign a predefined message using any of their private keys ( Posting, Active, Memo) and later trying to decode the signed message using their public key (Posting, Active, Memo - whichever was used to sign). If we can recover the original message, this validates that the user has to access/owns the account. So, they are authenticated. Now we can issue them a token (JSON Web Tokens) signed by us to authorize the use of our website/API.

Client

We need a predefined message preferably unique for each user. We will ask our users to sign the message using their Posting key. After signing we are going to send a POST request with the username, message, and signed message to our API login endpoint /users/login. API.post is an interface to send HTTP request using axios. If successful our server would respond with a JWT token which we can use to access/modify data.

screenshot-01.png

Users will get a popup like this to sign the message.

Here is how the code might look like.

const message = Date.now(); // Generating the message

window.steem_keychain.requestSignBuffer('username', message, 'Posting', async (r) => {
    if (r.success) {
        // User has signed the message
        try {
            // We are sending HTTP POST request to our API login endpoint
            // with the username, message, and the signed message
            const { token } = await API.post('users/login', {
              username,
              message,
              signed_message: r.result,
            });
            
            // Saving the token into localStorage for later use
            localStorage.setItem('token', token);
            
            // More codes to use the received token
        } catch(e) {
            console.log(e);
        }
    }
});

Server

We have seen how to ask users to sign and send the signed message to our login endpoint. Now let's see how the login endpoint might look like. I am going to assume we are using Express JS for our backend server.

First, we pick username, message, and signed message from the POST request's body. Then we are going to fetch the users Publick posting key from the chain. After that, we are going to recover the public key from the signed message and match it against the public key pulled from the chain.

If both match, we can safely assume the user is who they are claimed to be. We are going to issue them a JTW token which they can use to interact with our website/API.


const jwt = require('jsonwebtoken');
const { Router } = require('express');
const { Client, Signature, cryptoUtils } = require('dsteem');

const steemClient = new Client('https://api.steemit.com');
const router = new Router();

router.post('users/login', async (req, res) => {
    try {
        // Picking username, message, and signed messsage
        // from the request body
        const { username, message, signed_message: signedMessage} = req.body;

        // Fetching account info from the chain
        const [account] = await steemClient.database.getAccounts([username]);

        const pubPostingKey = account.posting.key_auths[0][0];

        // Recovering public key from the signed message
        const recoveredPubKey = Signature.fromString(signedMessage)
            .recover(cryptoUtils.sha256(message));

        if (pubPostingKey === recoveredPubKey.toString()) {
            // Public key matched.
            // We have verified the user has access to the account.

            // let's issue them a JTW token

            const token = jwt.sign({
              sub: username,
              // Any other data you may need
            }, process.env.JWT_SECRET, { expiresIn: '12h'});

            // Responding with the generated token
            return res.json({ token });
        }
    } catch(e) {
        console.log(e);
    }
    
    return res.json({ error: 'Invalid login details.' });
});

In the example above we are issuing only one token (access key), we can extend it to issue two or more tokens (refresh key) too. Also, make other endpoints to support full OAuth2 authentication flow. You can also extend it by generating the message on the server and saving them into the database along with the session to allow/control multiple sessions and enable users to end a session as we see on Facebook and Google.

Please let me know if you have any suggestions in the comments below.

I am not claiming this is the best way to authenticate users using Steem Keychain. Please use it at your own risk and test the codes in your projects. I can not be held responsible for any loss might happen as a result of using these codes.

MM.png



0
0
0.000
15 comments
avatar

You post has been manually curated by BDvoter Team! To know more about us please visit our website or join our Discord.

BDvoter Team

0
0
0.000
avatar

To listen to the audio version of this article click on the play image.

Brought to you by @tts. If you find it useful please consider upvoting this reply.

0
0
0.000
avatar

Wish I stayed in the computer world years ago and learned how to code and more. I guess its never to late but I just got so many things going on. Great post....

0
0
0.000
avatar

Thanks man. Yeah 100%, its never too late. Hard thing is to start as we all have many things going on.....

0
0
0.000
avatar

Brilliant. I am trying to do same thing in Python.

0
0
0.000
avatar

Great. @anthonyadavisii might be very interested in that. He is trying the same too.

0
0
0.000
avatar

I ended up going different way.
Server encode a token/secret using user's public key. Then user decode it using steem_keychain.requestVerifyKey. If successful then we got a handshake for the future.

0
0
0.000
avatar

It could be better in some cases than the way I wrote about. I am guessing we need to send 2 requests, one for fetching encoded message, another letting server know use successfully decoded the message.

But I think its more secured in some use cases. Thanks for sharing the idea.

0
0
0.000
avatar

Congratulations @reazuliqbal! You received a personal award!

Thank you for the witness votes you made to support your Steem community and for keeping the Steem blockchain decentralized

You can view your badges on your Steem Board and compare to others on the Steem Ranking

Do not miss the last post from @steemitboard:

Use your witness votes and get the Community Badge
Vote for @Steemitboard as a witness to get one more award and increased upvotes!
0
0
0.000
avatar

Congratulations @reazuliqbal! You received a personal award!

Ned is definitly not your friend anymore. Did you really downvote him?

You can view your badges on your Steem Board and compare to others on the Steem Ranking

Do not miss the last post from @steemitboard:

Downvote challenge - Add up to 3 funny badges to your board
Use your witness votes and get the Community Badge
Vote for @Steemitboard as a witness to get one more award and increased upvotes!
0
0
0.000