Hyperledger Composer module local_connection.json when deployed on Google Compute Engine - node.js

I have a hyperledger composer project that runs smoothly on localhost but I tried to deployed it to Google Cloud Compute Engine and does not work properly.
The project is divided in two parts: a composer network, and a nodejs backend to interact with the network. T
I followed the developer and deploy to single organization tutoriales from Composer site.
The nodejs server handles the registration and sign in logic and expose a rest api to interact with the client side. This is my code:
index.js
//post call to register graduate on the network
app.post('/api/registerGraduate', function(req, res) {
console.log("Creando cuenta graduado");
var graduateRut = req.body.graduaterut;
var cardId = req.body.cardid;
var firstName = req.body.firstname;
var lastName = req.body.lastname;
var email = req.body.email;
var phoneNumber = req.body.phonenumber;
network.registerGraduate(cardId, graduateRut, firstName, lastName, email, phoneNumber)
.then((response) => {
//return error if error in response
if (response.error != null) {
res.json({
error: response.error
});
} else {
//else return success
res.json({
success: response
});
}
});
});
network.js
registerGraduate: async function (cardId, graduateRut,firstName, lastName, email, phoneNumber) {
try {
//connect as admin
businessNetworkConnection = new BusinessNetworkConnection();
await businessNetworkConnection.connect('admin#degree');
//get the factory for the business network
factory = businessNetworkConnection.getBusinessNetwork().getFactory();
//create graduate participant
const graduate = factory.newResource(namespace, 'Graduate', graduateRut);
graduate.firstName = firstName;
graduate.lastName = lastName;
graduate.email = email;
graduate.phoneNumber = phoneNumber;
//add graduate participant
const participantRegistry = await businessNetworkConnection.getParticipantRegistry(namespace + '.Graduate');
await participantRegistry.add(graduate);
//issue identity
const identity = await businessNetworkConnection.issueIdentity(namespace + '.Graduate#' + graduateRut, cardId);
//import card for identity
await importCardForIdentity(cardId, identity);
//disconnect
await businessNetworkConnection.disconnect('admin#degree');
return true;
}
catch(err) {
//print and return error
console.log(err);
var error = {};
error.error = err.message;
return error;
}
},
I'm testing the rest api with postman. The error I'm getting back is "error": "Cannot find module './local_connection.json'" but that happens only the first time I send the post request. The second time the error is Error: Failed to add object with ID '166438715' in collection with ID 'Participant:org.degree.ucsd.University' as the object already exists.
Apparently despite the error is writing the data to the blockchain, but if I try to get the data I get an error stating that the cardid is not found.
So, what did I miss? If it works on localhost but not on a Google VM, is it the authentication? How can I solve this?
Thanks for your help!
EDIT!
importCardForIdentity function:
async function importCardForIdentity(cardName, identity) {
//use admin connection
adminConnection = new AdminConnection();
businessNetworkName = 'degree';
//declare metadata
const metadata = {
userName: identity.userID,
version: 1,
enrollmentSecret: identity.userSecret,
businessNetwork: businessNetworkName
};
//get connectionProfile from json, create Idcard
const connectionProfile = require('./local_connection.json');
const card = new IdCard(metadata, connectionProfile);
//import card
await adminConnection.importCard(cardName, card);
}

the 2nd error reported, is because (as you indicate), the participant is already created.
The real issue is trying to locate the newly issued 'graduateRut' business network card ('not found') from your client REST API or postman - in your current setup it would look for this on the same client under $HOME/.composer - is this on a different node to your nodeJS backend?
Also is your ImportCardForIdentity function something like shown here ?
Unable to issue identity card using Hyperledger Composer Javascript API

Related

I have this node.js cloud function but it does not work?

