Express session not working just with IE - javascript

Running my third party js app, it has seen so weird when play with session on the IE .
From here no problem using Chrome, FF and Safari.
My apps has the following design:
Any client can use my third party js code.
So, this widget.js just does a call for my app node using express.
This widget.js is a IIFE where on my app get the request and create the session using the code:
app.js
var emptygif = require('emptygif');
var express = require('express');
var uuid = require('node-uuid');
var expressSession = require('express-session');
var app = express();
app.set('trust proxy', 1)
app.use(expressSession({
genid: function(req) {
return uuid.v4(); // use UUIDs for session IDs
},
secret: '1234567890QWERTY',
cookie: { expires: new Date(Date.now() + 900000) },
secure: false,
httpOnly: true
}));
app.use(function(req, res, next) {
res.header('Access-Control-Allow-Credentials', true);
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
res.header('Access-Control-Allow-Headers', 'X-Requested-With, X-HTTP-Method-Override, Content-Type, Accept');
next();
});
app.get('/widget.js', function(request, response){
response.sendfile('widget.js');
});
app.get('/ping', function(req, res) {
var msg_out = load(req);
console.log( msg_out );
emptygif.sendEmptyGif(req, res, {
'Content-Type': 'image/gif',
'Content-Length': emptygif.emptyGifBufferLength,
'Cache-Control': 'public, max-age=0' // or specify expiry to make sure it will call everytime
});
});
function load(req){
var msg = {
user_token: req.session.token_bility,
time_stamp: new Date(),
user_agent: req.headers['user-agent'],
language: req.headers["accept-language"],
referrer: req.headers['referer']
}
return msg;
}
app.listen();
widget.js
(function(window, undefined) {
var _jq;
function drawWidget() {
console.log('drawing');
}
function loadSupportingFiles(_url,callback) {
var path = 'http://myserver/' + _url;
var oImg=document.createElement("img");
oImg.setAttribute('src', path);
var body = document.getElementsByTagName('body')[0];
body.appendChild(oImg);
callback();
};
function scriptLoadHandler() {
console.log('loading pixel');
}
loadSupportingFiles('ping', function() {
scriptLoadHandler();
});
})(window);
I already have done some searching by google for this questions but still without solution.
I will share with pleajure about any hint and knowledge from how to fix this.
Pay attention:
I forgotte to say some keys informations.
The token has been generated well for all browsers (IE, Chrome, FF and Safari).
But each hint for the server generate different tokens only for IE.
The behavior shows that IE expires every request.
IIFE ref.
https://en.wikipedia.org/wiki/Immediately-invoked_function_expression

Solution
After trying some parameters options this resolved.
cookie: { maxAge: 60*10000 },
secure: false,
httpOnly: true
I changed to maxAge instead expires.
These links helped me.
How to keep session in expressjs to not expire
http://mrcoles.com/blog/cookies-max-age-vs-expires/

Related

How to set cookies to client at the first time the client request the html in server-side-render(nextjs+express)

New to Node.js and I'm using nextjs+express+PHP-APi to implement SSR,
I need node-server to set cookies to client at the first time when the client request the node-server for the home page html, but it works in development mode but doesn't work at production mode
I'm converting an old protect(CSR) to SSR
In the old logic, when client first request our PHP server, it'll set cookies(uuid) along with the response, which means the client has completed the identity authentication, then, every request will bring the cookie.
and now in SSR, the node-server should do that stuff, I create a checkAuth function as the authentication middleware, when the client doesn't bring the uuid cookie with the first request, the node-server should request PHP-server for some data and then use the data as cookie along with the response.
now, it works in development mode, it can set cookie to localhost domain successfully, but doesn't work at production mode
const checkAuth = async (req, res, next) => {
try {
if (req) {
res.setHeader(
"Access-Control-Allow-Origin",
dev ? "localhost" : "[production jost]"
);
res.setHeader("Access-Control-Allow-Credentials", "true");
res.setHeader(
"Access-Control-Allow-Headers",
"Content-Type,Content-Length, Authorization, Accept,X-Requested-With"
);
res.setHeader(
"Access-Control-Allow-Methods",
"PUT,POST,GET,DELETE,OPTIONS"
);
if (req.headers && !req.headers.cookie) {
const response = await axios({
//... get data for cookie
});
const options = {
domain: req.headers.req,
path: "/",
secure: false,
httpOnly: true,
expires: new Date(Date.now() + 7200),
maxAge: 7200
};
res.cookie("uuid", response.data.uuid, options);
}
next();
} else {
next();
}
} catch (error) {
return res.redirect("/");
}
};
// ...other code
server.get("/", checkAuth, (req, res) => {
const actualPage = "/";
const queryParams = {};
return app.render(req, res, actualPage, queryParams);
});

