How to make Nodejs connect to Metamask account? - node.js

I have a backend write with Nodejs that can connect to contracts and perform functions in those contracts but the problem is I want my Metamask can pass only the account address to the backend. Is there any solution for this?

there are some third party packages like node-metamask
you can use them

add this code snippet in your views (hbs template engine) / HTML file.
When you load this page, the script will execute and it will get the metamask wallet address if it's installed.
<script>
async function connect()
{
if (window.ethereum) {
await window.ethereum.request({ method: "eth_requestAccounts" });
window.web3 = new Web3(window.ethereum);
const account = web3.eth.accounts;
const walletAddress = account.givenProvider.selectedAddress;
console.log(`Wallet: ${walletAddress}`);
window.location.href = `/setup?wallet=${walletAddress}`;
}
else {
alert("MetaMask is not installed");
}
}
</script>
after that, It will send the wallet address at a specific backend route to the express.
hence, you can perform actions in node JS with the integration of web3.
app.get('/setup', (req, res) => {
const address = req.query.wallet
app.locals.address = address
console.log(app.locals.address)
})
here's complete code example: https://github.com/billypentester/web3-dapp

Related

How does one secure api keys on sveltekit 1.0

I am using ghost, i made an integration and i would like to hide the api key from the front-end. I do not believe i can set restrictions on the ghost cms (that would also work). And i do believe so +page.js files are run on the browser also, so im a little confused on how to achieve this?
The interal sveltekit module $env/static/private (docs) is how you use secure API keys. Sveltekit will not allow you to import this module into client code so it provides an extra layer of safety. Vite automatically loads your enviroment variables from .env files and process.env on build and injects your key into your server side bundle.
import { API_KEY } from '$env/static/private';
// Use your secret
Sveltekit has 4 modules for accessing enviroment variables
$env/static/private (covered)
$env/static/public accessiable by server and client and injected at build (docs)
$env/dynamic/private provided by your runtime adapter; only includes variables with that do not start with the your public prefix which defaults to PUBLIC_ and can only be imported by server files (docs)
$env/dynamic/public provided by your runtime adapter; only includes variables with that do start with the your public prefix which defaults to PUBLIC_ (docs)
You don't need to hide the key.
Ghost Content API Docs:
These keys are safe for use in browsers and other insecure environments, as they only ever provide access to public data.
One common way to hide your third-party API key(s) from public view is to set up proxy API routes.
The general idea is to have your client (browser) query a proxy API route that you provide/host, have that proxy route query the third-party API using your credentials (API key), and pass on the results from the third-party API back to the client.
Because the query to the third-party API takes place exclusively on the back-end, your credentials are never exposed to the client (browser) and thus not visible to the public.
In your use case, you would have to create 3 dynamic endpoint routes to replicate the structure of Ghost's API:
src/routes/api/[resource]/+server.js to match /posts/, /authors/, /tags/, etc.:
const API_KEY = <your_api_key>; // preferably pulled from ENV
const GHOST_URL = `https://<your_ghost_admin_domain>/ghost/api/content`;
export function GET({ params, url }) {
const { resource } = params;
const queryString = url.searchParams.toString();
return fetch(`${GHOST_URL}/${resource}/?key=${API_KEY}${queryString ? `&${queryString}` : ''}`, {
headers: {
'Accept-Version': '5.0' // Ghost API Version setting
}
});
}
src/routes/api/[resource]/[id]/+server.js to match /posts/{id}/, /authors/{id}/, etc.:
const API_KEY = <your_api_key>; // preferably pulled from ENV
const GHOST_URL = `https://<your_ghost_admin_domain>/ghost/api/content`;
export function GET({ params, url }) {
const { resource, id } = params;
const queryString = url.searchParams.toString();
return fetch(`${GHOST_URL}/${resource}/${id}/?key=${API_KEY}${queryString ? `&${queryString}` : ''}`, {
headers: {
'Accept-Version': '5.0' // Ghost API Version setting
}
});
}
src/routes/api/[resource]/slug/[slug]/+server.js to match /posts/slug/{slug}/, /authors/slug/{slug}/, etc.:
const API_KEY = <your_api_key>; // preferably pulled from ENV
const GHOST_URL = `https://<your_ghost_admin_domain>/ghost/api/content`;
export function GET({ params, url }) {
const { resource, slug } = params;
const queryString = url.searchParams.toString();
return fetch(`${GHOST_URL}/${resource}/slug/${slug}/?key=${API_KEY}${queryString ? `&${queryString}` : ''}`, {
headers: {
'Accept-Version': '5.0' // Ghost API Version setting
}
});
}
Then all you have to do is call your proxy routes in place of your original third-party API routes in your app:
// very barebones example
<script>
let uri;
let data;
async function get() {
const res = await fetch(`/api/${uri}`);
data = await res.json();
}
</script>
<input name="uri" bind:value={uri} />
<button on:click={get}>GET</button>
{data}
Note that using proxy API routes will also have the additional benefit of sidestepping potential CORS issues.