I have this cloud function using node.js that listen every time a child is added on a specific node, then it sends a notification to the users. However when I added something on the database, it does not send anything. I am working on android studio java. Should I connect the function to the android studio, if it will only listen on the database and then send FCM messages on the device tokens.
also how to do debugging on this, I am using VS code.
This is my code:
const functions = require("firebase-functions");
const admin = require("firebase-admin");
admin.initializeApp();
exports.listen = functions.database.ref("/Emergencies/{pushId}")
.onCreate(async (change, context) => {
change.after.val();
context.params.pushId;
// Get the list of device notification tokens. Note: There are more than 1 users in here
const getDeviceTokensPromise = admin.database()
.ref("/Registered Admins/{uid}/Token").once("value");
// The snapshot to the user's tokens.
let tokensSnapshot;
// The array containing all the user's tokens.
let tokens;
const results = await Promise.all([getDeviceTokensPromise]);
tokensSnapshot = results[0];
// Check if there are any device tokens.
if (!tokensSnapshot.hasChildren()) {
return functions.logger.log(
'There are no notification tokens to send to.'
);
}
functions.logger.log(
'There are',
tokensSnapshot.numChildren(),
'tokens to send notifications to.'
);
// Notification details.
const payload = {
notification: {
title: "New Emergency Request!",
body: "Someone needs help check Emergenie App now!",
}
};
// Listing all tokens as an array.
tokens = Object.keys(tokensSnapshot.val());
// Send notifications to all tokens.
const response = await admin.messaging().sendToDevice(tokens, payload);
// For each message check if there was an error.
const tokensToRemove = [];
response.results.forEach((result, index) => {
const error = result.error;
if (error) {
functions.logger.error(
'Failure sending notification to',
tokens[index],
error
);
// Cleanup the tokens who are not registered anymore.
if (error.code === 'messaging/invalid-registration-token' ||
error.code === 'messaging/registration-token-not-registered') {
tokensToRemove.push(tokensSnapshot.ref.child(tokens[index]).remove());
}
}
});
return Promise.all(tokensToRemove);
});
This seems wring:
const getDeviceTokensPromise = admin.database()
.ref("/Registered Admins/{uid}/Token").once("value");
The {uid} in this string is not defined anywhere, and is also going to be treated as just a string, rather than the ID of a user - which is what I expect you want.
More likely, you'll need to:
Load all of /Registered Admins
Loop over the results you get from that
Get the Token value for each of them
If you are new to JavaScript, Cloud Functions for Firebase is not the easiest way to learn it. I recommend first using the Admin SDK in a local Node.js process or with the emulator suite, which can be debugged with a local debugger. After those you'll be much better equipped to port that code to your Cloud Functions.

Unable to retrieve auth string from auth endpoint - received status: 500

I using react native and backend is node.I trying implement pusher in my app.
Object {
"error": "Unable to retrieve auth string from auth endpoint - received status: 0 from http://10.0.27.124:8070/pusher/auth. Clients must be authenticated to join private or presence channels. See: https://pusher.com/docs/authenticating_users",
"status": 0,
"type": "AuthError",
}
Here is my react native code :
const pusher = new Pusher('73286f08a5b2aeeea398', {
cluster: 'ap1',
authEndpoint: 'http://10.0.27.124:8070/pusher/auth',
});
console.log(pusher)
const presenceChannel = pusher.subscribe('presence-channel');
Here is my node js code :
exports.authPusher = function (req, res) {
const socketId = req.body.socket_id;
const channel = req.body.channel_name;
console.log(req.body);
const presenceData = {
user_id: 'unique_user_id',
user_info: { name: 'Mr Channels', twitter_id: '#pusher' },
};
const auth = pusher.authenticate(socketId, channel, presenceData);
res.send(auth);
};
Thank you for answering.
I think you just forgot to use quote marks around authEndpoint parameter. Pusher is trying to call http instead of http://10.0.27.124:8070/pusher/auth.
const pusher = new Pusher('73286f08a5b2aeeea398', {
cluster: 'ap1',
authEndpoint: 'http://10.0.27.124:8070/pusher/auth',
});
If you open network tab on the web-developers toolbar of your browser, you must be able to see the actual request and debug it from there.

credentials used to authenticate does not have permission

I am trying to use firebase cloud messaging to deploy a http push notifications onto an actual device. When so, I am getting the error
"The credential used to authenticate this SDK does not have permission to send messages to the device corresponding to the provided registration token. Make sure the credential and registration token both belong to the same Firebase project."
I have checked the credentials on both the frontEnd and backEnd side and they all match up with my firebase correctly. I have tried to follow plenty of examples on here and I have missed on all of them. My node.js file looks like
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
export GOOGLE_APPLICATION_CREDENTIALS = "/Users/myname/mylocation/myfile.json"
exports.sendPushNotifications = functions.https.onRequest((req, res) => {
res.send("Attempting to send push notification")
console.log("LOGGER --- Trying to send push message..");
var uid = 'randomUIDString'
var fcmToken = 'myToken'
return admin.database().ref('/users/' + uid).once('value', snapshot => {
var user = snapshot.val();
console.log("username is " + user.name);
var payload = {
notification: {
title: 'Push Notification Title',
body: 'Test Notification Message'
}
}
admin.messaging().sendToDevice(fcmToken, payload)
.then(function(response) {
console.log('Succesfully sent message:', response);
console.log(response.results[0].error);
})
.catch(function(error) {
console.log('Error sending message', error);
});
})
})
The users name prints from calling the uid, but I am having trouble accessing the fcmToken.
I have checked my info.plist and the correct project is listed. Same with apple developer and appDelegate. is there something more that I am missing??

app.auth(...).signInWithCredential is not a function firebase error