Nightmarejs proxy

So I'm uising nightmare to generate scenarios of users using a page to get the HTTP-Requests of the page proxy-ed
To do so I'm using http-proxy and setting it like this :
var http = require('http'),
httpProxy = require('http-proxy');
httpProxy.createProxyServer({target:'http://localhost:9000', auth: 'test:test'}).listen(8000);
http.createServer(function (req, res) {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.write('request successfully proxied!' + '\n' + JSON.stringify(req.headers, true, 2));
res.end();
}).listen(9000);
and then I use nightmare like this :
var nightmare = Nightmare({show: true,
switches : {
'proxy-server': 'localhost:9000;',
'ignore-certificate-errors': true,
}})
nightmare.authentication('test', 'test')
.goto('http://localhost:4200/login')
.type('#name', 'test'+user)
.type('#pwd', 'test'+user)
.click('#log')
.wait('body')
but when I launch it, nightmare goes on the proxy (so localhost:9000) and ignores the goto(localhost:4200), can someone help me?

Jawbone API OAuth access_token handling with node.js (express & passport)

Has anyone successfully navigated Jawbone's OAuth2.0 authentication for their REST API?
I am unable to figure out how to access and send the authorization_code in order to obtain the access_token (steps 4 & 5 in the Jawbone API Authorization Documentation). I want to reuse the access_token for subsequent (AJAX-style) calls and avoid asking the user to reauthorize each time.
Each call of the API (get.sleeps) requires a full round trip of the auth process including this reauthorization to get an authorization_token (screen shot). Both the Jawbone and Passport Documentation is vague on this point.
My stack involves, node.js, the jawbone-up NPM, express.js and passport.js. The Passport Strategy for Jawbone appears to work correctly as I get valid data back.
The jawbone-up NPM explicitly does not help maintain the session (access_token), saying "This library does not assist in getting an access_token through OAuth..."
QUESTION: how do I actually use the OAUTH access_token in the API call? Can someone show me some code to do this?
Thanks
var dotenv = require('dotenv').load(),
express = require('express'),
app = express(),
ejs = require('ejs'),
https = require('https'),
fs = require('fs'),
bodyParser = require('body-parser'),
passport = require('passport'),
JawboneStrategy = require('passport-oauth').OAuth2Strategy,
port = 5000,
jawboneAuth = {
clientID: process.env.JAWBONE_CLIENT_ID,
clientSecret: process.env.JAWBONE_CLIENT_SECRET,
authorizationURL: process.env.JAWBONE_AUTH_URL,
tokenURL: process.env.JAWBONE_AUTH_TOKEN_URL,
callbackURL: process.env.JAWBONE_CALLBACK_URL
},
sslOptions = {
key: fs.readFileSync('./server.key'),
cert: fs.readFileSync('./server.crt')
};
app.use(bodyParser.json());
app.use(express.static(__dirname + '/public'));
app.set('view engine', 'ejs');
app.set('views', __dirname + '/views');
// ----- Passport set up ----- //
app.use(passport.initialize());
app.get('/',
passport.authorize('jawbone', {
scope: ['basic_read','sleep_read'],
failureRedirect: '/'
})
);
app.get('/done',
passport.authorize('jawbone', {
scope: ['basic_read','sleep_read'],
failureRedirect: '/'
}), function(req, res) {
res.render('userdata', req.account);
}
);
passport.use('jawbone', new JawboneStrategy({
clientID: jawboneAuth.clientID,
clientSecret: jawboneAuth.clientSecret,
authorizationURL: jawboneAuth.authorizationURL,
tokenURL: jawboneAuth.tokenURL,
callbackURL: jawboneAuth.callbackURL
}, function(token, refreshToken, profile, done) {
var options = {
access_token: token,
client_id: jawboneAuth.clientID,
client_secret: jawboneAuth.clientSecret
},
up = require('jawbone-up')(options);
up.sleeps.get({}, function(err, body) {
if (err) {
console.log('Error receiving Jawbone UP data');
} else {
var jawboneData = JSON.parse(body).data;
console.log(jawboneData);
return done(null, jawboneData, console.log('Jawbone UP data ready to be displayed.'));
}
});
}));
// HTTPS
var secureServer = https.createServer(sslOptions, app).listen(port, function(){
console.log('UP server listening on ' + port);
});
You weren't too far off, you were already getting the token. To make your code work a few steps are needed:
Add the concept of a "session", data that exists from request to request as a global variable. When you do a full web app use express-sessions and passport-sessions and implement user management. But for now we just add a global for a single user state.
var demoSession = {
accessToken: '',
refreshToken: ''
};
Pass in a user object in the done() of JawboneStrategy. This is because the "authorize" feature of passport is expecting a user to exist in the session. It attaches the authorize results to this user. Since we are just testing the API just pass in an empty user.
// Setup the passport jawbone authorization strategy
passport.use('jawbone', new JawboneStrategy({
clientID: jawboneAuth.clientID,
clientSecret: jawboneAuth.clientSecret,
authorizationURL: jawboneAuth.authorizationURL,
tokenURL: jawboneAuth.tokenURL,
callbackURL: jawboneAuth.callbackURL
}, function(accessToken, refreshToken, profile, done) {
// we got the access token, store it in our temp session
demoSession.accessToken = accessToken;
demoSession.refreshToken = refreshToken;
var user = {}; // <-- need empty user
done(null, user);
console.dir(demoSession);
}));
Use a special page to show the data "/data". Add a route to separate the auth from the display of service.
app.get('/done', passport.authorize('jawbone', {
scope: ['basic_read','sleep_read'],
failureRedirect: '/'
}), function(req, res) {
res.redirect('/data');
}
);
Lastly the Jawbone Up sleeps API is a little tricky. you have to add a YYYYMMDD string to the request:
app.get('/data', function(req, res) {
var options = {
access_token: demoSession.accessToken,
client_id: jawboneAuth.clientID,
client_secret: jawboneAuth.clientSecret
};
var up = require('jawbone-up')(options);
// we need to add date or sleep call fails
var yyyymmdd = (new Date()).toISOString().slice(0, 10).replace(/-/g, "");
console.log('Getting sleep for day ' + yyyymmdd);
up.sleeps.get({date:yyyymmdd}, function(err, body) {
if (err) {
console.log('Error receiving Jawbone UP data');
} else {
try {
var result = JSON.parse(body);
console.log(result);
res.render('userdata', {
requestTime: result.meta.time,
jawboneData: JSON.stringify(result.data)
});
}
catch(err) {
res.render('userdata', {
requestTime: 0,
jawboneData: 'Unknown result'
});
}
}
});
});
I have created a gist that works for me here thats based on your code: https://gist.github.com/longplay/65056061b68f730f1421
The Jawbone access token expires in 1 year so you definitely don't need to re-authenticate the user each time. Also you are provided with a refresh_token as well, so you can refresh the access token when needed.
Once you have the access_token you have to store it somewhere, preferably in some sort of a database or a file storage for later use, then you use that token for each request made to the Jawbone REST API.
The jawbone-up module uses request internally, so I'm going to show you how to make a request with it (it should be pretty much the same with any other module).
Here is how you can get the user's profile (the most basic API call):
var request = require('request')
request.get({
uri:'https://jawbone.com/nudge/api/v.1.1/users/#me',
auth:{bearer:'[ACCESS_TOKEN]'},
json:true
}, function (err, res, body) {
// body is a parsed JSON object containing the response data
})
There is another module called Purest which also uses request internally, but hides some of the complexity around using a REST API. Here is how the same request would look like using that module:
var Purest = require('purest')
var jawbone = new Purest({provider:'jawbone'})
jawbone.get('users/#me', {
auth:{bearer:'[ACCESS_TOKEN]'}
}, function (err, res, body) {
// body is a parsed JSON object containing the response data
})
Alternatively for authenticating the user (getting the access_token) you can use another module called Grant which I personally use, but either one should work.

