Ooth — user accounts for node.js
One of meteor’s mind-blowing features is its out-of-the-box accounts system. In the wider node ecosystem such an easy solution has yet to be found.
I’ve already taken my time to describe the drawbacks of a monolithic framework that takes care of everything for you. It is much better to follow the UNIX philosophy, where an application has different components, each of which does one thing and does it well.
One of these things has to be the management of the user’s identity.
Where are them accounts systems?
If you search for something like that for node, you soon end up reading about passport.js. It is an awesome library, that allows you to quickly authenticate a user, using a lot of different strategies. But many subtle, security-critical aspects of the management of a user’s identity are left as an exercise to the reader. What about the storage of the user’s credentials? What about things like sending a verification email, changing a password, resetting a forgotten password? What about adding, removing and setting up authentication strategies? What about the subtle interplay between different strategies, like when someone logs in with a Facebook account associated with an existing email address? It can’t be that every new web application developer has to find out the solutions to each of these problems.
Meteor’s account system solves most of these problems very well — with the unfortunate shortcoming that it is strongly coupled to the rest of the framework.
If there is a solution to this in the node ecosystem, I’m not aware of it — please let me know in the comments. If it doesn’t exist, a discussion has to happen about how such a solution should look like. In this post I want to contribute to this discussion, with a little specification blueprint for the features such a system should have.
As a code name, I’ll call this system “ooth” (but don’t google image it).
Deployable as a separate microservice
One should be able to run this system independently from the rest of the server logic.
As you can see in the diagram above, the user should be able to use a number of different authentication strategies. Examples would include email+password, Facebook, Google, OpenID, OAuth… One should be able to extend the system with new strategies.
Everything to JWT
In the above diagram, the ooth service provides identity information that the client uses with the actual application. As a format, JSON Web Tokens (JWT), seem to be designed explicitly for such purpose.
By this design the actual application needs to be able to handle only one authentication system: decrypting and verifying a JWT token. If desired, the application can then start a session, so no JWT token is required for further requests to the api. In an express server, this can easily be done with the above library, passport.js.
Specialized to managing user identities
Ooth should manage exclusively user identities and authentication strategies. In particular, things that are not entailed by this are
- User profiles. Does the user have a display name? a picture? a bio? User profiles can vary a lot between apps, and are as such part of an application’s business logic, and should be handled by the application.
- Authorization relevant data, like user roles and permissions. Like profiles, authorization logic is specific to the resources provided by the application and should be handled there.
Built on top of existing technologies
Since we want this solution to be part of the node ecosystem, we should use node libraries whenever possible. express.js is an established, lightweight server library, and passport.js already provides a huge amount of authentication strategies. Ooth should build on top of them.
The server API should have the following structure, extensible with strategies, each of which can have many methods (such as login or register)
- / get JWT from current session
- /login start a session using a JWT
- /logout terminate session
- /strategy1/register create a user using strategy 1
- /strategy1/login log in using strategy1, get a JWT
This no-credentials registration can be useful for when an application requires a user already to have an identity before they actually register.
Local strategy done right
The most important strategy to support should be email/password, with at least the following routes
Carefully managed interplay between strategies
One of Ooth’s biggest contributions should be handling the corner cases of the interplay between strategies. In particular:
- Changes to the identity data by different strategies should be isolated. Generally speaking, no strategy should be able to access or modify data written by another strategy.
- Particular care should be put in the handling of things that should be unique across strategies, such as emails or usernames. Each strategy may or may not come with an email. Upon registering a strategy (or linking it with an account), if the email exists already with an other account, this should result in an error. Conversely, any registered email with any strategy should be usable for logging with the email/password strategy.
Try it out, contribute, join the discussion
Ooth primarily represents an opinion: that there should be a drop-in-solution for the management of user identities in the node ecosystem. In this article I tried to fledge out the specifications of such a system.
I have further been working on an implementation of such a system. The github repository is http://github.com/nmaro/ooth. It’s a “lerna” project, i.e. it contains multiple NPM packages, among which the following:
It’s by no means stable and contains only a few of the features described here. But if you agree with the vision depicted here, you might want to contribute to the implementation, or join the discussion in the repository issues.
You can get started with the example published here.
Follow me to discover the ideal stack
I’m just a fellow programmer. My main app is based on meteor, but since I understood that small libraries with clear roles are the way to go, I’ve been looking for a good replacement of the stack. Yet, as many came to realize, meteor is so innovative that this task is way harder than anticipated. I believe that good parts of the puzzle are an apollo server for your application data and business logic (I’ve written a 100-line tutorial here), and a next.js app (which, even though it has the ability to render components on the server, should be kept separate from the server logic as I’ve explained here). My aim is that ooth become the last big piece of the puzzle.
Join me in this journey by clicking on the “follow” button here below.