I'm trying to add google login to my node js firebase app. I'm getting the following error:
app.auth(...).signInWithCredential is not a function
Here's the code on the server
var credential = firebase.auth.GoogleAuthProvider.credential(id_token);
return app.auth().signInWithCredential(credential);
On the client
// We need to register an Observer on Firebase Auth to make sure auth is initialized.
var unsubscribe = app.auth().onAuthStateChanged((firebaseUser) => {
unsubscribe();
// Check if we are already signed-in Firebase with the correct user.
// Build Firebase credential with the Google ID token.
var credential = firebase.auth.GoogleAuthProvider.credential(googleUser.getAuthResponse().id_token);
// Sign in with credential from the Google user.
firebase.auth().signInWithCredential(credential).catch( (error) => {
// Handle Errors here.
var errorCode = error.code;
var errorMessage = error.message;
// The email of the user's account used.
var email = error.email;
// The firebase.auth.AuthCredential type that was used.
var credential = error.credential;
console.dir(errorMessage);
}).then( (success) => {
console.dir(success);
var id_token = googleUser.getAuthResponse().id_token;
axios.post('/auth/google-login', {
id_token: id_token
}).then(response => {
var result = response.data;
if (result.status === "ok") {
this.setState({complete: true});
} else if (result.error) {
this.setState({error: result.error});
}
}).catch(error => {
console.dir(error);
this.setState({error: "error"});
});
});
});
What I'm I doing wrong?
At the top of the nodejs file, I have
var app = require('./base');
where base :
var serviceAccount = require("./service-account-file.json");
const app = fadmin.initializeApp({
credential: fadmin.credential.cert(serviceAccount),
databaseURL: "https://test.firebaseio.com"
});
module.exports = app;
Hi there are hints that the problem starts with node version 6.21. that
app.auth().signInWithCredential(credential)
Don't work
It works in v6.2.0.
Try signInWithEmailAndPassword
Use latest version of node
The function signInWithCredential is part of the Firebase JavaScript SDK
The Firebase JavaScript SDK implements the client-side libraries used by applications using Firebase services.
The Firebase Admin SDK is intended to run on a server or other privileged environment. The Admin SDK does not include sign-in functions.

Is it possible to integrate bot-builder into an existing express app?