How to get updates from backend automatically in client side

I have 3 apps, mobile app (react native), web app (react js) and a Nodejs app.
the mobile app is an e-commerce mobile app, and the web app is the admin page (client side). The admin page get all data from NodeJs app (api) that use firebase admin sdk.
The problem is that i want everytime a user place an order using the mobile app, the admin page automatically fetch data from NodeJs app.
api code :
router.post("/orders", function(req, res, next) {
const {filterBy} = req.body;
let orders = [];
if(filterBy === "all"){
db.ref("orders").on('value',function(snap){
if(snap.val){
snap.forEach(function(child){
let items = child.val();
orders.push(items);
})
}
})
}else{
db.ref("orders").orderByChild("generalStatus").equalTo(filterBy)
.on('value',function(snap){
if(snap.val){
snap.forEach(function(child){
let items = child.val();
orders.push(items);
})
}
})
}
res.json(orders.reverse());
});
You can create a socket connection between your web-app and backend. Whenever someone places an order on the mobile app, you can fire an event in backend api which the client can listen too.
You can use socket.io https://socket.io/get-started/chat
You can also add listener on firebase document and use the method on to check for any changes in DB eg if you want to get updates from chat db you can use this code e.g.
const chatRef = database().ref(firebaseMessages.CHAT);
let listener = chatRef.on('value', snapshot => {
console.log('User data: ', snapshot.val());
});
//...
// Later when you no longer need the listener
chatRef.off('value', listener);

Nodejs Fetch API: unable to verify the first certificate

I'm using the fetch API module in my Philips Hue project and when I make a call to the local ip address (my hub) it produces that error in the title.
const fetch = require('node-fetch');
const gateway = "192.168.0.12";
const username = "username";
let getLights = function(){
fetch(`https://${gateway}/api/${username}/lights`, {
method: 'GET'
}).then((res) => {
return res.json();
}).then((json) => {
console.log(json);
});
}
module.exports = {getLights};
Any SECURE fix this will eventually go onto the public internet for me to access my lights from anywhere sooo?
To skip the SSL tests, you can use this:
process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = 0;
It seems like you tried to access it using HTTPS. Most likely on your local network it is going to be HTTP
So by changing https://${gateway}/api/${username}/lights to http://${gateway}/api/${username}/lights should work.
If you're trying to keep it HTTPS then you will have to install a SSL certificate authority onto your network.
These may be useful sources if you're trying to get that done:
https://www.freecodecamp.org/news/how-to-get-https-working-on-your-local-development-environment-in-5-minutes-7af615770eec/
https://letsencrypt.org/docs/certificates-for-localhost/

Get my Action’s server URL in (JavaScript) fulfilment code

I am using actionssdk and I build my Action fulfilments using Javascript and node.js + Express.
I am looking for a way to get the url (protocol + host name + port) of the server where the fulfilment is hosted.
Is there a simple way to do this? E.g. in the MAIN intent? Is there some conv-property I can use? Can I get hold of a req-parameter in the MAIN-intent, from which I can deduct hostname etc?
const express = require('express');
const expressApp = express();
const { actionssdk, ... } = require('actions-on-google');
const app = actionssdk({
ordersv3: true,
clientId: ...
});
expressApp.post('/fulfilment', app);
app.intent('actions.intent.MAIN', (conv) => {
let myUrl: string = ... // ???????
...
});
(background: obviously I know myself to where I deployed my fulfilment code. But I have a reusable template for fulfilment code in which I want to refer to the host url, and I do not want to type that in manually each time I develop a new Action).
You can get access to the request object in a middleware via Framework Metadata which is by default of type BuiltinFrameworkMetadata which contains objects used by Express
For example, you can use it like this, which will be ran before each request:
app.middleware((conv, framework) => {
console.log(framework.express.request.headers.host)
})

