OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-note / test / unit / challenge-manager.test.js

105 lines · javascript ref:
import test from 'brittle'
import { ChallengeManager } from '../../lib/auth/challenge-manager.js'
import { MockRegistry } from '../helpers/fixtures.js'

test('ChallengeManager - generate challenge', async (t) => {
  const mgr = new ChallengeManager({ ttl: 60000 })

  const result = await mgr.generate({ context: 'test' })
  t.ok(result.challengeId, 'has challengeId')
  t.ok(result.challenge, 'has challenge string')
  t.ok(result.expiresAt > Date.now(), 'expires in the future')

  mgr.destroy()
})

test('ChallengeManager - verify valid challenge', async (t) => {
  const mgr = new ChallengeManager({ ttl: 60000 })

  const result = await mgr.generate()

  const valid = await mgr.verify(result.challengeId, result.challenge)
  t.ok(valid, 'challenge verifies')

  mgr.destroy()
})

test('ChallengeManager - consume prevents replay', async (t) => {
  const mgr = new ChallengeManager({ ttl: 60000 })

  const result = await mgr.generate()

  const consumed = await mgr.consume(result.challengeId)
  t.ok(consumed, 'consumed successfully')

  // Try to consume again
  const replayGet = await mgr.get(result.challengeId)
  t.absent(replayGet, 'consumed challenge returns null on get')

  mgr.destroy()
})

test('ChallengeManager - expired challenge rejected', async (t) => {
  const mgr = new ChallengeManager({ ttl: 1 }) // 1ms TTL

  const result = await mgr.generate()

  // Wait for expiry
  await new Promise(resolve => setTimeout(resolve, 10))

  const record = await mgr.get(result.challengeId)
  t.absent(record, 'expired challenge returns null')

  mgr.destroy()
})

test('ChallengeManager - verify wrong challenge string fails', async (t) => {
  const mgr = new ChallengeManager({ ttl: 60000 })

  const result = await mgr.generate()

  const valid = await mgr.verify(result.challengeId, 'wrong-challenge-string')
  t.absent(valid, 'wrong challenge string fails')

  mgr.destroy()
})

test('ChallengeManager - verify unknown challengeId fails', async (t) => {
  const mgr = new ChallengeManager({ ttl: 60000 })

  const valid = await mgr.verify('nonexistent-id', 'whatever')
  t.absent(valid, 'unknown challengeId fails')

  mgr.destroy()
})

test('ChallengeManager - with persistent storage', async (t) => {
  const db = new MockRegistry()
  const mgr = new ChallengeManager({ ttl: 60000, db })

  const result = await mgr.generate()

  // Should be stored in db
  const entry = await db.get(`challenge:${result.challengeId}`)
  t.ok(entry, 'challenge stored in db')
  t.is(entry.value.challenge, result.challenge, 'stored challenge matches')

  mgr.destroy()
})

test('ChallengeManager - stats', async (t) => {
  const mgr = new ChallengeManager({ ttl: 60000 })

  await mgr.generate()
  await mgr.generate()
  const result3 = await mgr.generate()
  await mgr.consume(result3.challengeId)

  const stats = await mgr.stats()
  t.is(stats.total, 3, 'total challenges')
  t.is(stats.active, 2, 'active challenges')
  t.is(stats.used, 1, 'used challenges')

  mgr.destroy()
})