Work in Progress: A Simple Flask App for Hive Keychain Login

avatar
(Edited)

Hey everyone,

Today I wanted to share something I've been tinkering with – a very early, crude, and simple Flask application designed to handle a Hive Keychain login using a posting key signature. This is definitely a work in progress, but I thought I'd put it out there for anyone interested.

The project is called flask_keychain and the idea is to provide a basic backend that can:

  1. Receive a login request from a frontend that uses Hive Keychain to sign a message.
  2. Verify the signature against the user's posting key.
  3. If valid, issue a simple session token.

How It Works (The Gist)

The system involves a simple HTML frontend that uses JavaScript to interact with the Hive Keychain browser extension, and a Python Flask backend to verify the signed message.

  1. Frontend (JavaScript & Keychain):

    • When you enter your username and click "Login," the JavaScript captures the username.
    • It then calls window.hive_keychain.requestSignBuffer(). This prompts Hive Keychain to ask you to sign a message (in this basic example, it's the current UTC date/time string) using the posting key of the entered username.
    • If you approve in Keychain, the extension returns the signature (response.result) and the public posting key (response.publicKey) that was used.
    • The JavaScript then sends your username, the signature (as "challenge"), the publicKey, and the original message (as "proof") to the Flask backend's /login endpoint.

    Here's the core JavaScript that handles the Keychain interaction and the call to the backend:

    function getCurrentUTCDateTime() {
      const now = new Date();
      return now.toISOString();
    }
    document
      .getElementById("loginForm")
      .addEventListener("submit", function (e) {
        e.preventDefault();
        const username = document.getElementById("username").value.trim();
        const status = document.getElementById("status");
        status.textContent = "";
        if (!username) {
          status.textContent = "Please enter your Hive username.";
          return;
        }
        if (typeof window.hive_keychain === "undefined") {
          status.textContent = "Hive Keychain extension not detected!";
          return;
        }
        const datetimeToSign = getCurrentUTCDateTime(); // This will be our message
        window.hive_keychain.requestSignBuffer(
          username,
          datetimeToSign, // Message to sign
          "Posting", // Key type
          function (response) {
            if (response.success) {
              status.textContent = "Posting signed! Sending to API...";
              const signature = response.result;
              const pubkey =
                response.publicKey ||
                (response.data && response.data.publicKey) ||
                null;
              if (!pubkey) {
                status.textContent =
                  "Could not retrieve public key from Keychain response.";
                return;
              }
              const payload = {
                challenge: signature, // The signature from Keychain
                username: username,
                pubkey: pubkey, // The public key Keychain used
                proof: datetimeToSign, // The original message that was signed
              };
              fetch("/login", {
                method: "POST",
                headers: { "Content-Type": "application/json" },
                body: JSON.stringify(payload),
              })
                .then((r) => r.json())
                .then((data) => {
                  if (data.success) {
                    let msg = "Login successful! <br>";
                    if (data.token) {
                      localStorage.setItem("token", data.token);
                      msg += ` Token: <code>${data.token}</code> <br>`;
                    }
                    status.innerHTML = msg;
                  } else {
                    status.textContent = data.error || "Login failed.";
                  }
                })
                .catch((err) => {
                  status.textContent = "API error: " + err;
                });
            } else {
              status.textContent = "Keychain signature failed.";
            }
          },
        );
      });
    
  2. Backend (Python Flask & hive-nectar):

    • The /login route in Flask receives the username, signature (referred to as "challenge" from the frontend), publicKey, and original message (referred to as "proof" from the frontend).
    • It fetches the actual posting keys for the provided username from the Hive blockchain using hive-nectar.
    • It verifies that the publicKey sent by the client is indeed one of the account's valid posting keys.
    • It then uses nectar-graphenebase's (a component of hive-nectar) verify_message function to check if the signature is valid for the given message and publicKey.
    • If everything checks out, it generates a secure random session token (using secrets.token_urlsafe) and sends it back to the client.

    Here’s the key Python snippet from app.py for the verification:

    # (Previous steps: get data from request, fetch account's posting keys)
    # 'message' is what Keychain signed (datetimeToSign from JS, sent as 'proof')
    # 'signature' is the hex string from Keychain (sent as 'challenge' from JS)
    # 'pubkey' is the public key Keychain reported using for signing
    
    # Check that provided pubkey is one of the account's actual posting keys
    if pubkey not in posting_keys:
        return jsonify({
            "success": False,
            "error": "Provided public key is not a valid posting key for this account.",
        }), 400
    
    # Verify signature
    try:
        recovered_pubkey_bytes = verify_message(message, bytes.fromhex(signature))
        recovered_pubkey_str = str(PublicKey(recovered_pubkey_bytes.hex(), prefix="STM"))
        valid = recovered_pubkey_str == pubkey
    except Exception as e:
        return jsonify({"success": False, "error": f"Signature verification error: {str(e)}"}), 400
    
    if not valid:
        return jsonify({"success": False, "error": "Signature is invalid."}), 401
    
    # Success: generate and return a session token
    token = secrets.token_urlsafe(32)
    sessions[token] = username # Simple in-memory store for this example
    return jsonify({"success": True, "username": username, "token": token})
    

This frontend/backend interaction provides a basic but functional way to authenticate a Hive user via their posting key.

The Basic Frontend

I've included a super simple HTML template (login.html) that just provides a basic login button which uses the JavaScript above to trigger the Keychain signature request and send it to the Flask backend. If successful, it just displays the token.

The very basic login page showing a successful token response.

Where to Find It

This is very much a proof-of-concept and a starting point. If you're interested in playing around with it or seeing the basic structure, you can find the code on GitHub:
https://github.com/TheCrazyGM/flask_keychain

It's not meant to be a production-ready solution as-is, but more of a demonstration of how one might start building a Flask backend for Hive Keychain posting key logins. Maybe it'll be useful to someone looking to integrate Hive login into their Python web projects!

EDIT: I would like to give a shoutout to @sagarkothari88 who made the distriator api login, which inspired this idea.

As always,
Michael Garcia a.k.a. TheCrazyGM



0
0
0.000
9 comments
avatar

Doing the same thing for https://hiveearnings.botlord.eu/ using flask, signin via Hive Keychain. It’s not really needed yet (no info there yet that requires login), but wanted to give it a try as well, and will need it in the future.

!HOPE
!PIZZA
!INDEED
!ALIVE
!BBH
!STRIDE
!WEIRD
!DUO
!PIMP

0
0
0.000
avatar
(Edited)

I have future use, which is why I looked into it, and while I was working on some tools making use of Distriator the other day, the api login / return token just kinda meshed with what I had in mind. And of course python is more my thing for the backend, so wanted to give it a shot using hive-nectar (which also added to my knowledge how the cryptography works in it, as it still needs some work there too.)

0
0
0.000
avatar

Tell me more about Distriator!!! ;-) Sounds interesting!
BTW: I’ve recently switched the HiveEarnings tool too the Hive-Nectar lib as well! Still want to do a post about it too ;-)

!HOPE
!PIZZA
!INDEED
!ALIVE
!BBH
!STRIDE
!WEIRD

0
0
0.000
avatar

I'm glad someone is using it! Pypi tells me people are downloading it, but I don't hear many first hand accounts. So, please feel free to reach out to me of something isn't working right!

As far as what I was doing using the Distriator api, I'll have to ask my PM if he even wants me talking about that yet. 😅

0
0
0.000
avatar

Lol! I think I know your PM as well ;-)
Regarding Nectar: loving it, and so far no issues! Will let you know if that changes :-D Currently also using it in my next project! Would also love to recode the tipping bots to start using it as well. There's future in Nectar, there's none in Beem...
!HOPE
!PIZZA
!INDEED
!ALIVE
!BBH
!STRIDE
!WEIRD

0
0
0.000
avatar

Given that I don't (yet) know what a flask app is, I don't have much to say on this tool yet, other than it sounds like it would be very useful. 😁 🙏 💚 ✨ 🤙

0
0
0.000