Support MFA recovery tokens

This commit is contained in:
garrettmills
2020-05-30 17:21:47 -05:00
parent a1a70e0548
commit 8680242349
25 changed files with 393 additions and 10 deletions

View File

@@ -0,0 +1,23 @@
const { Model } = require('flitter-orm')
const bcrypt = require('bcrypt')
class MFARecoveryCodeModel extends Model {
static get schema() {
return {
code: String,
used: { type: Boolean, default: false },
generated: { type: Date, default: () => new Date },
}
}
static async create(value) {
const code = await bcrypt.hash(value, 10)
return new this({ code })
}
async verify(code) {
return await bcrypt.compare(code, this.code)
}
}
module.exports = exports = MFARecoveryCodeModel

View File

@@ -1,5 +1,7 @@
const { Model } = require('flitter-orm')
const speakeasy = require('speakeasy')
const MFARecoveryCode = require('./MFARecoveryCode.model')
const uuid = require('uuid/v4')
class MFATokenModel extends Model {
static get services() {
@@ -10,9 +12,34 @@ class MFATokenModel extends Model {
return {
secret: String,
otpauth_url: String,
recovery_codes: [MFARecoveryCode],
}
}
async attempt_recovery(code) {
for ( const token of this.recovery_codes ) {
if ( await token.verify(code) && !token.used ) {
token.used = true
return true
}
}
return false
}
async generate_recovery() {
this.recovery_codes = []
const values = []
for ( let i = 0; i < 4; i++ ) {
const value = uuid()
values.push(value)
this.recovery_codes.push(await MFARecoveryCode.create(value))
}
return values
}
verify(value) {
return speakeasy.totp.verify({
secret: this.secret,