User accounts without Meteor

One of the first major roadblocks I hit when trying to build a react app without Meteor is the lack of a full featured user accounts system. I’ve relied on Meteor for so long I don’t know how to build something similar from scratch. How do other people do it?

3 Likes

Maybe http://passportjs.org/ ? Or there’s also https://auth0.com/

1 Like

@sacha Discover Meteor should branch out into other categories.

Don’t rely on just Meteor. I’d like to learn about passportjs and apollo and all of those other fantastic things.

I’ll pay. People will pay. Don’t limit yourself.

Thanks! The problem is that I don’t really know much about these other things myself… The downsides of being so committed to Meteor I guess!

I’m a bit uneasy with the amount of things I’ve come to take for granted with Meteor.

7 Likes

I’ve been asked to help add some features to an old Backbone.Marionette based CRM, with a thousand+ line gulp file. I swear there’s a flight sim hidden in this thing … I miss you Meteor build tool, please let me take you for granted! :slight_smile:

4 Likes

I am hoping that apollo has an account system. I think MDG could monetize it like auth0

I think the idea for that would be to use Meteor with Apollo.

2 Likes

I think we eventually want to factor accounts out of Meteor so you can use it in any Apollo app, but it’s not a top priority at the moment.

@gadicc started some interesting work in this regard: https://github.com/gadicc/apollo-passport, https://github.com/gadicc/apollo-passport-react.

I ran into the same issue.

Typically outside of Meteor you will have to ‘apps’ a server app that acts as an API and also an app that is the client. Sometimes for ease of use the API also serves out the HTML/JS/CSS (i’m not a fan of doing this with Node but that’s another post lol). For simplicity of the example lets assume they’re on the same server.

I would recommend learning how to manually roll your own auth before using a library or service because it will let you make better choices with that lib and it will help prevent security holes because you actually have an idea of what’s going on.

I would start by learning everything you can about JWT authentication. It’s very common for SPA or mobile apps and it’s also very similar to how Meteor accounts work except the JWTs can’t be revoked from the server by default since they don’t have to be saved to the database.

The general flow is going to look like:

  • user requests to create an account and passes user/pass to server to http://foo.com/signup with a POST request

  • next the server accepts the request and checks to see if the user exists and if it doesn’t it creates a new document with the email and any other metadata needed (createdAt for ex). The password can’t be stored in plain text like the email so it’s “hashed” with something like bcrypt (and on future login, run a compare function to check if the hash matches the login password).

  • once the user is created and saved to the DB the server generates a “token” that the client can use instead of sending their username/password over the wire for every request. finally the server responds back to the client passing the token and usually the newly created user document in JSON.

  • the client receives a 200 and stores the token in local storage for future requests, it may also save the new user to memory (a redux store or in the top level of react local state). check your Meteor apps local storage for Meteor.loginToken to see this token.

  • now if the client needs to request all posts from the API it can just make the AJAX call but this time passing in the token into the header

  • server gets a request for /posts and (if it’s a JWT token) will look at it’s payload, check to make sure it hasn’t been modified by a hacker, and then use the userId key or perhaps a roles key to see who the user is and what roles they have. at this point it’s up to you to figure out how you want to protect the posts endpoint… should they just be logged in? if so you can just let the route continue (as opposed to returning a not authorized error) or should you only return posts they own? You can use this user information in the token to take additional steps

  • the client gets the posts (or unauthorized error) in the response and they can put the data wherever they’d like (redux, inject it into the dom with jquery, etc…). If the user wants to log out you just delete the local storage and the token is gone.


However, if you would like additional security then it can be taken further. You could have a refresh token that lasts for years and then a normal token that only lasts for a few mins. This way if someone gets the token over the wire (but you should be using https anyway), then they can only use it for a few mins. The client would have to keep refreshing the token before it expires.

You can also revoke tokens. This requires you to store the token in the database so that kind of defeats the purpose of having a ‘stateless’ token in the first place. However, once saved in the user doc, it can be revoked at will (like the Meteor token does now).

Anyway hope this helps paint broad strokes on how to get started.

4 Likes

Thanks for explaining all that! Certainly helps

1 Like

Yikes, that’s scary.

It looks like there’s a lot of demand for hassle-free accounts systems. Maybe Meteor could even develop a guide on using Apollo with passport.js.

