Git Product home page Git Product logo

couchdb-jwt-auth-server's Introduction

CouchDB JWT Authentication Server

npm David Build Status

This is an Express app and CLI tool that manages JSON Web Tokens for a CouchDB server. This acts as a small independent server for generating, renewing and invalidating JWTs that can be used with CouchDB.

This service requires couch_jwt_auth to be installed on the CouchDB server.

Note: This library is still being actively developed and may be not be totally secure. It is not recommended to use it in a production environment.

Motivation

CouchDB authentication is really easy to use, but doesn't work great in every situation. Suppose you have a third-party API server that is capable of making authenticated CouchDB requests on behalf of a browser client. If you use Basic Authentication, you'll need to store the user's credentials and send them with every request. Cookie Authentication is great if the browser is directly accessing the server, but due to cookie rules you won't be able to transfer that token to the third-party server.

This is where JSON Web Tokens come into play. They allow you to proxy CouchDB's user sessions while ensuring that they came from trusted source. This way, the token can be sent to any server so it can act on behalf of the user. It works like this:

  1. A client signs in with this server using a username and password.
  2. The server authenticates with CouchDB and sends a new JSON Web Token back to the client.
  3. The client can now use this token to make secure requests against CouchDB.

A few things to mention:

  • JWTs are similar to proxy authentication. The user's session is stored in the token and CouchDB uses that to authenticate directly. The _users database is only used by this server and is never touched by CouchDB. This means that you can separate your user and application databases into multiple CouchDB instances.
  • This server maintains sessions so that tokens can be easily invalidated in case an attacker gains control of the key. couch_jwt_auth only verifies that the JWT has a valid signature and hasn't expired— it does not verify that the session is still valid. To combat this issue, expirations on tokens are set very low (usually 5 minutes or less) and instead the client will need to renew them on a regular basis.

Install

Grab a copy from NPM. If you install globally, the CLI tool is made available.

npm i couchdb-jwt -g

This library also exports an Express app, so you can install and use it within an existing project.

npm i couchdb-jwt --save

To use this library properly, you will need to set the following configuration for CouchDB. Make the secret anything you'd like, just be sure to let this library know what it is.

One thing to note is that this library uses the raw secret whereas couch_jwt_auth uses a base64 encoded secret. Here's a way to encode a string to base64

echo -n 'keyboardcat' | openssl base64

The output, in the example a2V5Ym9hcmRjYXQ= is what you use for hs_secret.

[jwt_auth]
hs_secret=a2V5Ym9hcmRjYXQ=
username_claim=name

CLI Usage

$ couchdb-jwt [OPTIONS]

-c, --config <file>     A config file with your options. It's a JSON object.
--couchdb <url>         URL to the CouchDB server that manages users and has
                        couch_jwt_auth installed.
