I am new nodejs, Currently I am using rest-client to fetch the data from proxy service. Initially By using POST method of rest Client I am able to login my proxy and I am getting a success response.
after login Immediately I am calling 'get'(proxy/user/me) method for fetching the session data, then I am facing login failed message, How can I will check each time before fetching any other data using nodejs.
//Example POST method invocation for Login
//after Login get method invocation
var Client = require('node-rest-client').Client;
var client = new Client();
var fs = require('fs');
var email = "raj#ioynx.io";
var proxy = "http://google.com";
var password = "abcd";
// set content-type header and data as json in args parameter
var args = {
data: JSON.stringify({ username: email, password: password }),
headers: { "Content-Type": "application/json" }
};
client.post(proxy+"login", args, function (data, response) {
//Sucessfully Login message Dis[play with status Code 200
console.log(response.statusCode);
if (parseInt(response.statusCode) === 200) {
//after success I am trying to fetch session Data, 'get' mehode will always shows ,there is no login.
client.get(proxy+"user/me", function (sessionData, sessionResponse) {
//The second URL for fetching session data always shows { message: "not Login message" }
console.log(sessionData, 'Dubt', sessionResponse.statusCode);
});
}
});
You will need to wrap each method with some sort of authentication checker. Something like this-
function wrapAuthentication(fn) {
return function() {
// Check authentication
if(authenticated) {
fn.apply(fn, arguments);
}
}
}
function updateUserData() {
// Do stuff
}
client.post(proxy+"login", args, wrapAuthentication(updateUserData));
You're rest framework may support something like this already.
Related
I have a shopify store mystore and I have an nodejs app myapp. I need to do is when something happens on mystore a webhook will be created/registered in my nodejs app. I have tried https://www.npmjs.com/package/#shopify/koa-shopify-webhooks this package but it is not working for me and I don't think that it is the same thing that I want. I just want that when let suppose order is created in store a webhook is registered.
if you just have to register a webhook you can use this code.
You just have to change the webhook topic and the endpoint.
This is for orders/create webhook registration
add shopify-api-node and request-promise packages and require them
const ShopifyAPIClient = require("shopify-api-node");
const request = require("request-promise");
then
const createOrderWebhook = await registerWebhook(yourShopDomain, yourShopAccessToken, {
topic: "orders/create",
address: "Your node app end point" //www.example.com/webhooks/createOrder,
format: "json",
});
add your registerWebhook function
const registerWebhook = async function (shopDomain, accessToken, webhook) {
const shopify = new ShopifyAPIClient({
shopName: shopDomain,
accessToken: accessToken,
});
const isCreated = await checkWebhookStatus(shopDomain, accessToken, webhook);
if (!isCreated) {
shopify.webhook.create(webhook).then(
(response) => console.log(`webhook '${webhook.topic}' created`),
(err) =>
console.log(
`Error creating webhook '${webhook.topic}'. ${JSON.stringify(
err.response.body
)}`
)
);
}
};
for checking the webhook already not created at Shopify you can use following code
const checkWebhookStatus = async function (shopDomain, accessToken, webhook) {
try {
const shopifyWebhookUrl =
"https://" + shopDomain + "/admin/api/2020-07/webhooks.json";
const webhookListData = {
method: "GET",
url: shopifyWebhookUrl,
json: true,
headers: {
"X-Shopify-Access-Token": accessToken,
"content-type": "application/json",
},
};
let response = await request.get(webhookListData);
if (response) {
let webhookTopics = response.webhooks.map((webhook) => {
return webhook.topic;
});
return webhookTopics.includes(webhook.topic);
} else {
return false;
}
} catch (error) {
console.log("This is the error", error);
return false;
}
};
Happy coding :)
You can not create/register a new webhook when the order created.
Webhooks are a tool for retrieving and storing data from a certain event. They allow you to register an https:// URL where the event data can be stored in JSON or XML formats. Webhooks are commonly used for:
Placing an order
Changing a product's price
Notifying your IM client or your pager when you are offline
Collecting data for data-warehousing
Integrating your accounting software
Filtering the order items and informing various shippers about the order
Removing customer data from your database when they uninstall your app
I'm relatively new to Node.js and I'm creating a server that will accept a POST request from a mobile app whose body contains a credential that will then be verified via a GET to another server. If the GET response validates the credential, then the UID is extracted and a call is made to the firebase admin SDK to create a custom token. Here is a snippet of the code and two functions that are called to (a) validate the credential and (b) generate the custom token.
//Listen for app to POST Credential
app.post('/', function(request, response) {
console.log('Request Body: ',request.body);
var Credential = request.body;
//Validate Credential
validateCredential(Credential)
//Get Authorization Token
getToken(userID)
//Return Token for POST Response
response.set('Content-Type','Text');
response.end(firebaseAuthToken);
});
//Create listener for POST function
app.listen(port, function() {
console.log('AuthServer is running and listening on port '+port);
});
//Function to Validate Credential
async function validateCredential(crdntl) {
//Call Service to validate Credential received
axios({
method: 'get',
url: 'https://.....',
})
.then(function(response) {
...check credential validation data
})
.catch(function (error) {
console.log('ERROR: Unable to Validate Credential');
//Unable to create Validate Credential so return error message for POST response
return ('ERROR1');
});
}
async function getToken(uid) {
admin.auth().createCustomToken(uid)
.then(function(customToken) {
var AuthToken = customToken;
var decoded = jwt.decode(AuthToken);
console.log('Decoded Token: '+'\n',decoded);
//Return Authorization Token for POST response
return (AuthToken);
})
.catch(function(error) {
console.log('ERROR: Unable to Create Custom Token', error);
//Unable to create Token so return error message for POST response
return ('ERROR2');
});
}
}
I need the result of the validateCredential function to be returned and its result passed to the getToken function and its result returned so that the POST response can be sent. I know these function are async and I can chain them with callbacks or promises.
The real issue is how to make the POST response wait for a callback from the getToken function as the ultimate goal is to pass the custom token back to the mobile app in the body of the POST response.
Any help would be appreciated.
Your validateCredential and getToken functions are already async which in turn returns promise, To wait in POST function for these functions to send response, You have to make POST function async and then use await keyword while calling those 2 functions, when you use await function execution waits until function response which is Promise resolves, Here is sample code.
//Listen for app to POST Credential
app.post('/', async function(request, response) {
console.log('Request Body: ',request.body);
var Credential = request.body;
//Validate Credential
var userId = await validateCredential(Credential) //Waits until userId comes
//Get Authorization Token
var firebaseAuthToken = await getToken(userID) //waits until Token comes
//Return Token for POST Response
response.set('Content-Type','Text');
response.end(firebaseAuthToken);
});
I have created the below script to pull stats from the Jelastic api so i can gather resource stats over a time. The end goal is to log the data to a spreadsheet.
Below is my code that handles authenticating and then making the request to GetSumStats.
If i run the code, some of the time the results returned are expected.
{
iops_used: 0,
duration: 3600,
cpumhz: 7,
start: '',
disk: 7857,
mem: 725212,
cpu: 24002,
capacity: 9,
net: { in_int: 96004, out_int: 96004, in_ext: 9181, out_ext: 9395 },
chanksused: 7,
nodeid: 'sum'
}
But other times the request fails with the error.
{ result: 702,
source: 'JEL',
error: 'not authenticated (different session key)',
stats: [] }
Is this a timing issue or a known issue? Maybe the script is too fast and the API doesn't know about the session id yet? That's why i introduced the setTimeout
var sites = require('./sites.json').sites,
credentials = require('./credentials.json'),
Client = require('node-rest-client').Client,
util = require('./util.js');
(function () {
"use strict";
var client = new Client();
var session;
login();
function login() {
var args = {
parameters: {
appid: sites[2].appId,
login: credentials.email,
password: credentials.password
}
};
client.registerMethod("login", "https://app.j.hostapi.co.uk/1.0/users/authentication/rest/signin", "GET");
client.methods.login(args, function (data, response) {
// parsed response body as js object
data = util.parseResponse(data);
session = data.session;
console.log(session);
// Tried to pause here in case it was too quick
setTimeout(function() {
getSumStats();
}, 3000);
});
}
function logout() {
var args = {
parameters: {
appid: sites[2].appId,
session: session
}
};
client.registerMethod("logout", "https://app.j.hostapi.co.uk/1.0/users/authentication/rest/signout", "GET");
client.methods.logout(args, function (data, response) {
// parsed response body as js object
data = util.parseResponse(data);
console.log(data);
// raw response
//console.log(response);
});
}
// Failure here
function getSumStats() {
var args = {
parameters: {
domain: sites[2].domain,
session: session,
duration: 3600
}
};
client.registerMethod("getSumStats", "https://app.j.hostapi.co.uk/1.0/environment/control/rest/getsumstat", "GET");
client.methods.getSumStats(args, function (data, response) {
// parsed response body as js object
data = util.parseResponse(data);
console.log(data.stats);
logout();
});
}
})();
You are limited to 1 concurrent login session. The login is pinned by IP / User Agent.
If you need to create multiple concurrent login sessions, you can try using a unique UA per session to avoid conflicts.
I am writing a Node.js client for a REST API that uses OAuth2. I am using Bluebird and promises (and sending the access token in the header) and I was wondering when would be a good time to check if the access token is already granted (exists) or still valid (not expired).
So far, I have come up with this:
'use strict';
var Bluebird = require('bluebird');
var request = Bluebird.promisifyAll(require('request'), { multiArgs: true });
var Oauth = require('oauth');
var OAuth2 = OAuth.OAuth2;
var _ = require('lodash');
function Client(options) {
this.options = _.assign({
url: '<API URL>',
oauth2Url: 'oauth2/token',
apiVersion: process.env.apiVersion,
consumerKey: process.env.consumerKey,
consumerSecret: process.env.consumerSecret
}, options);
if (!this.options.url) {
throw new Error('Missing client url.');
}
...
if (!this.options.consumerSecret) {
throw new Error('Missing consumer secret.');
}
if(!this.access_token){
var oauth2 = new OAuth2(
this.options.consumerKey,
this.options.consumerSecret,
this.options.url + this.options.version,
null,
this.options.oauth2Url,
null);
oauth2.getOAuthAccessToken(
'',
{'grant_type':'client_credentials'},
function (e, access_token, refresh_token, results){
this.access_token = access_token;
this.refresh_token = refresh_token;
done();
});
}
}
Client.prototype.queryApi = function (options, callback) {
return request.postAsync({
headers: {
Authorization: 'Bearer ' + access_token
},
url: this.options.url + this.options.apiVersion,
body: JSON.stringify(options)}).
then(function (result) {
var json = JSON.parse(result[1]);
if (_.isFunction(callback)) {
callback(null, json);
}
return json;
}).
catch(function (err) {
if (_.isFunction(callback)) {
callback(err);
return;
}
throw err;
});
};
module.exports = Client;
I am new to both Oauth/Oauth2 and Node.js and I was just wondering if I am checking for the access token in the right place and how/where can I also check if it expired or not. Thanks!
First of all there is two way to check whether access token is expired or not
By knowing token_expiration value from your oauth app.In this case you need to keep task running on your app that will determine wheter access_token is expired or not.(Not recommended way of handling access token)
Handle the response from Authorization server stating that your acces token has been expired.In this case you need to get new access token by presenting refresh token.
You can write 'tokenPersistanceFunction' that will be called when your oauth values(access_token,refresh_token) are updated.
I have modified your code to reflect these changes
function tokenPersistanceFunction(updatedOauth){
// Here you will get Updated Oauth values
// Save these to DB
return saveAccessToken(updatedOauth.access_token, updatedOauth.refresh_token);
}
Client.prototype.queryApi = function (options, tokenPersistanceFunction, callback) {
return request.postAsync({
headers: {
Authorization: 'Bearer ' + access_token
},
url: this.options.url + this.options.apiVersion,
body: JSON.stringify(options)}).
then(function (result) {
// You have some indication from your oauth server, that your access_token is expired.
// You can check your response here to know whether access_token is expired or not.
// If access_token is expired, Make request to refresh access token.
// In your case
if(AccessTokenIsExpired){
// Function that will make request to refresh access_token by presenting refresh_token
return <functionThatRefreshesAccessToken>( refreshAccessTokenOptions,tokenPersistanceFunction)
.then(function(result){
//Extract access_token, refresh_token from response
// call 'tokenPersistanceFunction' to store these token in your DB.
return tokenPersistanceFunction(updatedOauth);
})
.then(function(savedOauthTokensSuccess){
// Now you have the updated Oauth tokens, you can make request to get resource
// this call will return you the actual response.
return queryApi(options, tokenPersistanceFunction, callback);
})
}else{
var json = JSON.parse(result[1]);
if (_.isFunction(callback)) {
callback(null, json);
}
return json;
}
}).
catch(function (err) {
if (_.isFunction(callback)) {
callback(err);
return;
}
throw err;
});
};
What is the best way (most secure and easiest) to authenticate a user for a server side route?
Software/Versions
I'm using the latest Iron Router 1.* and Meteor 1.* and to begin, I'm just using accounts-password.
Reference code
I have a simple server side route that renders a pdf to the screen:
both/routes.js
Router.route('/pdf-server', function() {
var filePath = process.env.PWD + "/server/.files/users/test.pdf";
console.log(filePath);
var fs = Npm.require('fs');
var data = fs.readFileSync(filePath);
this.response.write(data);
this.response.end();
}, {where: 'server'});
As an example, I'd like to do something close to what this SO answer suggested:
On the server:
var Secrets = new Meteor.Collection("secrets");
Meteor.methods({
getSecretKey: function () {
if (!this.userId)
// check if the user has privileges
throw Meteor.Error(403);
return Secrets.insert({_id: Random.id(), user: this.userId});
},
});
And then in client code:
testController.events({
'click button[name=get-pdf]': function () {
Meteor.call("getSecretKey", function (error, response) {
if (error) throw error;
if (response)
Router.go('/pdf-server');
});
}
});
But even if I somehow got this method working, I'd still be vulnerable to users just putting in a URL like '/pdf-server' unless the route itself somehow checked the Secrets collection right?
In the Route, I could get the request, and somehow get the header information?
Router.route('/pdf-server', function() {
var req = this.request;
var res = this.response;
}, {where: 'server'});
And from the client pass a token over the HTTP header, and then in the route check if the token is good from the Collection?
In addition to using url tokens as the other answer you could also use cookies:
Add in some packages that allow you to set cookies and read them server side:
meteor add mrt:cookies thepumpinglemma:cookies
Then you could have something that syncs the cookies up with your login status
Client Side
Tracker.autorun(function() {
//Update the cookie whenever they log in or out
Cookie.set("meteor_user_id", Meteor.userId());
Cookie.set("meteor_token", localStorage.getItem("Meteor.loginToken"));
});
Server Side
On the server side you just need to check this cookie is valid (with iron router)
Router.route('/somepath/:fileid', function() {
//Check the values in the cookies
var cookies = new Cookies( this.request ),
userId = cookies.get("meteor_user_id") || "",
token = cookies.get("meteor_token") || "";
//Check a valid user with this token exists
var user = Meteor.users.findOne({
_id: userId,
'services.resume.loginTokens.hashedToken' : Accounts._hashLoginToken(token)
});
//If they're not logged in tell them
if(!user) return this.response.end("Not allowed");
//Theyre logged in!
this.response.end("You're logged in!");
}, {where:'server'});
I think I have a secure and easy solution for doing this from within IronRouter.route(). The request must be made with a valid user ID and auth token in the header. I call this function from within Router.route(), which then gives me access to this.user, or responds with a 401 if the authentication fails:
// Verify the request is being made by an actively logged in user
// #context: IronRouter.Router.route()
authenticate = ->
// Get the auth info from header
userId = this.request.headers['x-user-id']
loginToken = this.request.headers['x-auth-token']
// Get the user from the database
if userId and loginToken
user = Meteor.users.findOne {'_id': userId, 'services.resume.loginTokens.token': loginToken}
// Return an error if the login token does not match any belonging to the user
if not user
respond.call this, {success: false, message: "You must be logged in to do this."}, 401
// Attach the user to the context so they can be accessed at this.user within route
this.user = user
// Respond to an HTTP request
// #context: IronRouter.Router.route()
respond = (body, statusCode=200, headers) ->
this.response.statusCode statusCode
this.response.setHeader 'Content-Type', 'text/json'
this.response.writeHead statusCode, headers
this.response.write JSON.stringify(body)
this.response.end()
And something like this from the client:
Meteor.startup ->
HTTP.get "http://yoursite.com/pdf-server",
headers:
'X-Auth-Token': Accounts._storedLoginToken()
'X-User-Id': Meteor.userId()
(error, result) -> // This callback triggered once http response received
console.log result
This code was heavily inspired by RestStop and RestStop2. It's part of a meteor package for writing REST APIs in Meteor 0.9.0+ (built on top of Iron Router). You can check out the complete source code here:
https://github.com/krose72205/meteor-restivus
Because server-side routes act as simple REST endpoints, they don't have access to user authentication data (e.g. they can't call Meteor.user()). Therefore you need to devise an alternative authentication scheme. The most straightforward way to accomplish this is with some form of key exchange as discussed here and here.
Example implementation:
server/app.js
// whenever the user logs in, update her apiKey
Accounts.onLogin(function(info) {
// generate a new apiKey
var apiKey = Random.id();
// add the apiKey to the user's document
Meteor.users.update(info.user._id, {$set: {apiKey: apiKey}});
});
// auto-publish the current user's apiKey
Meteor.publish(null, function() {
return Meteor.users.find(this.userId, {fields: {apiKey: 1}});
});
lib/routes.js
// example route using the apiKey
Router.route('/secret/:apiKey', {name: 'secret', where: 'server'})
.get(function() {
// fetch the user with this key
// note you may want to add an index on apiKey so this is fast
var user = Meteor.users.findOne({apiKey: this.params.apiKey});
if (user) {
// we have authenticated the user - do something useful here
this.response.statusCode = 200;
return this.response.end('ok');
} else {
// the key is invalid or not provided so return an error
this.response.statusCode = 403;
return this.response.end('not allowed');
}
});
client/app.html
<template name="myTemplate">
{{#with currentUser}}
secret
{{/with}}
</template>
Notes
Make /secret only accessible via HTTPS.
While it's very likely that the user requesting /secret is currently connected, there is no guarantee that she is. The user could have logged in, copied her key, closed the tab, and initiated the request sometime later.
This is a simple means of user authentication. I would explore more sophisticated mechanisms (see the links above) if the server-route reveals high-value data (SSNs, credit cards, etc.).
See this question for more details on sending static content from the server.
I truly believe using HTTP headers are the best solution to this problem because they're simple and don't require messing about with cookies or developing a new authentication scheme.
I loved #kahmali's answer, so I wrote it to work with WebApp and a simple XMLHttpRequest. This has been tested on Meteor 1.6.
Client
import { Meteor } from 'meteor/meteor';
import { Accounts } from 'meteor/accounts-base';
// Skipping ahead to the upload logic
const xhr = new XMLHttpRequest();
const form = new FormData();
// Add files
files.forEach((file) => {
form.append(file.name,
// So BusBoy sees as file instead of field, use Blob
new Blob([file.data], { type: 'text/plain' })); // w/e your mime type is
});
// XHR progress, load, error, and readystatechange event listeners here
// Open Connection
xhr.open('POST', '/path/to/upload', true);
// Meteor authentication details (must happen *after* xhr.open)
xhr.setRequestHeader('X-Auth-Token', Accounts._storedLoginToken());
xhr.setRequestHeader('X-User-Id', Meteor.userId());
// Send
xhr.send(form);
Server
import { Meteor } from 'meteor/meteor';
import { WebApp } from 'meteor/webapp';
import { Roles } from 'meteor/alanning:roles'; // optional
const BusBoy = require('connect-busboy');
const crypto = require('crypto'); // built-in Node library
WebApp.connectHandlers
.use(BusBoy())
.use('/path/to/upload', (req, res) => {
const user = req.headers['x-user-id'];
// We have to get a base64 digest of the sha256 hashed login token
// I'm not sure when Meteor changed to hashed tokens, but this is
// one of the major differences from #kahmali's answer
const hash = crypto.createHash('sha256');
hash.update(req.headers['x-auth-token']);
// Authentication (is user logged-in)
if (!Meteor.users.findOne({
_id: user,
'services.resume.loginTokens.hashedToken': hash.digest('base64'),
})) {
// User not logged in; 401 Unauthorized
res.writeHead(401);
res.end();
return;
}
// Authorization
if (!Roles.userIsInRole(user, 'whatever')) {
// User is not authorized; 403 Forbidden
res.writeHead(403);
res.end();
return;
}
if (req.busboy) {
// Handle file upload
res.writeHead(201); // eventually
res.end();
} else {
// Something went wrong
res.writeHead(500); // server error
res.end();
}
});
I hope this helps someone!
Since Meteor doesn't use session cookies, client must explicitly include some sort of user identification when making a HTTP request to a server route.
The easiest way to do it is to pass userId in the query string of the URL. Obviously, you also need to add a security token that will prove that the user is really who the claim they are. Obtaining this token can be done via a Meteor method.
Meteor by itself doesn't provide such mechanism, so you need some custom implementation. I wrote a Meteor package called mhagmajer:server-route which was thoroughly tested. You can learn more about it here: https://blog.hagmajer.com/server-side-routing-with-authentication-in-meteor-6625ed832a94