Bootstrapping a Graphql + Postgresql Backend

Posted: 31 January, 2020 Category: rough notes Tagged: postgresqlgraphql

This is the first of a bunch of posts that are going to be copy paste dumps of notes I jot down to myself as I bootstrap portions of my app, learning as I go.

As rough notes to myself, they may not make much sense to another reader. BUT, they may contain some useful wayfinders if you're a first-timer who learns the way a generalist does.

Summary of Learnings during backend bootstrapping

the graphiql app that comes with express-graphql doesn't have a panel for setting headers / bearer tokens. Had to download a standalone client (graphiql-app). Got the windows binary here. You have to explicitly populate the header boxes with "authorization", "Bearer xxx" in the UI.

JWT overview crypto lib is now absorbed into node, do not install sep pkg. Most modern examples seem to use AES eg this snippet

Ok so it turns out that crypto (node built-in is for allpurpose crypto and bcrypt is for crypto that is compuationally expensive, for sensitive stuff like passwords etc). see SO.

The iv in cipheriv actually means "initialization vector", and it's basically a salt. It's not as big a secret as the secret that it's salting, so no need to freak out about where to store it.

Algorithms: candidates I've come across are the 2 discussed here. Per discussion, went with the -ctr variant.

base64 vs hex encoding: the former is just more compact is all (base 64, so 3 source bytes get encoded into 4 bytes, unlike base 16 hex where 1 source byte gets encoded to 2 bytes). Btw the only encodings nodejs supports are:

- ascii.
- base64.
- hex.
- ucs2/ucs-2/utf16le/utf-16le.
- utf8/utf-8.
- binary/latin1 (ISO8859-1, latin1 only in node 6.4. 0+)

rem this for generating aes encryption key which HAS to be 256 bits long:

// 256 bits = 32 bytes so crypto.randomBytes(32)
import crypto from 'crypto'
console.log(crypto.randomBytes(32).toString('base64'))

Diff between encryption and hashing: Encryption is a two-way function; what is encrypted can be decrypted with the proper key. Hashing, however, is a one-way function that scrambles plain text to produce a unique message digest. With a properly designed algorithm, there is no way to reverse the hashing process to reveal the original password bcrypt is an adaptive hasher. Hashes are rather lossy even if len(m) < len(hash(m). Great stackexchange Q&A here

Great discussion of salts and their function here

Phase I: Backend

What's done so far: This could be a useful template if you're building out a backend of your own. As this was done mainly as a learnin project, I left out several key aspects that I will have to come back and wire in later. Notably typescript, unit tests, and continuous integration. Watch for future posts.

migrations

general gql routing (un-authed)

  • signup api (stub) => signup mutation
  • login api (stub) => login mutation
  • tasks api - all tasks

auth

  • add email, passsword fields as new db migration (#3)
  • SIGNUP endpoint

    • encrypt provided password using bcrypt
    • need to yarn add bcrypt. But I'd uninstalled python and a bunch of other build tools during some cleanup, and node-gyp would not run. So on windows, workaround was to:

  • LOGIN (authenticate) endpoint using JWTs

    • generate a signed token for future requests
    • encrypt the token b4 sending
  • update hub server to accept bearer token

    • add middleware to detect the bearer token in express server
    • update express-graphql server to set the context option based on the token that the middleware has verified, decrypted and stuffed into the request object. See api docs here
  • decrypt the token on receipt
  • reimpl tasks query as authed query (currently authed user's tasks only)

    • protect the tasks mutation by updating the query to only use the user id from a valid token