AngularJS and ExpressJS session management?

I would like to keep session across all the page. For this project, I am using expressJs, nodeJS as server side. AngularJS in front end.
I am not sure, how to handle session when view changes or url changes. Because I need to take care of both expressJS router or angularJs router.
What approach should I follow?
angularJS router
myApp.config(['$routeProvider', function($routeProvider) {
$routeProvider.when('/welcome', {templateUrl: 'partials/welcome.html', controller: 'MyCtrl2'});
$routeProvider.when('/login', {templateUrl: 'partials/login.html', controller: 'MyCtrl2'});
$routeProvider.when('/signup', {templateUrl: 'partials/signup.html', controller: 'singupController'});
$routeProvider.otherwise({redirectTo: '/'});
}]);
Signup controller
myApp.controller('singupController',function($scope,$rootScope,$http){
$scope.doSingnup = function() {
var formData = {
'username' : this.username,
'password' : this.password,
'email' : null
};
var jdata = JSON.stringify(formData);
$http({method:'POST',url:'/signup',data:jdata})
.success(function(data,status,headers,config){
console.log(data);
}).
error(function(data,status,headers,config){
console.log(data)
});
}
})
ExpressJS router
module.exports = exports = function(app, db) {
var sessionHandler = new SessionHandler(db);
var contentHandler = new ContentHandler(db);
// Middleware to see if a user is logged in
app.use(sessionHandler.isLoggedInMiddleware);
app.get('/', contentHandler.displayMainPage);
app.post('/login', sessionHandler.handleLoginRequest);
app.get('/logout', sessionHandler.displayLogoutPage);
app.get("/welcome", sessionHandler.displayWelcomePage);
app.post('/signup', sessionHandler.handleSignup);
app.get('*', contentHandler.displayMainPage);
// Error handling middleware
app.use(ErrorHandler);
}
After signup, I would like to redirect to the login page. How can I do that in the above router. which one of the following should I use to change the view of app
1) $location of angularJS
2) redirect of ExpressJS
So i had the same problem and to be fair i might have read the approach somewhere i don't remember anymore.
Problem: Angular builds single page apps. After refresh, you loose scope and with it the authenticated user.
Approach
AngularJS modules offer a startup function called run which is called always when the page is loaded. Perfect for refresh/reload.
myApp.run(function ($rootScope, $location, myFactory) {
$http.get('/confirm-login')
.success(function (user) {
if (user && user.userId) {
$rootScope.user = user;
}
});
}
express-session saves the sessions for you and authenticates you with the sessionId your browser sends. So it always knows if you are authenticated or not.
router.get('/confirm-login', function (req, res) {
res.send(req.user)
}
);
All i had to do is, after refreshing and all dependencies were loaded, ask if i am authenticated and set $rootScope.user = authenticatedUserFromExpress;
There are two different concepts here - server side session state and the user state on the client side in Angular. In express you can use the session via req.session to manage session based data.
On the angular side, there is only scope in your controllers. If you want to keep track of some data across multiple controllers, you need to create a service to store the data in and inject the service into the controllers you need.
A typical lifecycle is to first check if there is data already in the service, if so use it. If not, wait for the data to be populated (by the user or app or whatever) then detect those changes and synchronize with your service.
signup controller
function SignupCtrl($scope, $http, $location) {
$scope.form = {}; // to capture data in form
$scope.errorMessage = ''; // to display error msg if have any
$scope.submitPost = function() { // this is to submit your form can't do on
//traditional way because it against angularjs SPA
$http.post('/signup', $scope.form).
success(function(data) { // if success then redirect to "/" status code 200
$location.path('/');
}).error(function(err) { // if error display error message status code 400
// the form can't be submitted until get the status code 200
$scope.errorMessage = err;
});
};
}
sessionHandler.handleSignup
this.handleSignup = function(req, res, next) {
"use strict";
// if you have a validate function pass the data from your
// Signup controller to the function in my case is validateSignup
// req.body is what you need
validateSignup(req.body, function(error, data) {
if(error) {
res.send(400, error.message); // if error send error message to angularjs
}else {
// do something else
// rmb to res.send(200)
}
});
}
validatesignup
function validateSignup(data,callback) {
"use strict"; // the data is req.body
//so now you can access your data on your form
// e.g you have 2 fields name="password" and name="confirmPassword on your form"
var pass = data.password,
comPass = data.confirmPassword;
if(pass != comPass){
callback(new Error('Password must match'), null);
// then show the error msg on the form by using
//angular ng-if like <div ng-if="errorMessage">{{errorMessage}}</div>
}else{
callback(null, data);
}
}
hope this help
Of all the answers here, I like #alknows's approach best. However, like the other answers that suggest you send a request to the server to get the current user data, there are a couple issues I take with them:
You have to deal with race conditions as a result of your AJAX ($http) call.
You're sending an unnecessary request to the server after it already rendered your index.html
I tried #alknow's approach and it worked out for me after I was able to resolve the many race conditions that came up as a result of my angular app controllers and config needing the current user to do their job. I try my best to avoid race conditions when appropriate, so I was a bit reluctant to continue with this approach. So I thought of a better approach: send the current user data down with your index.html and store it locally.
My Approach: Embed currentUser in index.html & store locally on client
In index.html on your server, make a script tag to hold whatever data you want to pass to the client:
```
<!--YOUR OTHER index.html stuff go above here-->
<script id="server-side-rendered-client-data" type="text/javascript">
var __ssr__CData = {
currentUser: { id: '12345', username: 'coolguy', etc: 'etc.' }
}
</script>
```
Then, as #alknows suggested, in app.js or wherever you initiate your angular app, add app.run(..., () => {...}). In app.run(), you will want to grab the server side rendered client data object, which I named obscurely __ssr_CData so that I am less likely to run into name collisions across the global namespace later in my other javascript:
var myAngularApp = angular.module("mainApp", ['ngRoute']);
myAngularApp.run(function ($rootScope) {
const currentUserFromServer = __ssr__CData.currentUser
const currentUserAccessTokenFromServer = __ssr__CData.accessToken
const currentUser =
CurrentUser.set(currentUserAccessTokenFromServer, currentUserFromServer)
$rootScope.currentUser = currentUser
});
As you know app.run() will be called whenever the page does a full reload. CurrentUser is a global class for managing my angular app's current user in the single page environment. So when I call CurrentUser.set(...) it stores the current user data in a place I can retrieve later in my angular app by calling CurrentUser.get(). So in any of your angular app controller's you can now retrieve the current user the server provided by simply doing this:
myAngularApp.controller('loginController',function($scope, $rootScope, $http){
//check if the user is already logged in:
var currentUser = CurrentUser.get()
if(currentUser) {
alert("HEY! You're already logged in as " +currentUser.username)
return $window.location.href = "/";
}
//there is no current user, so let user log in
//...
}
In that example, I made use of CurrentUser.get(), which I explained above, to get the previously stored current user from the server. I could have also retrieved that current user by accessing $rootScope.currentUser because I stored it there, too. It's up to you.
myAngularApp.controller('signupController',function($scope, $rootScope, $http){
//check if the user is already logged in:
var currentUser = CurrentUser.get()
if(currentUser) {
alert("HEY! You're already logged in as " +currentUser.username)
return $window.location.href = "/";
}
//there is no current user, so let user signup
//... you run your signup code after getting form data
$http({method:'POST',url:'/signup',data:jdata})
.success(function(data,status,headers,config){
//signup succeeded!
//set the current user locally just like in app.js
CurrentUser.set(data.newUser)
//send user to profile
return $window.location.href = "/profile";
})
.error(function(data,status,headers,config){
//something went wrong
console.log(data)
});
}
Now, after a new user has signed up, your server returned the new user from the AJAX call. We set that new user as the current user by calling CurrentUser.set(...) and send the user to their profile. You can now get the current user in the profile controller the same way you did to check if the current user existed in the login and signup controllers.
I hope this helps anyone who comes across this. For your reference, I'm using the client-sessions module to handle sessions on my server.

Resources