I have an existing node/express chatbot application that connects to several chat platforms using ExpressJS' next(), next() middleware design pattern. I send a 200 response at the very beginning to acknowledge the receipt of a message, and send a new POST request to send a message from my last middleware.
app.post("/bots", receiveMsg, doStuff, formatAndSendMsg, catchErrors);
Now I want to integrate Skype as a channel for my bot, but the NodeJS library for bot-framework has a totally different way of doing things, using events and such magic that I haven't fully understood yet:
var connector = new builder.ConsoleConnector();
app.post("/skype", connector.listen());
var bot = new builder.UniversalBot(connector, function (session) {
session.send("You said: %s", session.message.text);
});
It doesn't look like these are compatible ways to do things, so what is the best way to receive a message and then send a response to a user without having to change my express routing to fit bot-builder in? Can I get a Session object with Session.send() to respond to? Will I have to do all the addressing manually? Is there a method that resembles this:
app.post("/skype", (req, res, next) => {
const address = req.body.id;
const message = new builder.Message(address, messageBody).send()
}
Or:
app.post("/skype", connector.listen(), (req, res, next) => {
// (res.locals is available in every express middleware function)
const session = res.locals.botFrameworkSession;
// do stuff
session.send(message);
}
You can register bot application in your existing express applications. Bot builder SDK is also compatible in expressjs framework. You can refer to official sample which is also leveraging express.
Don't forget to modify the messsaging endpoint in your bot registration to your bot's endpoint, e.g.
https://yourdomain/stuff
in your scenario. Please refer to https://learn.microsoft.com/en-us/azure/bot-service/bot-service-quickstart-registration for more info.
Building messages, addressing them, and sending those messages are all possible using the official bot framework NodeJS library. What I couldn't do with that library was receive messages and verify their authenticity on my routes without making major changes to my design (using request middleware - next() - to process the incoming request) which is already in production with other bots and not easy to change.
Here's my workaround: First is this BotFrameworkAuthenticator class that I create based on the Microsoft documentation here: https://learn.microsoft.com/en-us/azure/bot-service/rest-api/bot-framework-rest-connector-authentication
It is instantiated with the appID and appPassword from your Bot Framework app.
import axios from "axios";
import * as jwt from "jsonwebtoken";
import * as jwkToPem from 'jwk-to-pem';
export class BotFrameworkAuthenticator {
private appId: string;
private appPassword: string;
private openIdMetadata: any;
// The response body specifies the document in the JWK format but also includes an additional property for each key: endorsements.
private validSigningKeys: any;
// The list of keys is relatively stable and may be cached for long periods of time (by default, 5 days within the Bot Builder SDK).
private signingKeyRefreshRate: number = 432000; // in seconds (432000 = 5 days)
constructor(appId, appPassword) {
this.appId = appId;
this.appPassword = appPassword;
this.getListOfSigningKeys();
}
// response data should contain "jwks_uri" property that contains address to request list of valid signing keys.
public async getOpenIdMetaData() {
// This is a static URL that you can hardcode into your application. - MS Bot Framework docs
await axios.get("https://login.botframework.com/v1/.well-known/openidconfiguration").then(response => {
this.openIdMetadata = response.data;
logger.info("OpenID metadata document recieved for Bot Framework.");
}).catch(err => {
logger.warn(err.message, "Could not get OpenID metadata document for Bot Framework. Retrying in 15 seconds...");
setTimeout(this.getListOfSigningKeys, 15000);
})
}
public async getListOfSigningKeys() {
await this.getOpenIdMetaData();
if (this.openIdMetadata && this.openIdMetadata.jwks_uri) {
// previous function getOpenIdMetaData() succeeded
await axios.get(this.openIdMetadata.jwks_uri).then(response => {
logger.info(`Signing keys recieved for Bot Framework. Caching for ${this.signingKeyRefreshRate / 86400} days.`);
this.validSigningKeys = response.data.keys;
setTimeout(this.getListOfSigningKeys, (this.signingKeyRefreshRate * 1000));
}).catch(err => {
logger.error(err.message, "Could not get list of valid signing keys for Bot Framework. Retrying in 15 seconds");
setTimeout(this.getListOfSigningKeys, 15000);
});
} else {
// previous function getOpenIdMetaData() failed, but has already queued this function to run again. Will continue until succeeds.
return;
}
}
/**
* Verifies that the message was sent from Bot Framework by checking values as specified in Bot Framework Documentation:
* https://learn.microsoft.com/en-us/azure/bot-service/rest-api/bot-framework-rest-connector-authentication#step-4-verify-the-jwt-token
* Retrieves the Bearer token from the authorization header, decodes the token so we can match the key id (kid) to a key in the OpenID
* document, then converts that key to PEM format so that jwt/crypto can consume it to verify that the bearer token is
* cryptographically signed.
* If the serviceUrl property in the token doe not match the serviceUrl property in the message, it should also be rejected.
*/
public verifyMsgAuthenticity(serviceUrl: string, headers: any) {
try {
const token = headers.authorization.replace("Bearer ", "");
const decoded = jwt.decode(token, { complete: true }) as any;
const verifyOptions = {
issuer: "https://api.botframework.com",
audience: this.appId,
clockTolerance: 300, // (seconds) The token is within its validity period. Industry-standard clock-skew is 5 minutes. (Bot Framework documentation);
}
const jwk = this.lookupKey(decoded.header.kid)
const pem = jwkToPem(jwk);
const verified = jwt.verify(token, pem, verifyOptions) as any;
if (!serviceUrl || serviceUrl !== verified.serviceurl) {
logger.warn("Non-matching serviceUrl in Bot Framework verified token!")
return false;
}
return true;
} catch (err) {
logger.warn("Received invalid/unsigned message on Bot Framework endpoint!", err.message)
return false;
}
}
// Finds the relevant key from the openID list. Does not transform the key.
private lookupKey(kid) {
const jwk = this.validSigningKeys.find((key) => {
return (key.kid === kid);
});
return jwk;
}
}
Use the BotFrameworkAuthenticator class like this at the very beginning of your express route to verify that all incoming requests are valid.
const botFrameworkAuthenticator = new BotFrameworkAuthenticator(appID, appPassword);
router.post("/", (req: Request, res: Response, next: NextFunction) => {
if (botFrameworkAuthenticator.verifyMsgAuthenticity(req.body.serviceUrl, req.headers) === true) {
res.status(200).send();
next();
} else {
// unsafe to process
res.status(403).send();
return;
}
});
And to send messages using the regular Bot Framework library without having a Session object that would normally be created by the Bot Framework library when it receives an incoming message:
import * as builder from "botbuilder";
// instantiate the chatConnector (only once, not in the same function as the sending occurs)
const botFrameworkSender = new builder.ChatConnector({ appId, appPassword });
//---------------------------------------------
const skypeMsg = req.body;
const address = {
channelId: skypeMsg.channelId,
user: skypeMsg.from,
bot: skypeMsg.recipient,
conversation: skypeMsg.conversation
};
const response = new builder.Message().text(someText).address(address).toMessage();
const formattedResponses = [response];
botFrameworkSender.send(formattedResponses, logErrorsToConsole);
Note that all of the builder.Message() -- .attachment(), .images(), etc.. -- functions can be used, not just the text()

Resources