Like Debergalis said, some kind of transition plan would be necessary. Don’t leave us stranded here :stuck_out_tongue:

1 Like

For now, I think using Meteor Accounts with Apollo is a great option! There are no problems with this approach that I’m aware of, as long as you are OK storing your user accounts in MongoDB.

Anyway, I wouldn’t go overboard with JWTs or anything, I’d suggest just using regular passport.js stuff with cookies to start with if you can’t use Meteor Accounts for some reason. It’s not too hard to set up, the documentation is pretty bad though.

3 Likes

I agree. Just to clarify for future readers, I meant that it’s a good idea to roll your own auth for learning purposes, to better understand how it works. Then use something like passport.js for production.

However, if one is using a Meteor app I would just keep Meteor auth in place and store users in Mongo… much easier :slight_smile:

3 Likes

One thing missing from the existing open-source auth packages is the ability to work with heterogenous backends - passport is great if your server is running express, but what if you have one server that is running express and another one written in django or rails? I’d really like to see a single-sign-on solution that would work across all of these different technologies.

And I’m hesitant to go with the ‘roll your own’ approach because I feel that auth systems really need to be vetted by a security expert, or at least enough knowledgeable eyeballs - it’s far too easy to create security holes.

I think you just need to keep track of the cookie, and you can send it along to the “accounts server” to check who the logged in user is. I think rather than reimplementing login on every server, it would be better to use an API/microservice approach where only one server deals with the storage details, especially if you have different languages involved.

3 Likes

There was a good article on Scotch io a couple of years ago on how to build a token based authentication system from scratch. They recommend using Passport or something similar, but it gives a really good understanding of how things work under the hood.

1 Like

Thought I’d follow up to this in case anyone reads in the future, since I ultimately learned how to do this myself. It’s pretty easy, but you have to be prepared to go all the way with it.

Not going to do a full write up right now but here’s some quick tips:

  1. Install PassportJS
  2. Install bcrypt.
  3. Create a LocalStrategy (from passport-local package) for registering new users. The password you save for the user in the database should be hashed with bcrypt.
  4. Create a LocalStrategy (from passport-local package) for signing in. All this will do is find the user from the database based on their username/email and check if the password they sent hashes to the same value stored in the db.
  5. [Optional] create a BasicStrategy (from passport-http package) that allows you to access API endpoints using Basic Authentication (for example, so you can fetch data through the command line without maintaining a session)
  6. Use express-session. Send your cookies through HTTP only and set secure to true (if you are in production)
  7. Depending on where you are going to keep your user sessions (i.e. Mongo, MySQL, Redis) you will need to install a session store. Since I use Sequelize, I used the connect-session-sequelize package
  8. To protect API endpoints from unauthorized access, create a middleware in express to check if req.isAuthenticated() is true, if so then simply return next(), if not, try to authenticate the user using basic authentication (passing the username and password in the authorization header).

On the client side, when a user logs in successfully, they should get back a user object with details for their profile. Save this somewhere (a Redux store for example) and use this to display the user’s info. When a user’s session expires and they try to hit some protected endpoint they will get a 401 Unauthorized code, at this point you can delete the user’s profile info. If the user is null, the application should be in a “logged out” state so the user isn’t looking at a bunch of unhelpful UI.

If you need more granular auth-checks (roles, permissions, etc). I recommend creating an Activity based authorization scheme. In your database, create a table for Roles and one for Activities. Roles will have many activities and activities can belong to many roles. Roles can also belong to many users.

When you want to protect a route based on a Role, use some sort of “hasPermission” middleware that takes an activity id. Checking if a user is authorized then becomes a simple matter of checking if any Role assigned to a user has been granted permission for an activity with that id.

With this I have a pretty robust authentication system similar to what Meteor gave me out of the box (plus an authorization system). There’s no password reset which I haven’t needed at the moment since I’m building in house enterprise applications. The user’s profile info doesn’t update in real time either, but that’s also not a big deal.

Use HTTPS if env == ‘production’

I don’t recommend JWT for sessions.

1 Like

I’m in virtually this same scenario. Meteor’s user account system is absolutely lovely, I wish there were similar solutions baked into other frameworks.

1 Like