| 2. Signup users with only CouchDB?

I want to expose CouchDB to public access to test out collaborative commenting/editing which, of course, requires grappling with--at minimum--some basic security.

First up is enabling SSL. The official documentation here says to modify the local.ini file; my local.ini file is empty and the default.ini file is overwritten with every upgrade to CouchDB so I instead using the in-browser Fauxton interface to add the circled fields to the SSL section:



After using "sudo service couchdb stop" followed by "sudo service couchdb start", I confirmed SSL was up and running. However, I wanted to only allow connections over HTTPS and unfortunately this is a feature that has gone missing with recent upgrades. The issue can be seen here:

https://github.com/apache/couchdb/issues/2106

Looks like I need to learn Erlang sooner rather than later. In any case, the goal here is to see how far one can get with the most minimalist of tools so onward.

There isn't any way to send a captcha or verification email from CouchDB upon a new user request in order to prevent a nefarious actor from creating a user database and using it for whatever purposes they want and/or spamming the database with millions of new user requests.

What to do? For the first issue, the idea I decided upon first is security through "specificity"; I will use a validation doc to ensure that only certain amounts and types of data are written to the database at minimum time intervals. This should prevent someone from using a newly created database for free general-purpose JSON storage for whatever they have in mind.

So first I enable the 'couch_peruser' setting which allows a matching database to be created for every user added to the '_users' authentication database.

Then I use the pouchdb-authentication library from here to...problem! It doesn't look like there is a way for anonymous users to request an account from CouchDB which, on the one hand, is good for preventing spam-database-creation from bad actors but, on the other hand, that means I'll have to create a separate authentication server for creating users. The go-to solution is obviously going to be NodeJS in order to keep the required technology to a minimum (e.g. we're already using JavaScript). There is also a Node module called Nodemailer that lets you send emails; using that I can create a super basic sign-up process:

  • 1. send users a main page served from CouchDB with a signup link
  • 2. the signup button redirects to a login form page from my NodeJS server (remember to ensure SSL is enabled) since sign-up credentials are being sent and is just good practice anyway
  • 2.a. form page must validate form entries
  • 3. my NodeJS server generates a random number and emails that number to supplied email, sends a "enter sent code" page back to user, waits a set amount of time for a reply
  • 3.a. need to scrub/validate email address
  • 4. If reply with matching number is received, send "create new user" command to CouchDB, wait for success, then send "account created" message back to user along with redirect link to CouchDB logIn page
  • 5. I can then use PouchDB's authentication module to logIn

I added a basic 'Sign Up' link on my CouchDB served 'index.html' page to localhost:3000 where my HTTPS Node server is running.
Creating an HTTPS Node server with a self-signed certificate is extremely easy. I used these two lines in the terminal to create the necessary files in my root server directory:


openssl genrsa > privkey.pem
openssl req -new -x509 -key privkey.pem -out couchdb.pem -days 1095

Then I used this code to create a barebones HTTPS server that listens at https://localhost:3000:


const https = require('https');

const options = {
  key: fs.readFileSync('cert/privkey.pem'),
  cert: fs.readFileSync('cert/signupServer.pem')
};

const hostname = 'localhost';
const port = 3000;

const server = https.createServer(options, (req, res) => {
  
  //handle routing
  if(req.url === '/'){
    ...
  }
  else{
    res.statusCode = 404;
    res.setHeader('Content-Type', 'text/html');
    res.end("<p>Sorry! The page you are looking for does not exist</p>");
  }
  
});

server.listen(port, hostname, () => {
  console.log(`Server running at https://${hostname}:${port}/`);
});

I can actually just skip the email verification for now (and the need to persist state for a particular user--along with all of the security concerns--in order to identify them when they respond with the emailed unique code) and just create a user in CouchDB from my NodeJS server. This allows me to mock up the entire flow of the website experience as quickly as possible.

Next was to create a basic HTML sign up form like so:

<head>
</head>

<body>
  <a href="/logIn">Log In</a>
  <h1>Chalk Sign Up</h1>
    <form action="/signUp" method="post">
      <label for="fname">Username:</label><br>
      <input type="text" id="fname" name="fname"><br>
      <label for="pass">Password:</label><br>
      <input type="text" id="pass" name="pass"><br><br>
      <input type="submit" value="Submit">
    </form>
</body>

Then create the server-side user creation process. This involves getting the form data, parsing it and then using a PUT request according to the CouchDB docs here to create a new user in the CouchDB database.

Upon successful creation, I send a link to the login.html page and allow users to log in. On the login page, users type their username and password which is pulled from the HTML form and used to create a POST request + a query string using the 'next' parameter to the '_sessions' endpoint to get a cookie. The 'next' parameter according to the docs allow you to have an automatic redirect after logging in.

After getting all of this working the main problem is that there was a shift in how the browser was letting me access pages on my CouchDB instance, now every time I went to access a page it required credentials even though I am already logged in to Fauxton. Even more, you can't serve a web page from a "main website database" without supplying credentials. So there are two possible solutions:

  1. is to move the login page to my NodeJS Authentication server, after the users sign in you redirect them to their own private database which will contain all necessary website files for them to interact with their data.
  2. is to put all of the "main website files" in a main database where all users are added in a "viewing" role to allow access but are prevented from modifying any files in that database. The advantage of this approach is that you don't duplicate the website files for hundreds of thousands of users.

I decided to go with a mixture of solution #1 and #2 where the HTML in the user's directory fetch resources from the "main website database" (where they are added with specific viewing role upon user creation) and dynamically build out their html page.

Also for obvious security reason I'm moving hard-coded admin credentials in my authorization server code into a text file which I read and load into variables. This allows me to upload the code for the server for others to view and use and leave the credentials file on my computer.

With that decided I moved the 'login.html' page to be served from the NodeJS server root directory and now have basic navigation between the signup and login screens, the working retrieval of a cookie and the ability to access the user database with that cookie authentication.

Then I created an html file that loads resources from the main database and serve that file from the 'home' document of the new user database. Additionally, I create a single line 'init.js' file that creates a PouchDB database instance connected to the user's CouchDB database; this is also served from the 'home' document as an attachment. With this done, I now have the entire structure of how my website can operate:

  • 1. Users sign up (and log in) through an authorization server and are assigned their own databases with some starter website files
  • 2. Every user has viewing rights of the main website database in order to allow their web app to pull information from there (searching, website resources, etc.)
  • 3. Users can friend other users to allow other people to view information they choose to expose
  • 4. Users can manage all of these details from their account page
  • With this infrastructure you can now create any "social media" web applications you want on top of it.
The full code for this authorization process can be seen on my github page in the files 'httpsSignupServer.js', 'login.html' and 'updateScriptNodeJS.js'.

As for an answer to the post's question: can you sign up users using only CouchDB? All these years later after I first asked the question, the answer is an unfortunate no. The reality of a two-tier web stack remains to be fulfilled.

Now to explore the data schema for a commenting system.

Next.