Home Reference Source

src/usuarios/tokens.js

const jwt = require('jsonwebtoken')
const crypto = require('crypto')
const moment = require('moment')

const { InvalidArgumentError } = require('../erros')

const allowlistRefreshToken = require('../../redis/allowlist-refresh-token')
const blocklistAccessToken = require('../../redis/blocklist-access-token')

function criaTokenJWT (id, [tempoQuantidade, tempoUnidade]) {
  const payload = { id }
  const token = jwt.sign(payload, process.env.CHAVE_JWT, {
    expiresIn: tempoQuantidade + tempoUnidade
  })
  return token
}

async function verificaTokenJWT (token, nome, blocklist) {
  await verificaTokenNaBlocklist(token, nome, blocklist)
  const { id } = jwt.verify(token, process.env.CHAVE_JWT)
  return id
}

async function verificaTokenNaBlocklist (token, nome, blocklist) {
  if (!blocklist) {
    return
  }

  const tokenNaBlocklist = await blocklist.contemToken(token)
  if (tokenNaBlocklist) {
    throw new jwt.JsonWebTokenError(`${nome} inválido por logout!`)
  }
}

function invalidaTokenJWT (token, blocklist) {
  return blocklist.adiciona(token)
}

async function criaTokenOpaco (id, [tempoQuantidade, tempoUnidade], allowlist) {
  const tokenOpaco = crypto.randomBytes(24).toString('hex')
  const dataExpiracao = moment().add(tempoQuantidade, tempoUnidade).unix()
  await allowlist.adiciona(tokenOpaco, id, dataExpiracao)
  return tokenOpaco
}

async function verificaTokenOpaco (token, nome, allowlist) {
  verificaTokenEnviado(token, nome)
  const id = await allowlist.buscaValor(token)
  verificaTokenValido(id, nome)
  return id
}

async function invalidaTokenOpaco (token, allowlist) {
  await allowlist.deleta(token)
}

function verificaTokenValido (id, nome) {
  if (!id) {
    throw new InvalidArgumentError(`${nome} inválido!`)
  }
}

function verificaTokenEnviado (token, nome) {
  if (!token) {
    throw new InvalidArgumentError(`${nome} não enviado!`)
  }
}

module.exports = {
  access: {
    nome: 'access token',
    lista: blocklistAccessToken,
    expiracao: [15, 'm'],
    cria (id) {
      return criaTokenJWT(id, this.expiracao)
    },
    verifica (token) {
      return verificaTokenJWT(token, this.nome, this.lista)
    },
    invalida (token) {
      return invalidaTokenJWT(token, this.lista)
    }
  },
  refresh: {
    nome: 'refresh token',
    lista: allowlistRefreshToken,
    expiracao: [5, 'd'],
    cria (id) {
      return criaTokenOpaco(id, this.expiracao, this.lista)
    },
    verifica (token) {
      return verificaTokenOpaco(token, this.nome, this.lista)
    },
    invalida (token) {
      return invalidaTokenOpaco(token, this.lista)
    }
  },
  verificacaoEmail: {
    nome: 'token de verificação de e-mail',
    expiracao: [1, 'h'],
    cria (id) {
      return criaTokenJWT(id, this.expiracao)
    },
    verifica (token) {
      return verificaTokenJWT(token, this.nome)
    }
  }
}