restexpresshyperledger-fabrichyperledger-fabric-sdk-js

How can I register a Client through an express REST API in Hyperledger fabric


I want to register a user of the application through a REST API. I have already enrolled the admin and a user through the enrollAdmin.js and registerUser.js function but I want to call these functions through the node SDK and register users dynamically with there username (UUID) so that it's completly anonymous.
As the username I want to create a unique UUID and save that in the world state but also save that UUID on an off-chain database together with the personal information like password and name so that I can associate the personal information with the UUID.
Right know I'm confused by all the different steps I have to do to register a new user:
In what order do I have to enroll and register the user and should they all be defined in the express API or in chaincode?

This is my first approach of creating the REST Api and till now I have only defined the layout, the connection profile and wallet.
I would appreciate if somebody could help me implement the registration process in the express REST API so that an Identity for the UUID gets saved in the world state.
Thanks in advance.

'use strict';

var express = require('express');
var bodyParser = require('body-parser');
var app = express();

// Setting for Hyperledger Fabric
const { Wallets, FileSystemWallet, Gateway } = require('fabric-network');
const path = require('path');
const fs = require('fs');

const channelName = 'mychannel';
const mspOrg1 = 'Org1MSP';
const walletPath = path.join(__dirname, '..', 'wallet');
const ccpPath = path.resolve(__dirname, '..', 'connection-org1.json');

//register
app.post('/api/register', async function (req, res) {
    try{

        // Create a new file system based wallet for managing identities.
        const walletPath = path.join(process.cwd(), 'wallet');
        const wallet = new FileSystemWallet(walletPath);
        console.log(`Wallet path: ${walletPath}`);

    } catch (error)  {

    }
});

//login
app.post('/api/login', async function (req, res) {
    try{

        // Create a new file system based wallet for managing identities.
        const walletPath = path.join(process.cwd(), 'wallet');
        const wallet = new FileSystemWallet(walletPath);
        console.log(`Wallet path: ${walletPath}`);
        
    } catch (error)  {

    }
});

app.listen(3000, ()=>{
    console.log("***********************************");
    console.log("API server listening at localhost:3000");
    console.log("***********************************");
  });

Solution

  • The process of how you want it to be is simple. In the middle, the off-chain database is used as a mapping table. I wrote only the core process logic.

    /api/v1/register

    1. validation check
      Validate that the user's id is unique, that the required information value is missing, that the regular expression is correct, and that there is no wrong information.

    2. generate random UUID
      Create a random, unique uuid first. npm/uuid

    const UUID = uuid.v4();
    
    1. register/enroll user to fabric-ca
      Perform the registration process as a user of the fabric. The information that goes into this process is UUID, and the user's information will not be stored in the blockchain.
      fabricUser is a newly created class, and returns the result after fabric user registration and enroll process are performed by the Enroll method.
    enrollment = await fabricUser.Enroll(UUID);
    await wallet.put(enrollment);
    
    1. insert to database
      While saving the user information in the database, map it by storing the UUID created above. The database was created as an example, assuming mongodb.
    db.collection('User').insertOne({
        'uuid': UUID,
        'user_id': <input_user_id>,
        ...
    });
    

    /api/v1/login

    The login process is as follows. I don't know what authentication/authentication method you want to use, so I'll assume a token authentication method based on auth 2.0.

    1. Verify the validity of the necessary information required for login and whether there is any incorrect information.

    2. get UUID
      generateAuthToken is a new function that generates JWT.

    let res = await db.collection("User").findOne({'user_id': `<input_user_id>` });
    return generateAuthToken(res.uuid);
    

    /api/v1/invoke

    Fabric resource request process is as follows.

    1. Token validation and resource authorization check

    2. get userName from token
      getPayload is a function that gets the payload value located at the 1st index from the token.

    const rawPayload = getPayload(token);
    const jsonPayload = JSON.parse(rawPayload);
    return jsonPayload
    
    1. get wallet & invoke chaincode
      The fabricChaincode is a function that wraps the invoke process of fabric-sdk. It is a function that executes invoke by inputting identity, chaincode information, and parameters, and returns a result.
    const user = await db.collection("User").findOne({'user_id': jsonPayload.user_id });
    const fabricIdentity = await wallet.get(user.uuid);
    const res = fabricChaincode.invoke(fabricIdentity, `<your_chaincode_info>`, `<input_chaincode_params>`)
    return res;
    

    [EDIT]

    Add it for your understanding.

    fabricUser.js

    /*
     * Copyright IBM Corp. All Rights Reserved.
     *
     * SPDX-License-Identifier: Apache-2.0
     */
    
    'use strict';
    
    const { Wallets } = require('fabric-network');
    const FabricCAServices = require('fabric-ca-client');
    const fs = require('fs');
    const path = require('path');
    
    async function Enroll(user_id) {
        try {
            // load the network configuration
            const ccpPath = path.resolve(__dirname, '..', '..', 'test-network', 'organizations', 'peerOrganizations', 'org1.example.com', 'connection-org1.json');
            const ccp = JSON.parse(fs.readFileSync(ccpPath, 'utf8'));
    
            // Create a new CA client for interacting with the CA.
            const caURL = ccp.certificateAuthorities['ca.org1.example.com'].url;
            const ca = new FabricCAServices(caURL);
    
            // Create a new file system based wallet for managing identities.
            const walletPath = path.join(process.cwd(), 'wallet');
            const wallet = await Wallets.newFileSystemWallet(walletPath);
            console.log(`Wallet path: ${walletPath}`);
    
            // Check to see if we've already enrolled the user.
            const userIdentity = await wallet.get(user_id);
            if (userIdentity) {
                console.log(`An identity for the user ${user_id} already exists in the wallet`);
                return;
            }
    
            // Check to see if we've already enrolled the admin user.
            const adminIdentity = await wallet.get('admin');
            if (!adminIdentity) {
                console.log('An identity for the admin user "admin" does not exist in the wallet');
                console.log('Run the enrollAdmin.js application before retrying');
                return;
            }
    
            // build a user object for authenticating with the CA
            const provider = wallet.getProviderRegistry().getProvider(adminIdentity.type);
            const adminUser = await provider.getUserContext(adminIdentity, 'admin');
    
            // Register the user, enroll the user, and import the new identity into the wallet.
            const secret = await ca.register({
                affiliation: 'org1.department1',
                enrollmentID: user_id,
                role: 'client'
            }, adminUser);
            const enrollment = await ca.enroll({
                enrollmentID: user_id,
                enrollmentSecret: secret
            });
            const x509Identity = {
                credentials: {
                    certificate: enrollment.certificate,
                    privateKey: enrollment.key.toBytes(),
                },
                mspId: 'Org1MSP',
                type: 'X.509',
            };
            await wallet.put(user_id, x509Identity);
            console.log(`Successfully registered and enrolled admin user ${user_id} and imported it into the wallet`);
    
        } catch (error) {
            console.error(`Failed to register user ${user_id}: ${error}`);
            process.exit(1);
        }
    }
    
    module.exports = {
        Enroll
    }
    

    api.js

    const uuid = require('uuid');
    const fabricUser = require('./fabricUser);
    const UUID = uuid.v4();
    let res = await fabricUser.Enroll(UUID);
    console.log(res);