Im building an app that serve both http and ws. Users login first over HTTP to a Laravel Server. That returns a JWT that is used to allow login over WS.
Ihv added a MIDDLEWARE_HANDSHAKE that gets the token and make a request to Laravel Server to ask if that token is valid and the user has access to WS (Not every logged user is allowed to WS);
Client code:
var options = {
host: '127.0.0.1:3000',
query: {
source: 'web',
token: '',
}
};
var socket;
$.post('http://127.0.0.1:8000/authenticate', {
email: 'chadd01#example.org',
password: '1234'
}, function(data, textStatus, xhr) {
options.query.token = data.token;
//ALL PERFECT UNTILL HERE
// Initiate the connection to the ws server
socket = socketCluster.connect(options)
.on('connect', function(data) {
console.log('CONNECTED', data);
})
.on('error', function(data) {
console.log('ERROR', data.message);
});
});
SocketCluster Server code:
scServer.addMiddleware(scServer.MIDDLEWARE_HANDSHAKE, function(request, next) {
var query = url.parse(request.url, true).query;
switch (query.source) {
case 'web':
case 'mobile-app':
validateUser(query)
.then((response) => {
next(); //Allowed
})
.catch((code) => {
next(code); //Blocked with StatusCode
});
break;
default:
next(true, 'NOT_AUTHORIZED'); // Block
break;
}
});
validateUser = (credentials = {}) => {
return new Promise((resolve, reject) => {
request({ url: API + 'webSocket/users/' + credentials.token, method: 'GET' }, (error, response, body) => {
if (response.statusCode === 200) {
resolve(body);
}
reject(response.statusCode);
});
});
};
While implementing this middleware like this i keep getting this response from ws server even when validation is successfull:
WebSocket connection to 'ws://127.0.0.1:3000/socketcluster/?source=web&token=<_TOKEN_>' failed: Connection closed before receiving a handshake response
(index):149 ERROR Socket hung up
But, if i implement the HANDSHAKE_MIDDLEWARE like this:
scServer.addMiddleware(scServer.MIDDLEWARE_HANDSHAKE, function(request, next) {
var validUser = true;
if (validUser){
return next();
}
return next('NOT_A_VALID_USER');
});
All goes fine:
CONNECTED Object {id: "W067vqBc9Ii8MuIqAAAC", pingTimeout: 20000, isAuthenticated: true, authToken: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b2tlbiI6I…xOTV9.E4bLPh4Vjk9ULvfhW6prjBbVt0vOD32k63L1vlDtGrU"}
So the problem seems to be in the Promise callback.
Any advice if this is not the right way to implement?
Thanks.
A big reason why JWT is used on SocketCluster is to handle logging in and authentication, have you considered just using WS?
Take a look at SocketCluster authentication.
Just how your current HTTP code check the login data, you can do the same for WS and use socket.setAuthToken to set the token (here is an example that I used in my project):
socket.setAuthToken({
email: credentials.email,
id: returnedData.id,
permission: returnedData.permission
});
You can then do requests to the WS server still using on/emit, and do a check to see if they are authenticated. Here's a modified snippet of my authCheck function:
const token = socket.getAuthToken();
if (token && token.email) {
console.log('Token Valid, User is: ', token.email);
// user validated - continue with your code
} else {
console.log('Token Invalid, User not authenticated.');
// respond with error message to the user
}
Related
I am currently building my server to execute an authentication function on startup using server/app listener. I am trying to use an external function in my controller file to do this, my server code below:
//Start listener on the port
app.listen(port, () => {
console.log(`Server is listening to port ${port}... (http\://localhost\:3000)`);
});
//Start Authentication when server starts
app.on('listening', () => {
console.log('Running Authentication...')
auth.main()
});
And am using these async modules in my auth, is this the correct way of executing a start up function? For the data I want to send back, should I store them in global variables and then use 'res.send' at the end of my function? and is there any way I could improve my code?
const main = (req, res) => {
//Asynchronous Waterfall Call of 'Getting Access Tokens' (Stages 1-3)
async.waterfall([
getCookieData,
getAuthCode,
getAccessToken
], (err, result) => {
if (err) {
alert('Something is wrong!');
}
return alert('Done!');
});
//STAGE 1 - USE CUSTOMER DETAILS TO RETRIEVE COOKIE AND CSRF SESSION DATA
getCookieData = async (callback) => {
//If the paramter test passes then proceed
var options = {
//Type of HTTP request
method: 'POST',
//URI String
uri: authURL,
//HTTP Query Strings
qs: {
'client_id': clientID
},
//HTTP Headers
headers: {
'cache-control': 'no-cache',
'Accept': 'application/json',
'Host': host,
'Content-Type': 'application/json'
},
//HTTP Body
body: {
'username': username,
'password': password
},
json: true // Automatically stringifies the body to JSON
//resolveWithFullResponse: true // Get the full response instead of just the body
//simple: false // Get a rejection only if the request failed for technical reasons
};
console.log(`Beginning HTTP POST Request to ${authURL}...`);
//await literally makes JavaScript wait until the promise settles, and then go on with the result.
await rp(options)
.then((parsedBody) => {
//POST Succeeded...
Console.log('Successful HTTP POST!');
try {
let csrf = response.body('csrftoken'),
ses = response.body('session'),
sesk = `session=${ses}`;
} catch (e) {
console.log(`STAGE 1 - Error occurred when assigning url variables. Error: ${e}`);
console.error('STAGE 1 - Error occurred when assigning url variables. Error:', e);
callback(`STAGE 1 - Error occurred when assigning url variables. Error: ${e}`)
}
console.log(`Successful grab of the cookie: ${ses} and csrf token: ${csrf}. Getting authorisation code now!`);
//Asynchronous callback for the next function - return = defensive architecture
return callback(null, authCodeURL, customerID, clientID, csrf, sesk);
})
.catch((err) => {
if (res.statusCode == 400) {
console.log(`Error Message: ${res.body.message}. Status: ${res.body.status}`);
console.error('Error Message:', res.body.message, 'Status:', res.body.status);
callback(`Error Message: ${res.body.message}. Status: ${res.body.status}`);
} else if (res.statusCode == 401) {
console.log(`Error Message: ${res.body.message}. Status: ${res.body.status}`);
console.error('Error Message:', res.body.message, 'Status:', res.body.status);
callback(`Error Message: ${res.body.message}. Status: ${res.body.status}`);
} else {
console.log(`Failed to retrieve the cookie data! Error: ${error}`);
console.error('Failed to retrieve the cookie data! Error:', error);
callback(`Failed to retrieve the cookie data! Error: ${error}`);
}
});
},
//STAGE 2 - USE COOKIES AND CSRF TOKEN TO GET AUTH CODE
//Is the word async needed? it is not asyncchronous but sequential
getAuthCode = async (authCodeURL, customerID, clientID, csrf, sesk, callback) => {
//If successful, proceed:
var options = {
method: 'POST',
uri: authCodeURL,
qs: {
'client_id': clientID,
'response_type': 'code',
'scope': 'all'
},
headers: {
'X-CSRF-TOKEN': csrf,
'Accept': 'application/json',
'Cookie': sesk,
'Content-Type': 'application/json'
},
body: {
'customer_id': customerID
},
json: true // Automatically stringifies the body to JSON
};
console.log(`Beginning HTTP POST Request to ${authCodeURL}...`);
//await literally makes JavaScript wait until the promise settles, and then go on with the result.
await rp(options)
.then((parsedBody) => {
//POST Succeeded...
Console.log('Successful HTTP POST!');
try {
let authCode = response.body.JSON('auth_code'),
swapurl = `https://${host}${url3}`;
} catch (e) {
console.log(`STAGE 2 - Error occurred when assigning url variables. Error: ${e}`);
console.error('STAGE 2 - Error occurred when assigning url variables. Error:', e);
callback(`STAGE 2 - Error occurred when assigning url variables. Error: ${e}`);
}
console.log(`The authorisation Code is ${authcode}. Getting Access Token now!`);
//Asynchronous callback for the next function - return = defensive architecture
return callback(null, swapURL, clientID, clientSecret, authCode);
})
.catch((err) => {
if (res.statusCode == 400) {
console.log(`Error Message: ${res.body.message}. Extra: ${res.body.extra}`);
console.error('Error Message:', res.body.message, 'Extra:', res.body.extra);
callback(`Error Message: ${res.body.message}. Extra: ${res.body.extra}`);
} else {
console.log(`Failed to retrieve the authorisation code! Error: ${error}`);
console.error('Failed to retrieve the authorisation code! Error: ', error);
callback(`Failed to retrieve the authorisation code! Error: ${error}`);
}
});
},
//STAGE 3 - USE AUTH CODE TO GET ACCESS TOKEN
//ASYNC NEEDED?
getAccessToken = async (swapURL, clientID, clientSecret, authCode, callback) => {
//If successful, proceed:
var options = {
method: 'POST',
uri: swapURL,
qs: {
'client_id': clientID,
'grant_type': 'authorization_code',
'client_secret': clientSecret,
'code': authCode
},
json: true // Automatically stringifies the body to JSON
};
console.log(`Beginning HTTP POST Request to ${swapURL}...`);
//await literally makes JavaScript wait until the promise settles, and then go on with the result.
await rp(options)
.then((parsedBody) => {
//POST Succeeded...
Console.log('Successful HTTP POST!');
try {
let accessToken = response.body('access_token'),
refreshToken = response.body('refresh_token');
} catch (e) {
console.log(`STAGE 3 - Error occurred when assigning url variables. Error: ${e}`);
console.error('STAGE 3 - Error occurred when assigning url variables. Error:', e);
callback(`STAGE 3 - Error occurred when assigning url variables. Error: ${e}`);
}
console.log(`The access Token is ${accessToken} and the refreshToken which is ${refreshToken}! These are only valid for 2 hours!`);
//Asynchronous callback for the waterfall - return = defensive architecture
return callback(null, 'done');
})
.catch((err) => {
console.log(`Failed to retrieve the access/refresh Token! Error: ${error}`);
console.error('Failed to retrieve the access/refresh Token! Error:', error);
callback(`Failed to retrieve the access/refresh Token! Error: ${error}`);
});
}
}
You can use youre's auth.main() as middleware or simply run you're code as
app.listen(port, () => {
console.log(`Server is listening to port ${port}...(http\://localhost\:3000)`);
auth.main();
});
or Simply place whatever code you need to run at bootstrap there.
let express = require("express");
...
auth.main();
app.listen(444);
I want to make a request or api call on server 1, this server then automatically request to Server 2 and send the response back to server 1. I am using NodeJs and Express.
Example:
app.post('/api/Is', function(req, response, callback) {
})
I am calling that API in postmain as : http://localhost:3000//api/Is
So it should automatically go on http://localhost:5000//api/Is and send the response back to http://localhost:3000//api/Is call.
I should only call http://localhost:3000//api/Is and in backend code it will take request body and pass it to http://localhost:5000//api/Is and send the response back to http://localhost:3000//api/Is
I think you can consider use the the proxy lib like 'node-http-proxy', the most easy way.
otherwise, you must be transfer the request and response use 'http moudle', like this(no debug, not sure it will work perfectly~):
const http = require('http');
app.post('/api/Is', function(req, response, callback) {
const options = {
host:'localhost',
port:'5000',
path:'/api/Is',
method: 'POST'
// maybe need pass 'headers'?
};
let proxyBody = '';
const req2 = http.request(options, function(res2) {
res2.on('data',function(chunk){
proxyBody += chunk;
}).on('end', function(){
// here can feedback the result to client, like:
// const { headers } = res2;
// response.send(proxyBody)
});
});
// .on('error'){} here handle the error response
req2.end();
});
you need to use any library to make API call from server1 to server2. below code I am using fetch library.
To install the fetch library
npm install node-fetch --save
//SERVER1//
const fetch = require('node-fetch');
router.get("/api/Is", async (req, res) => {
try{
let {success, data} = await getDataFromServer2(req);
if(success) return res.send({success: true, data: data})
res.send({success: false})
}catch(e){
res.send({success: false})
}
});
function getDataFromServer2(req){
return fetch('http://localhost:5000//api/Is', {
method: 'post',
body: req,
headers: { 'Content-Type': 'application/json' },
}).then(res => res.json())
.then((response)=>{
return {
success: true,
data: response
}
}).catch((error) => {
throw new Error("unable to fetch the roles ")
})
}
var express = require('express'),
instagram = require('instagram-node').instagram(),
app = express();
instagram.use({access_token: 'MY_ACCESS_TOKEN'});
instagram.use({
client_id: 'my_client_id',
client_secret: 'my_client_secret'
});
app.get('/', function(req, res) {
instagram.use({ access_token: 'MY_ACCESS_TOKEN' });
instagram.media_popular(function(err, medias, remaining, limit) {
if (err) {
res.send(err.body);
}
else {
console.log(medias);
res.render('pages/index', { grams: medias });
}
});
});
app.listen(8888,function(){
console.log("listening port 8888")
});
So, this is my code. When I'm hitting the http://localhost:8888, I'm getting the following page
What am I doing wrong?
Why am I not logged in when I get the 404 page?
Not really sure what is the problem but as an workaround you can use requestjs to send GET request using this
https://api.instagram.com/v1/media/popular?access_token=ACCESS-TOKEN
url. You will get the same data. Might not be a clear solution but I know that work for sure.
check your access token while passing through the URL. I got this page when my URL was not correct.
You can use this code after getting access token for getting user data from instagram.
exports.getUserData = function(req, res) {
var outputJSON = "";
var requestUrl = 'https://api.instagram.com/v1/users/self/?access_token=YOUR ACCESS TOKEN HERE';
request.get({ url: requestUrl, json: true }, function(err, response, accessToken) {
if (response.statusCode !== 200) {
outputJSON = { 'status': 'failure', 'messageId': 403, 'message': err };
} else {
outputJSON = { 'status': 'success', 'messageId': 200, 'data': response.body }
//console.log('response',response.body);
}
res.jsonp(outputJSON);
});
}
I'm new to Sails.js and I was trying to make a filter to authorize using a Bearer token which come from a higher server, a gatekeeper which is responsable to do the OAuth2 authentication from GitHub API. The services streams works well. I'm already aware of Passport.js but I'm trying to implement this on my own. I came with a policy which looks like:
module.exports = function (req, res, next) {
var httpsExec = require('https');
if (req.headers.authorization) {
var parts = req.headers.authorization.split(' ');
if (parts.length == 2) {
var tokenType = parts[0]
, credentials = parts[1];
if (/^Bearer$/i.test(tokenType) || /^bearer$/i.test(tokenType)) {
httpsExec.request({
host: 'api.github.com',
post: 443,
path: '/user',
method: 'GET',
headers: {'Authorization': 'token ' + credentials, 'User-Agent': 'curly'}
}, function (response) {
var responseData = '';
response.setEncoding('utf8');
response.on('data', function (chunk) {
responseData += chunk;
});
response.once('error', function (err) {
next(err);
});
response.on('end', function () {
try {
req.options.user = JSON.parse(responseData);
next();
} catch (e) {
res.send(401, {error: e});
}
});
}).end();
} else {
console.err("The token is not a Bearer");
res.send(401)
}
}
} else {
res.send(401, {error: "Full authentication is necessary to access this resource"})
}
};
The policy is called once I hit the controller route but it throws a _http_outgoing.js:335
throw new Error('Can\'t set headers after they are sent.');
^
Error: Can't set headers after they are sent.
And the process is terminate.
The problem I think is the next() and the returns I tried everywhere I think, to put the next() call, but still gives me this error, if I remove then I lock the request on the policy.
EDIT
I did a simple sample of policy where I just set some property on req.options.values and happened the same problem, so maybe could be an issue with req.options.requestData = JSON.parse(responseData); ? How else could I set a property to send to controller ?
response.once('error', function (err) {
next(err);
});
response.on('end', function () {
try {
req.options.user = JSON.parse(responseData);
next();
} catch (e) {
res.send(401, {error: e});
}
});
both are getting executed.to check console.log("something") in error to see if there is error.
This happens when you're trying to modify the request and response together or modify one of them twice.
In your code, I think the callback is being called twice and you are also modifying the response at the same time. Check the lines where you're calling callback "next()". You'll find your issue.
I try to use my compound.js-application as a (transparent) proxy-server. When a user tries to request a external website, the application will check, if the user with that ip-address was authenticated before.
If so, the external site will be shown, if not, the user will be encouraged to login.
The problem is, that the response is not processed, when there is an access to the database-object "User".
When I comment out the database section and just use the code inside the anonymous function, the programm works as expected.
action('exturl', function () {
User.all({ where: { ipaddress: req.ip }}, function(err, users) {
if (users.length > 0) {
this.user = user[0];
var proxy = http.createClient(80, req.headers['host'])
var proxy_request = proxy.request(req.method, req.url, req.headers);
proxy_request.addListener('response', function (proxy_response) {
proxy_response.addListener('data', function(chunk) {
res.write(chunk, 'binary');
});
proxy_response.addListener('end', function() {
res.end();
});
res.writeHead(proxy_response.statusCode, proxy_response.headers);
});
req.addListener('data', function(chunk) {
proxy_request.write(chunk, 'binary');
});
req.addListener('end', function() {
proxy_request.end();
});
} else {
redirect(path_to.login);
}
});
});
Is there a failure inside my code? I don't know what I am doing wrong.