Skip to main content

Password Manager

Length: Medium
Complexity:
Duration: 10-12 minutes

Overview

This sample application represents a basic blockchain-based password manager. The app stores and retrieves encrypted, immutable passwords from the blockchain using mintBlue's SDK.

To improve the learning experience, we added three code challenges and their solutions. This approach is designed to help you become more familiar with using the mintBlue SDK.

Diagram explaining password manager example app

Prerequisites

You have two options to run the code:

  1. Create a project on your machine and copy the code
  2. Play with the code in CodeSandBox

How does the password manager work?

Below, you can find the code for a basic blockchain-based password manager. The password manager leverages mintBlue's SDK to store encrypted transactions using your keys on the blockchain. You can then again use these keys to decrypt a transaction's content to retrieve the password for a given account.

ℹī¸ The Non-Custodial Keys section in the docs explains in detail how this works.

const { Mintblue } = require("@mintblue/sdk");

// show the user how to use the app when they don't provide the right arguments
if (process.argv.length < 3 || !["get", "add"].includes(process.argv[2])) {
    console.log("Usage:");
    console.log("- Add/Update password: node manager.js add [account] [password]");
    console.log("- Retrieve password: node manager.js get [account]");
    process.exit();
}

const command = process.argv[2];
const account = process.argv[3];
let password = process.argv[4];

async function main() {
    const sdkToken = "<YOUR-SDK-TOKEN>";
    let client = await Mintblue.create({ token: sdkToken });
    let projectId = "<YOUR-PROJECT-ID>";

    if (command === "add") {
        // store account and password encrypted on blockchain
        const { txid } = await client.createTransaction({
            project_id: projectId,
            outputs: [
                {
                    type: "data",
                    value: { account, password },
                    encrypt: true,
                },
            ],
        });
        console.log(`Password stored encrypted in transaction: https://whatsonchain.com/tx/${txid}`);
    }

    if (command === "get") {
        // get all of this project's transactions
        const transactions = await client.listTransactions({ project_id: projectId });

        // find password record for requested account and decrypt it
        for (let i = 0; i < transactions.length; i++) {
            const txn = await client.getTransaction({
                txid: transactions[i].txid,
                parse: true, // default is true
            });

            // Show decrypted password to user when found
            if (txn.outputs[0].value.account === account) {
                console.log(`Password for ${account} is: ${txn.outputs[0].value.password}`);
                process.exit();
            }
        }
        console.log(`Password not found for: ${account}`);
    }
}

main();

To use the code, make sure to initialize npm with npm init -y and install the mintBlue SDK:

npm install @mintblue/sdk

Next, let's run the example.

How to run the example

Make sure to replace the <YOUR-SDK-TOKEN> with your SDK Access Token and <YOUR-PROJECT-ID> with your project ID. If you are running the code on your machine, name the file manager.js.

To run the example, you can execute the file with node. When you don't provide the required arguments, it will print how to use the password manager.

node manager.js

# Usage:
- Add/Update password: node manager.js add [account] [password]
- Retrieve password: node manager.js get [account]
  • Add a new account

Let's store our PayPal account using the following command.

node manager.js add paypal mysecretpass

# Output
Password stored encrypted in transaction: https://whatsonchain.com/tx/246d5a0be20819c96c5e605ef721c6581acaceebba59c7aec52c66974b422915
  • Retrieve password for an account

You can retrieve the password for your PayPal using the get command.

node manager.js get paypal

# Output
Password for paypal is: mysecretpass

Cool, right? Now, let's get our hands dirty.

Challenges

Challenges allow you to learn about mintBlue's SDK in a playful way. Let's get started!

Challenge 1: Add tags

Explanation: We want the user to be able to add a tag when creating a new account. This allows for better filtering capabilities to find accounts. You need to accept an additional argument from the terminal to add the tag. Here's how you can do this:

# node manager.js add [account] [password] [tag]

const command = process.argv[2];
const account = process.argv[3];
let password = process.argv[4];
let tag = process.argv[5]; # Accept tag

Goal: You need to modify the data output to store the tag. You can use the Data Output how-to guide if you are unsure how to do this.

After adding the tagging behavior, we should be able to use the add command like this with a finance tag.

node manager.js add paypal mysecretpass finance

When verifying the transaction in the mintBlue Console, it should look like this:

[
    {
        "type": "data",
        "encrypt": true,
        "sign": false,
        "value": {
            "tag": "finance",
            "account": "paypal",
            "password": "mysecretpass"
        }
    },
    {
        "type": "payment",
        "to": "1Juc9Tjmb6wc6yCaWFuB9QdEEQ621992ks",
        "satoshis": 24850
    }
]

Here's the full code solution:

Details
Solution 1
const { Mintblue } = require("@mintblue/sdk");

// show the user how to use the app when they don't provide the right arguments
if (process.argv.length < 3 || !["get", "add"].includes(process.argv[2])) {
    console.log("Usage:");
    console.log("- Add/Update password: node manager.js add [account] [password] [tag]");
    console.log("- Retrieve password: node manager.js get [account]");
    process.exit();
}

const command = process.argv[2];
const account = process.argv[3];
let password = process.argv[4];
let tag = process.argv[5];

