@xeredo/crypto-auth

0.1.0 • Public • Published

@xeredo/crypto-auth

Authenticate HTTP requests using cryptographic signatures

Supports hapi and fetch

NOTE: Keys are using @xeredo/easy-crypto

If you prefer using node's KeyObject, just use require('@xeredo/crypto').private/public.fromKeyObject(key) where necesarry

Usage

Client

'use strict'

async function main () {
  const ec = require('@xeredo/easy-crypto')
  // generate some keys
  const pair = ec.generateKeyPair('rsa', 4096)

  const Auth = require('@xeredo/crypto-auth')
  const client = Auth.client(pair)

  const fetch = require('node-fetch')
  console.log(await (await fetch('http://localhost:3333/registerClient', {
    method: 'POST',
    // export key as PEM
    body: new URLSearchParams({ key: pair.pub.export() })
  })).json())

  // send a signed request
  const req = await fetch(...client.signFetch('http://localhost:3333/hello', 'POST', { name: 'Johnson' }))
  // get result
  const res = await req.json()

  console.log(res)
}

main().then(() => {}, console.error)

Server

'use strict'

const Hapi = require('@hapi/hapi')
const Auth = require('@xeredo/crypto-auth')
const Joi = require('joi')
const ec = require('@xeredo/easy-crypto')

const init = async () => {
  const server = Hapi.server({
    port: 3333,
    host: 'localhost',
    debug: { request: ['error'] }
  })

  // you want to use a database instead
  const devices = {}

  await server.register({
    plugin: Auth.hapi
  })

  server.auth.strategy('session', 'crypto-auth', {
    getDevice (fingerprint) {
      console.log('authorizing', fingerprint)
      if (devices[fingerprint]) {
        return devices[fingerprint]
      }
    }
  })
  server.auth.default({
    strategy: 'session',
    payload: true
  })

  server.route({
    method: 'POST',
    path: '/registerClient',
    config: {
      auth: false,
      validate: {
        payload: Joi.object({
          key: Joi.string().required()
        })
      },
      handler: async (request, h) => {
        const key = ec.public.fromBuffer(request.payload.key)
        const fingerprint = key.spkiFingerprint()
        console.log('added device', fingerprint)

        devices[fingerprint] = {
          key,
          credentials: {
            fingerprint,
            // user-id
            id: String(Math.random())
          }
        }

        return { added: fingerprint }
      }
    }
  })

  server.route({
    method: 'POST',
    path: '/hello',
    config: {
      validate: {
        payload: Joi.object({
          name: Joi.string().required()
        })
      },
      handler: async (request, h) => {
        return { msg: `Hello ${request.payload.name}`, credentials: request.auth.credentials }
      }
    }
  })

  await server.start()
  console.log('Server running on %s', server.info.uri)
}

process.on('unhandledRejection', (err) => {
  console.log(err)
  process.exit(1)
})

init()

API

Client

client(key<EasyCrypto.PrivateKey>)

  • .signFetch(url<string>, method<string>, body<Object>)
    • Sign a request and return parameters for node-fetch or window.fetch
  • .sign(url<string>, method<string>, body<Object>)
    • Sign a request and return headers. You can use this if you want to use other libraries instead of fetch
  • .fingerprint
    • SPKI Fingerprint of the key in hex

Server

.hapi

  • tolerance<Integer>: Tolerate this much difference in timestamp. Default: 5 minutes.
  • getKey(fingerprint<string>): Function that will be used to retrive a key
    • Expected to return object with keys:
    • .key<EasyCrypto.PublicKey>
    • .credentials<object>: Optional, extra details for hapi credentials object
    • or undefined if key not found
    • Throw @hapi/boom errors only

Readme

Keywords

Package Sidebar

Install

npm i @xeredo/crypto-auth

Weekly Downloads

1

Version

0.1.0

License

MPL-2.0

Unpacked Size

10.9 kB

Total Files

8

Last publish

Collaborators

  • mkg20001