Thanks for the responses guys.
I'll address in two parts:
First, adding cookie-parsing to the HTTP-in node is a general concept. It's a requirement to do the kind of cookie-based authentication that I'd like to do, but not related to authentication per-se. It'd be required for passport-based auth as well.
I tested it out myself with modified HTTP-in nodes and added function nodes that added and parsed cookies. When I added a cookie with the function node, it was available in the cookie-parser on the next call. When I added a cookie with cookie-parser, I was able to parse it with a function node and extract that cookie. I'll send the flow here shortly - I think it demonstrates that cookie-parser can be safely added without affecting existing nodes.
The retrieving of cookies in particular is really challenging and error prone so if anything that's why I'd like to see it make the trip upstream.
Second, I'll address authentication in particular and Julian's comments (thank you for your thoughtful response btw).
Whilst we're here, I'm also looking at how we can improve the authentication story around the HTTP In nodes. Currently you can set all-or-nothing basic auth via the settings file. I'm thinking about what we can do on a per-node basis, and what options we ought to provide out of the box (Basic auth, bearer token etc) and how it may be extensible (passport etc)
I think HTTP-in should not have auth built into it. Instead, we should use HTTP-in as the interface and add an authentication mechanism after the HTTP-in node.
Some context: I'm using node-red to wire up web-based microservices, so my flow looks like the following:
HTTP-In -> Auth -> Microservice -> HTTP-out
By doing it this way I can make MicroService calls through other channels - HTTP is just an interface to the service itself.
I could also do:
OtherMechanismIn -> Auth -> microservice -> OtherMechanismOut
The OtherMechansimIn flow can pass a JWT in on the payload and now I have authentication that is not coupled to web-based techniques. It can use OAuth tokens, can be placed in a Bearer Token, etc.
In a general case I think a node for auth makes a lot of sense - it decouples the auth mechanism from the interface used to access it (which is the reason I use node-red).
Basic auth is always handy to have as it is the lowest common denominator but it is really only useful for test/dev cases as it isn't very secure for live. Bearer tokens can be good but require some careful thoughts on implementation so that they actually provide assurance and cannot be bypassed/hijacked (MiTM hijacking is a common problem).
Not to be contrarian but basic auth is useless even for development. The only case for using it is to secure a resource, and you don't secure a resource that isn't live or publicly available unless your testing your authentication flows. You can't use it to test authentication flows because it doesn't allow you to easily clear the authentication credentials. In every case where you'd use basic auth, you'd be better off keeping it on a local server and not exposing the resource the the public at all until your authentication system is ready.
Bearer-token is great for API-type calls where you can add the token to the headers of every request, but they don't work for browser-based environments where the authentication information needs to persist across page loads. This requires the use of a cookie - hence my desire to add cookie-parser to the HTTP-in node.
JWT is a nice, simple implementation but isn't really enough on its own. Passport is pretty much the standard implementation for node.js authentication and immediately gives you access to a great many useful libraries including Azure, Google, Facebook, Twitter, etc cloud authentications.
JWT is just a secure way to store information client-side with cookies. I was able to use facebook and passport (without sessions) and subsequently save the facebook access_token into a JWT for later use. The nice thing about using JWT is that you can use it where something like passport isn't appropriate (ie: an API endpoint secured by an access token). The same mechanism (JWT) can be used to authenticate both web-based endpoints using passport or API endpoints with a developer-generated access token.
Passport itself is something I'll address further down, but suffice it to say that the requirements of passport are very intrusive as you have to add custom middleware to the express route to get it working. With a generalized HTTP-in node, you either have to have this middleware applied all the time, or set an option to enable / disable it. I actually did both of these and found it to be very complicated. With JWT, it became almost unnecessary.
Of course, with all of these, you still have to maintain a secure "database" of acceptable identifiers unless you want anyone with a cloud id to be able to get access. With JWT and other self-serve authentication methods, you will need something to manage both the id and the secured hash of the password.
That's a general requirement for authentication systems. You have to store user ID's and crypted / salted passwords somehow with passport-local too. I don't see this as an argument against JWT - JWT is just concerned with storing data in a secure way.
This is likely to need it's own admin UI, possibly a user UI too if you want users to be able to sign up for themselves.
Same thing as above - that's a requirement for passport as well. It doesn't provide a UI for you - it just has some methods that make authentication via various methods a little easier. You still have to do all the work of storing the credentials and even figuring out how to secure them.
As you already have something in place for securing the admin UI, you could build on that.
Mine is a web form that sends credentials to a user microservice for authentication. The microservice returns back the user info and permissions which are stored in a JWT and stored in a cookie. That's the flow you'll have to use for any auth system, but replace "microservice" with whatever you're doing to secure your user (facebook, local, etc).
All of this needs HTTPS wire encryption too otherwise it's pretty pointless.
This is just a requirement for any secure system anyway. Of note is that, even over HTTP, the data in the JWT is still cryptographically secure. Without SSL it just makes that token useless as a secret for authentication. You can still store data in it safely though.
It is complex but it would really lift Node-Red up towards something that is a lot more robust and production ready. It would be great to see. Personally, I wouldn't reinvent the wheel. Although getting started may be a little more complex with Passport, the eventual benefits would, I think, be well worth it.
I actually started the http-node-contrib-auth module as a passport node (check out dat commit history) and I'll probably take that work and branch it into a new http-node-contrib-passport module. It allowed you to configure a strategy and install new strategies, as well as control the authenticate middleware entirely within node-red (no custom httpNodeMiddleware needed).
I moved away from a passport-based implementation as the foundation of node-red auth for many reasons:
- The HTTP-in node needs to be modified to support the passport.authenticate() middleware in the express chain.
- You could do this with custom middleware in the settings.js file but this takes away the "drag and drop" ability for the node to work. If you add the "auth-passport" node, you'd still need to modify your configuration to make it work. At that point you lose the benefit of node-red - you could just drop the entire thing express and call it a day. Drag-and-drop into the flow is what makes node-red so awesome and I wanted a solution that kept this.
- passport is a PAIN without session support and sessions required a massive overhaul with intrusive middleware applied to the entire application. JWT makes sessions possible in a non-intrusive way.
- Passport and sessions would still require the cookie-parser middleware to be on HTTP-in anyway so either way this is still useful.
Passport is great for connect/express apps and it's multiple strategy design is VERY elegant, but it's designed for use with express and it definitely shows. It's tightly coupled with the concept of routes, middleware, and (to an extent) the express-sessions package. It's meant to be easy to get started, but it's not a robust system that can realistically be decoupled from express apps. For flow-based programming, we need a fundamentally different approach (authenticating on flows instead of middleware).
You'd be better off taking the passport design and creating a new authentication system that specifically targets flow-based applications (we could still use passport strategies though which would be really cool). That might be my next project.
After all that, for a web-based user you still have the problem of storing authentication data on the client or the user has to log into every page. That means either sessions (which passport uses) or some other cryptographically secure cookie (ie: a JWT or something similar). JWT gives us a way to store cookies within a flow-based environment.
Of interest, how do you prevent man-in-the-middle hijacking of the token?
The only way to prevent MITM effectively is to use HTTPS. This is just a general requirement - nothing specific to JWT or authentication. When you add cookie you can use the secure=true and httpOnly=true options to make sure you're functionally safe from MITM and XSS attacks on the cookie itself.