Skip to content

Commit

Permalink
first pass at adding basic csrf tokens
Browse files Browse the repository at this point in the history
  • Loading branch information
brianleroux committed Mar 13, 2024
1 parent a025100 commit c7c4712
Show file tree
Hide file tree
Showing 6 changed files with 62 additions and 0 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"main": "src/index",
"types": "types/index.d.ts",
"scripts": {
"test:one": "cross-env tape 'test/unit/src/http/csrf/*-test.js' | tap-arc",
"lint": "eslint --fix .",
"test:unit": "cross-env tape 'test/unit/**/*-test.js' | tap-arc",
"test:integration": "cross-env tape 'test/integration/**/*-test.js' | tap-arc",
Expand Down
1 change: 1 addition & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ let {
- [`http()`](https://arc.codes/docs/en/reference/runtime-helpers/node.js#arc.http)
- [`http` middleware](https://arc.codes/docs/en/reference/runtime-helpers/node.js#middleware)
- [`http.session`](https://arc.codes/docs/en/reference/runtime-helpers/node.js#arc.http.session)
- [`http.csrf`](https://arc.codes/docs/en/reference/runtime-helpers/node.js#arc.http.csrf)

**[`@queues` methods](https://arc.codes/docs/en/reference/runtime-helpers/node.js#arc.queues)**
- [`queues.subscribe()`](https://arc.codes/docs/en/reference/runtime-helpers/node.js#arc.queues.subscribe())
Expand Down
9 changes: 9 additions & 0 deletions src/http/csrf/create.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
let crypto = require('node:crypto')

/** creates a signed token [rando].[timestamp].[sig] */
module.exports = function create (data) {
data = data || Buffer.from(crypto.randomUUID().replace(/-/g, ''))
const secret = 'changeme' || process.env.ARC_APP_SECRET
const ts = Date.now()
return `${data}.${ts}.${crypto.createHmac('sha256', secret).update(data).digest('hex').toString()}`
}
12 changes: 12 additions & 0 deletions src/http/csrf/verify.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
let create = require('./create')

/** ensures payload is valid token that hasn't expired */
module.exports = function verify (payload) {
const [ data, ts, sig ] = payload.split('.')
const elapsed = Date.now() - ts
const fiveMinutes = 300000
if (elapsed > fiveMinutes) return false
const gen = create(data)
const sig2 = gen.split('.').pop()
return sig2 === sig
}
5 changes: 5 additions & 0 deletions src/http/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ let bodyParser = require('./helpers/body-parser')
let interpolate = require('./helpers/params')
let url = require('./helpers/url')
let responseFormatter = require('./_res-fmt')
let create = require('./csrf/create')
let verify = require('./csrf/verify')

// Unified async / callback HTTP handler
function httpHandler (isAsync, ...fns) {
Expand Down Expand Up @@ -95,6 +97,9 @@ http.helpers = { bodyParser, interpolate, url }
// Session
http.session = { read, write }

// CSRF
http.csrf = { create, verify }

module.exports = http

/**
Expand Down
34 changes: 34 additions & 0 deletions test/unit/src/http/csrf/create-and-verify-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
let http = require('../../../../../src/http')
let test = require('tape')

test('exists', t => {
t.plan(2)
t.ok(http.csrf.create, 'create')
t.ok(http.csrf.verify, 'verify')
})

test('create a value', t => {
t.plan(1)
let val = http.csrf.create()
t.ok(val, 'created value')
})

test('verify a value', t => {
t.plan(1)
let val = http.csrf.create()
t.ok(http.csrf.verify(val), 'value verified')
})

test('tampered token is falsy', t => {
t.plan(1)
let tamperedToken = "3d879d515ab241429c97dfea6d1e1927.1584118407000.b0b34563d569030cbe9a4ea63312f23729813b838478420e3811c0bfeaf3add1"
t.ok(http.csrf.verify(tamperedToken) === false, 'value falsy')
})

test('token expired is falsy', t => {
t.plan(1)
let expiredToken = "3d879d515ab241419c97dfea6d1e1927.1584118407000.b0b34563d569030cbe9a4ea63312f23729813b838478420e3811c0bfeaf3add1"
t.ok(http.csrf.verify(expiredToken) === false, 'value falsy')
})


0 comments on commit c7c4712

Please sign in to comment.