jwt-couchdb
JWT endpoint to authenticate users and create JSON Web Tokens out of the CouchDB's session API.
Usage
Install it:
npm i -g jwt-couchdb
Run it:
COUCH=/path/to/config.json jwt-couchdb
It will open up a server on http://localhost:5985
that accepts POST requests to create JWT
tokens out of a valid CouchDB's authenticated user.
In order to get a token we need valid credentials. Both basic HTTP authentication and a JWT token
are valid choices.
Practically, both approaches are the same because they both give you a new token.
Semantically they are different though, as using HTTP authentication means that you're logging in,
while using an existing JWT token means you want to renew it.
Config
The config file is in JSON format, here's an example of all the options you can set:
{
"endpoint": "http://127.0.0.1:5984",
"options": {
"algorithms": ["HS256"],
"expiresIn": "30s"
},
"secret": "supersecret",
"port": 3000
}
At a minimum, you need to set the secret
. This value is the same that you used on
couch_jwt_auth
on your CouchDB server's configuration.
CouchDB's counterpart
In order for CouchDB to accept JWT tokens, you need to have an authentication handler on CouchDB's
land. Your best bet is to use couch_jwt_auth
.
Until merged, I suggest you use UXtemple's
fork as it supports token
blacklisting which can come in very handy if a token goes missing in the wild and a possible
attacker gains access to it.
How do I authenticate?
### Basic HTTP auth
You need to send a POST
with an Authorization
header that looks like
Basic BASE64_USERNAME_COLON_PASSWORD
.
curl -vX POST -H "Authorization: Basic dGVzdDpwYXNz" http://127.0.0.1:5985
BASE64_USERNAME_COLON_PASSWORD
is a base64 encoded version of username:password
. The example
above encodes test:pass
into dGVzdDpwYXNz
.
How to base64 that value?
- in the browser use the function
btoa:
btoa('test:pass')
, - in node you could leverage the bota package. It works like
the browser version:
require('btoa')('test:pass')
, and - in command line:
echo -n 'test:pass' | openssl base64
.
JWT auth
Authenticating with a token isn't that different at all. You need to send a POST
with an
Authorization
header that looks like Bearer JWT_TOKEN
.
curl -vX POST -H "Authorization: Bearer
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoiU29tZSBVc2VyIiwicm9sZXMiOltdfQ.v4QRSYnAOen_NMBzlMER_Jrkep0xEz2kL09KscALC_c" http://127.0.0.1:5985
Real life examples in JS land :)
Now, those examples were done with curl
and despite us loving curl
we don't use it (at least
directly) from the web or node.
Using fetch
fetch('http://127.0.0.1:5985', {
method: 'POST',
headers: {
Authorization: `Basic ${btoa('user:password')}`
}
}).then(res => res.json()).then(token => {
fetch('https://auth-only-stuff.com/resource', {
headers: {
Authorization: `Bearer ${token}`
}
});
var myToken = token;
sessionStorage.setItem('token', token);
localStorage.setItem('token', token);
});
Renewing your token
A token may expire. How do you know if it does? It will hold an exp
claim in its payload
.
If it does it's probably a good idea to have a mechanism to renew it before it expires so that our
users is always logged in throughout their use of our app.
That's when token renewing comes in handy. The good thing about this library is that creating a
token out of a user and password is almost the same as renewing that token you previously got.
The only difference from the example above is in the Authorization
header, instead of using
the keyword Basic
we use Bearer
which tells the server which auth mechanism we want to use.
E.g.:
the type of authentication
var token = `eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoiU29tZSBVc2VyIiwicm9sZXMiOltdfQ.v4QRSYnAOen_NMBzlMER_Jrkep0xEz2kL09KscALC_c`;
// renew token
fetch('http://127.0.0.1:5985', {
method: 'POST',
headers: {
Authorization: `Basic ${token}`
}
}).then(res => res.json()).then(newToken => {
// do something with the new token :)
});
Refreshing the roles
Sometimes (particularly when renewing a token) it might come in handy to get the list of roles from
the _users
database in CouchDB again. To do so, query the service with refresh-roles
at the end
like: http://127.0.0.1:5985/refresh-roles
.
How do I blacklist a token?
See this as that's couch_jwt_auth's realm.
SSL all the things
Sending authentication credentials over an unencrypted connection is a very bad practice.
Plus, now that Let's Encrypt has been giving away free SSL certs for
a while, securing your endpoints isn't an excuse anymore.
If you plan on exposing this endpoint directly to the internet, you may want to grab the handler
and put together your own server that deals with HTTPS.
Because our setups most times have multiple services running on SSL, we're generally putting this
behind nginx to proxy requests to the service. Here's an nginx config file that may come in handy.
server {
listen 80;
server_name my-login.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name my-login.com;
root /usr/share/nginx/html;
ssl on;
ssl_certificate /etc/letsencrypt/live/my-login.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/my-login.com/privkey.pem;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH';
ssl_session_cache shared:SSL:1m;
location / {
proxy_pass http://localhost:5985;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Ssl on;
}
}
License MIT.
with <3 by UXtemple.