hoodie-plugin-users

[![Build Status](https://travis-ci.org/hoodiehq/worker-users.png?branch=master)](https://travis-ci.org/hoodiehq/worker-users)

npm install hoodie-plugin-users
6 downloads in the last day
80 downloads in the last week
435 downloads in the last month

Build Status

Hoodie Users Plugin

This plugin is used internally by hoodie-server. It picks up user sign ups, creates user databases and handles password resets.

Besides that it also provides a view for pocket to manage the users of your app.

How things work behind the curtain

Hoodie is using CouchDB's built-in authentication module based on the _users database. When a user signs up using `hoodie.account.signUp('joe@example.com','secret'), the following doc gets created:

{
   "_id": "org.couchdb.user:user/joe@example.com",
   "_rev": "4f9b7ea8424eb477b8cbb6cba49e0dd5",
   "name": "user/joe@example.com",
   "roles": [],
   "type": "user",
   "ownerHash": "uuid567",
   "database": "user/uuid567",
   "password_scheme": "pbkdf2",
   "derived_key": "4b1d247f488fd4f335a4e0d16f2c5386a5ac98b1",
   "salt": "564aa550233a06f3bbed1cf014ee21e1"
}

The worker of the Users Plugin listens to the changes feed of the _users database. If a new doc of type: "user" pops up the worker confirms the user (if auto confirmation is enabled). To confirm a user, the worker creates a database "user/uuid567", uuid567 being the autogenerated and globally unique user hash. It then sets the database security so that only user joe@example.com can access it. Once finished, the _users doc will be flagged as confirmed by setting roles to ["confirmed", "uuid567"]. If an error occured, the roles property will be set to ["error"] and an $error property will be added with an explanation.

Username changes

Changing a username is rather tricky in CouchDB land, as the username is part of the _users doc._id. So instead, an entire new doc has to be created and theh the old one needs to be removed.

When the user runs hoodie.account.changeUsername('secret', 'newjoe@example.com'), the user's _users doc will be updated with a new property: "$newUsername": "newjoe@example.com". The users worker will pick that up, create a new doc with "_id": "org.couchdb.user:user/newjoe@example.com", copy over all properties and then remove the old document. hoodie.account.changeUsername checks if the old _users doc has been removed. Once that happened, it will sign out silently, sign back in using the new username and then resolve its promise.

Password resets

The only database that a signed out user can write to securely (without other users being able to read it) is the _users database. So when a user runs hoodie.account.resetPassword( "joe@example.com" ), we create a special object with "_id": "org.couchdb.user:$passwordReset/joe@example.com/uuid123". uuid123 is a globally unique id that gets generated for the password reset and stored locally in the user's browser. The $passwordReset doc is technically a CouchDB user, so we set the password to uuid123 as well.

The worker picks up new $passwordReset, generates a new password for joe@example.com, updates Joe's _users doc and deletes $passwordReset again. If there is an error, it updates the $passwordReset doc with a descriptive $error property instead.

In the browser, hoodie.account.resetPassword listens to changes on the $passwordReset doc. If it gets removed, it resolves its promise. If the $error property gets added, it rejects with its message.

Anonymous users

Hoodie can be used entirely without accounts. That usually means, that data gets only stored in the user's browser. There are cases though when data needs to be synchronized to CouchDB, for example when an anonymous user wants to send an email, share data or do any different kind background tasks. You might also decide as an app developer that you want to store data of anonymous users to prevent data loss.

To create an account for an anonymous user and to kick off data synchronization, you can use hoodie.account.anonymousSignUp(). This will create a doc in _users that looks similar to what hoodie.account.signUp(username, password) generates:

{ "_id": "org.couchdb.user:anonymous_user/uuid567", "_rev": "4f9b7ea8424eb477b8cbb6cba49e0dd5", "name": "user/uuid567", "roles": [], "type": "user", "ownerHash": "uuid567", "database": "user/uuid567", "password_scheme": "pbkdf2", "derived_key": "4b1d247f488fd4f335a4e0d16f2c5386a5ac98b1", "salt": "564aa550233a06f3bbed1cf014ee21e1" }

The only differences are:

  1. username gets set to the users autogenerated hash, which is a globally unique uuid.
  2. password gets generated and stored locally in the users browser.

Hoodie will recognize that the user has an anonyomus account and will authenticate silently in the background. If than at some point the user signs up for a real account by running hoodie.account.signup( 'joe@example.com', 'secret'), it will internally be handled as a username change, from uuid567 to joe@example.com.

Plugin settings

Settings for the users plugin are not implemented yet. But what we plan to do is:

  1. Send tokens instead of passwords via email.
  2. Automatic user confirmation / send confirmation tokens via email + hoodie.account.confirm(token) / manual user confirmation / manual user management
  3. Set custom texts for the emails (user confirmation, password reset, etc)
npm loves you