Node.js cors request - Async example not working

I am using the node-cors module in an app. I've followed some of their examples to make a cors request.
I have a route that I'd like to dynamically add cors options for:
var cors = require('cors'),
user = require('../models/user');
router.get('/route', cors(corsOptions), function(req, res) {...}
This works (this comes straight from one of the examples):
var whitelist = ['http://example1.com', 'http://example2.com'];
var corsOptionsDelegate = function(req, callback){
var corsOptions;
if(whitelist.indexOf(req.header('Origin')) !== -1){
corsOptions = { origin: true, credentials: true }; // reflect (enable) the requested origin in the CORS response
}else{
corsOptions = { origin: false }; // disable CORS for this request
}
callback(null, corsOptions); // callback expects two parameters: error and options
};
This, however, doesn't work and I'm not sure what is wrong:
var corsOptions = function(req, callback){
user.findOne({
url: req.header('Origin')
},function(err,match){
var options;
if(!empty(match)){
options = {
origin:true,
credentials: true
};
}else{
options = {
origin: false,
credentials: false
};
}
callback(null, options);
});
};
Is this an issue with the module, or something I've implemented wrong?
You need to import this:
const cors = require('cors')({origin: true}); instead of const cors = require('cors');

Loopback IO OAuth not working

I am trying to get a https loopback server up and running protected by OAuth. I am using the loopback gateway sample project as a reference. But for some reason I can't get the OAuth piece to work. What I mean is, even after adding in the OAuth bits and pieces, the APIs don't seem to be protected. I get a response back even if there is no token in my request. This is what my server.js looks like
var loopback = require('loopback');
var boot = require('loopback-boot');
var https = require('https');
var path = require('path');
var httpsRedirect = require('./middleware/https-redirect');
var site = require('./site');
var sslConfig = require('./ssl-config');
var options = {
key: sslConfig.privateKey,
cert: sslConfig.certificate
};
var app = module.exports = loopback();
// Set up the /favicon.ico
app.middleware('initial', loopback.favicon());
// request pre-processing middleware
app.middleware('initial', loopback.compress());
app.middleware('session', loopback.session({ saveUninitialized: true,
resave: true, secret: 'keyboard cat' }));
// -- Add your pre-processing middleware here --
// boot scripts mount components like REST API
boot(app, __dirname);
// Redirect http requests to https
var httpsPort = app.get('https-port');
app.middleware('routes', httpsRedirect({httpsPort: httpsPort}));
var oauth2 = require('loopback-component-oauth2')(
app, {
// Data source for oAuth2 metadata persistence
dataSource: app.dataSources.pg,
loginPage: '/login', // The login page url
loginPath: '/login' // The login processing url
});
app.set('view engine', 'ejs');
app.set('views', path.join(__dirname, 'views'));
// Set up login/logout forms
app.get('/login', site.loginForm);
app.get('/logout', site.logout);
app.get('/account', site.account);
app.get('/callback', site.callbackPage);
var auth = oauth2.authenticate({session: false, scope: 'demo'});
app.use(['/protected', '/api', '/me', '/_internal'], auth);
app.get('/me', function(req, res) {
// req.authInfo is set using the `info` argument supplied by
// `BearerStrategy`. It is typically used to indicate scope of the token,
// and used in access control checks. For illustrative purposes, this
// example simply returns the scope in the response.
res.json({ 'user_id': req.user.id, name: req.user.username,
accessToken: req.authInfo.accessToken });
});
signupTestUserAndApp();
//var rateLimiting = require('./middleware/rate-limiting');
//app.middleware('routes:after', rateLimiting({limit: 100, interval: 60000}));
//var proxy = require('./middleware/proxy');
//var proxyOptions = require('./middleware/proxy/config.json');
//app.middleware('routes:after', proxy(proxyOptions));
app.middleware('files',
loopback.static(path.join(__dirname, '../client/public')));
app.middleware('files', '/admin',
loopback.static(path.join(__dirname, '../client/admin')));
// Requests that get this far won't be handled
// by any middleware. Convert them into a 404 error
// that will be handled later down the chain.
app.middleware('final', loopback.urlNotFound());
// The ultimate error handler.
app.middleware('final', loopback.errorHandler());
app.start = function(httpOnly) {
if(httpOnly === undefined) {
httpOnly = process.env.HTTP;
}
server = https.createServer(options, app);
server.listen(app.get('port'), function() {
var baseUrl = (httpOnly? 'http://' : 'https://') + app.get('host') + ':' + app.get('port');
app.emit('started', baseUrl);
console.log('LoopBack server listening # %s%s', baseUrl, '/');
});
return server;};
// start the server if `$ node server.js`
if (require.main === module) {
app.start();
}
function signupTestUserAndApp() {
// Create a dummy user and client app
app.models.User.create({username: 'bob',
password: 'secret',
email: 'foo#bar.com'}, function(err, user) {
if (!err) {
console.log('User registered: username=%s password=%s',
user.username, 'secret');
}
// Hack to set the app id to a fixed value so that we don't have to change
// the client settings
app.models.Application.beforeSave = function(next) {
this.id = 123;
this.restApiKey = 'secret';
next();
};
app.models.Application.register(
user.username,
'demo-app',
{
publicKey: sslConfig.certificate
},
function(err, demo) {
if (err) {
console.error(err);
} else {
console.log('Client application registered: id=%s key=%s',
demo.id, demo.restApiKey);
}
}
);
});
}
I don't get any errors when the server starts up. Thoughts?
Got it figured. More information here https://github.com/strongloop/loopback-gateway/issues/17, but basically I had my rest-api middleware not configured right.

Categories

Resources