async function main() {
    const sdkToken = "<YOUR-SDK-TOKEN>";
    let client = await Mintblue.create({ token: sdkToken });
    let projectId = "<YOUR-PROJECT-ID>";

    if (command === "add") {
        // store key value object with account and password encrypted on blockchain
        const { txid } = await client.createTransaction({
            project_id: projectId,
            outputs: [
                {
                    type: "data",
                    value: { account, password, tag },
                    encrypt: true,
                },
            ],
        });
        console.log(`Password stored encrypted in transaction: https://whatsonchain.com/tx/${txid}`);
    }

    if (command === "get") {
        // get all of this project's transactions
        const transactions = await client.listTransactions({ project_id: projectId });

        // find password record for requested account and decrypt it
        for (let i = 0; i < transactions.length; i++) {
            const txn = await client.getTransaction({
                txid: transactions[i].txid,
                parse: true, // default is true
            });

            // Show decrypted password to user when found
            if (txn.outputs[0].value.account === account) {
                console.log(`Password for ${account} is: ${txn.outputs[0].value.password}`);
                process.exit();
            }
        }
        console.log(`Password not found for: ${account}`);
    }
}

main();

Challenge 2: Storing a file with the password

Explanation: We want to store a relevant file containing information about the password with each password. To keep things simple, let's define a fixed Buffer output that you can use in your code.

const content = Buffer.from("Password information file");

**Goal: **You need to modify the outputs for the add command to store a file. You can use the File Output guide to learn how to store files and the Combine Outputs guide to learn how to combine differrent outputs.

Details
Solution 2
const { Mintblue } = require("@mintblue/sdk");

// show the user how to use the app when they don't provide the right arguments
if (process.argv.length < 3 || !["get", "add"].includes(process.argv[2])) {
    console.log("Usage:");
    console.log("- Add/Update password: node manager.js add [account] [password] [tag]");
    console.log("- Retrieve password: node manager.js get [account]");
    process.exit();
}

const command = process.argv[2];
const account = process.argv[3];
let password = process.argv[4];
let tag = process.argv[5];

async function main() {
    const sdkToken = "<YOUR-SDK-TOKEN>";
    let client = await Mintblue.create({ token: sdkToken });
    let projectId = "<YOUR-PROJECT-ID>";

    if (command === "add") {
        // store key value object with account and password encrypted on blockchain
        const { txid } = await client.createTransaction({
            project_id: projectId,
            outputs: [
                {
                    type: "data",
                    value: { account, password, tag },
                    encrypt: true,
                },
                {
                    type: "file",
                    value: {
                        contentType: "text/plain",
                        content: Buffer.from("Password information file"),
                    },
                },
            ],
        });
        console.log(`Password stored encrypted in transaction: https://whatsonchain.com/tx/${txid}`);
    }

    if (command === "get") {
        // get all of this project's transactions
        const transactions = await client.listTransactions({ project_id: projectId });

        // find password record for requested account and decrypt it
        for (let i = 0; i < transactions.length; i++) {
            const txn = await client.getTransaction({
                txid: transactions[i].txid,
                parse: true, // default is true
            });

            // Show decrypted password to user when found
            if (txn.outputs[0].value.account === account) {
                console.log(`Password for ${account} is: ${txn.outputs[0].value.password}`);
                process.exit();
            }
        }
        console.log(`Password not found for: ${account}`);
    }
}

main();

Challenge 3: List all accounts and passwords

Explanation: We want to list all our stored accounts and passwords. The output should use the following format ${account} - ${password}.

paypal - mysecretpass
google - mysecretgooglepass

You can use the following command block. Make sure to add the list command to the helper output.

if (process.argv.length < 3 || !["get", "add", "list"].includes(process.argv[2])) {
    // [...]
    console.log("- List all passwords: node manager.js list");
    process.exit();
}

// [...]

if (command === "list") {
    // list a project's passwords
}

Goal: Modify the code to print all accounts and passwords. You can take inspiration from the get command block.

Details
Solution 3
const { Mintblue } = require("@mintblue/sdk");

// show the user how to use the app when they don't provide the right arguments
if (process.argv.length < 3 || !["get", "add", "list"].includes(process.argv[2])) {
    console.log("Usage:");
    console.log("- Add/Update password: node manager.js add [account] [password] [tag]");
    console.log("- Retrieve password: node manager.js get [account]");
    console.log("- List all passwords: node manager.js list");
    process.exit();
}

const command = process.argv[2];
const account = process.argv[3];
let password = process.argv[4];
let tag = process.argv[5];

async function main() {
    const sdkToken = "<YOUR-SDK-TOKEN>";
    let client = await Mintblue.create({ token: sdkToken });
    let projectId = "<YOUR-PROJECT-ID>";

    if (command === "add") {
        // store key value object with account and password encrypted on blockchain
        const { txid } = await client.createTransaction({
            project_id: projectId,
            outputs: [
                {
                    type: "data",
                    value: { account, password, tag },
                    encrypt: true,
                },
                {
                    type: "file",
                    value: {
                        contentType: "text/plain",
                        content: Buffer.from("Password information file"),
                    },
                },
            ],
        });
        console.log(`Password stored encrypted in transaction: https://whatsonchain.com/tx/${txid}`);
    }

    if (command === "get") {
        // get all of this project's transactions
        const transactions = await client.listTransactions({ project_id: projectId });

        // find password record for requested account and decrypt it
        for (let i = 0; i < transactions.length; i++) {
            const txn = await client.getTransaction({
                txid: transactions[i].txid,
                parse: true, // default is true
            });

            // Show decrypted password to user when found
            if (txn.outputs[0].value.account === account) {
                console.log(`Password for ${account} is: ${txn.outputs[0].value.password}`);
                process.exit();
            }
        }
        console.log(`Password not found for: ${account}`);
    }

    if (command === "list") {
        // list a project's passwords
        const transactions = await client.listTransactions({ project_id: projectId });
        for (let i = 0; i < transactions.length; i++) {
            const txn = await client.getTransaction({
                txid: transactions[i].txid,
                parse: true, // default is true
            });
            console.log(`${txn.outputs[0].value.account} - ${txn.outputs[0].value.password}`);
        }
    }
}

main();

That's it!