--secret <secret>       The secret used to sign JWTs. This should match the
                        raw value CouchDB server has set for jwt_auth.hs_secret
                        (Note: hs_secret is base64 encoded in CouchDB's config)
--expiresIn <exp>       Time in seconds for JWT expiration. Default is 300 (5 min)
--session.store <name>  The library to use for storing session data. There are
                        two built in options: memory and couch. Additional
                        session options can be passed using the dot syntax.
--endpoint <ep>         The web server mount path. Defaults to '/'.
--port <port>           The port to start the HTTP server on. Defaults to 3000.
-h, --help              Show this message.
-v, --version           Print the currently installed version of this server.

Example call:

$ couchdb-jwt --couchdb http://admin:pass@localhost:5984 --secret keyboardcat --session.store couch

Config file

If you rather use a config file instead of passing options in the command line, you can just create a JSON file, i.e. config.json, with the options you want to use like:

{
  "couchdb": "http://admin:pass@localhost:5984",
  "secret": "keyboardcat",
  "session": {
    "store": "couch"
  }
}

And then call it with:

$ couchdb-jwt --config config.json

API Usage

This library exports a function that creates an Express app which makes it really easy to use. Pass options directly to the method.

var couchdbjwt = require("couchdb-jwt")({
  secret: "keyboardcat",
  endpoint: "/session",
  expiresIn: "2m",
  session: {
    store: "couch",
    db: "jwt_sessions"
  }
});

couchdbjwt.listen(3000);

Since Express is so flexible, you can use this with any existing Node.js HTTP server.

var app = require("express")();
var couchdbjwt = require("couchdb-jwt")({
  secret: "keyboardcat"
});

app.use("/auth", couchdbjwt);

Here are all of the available options:

  • couchdb String - The URL of the CouchDB server to authenticate against.
  • secret String - The secret to sign tokens with. This should match the couch_jwt_auth configuration in the target CouchDB server. This is the only required option.
  • endpoint String - Server mount path. Defaults to /.
  • algorithms String[] - An array of JSON Web Token hashing algorithms to validate tokens with. The first algorithm is used to sign tokens. Defaults to ["HS256"].
  • expiresIn String | Number - Amount of time a signed token will be valid for. When a string, this takes any ms valid value for time. Numbers are interpreted as seconds. Defaults to "5m".
  • handleErrors Boolean - Adds server routes for handling errors, including Not Found errors. Disable when using couchdb-jwt with a greater Express app. Defaults to true.
  • transform Function - A method that transforms the JWT payload into response data.
  • session Object - Session storage options. These values get passed directly to the session store when created.
    • session.store String | Function - The session storage to use. Built-in values include memory and couch. Other strings are required and used. Functions are considered storage creation methods and are expected to return a storage API object.
  • refreshRoles Function | Boolean - A method that is called on every token renewal that should return an array of updated user roles. This method is called with the JWT payload data and the parsed CouchDB connection options. By default, this method attempts to use the passed JWT to fetch the user's document for updated roles and will return the original role set if it fails. Set to false to disable role refresh and always use the original roles.

REST API Endpoints

This servers reveals four REST endpoints under the root path /. The endpoint is configurable through the endpoint option.

All of the endpoints, except for POST require the JSON Web Token to be set in the Authorization header like so:

Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoidCIsInJvbGVzIjpbXSwic2Vzc2lvbiI6IjQ2OGZjNDFjZWIwMmQwYmI0ZTcxZjI2ZThlMGNlMjE3IiwiaWF0IjoxNDU3NDg0MDE3LCJleHAiOjE0NTc0ODQzMTd9.9OjtQQ9ocpDx5ES9sFRgUxbjYNTBt1uX7vD_Lkj3SPg

GET /

Returns JSON data containing information about the current JWT and session.

curl http://127.0.0.1:3000 \
  -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoidCIsInJvbGVzIjpbXSwic2Vzc2lvbiI6IjQ2OGZjNDFjZWIwMmQwYmI0ZTcxZjI2ZThlMGNlMjE3IiwiaWF0IjoxNDU3NDg0MDE3LCJleHAiOjE0NTc0ODQzMTd9.9OjtQQ9ocpDx5ES9sFRgUxbjYNTBt1uX7vD_Lkj3SPg"
{
  "ok": true,
  "userCtx": {
    "name": "t",
    "roles": []
  },
  "session": "468fc41ceb02d0bb4e71f26e8e0ce217",
  "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoidCIsInJvbGVzIjpbXSwic2Vzc2lvbiI6IjQ2OGZjNDFjZWIwMmQwYmI0ZTcxZjI2ZThlMGNlMjE3IiwiaWF0IjoxNDU3NDg0MDE3LCJleHAiOjE0NTc0ODQzMTd9.9OjtQQ9ocpDx5ES9sFRgUxbjYNTBt1uX7vD_Lkj3SPg",
  "issued": "2016-03-09T00:40:17.000Z",
  "expires": "2016-03-09T00:45:17.000Z"
}

POST /

Authenticates against CouchDB with a username and password and returns a new JSON Web Token. You should send the server a json or url encoded request with username and password fields. The server will respond with a token and content type of application/jwt.

curl http://127.0.0.1:3000 \
  -X POST \
  -d username=test2 \
  -d password=test
{
  "ok": true,
  "userCtx": {
    "name": "t",
    "roles": []
  },
  "session": "468fc41ceb02d0bb4e71f26e8e0ce217",
  "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoidCIsInJvbGVzIjpbXSwic2Vzc2lvbiI6IjQ2OGZjNDFjZWIwMmQwYmI0ZTcxZjI2ZThlMGNlMjE3IiwiaWF0IjoxNDU3NDg0MDE3LCJleHAiOjE0NTc0ODQzMTd9.9OjtQQ9ocpDx5ES9sFRgUxbjYNTBt1uX7vD_Lkj3SPg",
  "issued": "2016-03-09T00:40:17.000Z",
  "expires": "2016-03-09T00:45:17.000Z"
}

PUT /

Generates a new JWT using an existing JWT. This will continue the current session with a new token. The server will respond with a token and content type of application/jwt.

curl http://127.0.0.1:3000 \
  -X PUT \
  -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoidCIsInJvbGVzIjpbXSwic2Vzc2lvbiI6IjQ2OGZjNDFjZWIwMmQwYmI0ZTcxZjI2ZThlMGNlMjE3IiwiaWF0IjoxNDU3NDg0MDE3LCJleHAiOjE0NTc0ODQzMTd9.9OjtQQ9ocpDx5ES9sFRgUxbjYNTBt1uX7vD_Lkj3SPg"
{
  "ok": true,
  "userCtx": {
    "name": "t",
    "roles": []
  },
  "session": "468fc41ceb02d0bb4e71f26e8e0ce217",
  "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoidCIsInJvbGVzIjpbXSwic2Vzc2lvbiI6IjQ2OGZjNDFjZWIwMmQwYmI0ZTcxZjI2ZThlMGNlMjE3IiwiaWF0IjoxNDU3NDg0MDgzLCJleHAiOjE0NTc0ODQzODN9.g3ZTxii8rFkB1cTBbB9Apoxu_Xd9jUEXH37GE4nmUNg",
  "issued": "2016-03-09T00:41:23.000Z",
  "expires": "2016-03-09T00:46:23.000Z"
}

DELETE /

Destroys the current JWT session, invalidating all existing JWTs made with this session.

curl http://127.0.0.1:3000 \
  -X DELETE \
  -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoidCIsInJvbGVzIjpbXSwic2Vzc2lvbiI6IjQ2OGZjNDFjZWIwMmQwYmI0ZTcxZjI2ZThlMGNlMjE3IiwiaWF0IjoxNDU3NDg0MDE3LCJleHAiOjE0NTc0ODQzMTd9.9OjtQQ9ocpDx5ES9sFRgUxbjYNTBt1uX7vD_Lkj3SPg"
{
  "ok": true,
  "userCtx": {
    "name": "t",
    "roles": []
  },
  "session": "468fc41ceb02d0bb4e71f26e8e0ce217",
  "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoidCIsInJvbGVzIjpbXSwic2Vzc2lvbiI6IjQ2OGZjNDFjZWIwMmQwYmI0ZTcxZjI2ZThlMGNlMjE3IiwiaWF0IjoxNDU3NDg0MDE3LCJleHAiOjE0NTc0ODQzMTd9.9OjtQQ9ocpDx5ES9sFRgUxbjYNTBt1uX7vD_Lkj3SPg",
  "issued": "2016-03-09T00:40:17.000Z",
  "expires": "2016-03-09T00:45:17.000Z"
}

Session Stores

The library stores JWT session IDs in a database of your choosing. There are two built-in options: memory and couchdb.

The memory store is the default store and is very simple. It keeps the ids in memory, but this means that it does not scale and leaks memory. Only use this one for local testing purposes.

The other built in store is couchdb. This creates a new database called jwt_sessions in CouchDB for storing sessions. CouchDB super admin credentials are only necessary if the database hasn't been set up yet.

There is one other store currently available that uses redis, CouchDB JWT Redis Session Store. If performance matters to you, this is the recommended store.

Contributing

I am always looking for contributors! Please submit an issue or pull-request with feedback, bug fixes, new features and questions.

couchdb-jwt-auth-server's People

Contributors

greenkeeperio-bot avatar tyler-johnson avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

couchdb-jwt-auth-server's Issues

couch store sessions should expire

The redis store will set a TTL value on sessions so that they can be auto cleaned. CouchDB does not have this out-of-box like redis does, but we we could fake it by setting an expire property on the document and having the store check the value before verifying it.

Couch store security and maintenance

Perhaps two things could be added to the couch store database module:

  • security, e.g.: limit to a role
  • ongoing maintenance like compactation.

What do you think about that?

Using token instead of credentials to authenticate admin user

Admin credentials are necessary if you're using the couch session store.

In order to avoid exposing those directly, what do you think about allowing the use of a token instead of a username/password? The token could be either one that doesn't expire or one that expires in 3 months or so (up to you) and is treated like an SSL cert that has to be renewed

I guess this opens the door to API keys to applications in a way. :)

What are your thoughts around this?
Cheers,
Darío

REST output should be consistent

The REST endpoints lack any kind of consistent behavior currently and this will make it harder for clients to integrate with it.

  • Server should output JSON errors with clear types. The jsonwebtoken library already does a good job with this, so this will be very straight forward.
  • Server should output the same Content-Type for every endpoint by default, probably JSON. The application/jwt type could still be produced, but only when the request has it marked in the Accept header.

if a user's roles change, the user must sign back in to get updated roles

This is an interesting problem because I don't think there is a straight forward solution. A user is given a set of roles. On sign in, this server fetches the user's session and turns it into a token. The problem arises when a user's roles change later on in the life of the token. The token does not get the updated set of roles and therefore will continue to access the database on the old set of roles. This creates two problems: users not having enough permission and getting denied (role was added) or the user has more permission than they should (role was removed).

Currently, the only way to solve this is to have the user sign out and sign in again, but this requires their username and password which may not always be available.

One solution may be to have the server request a new session on each token renewal. This has its downsides, however: this would require the database to be configured with public user profiles or for a super admin user and password to be set.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.