- Skip to navigation
- Skip to main content
- Skip to primary sidebar
- Skip to secondary sidebar
- Skip to footer
Blog Archives
CRM Migration (Part 2)
Continuing on my series on gradual migration from Coldfusion to Node-js today I’ll be writing up about the authentication system that allows me to have one login that tells both node and coldfusion that the user is authenticated. (Sorry about all the > showing up everywhere – Can’t figure out how to get rid of them)
Authentication
Having worked my way through many iterations of this code, I’m going to start with a quick rundown of the process it took me to get to the end result.
- “I can just proxy through all requests to the CF Server, that sends the cookies as well.” Good points of this is that there’s a node module for that (isn’t there always!), bad point is that if you want to have node communicate with coldfusion as well, for example sockets that call logic on CF, you can’t use the cookie from the CF Server if it’s set up correctly to prevent cookie jacking. So this was a no go as essentially I ended up with 3 sessions, Browser to CF, Browser to Nodejs and Nodejs to CF, not good!!
- “OK, so if I write my own proxy code I can control the cookie right?”. Well, not quite though we’re getting there, again with the same origin restriction on cookies this didn’t work so well.
- “Right, so I have to have all communication with CF sent via node and not store the CF cookie on the browser.” This is about right!
How It’s Done
So if you remember last week there was a function and variable I said I’d explain this week, testCookie and req.session.proxyCookie? Well both of those are to do with my authentication solution.
We start off with the basic cookie and session setup for node:
@use cookieParser security.cookieSecret @use session cookie: httpOnly: true secure: false maxAge: 3600000 * 8 # hour in ms * 8 = 8 hours secret: security.cookieSecret store: sessionstore saveUninitialized: true resave: true
Here we’re using a couchbase memcached session store to store our cookies with some extra settings.
Next we have three functions for working with the Cookies these are:
parseCookie = (cookie) ->
_cookie = request.jar()
if cookie? then _cookie.setCookie require('tough-cookie').Cookie.parse(_c), security.proxyAddress for _c in cookie
_cookie
testProxyCookie = (session) ->
try
_foundCFID = false
for key, cookie of parseCookie(session.proxyCookie)._jar.store.idx[security.proxyCookieDomain]['/']
if key.toLowerCase() is 'cfid' and cookie.value then _foundCFID = true
cookie.lastAccessed = new Date().toISOString()
return _foundCFID
catch
return false
@testCookie = (req, resp, cb, isLogin=false) =>
if req.session? and (not testProxyCookie(req.session) or isLogin) and resp.headers and resp.headers['set-cookie']
req.session.proxyCookie = request.jar()
req.session.proxyCookie resp.headers['set-cookie']
@getSessionCookie req, (sidCookie) ->
sessionstore.set sidCookie, req.session, cb
else cb()
@getSessionCookie = (req, cb) ->
cookieParser(security.cookieSecret) req, {}, (parseErr) ->
if parseErr then return next new Error 'Error parsing cookies.'
# Get the SID cookie
EXPRESS_SID_KEY = 'connect.sid'
cb (req.secureCookies and req.secureCookies[EXPRESS_SID_KEY]) or
(req.signedCookies and req.signedCookies[EXPRESS_SID_KEY]) or
(req.cookies and req.cookies[EXPRESS_SID_KEY]), sessionstore
@sessionAuth = (req, res, next) =>
@getSessionCookie req, (sidCookie) =>
req.sessionroomid = sidCookie
if testProxyCookie req.session then next()
else request
uri: "#{security.proxyAddress}index.cfm/users/nodetestlogin.json"
method: 'GET'
, (err, resp, data) => @testCookie req, resp, next, true
@all '*', @sessionAuth
Let’s Break it Down!!
So, to start off when a user hits a page, the `@all ‘*’, @sessionAuth` gets hit. this in turn calls the `getSessionCookie` which if you haven’t guessed loads the cookies sent to node and finds the ‘connect.sid’ cookie value. We then store this cookie value in the request as the req.sessionroomid which we’ll use later for sockets and updating just one client across all his tabs (I think this may be a security hole but I’m yet to go back and review it yet). Then we call `testProxyCookie`, this function does two important things, first, it checks that the cookie we have stored in the session for communicating with CF is valid, and second it updates the lastAccessed value. Updating the lastAccessed value is important as when you send the cookie through CF uses this to detect if your session should have timed out yet or not! If you don’t update this and have say a 30 minute session timeout on CF, then even if you keep contacting CF constantly, 30 minutes after your first contact your session will expire!! Back in `sessionAuth` if the proxy cookie is valid (`testProxyCookie()` returns true) then we continue via `next()`, if not we send a request to the CF server using request and then use `@testCookie` to set the proxyCookie from the result.
In short `@testCookie` reads the response from the CF Server, parses the cookies and then saves the value in the session value, we later use that cookie with request to authenticate to the CF Server. Beyond the above you can setup your node.js server as you normally would and this will take care of all the integration.
Till Next Time (Which I am yet to decide what I’ll write about!).
UPDATE
I found a nasty bug that runs around the storage of tough-cookie objects – basically it doesn’t work! My solution was to store the ‘set-cookie’ headers instead and then add a ‘parseCookie’ method to convert it into the actual cookie jar object. This means that when you make the call to the CF server you do the following:
request
uri: "#{security.proxyAddress}/index.cfm"
method: 'GET'
jar: parseCookie req.session.proxyCookie
, (err, resp, data) => #handle response here!
CRM Migration (Part 1)
Recently I’ve been able to start moving our internal Coldfusion based CRM onto Node.js, I’m going to start chronicling the difficulties and challenges this has produced and my solutions to these problems in this multi-part series CRM Migration. My aim is to get one of these up every wednesday.
Access
The first problem I came across is the nastiest one for a partial migration; How do I keep one login for both sites?
To solve this we need to solve a simpler question – How do I access legacy coldfusion pages that we aren’t migrating yet?
I tried using node-proxy, but that didn’t go so well after a while due to cookies making sessions do weird things, my eventual solution was as follows:
@get '/index.cfm*', (req,res) ->
request
uri: "#{security.proxyAddress}#{req.url.substring 1}"
method: 'GET'
jar: req.session.proxyCookie
, (err, resp, data) => testCookie req, resp, -> res.send resp.statusCode, resp.body
@post '/index.cfm*', (req,res) ->
request
uri: "#{security.proxyAddress}#{req.url.substring 1}"
method: 'POST'
jar: req.session.proxyCookie
, (err, resp, data) => testCookie req, resp, -> res.send resp.statusCode, resp.body
NOTE: I started off using zappajs, but then migrated to Express 4 so I replicated the helpers. The ‘@’ is essentially a reference to the express server variable.
I’ll start by explaining the reasoning behind the URL structure; ‘/index.cfm*’. The CRM in coldfusion runs on CFWheels, a MVC library, and I never got the url rewrites to work on IIS so an example format for Coldfusion URL’s is ‘/index.cfm/{controller}/{action}/{key}’. The two above handlers basically state that any request that starts with ‘/index.cfm’ is targeted to the coldfusion server, in case you haven’t guessed my approach is to field ALL http requests to the node.js server which will act as a proxy for the Coldfusion server if we haven’t migrated that across using node.js’s built in request library.
That’s basically it for access to the old Coldfusion pages, you may be curious about the ‘testCookie’ function and where on earth this ‘req.session.proxyCookie’ variable comes from, but that will be in Part 2.
Hint: It’s about authenticating the user for node.js and coldfusion!
See you all next week!
If you want to know about my basic setup for Node.js projects with express see my Nodejs Website Base.
Securing Express Routes with PassportJS
It’s been a while since I posted anything, but I finally found something worth blogging about! Besides my trip to India at the beginning of the year and not much work recently…
Recently I’ve been building a web app for my gaming clan to help them run their practices and also to help me learn more of Node.js and NOSQL (CouchDB in particular). My first big problem in building this app was that of user login, or authentication. After googling around a bit for some modules to help with this (I really like the modular nature of node.js!) I came across a unified login system called Auth0, which I have to say is really awesome. I decided to go with this as who hasn’t thought that letting people login using facebook or similar would be cool but it’s too hard to bother! After running through their node.js tutorial which makes use of PassportJS and their authentication strategy passport-auth0 I came up with the following basic login/authentication code. (This is stripped down a bit, for my full code see here)
http = require 'http'
express = require "express" # Note this is Express v3.5.x
app = module.exports = express()
httpServer = http.createServer app
passport = require 'passport'
Auth0Strategy = require 'passport-auth0'
security = require('./security')
strategy = new Auth0Strategy
domain: security.auth0.domain
clientID: security.auth0.clientID
clientSecret: security.auth0.clientSecret
callbackURL: '/callback'
, (accessToken, refreshToken, profile, done) ->
console.log "Hit Auth0 strategy"
done null, profile
passport.use strategy
# This is not a best practice, but we want to keep things simple for now
passport.serializeUser (user, done) -> done null, user
passport.deserializeUser (user, done) -> done null, user
helmet = require 'helmet'
app.configure ->
app.use express.logger 'dev'
app.set 'views', "#{__dirname}/views"
app.engine 'jade', require('consolidate').jade
app.set 'view engine', 'jade'
app.use helmet.xframe(), helmet.iexss(), helmet.contentTypeOptions(), helmet.cacheControl()
app.use express.json(), express.urlencoded()
app.use express.methodOverride()
app.use express.cookieParser()
app.use express.session
secret: security.cookieSecret
#cookie:
#httpOnly: true
# secure: true # for HTTPS only
app.use passport.initialize()
app.use passport.session()
app.use require('express-validator')()
app.all '/api/*', auth
auth = (req, res, next) -> if req.isAuthenticated() then next() else res.redirect '/login'
# Auth0 callback handler
app.get '/callback', passport.authenticate('auth0',
failureRedirect: '/login'
), (req, res) ->
if not req.user then throw new Error 'user null'
if req.user._json.appid?
req.session.dbconn = require('./../startup_security/nano').nano req.user._json.user_id, req.user._json.dbpass
res.redirect "/#{req.user._json.appid}/manage"
else res.redirect "/first-login"
# warning this would log you out of facebook if you logged in via the facebook button - don't use this in live!
app.get '/logout', (req, res) ->
req.logout()
delete req.session
delete req.user
res.redirect "https://simeonc.auth0.com/logout?returnTo=http://tawmanager.localhost:3000/"
app.get '/login', (req, res) -> res.render 'login.jade'
app.get '/first-login', auth, (req, res) -> res.render 'first-login.jade'
httpServer.listen 3000
console.log "Server Running on port 3000 via ExpressJS"
console.log "Authentication Via Passport.js and Auth0"
Now this is my final code after some help from the owner of passport-node0 here. Now I’ll detail some of the quirks I had to get through that are really, really poorly documented.
First; Authenticating individual routes. Initially from reading through what other people have written and the other information out there I tried this:
<pre>app.get '/first-login', passport.authenticate('auth0'), (req, res) -> res.render 'first-login.jade'</pre>
That is WRONG, and meant I spent many many hours looking at 302 redirects from that page back to the login page for no discernable reason…
Correct is:
auth = (req, res, next) -> if req.isAuthenticated() then next() else res.redirect '/login'</pre> <pre>app.get '/first-login', auth, (req, res) -> res.render 'first-login.jade'</pre> <pre>
Secondly; Authenticating all routes matching a particular prefix. From here and there around the web I thought you did this via:
app.use '/api', auth
Turns out that’s also wrong (this ones in the docs so I felt really stupid when I found it). Here’s the correct one:
app.all '/api/*', auth



