How to Use Steem Keychain Login in Your Node JS Website/API
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.
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.
Brilliant. I am trying to do same thing in Python.
Great. @anthonyadavisii might be very interested in that. He is trying the same too.
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.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.
!steem2email
Emailed 👌
Powered by witness untersatz!
!steem2email
Sorry, I don't have your email yet.
Register your email by sending a STEEM donation (any amount) to @steem2email with your email in the memo (encryption recommended, use Steem Keychain!).
Powered by witness untersatz!
You post has been manually curated by BDvoter Team! To know more about us please visit our website or join our Discord.
BDvoter Team
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....
Thanks man. Yeah 100%, its never too late. Hard thing is to start as we all have many things going on.....
Congratulations @reazuliqbal! You received a personal award!
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:
Vote for @Steemitboard as a witness to get one more award and increased upvotes!
Congratulations @reazuliqbal! You received a personal award!
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:
Vote for @Steemitboard as a witness to get one more award and increased upvotes!
Congratulations @reazuliqbal! You received a personal award!
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:
Vote for @Steemitboard as a witness to get one more award and increased upvotes!
Congratulations @reazuliqbal! You received a personal award!
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:
Vote for @Steemitboard as a witness to get one more award and increased upvotes!
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.