wip(backend): Disabling of MFA
This commit is contained in:
parent
a5ee0a0854
commit
6c2cb71f51
4 changed files with 124 additions and 26 deletions
|
@ -245,7 +245,7 @@ UserModel.prototype.passwordLogin = async function (req) {
|
|||
|
||||
// Check for MFA
|
||||
if (this.record.mfaEnabled) {
|
||||
if (!req.body.token) return this.setResponse(200, false, { note: 'mfaTokenRequired' })
|
||||
if (!req.body.token) return this.setResponse(403, 'mfaTokenRequired')
|
||||
else if (!this.mfa.verify(req.body.token, this.clear.mfaSecret)) {
|
||||
return this.setResponse(401, 'loginFailed')
|
||||
}
|
||||
|
@ -434,7 +434,7 @@ UserModel.prototype.guardedUpdate = async function ({ body, user }) {
|
|||
* Enables/Disables MFA on the account - Used when we pass through
|
||||
* user-provided data so we can't be certain it's safe
|
||||
*/
|
||||
UserModel.prototype.guardedMfaUpdate = async function ({ body, user }) {
|
||||
UserModel.prototype.guardedMfaUpdate = async function ({ body, user, ip }) {
|
||||
if (user.level < 4) return this.setResponse(403, 'insufficientAccessLevel')
|
||||
if (user.iss && user.status < 1) return this.setResponse(403, 'accountStatusLacking')
|
||||
if (body.mfa === true && this.record.mfaEnabled === true)
|
||||
|
@ -442,6 +442,29 @@ UserModel.prototype.guardedMfaUpdate = async function ({ body, user }) {
|
|||
|
||||
// Disable
|
||||
if (body.mfa === false) {
|
||||
if (!body.token) return this.setResponse(400, 'mfaTokenRequired')
|
||||
if (!body.password) return this.setResponse(400, 'passwordRequired')
|
||||
// Check password
|
||||
const [valid] = verifyPassword(body.password, this.record.password)
|
||||
if (!valid) {
|
||||
console.log('password check failed')
|
||||
log.warn(`Wrong password for existing user while disabling MFA: ${user.uid} from ${ip}`)
|
||||
return this.setResponse(401, 'authenticationFailed')
|
||||
}
|
||||
// Check MFA token
|
||||
if (this.mfa.verify(body.token, this.clear.mfaSecret)) {
|
||||
// Looks good. Disable MFA
|
||||
try {
|
||||
await this.unguardedUpdate({ mfaEnabled: false })
|
||||
} catch (err) {
|
||||
log.warn(err, 'Could not disable MFA after token check')
|
||||
return this.setResponse(500, 'mfaDeactivationFailed')
|
||||
}
|
||||
return this.setResponse(200, false, {})
|
||||
} else {
|
||||
console.log('token check failed')
|
||||
return this.setResponse(401, 'authenticationFailed')
|
||||
}
|
||||
}
|
||||
// Confirm
|
||||
else if (body.mfa === true && body.token && body.secret) {
|
||||
|
@ -493,7 +516,6 @@ UserModel.prototype.asAccount = function () {
|
|||
consent: this.record.consent,
|
||||
control: this.record.control,
|
||||
createdAt: this.record.createdAt,
|
||||
data: this.clear.data,
|
||||
email: this.clear.email,
|
||||
github: this.clear.github,
|
||||
img: this.record.img,
|
||||
|
@ -501,6 +523,7 @@ UserModel.prototype.asAccount = function () {
|
|||
initial: this.clear.initial,
|
||||
language: this.record.language,
|
||||
lastLogin: this.record.lastLogin,
|
||||
mfaEnabled: this.record.mfaEnabled,
|
||||
newsletter: this.record.newsletter,
|
||||
patron: this.record.patron,
|
||||
role: this.record.role,
|
||||
|
|
|
@ -9,10 +9,10 @@ import { setup } from './shared.mjs'
|
|||
const runTests = async (...params) => {
|
||||
await userTests(...params)
|
||||
await mfaTests(...params)
|
||||
await apikeyTests(...params)
|
||||
await accountTests(...params)
|
||||
await personTests(...params)
|
||||
await patternTests(...params)
|
||||
//await apikeyTests(...params)
|
||||
//await accountTests(...params)
|
||||
//await personTests(...params)
|
||||
//await patternTests(...params)
|
||||
}
|
||||
|
||||
// Load initial data required for tests
|
||||
|
|
|
@ -132,25 +132,85 @@ export const mfaTests = async (chai, config, expect, store) => {
|
|||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
describe(`${store.icon('mfa')} Multi-Factor Authentication (MFA) login flow`, () => {
|
||||
it(`${store.icon('mfa')} Should not login with username/password only`, (done) => {
|
||||
it(`${store.icon('mfa', auth)} Should not login with username/password only`, (done) => {
|
||||
chai
|
||||
.request(config.api)
|
||||
.post('/login')
|
||||
.send({
|
||||
username: store.account.username,
|
||||
password: store.account.password,
|
||||
username: secret[auth].username,
|
||||
password: secret[auth].password,
|
||||
})
|
||||
.end((err, res) => {
|
||||
expect(err === null).to.equal(true)
|
||||
expect(res.status).to.equal(403)
|
||||
expect(res.body.result).to.equal('error')
|
||||
expect(res.body.error).to.equal('mfaTokenRequired')
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it(`${store.icon('mfa')} Should login with username/password/token`, (done) => {
|
||||
chai
|
||||
.request(config.api)
|
||||
.post('/login')
|
||||
.send({
|
||||
username: secret[auth].username,
|
||||
password: secret[auth].password,
|
||||
token: authenticator.generate(secret[auth].mfaSecret),
|
||||
})
|
||||
.end((err, res) => {
|
||||
expect(err === null).to.equal(true)
|
||||
expect(res.status).to.equal(200)
|
||||
expect(res.body.result).to.equal('success')
|
||||
expect(res.body.note).to.equal('mfaTokenRequired')
|
||||
//expect(res.body.account.mfaEnabled).to.equal(true)
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it(`${store.icon('mfa')} Should not login with a wrong token`, (done) => {
|
||||
chai
|
||||
.request(config.api)
|
||||
.post('/login')
|
||||
.send({
|
||||
username: secret[auth].username,
|
||||
password: secret[auth].password,
|
||||
token: '1234',
|
||||
})
|
||||
.end((err, res) => {
|
||||
expect(err === null).to.equal(true)
|
||||
expect(res.status).to.equal(401)
|
||||
expect(res.body.result).to.equal('error')
|
||||
expect(res.body.error).to.equal('loginFailed')
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it(`${store.icon('mfa', auth)} Should disable MFA`, (done) => {
|
||||
chai
|
||||
.request(config.api)
|
||||
.post(`/account/mfa/${auth}`)
|
||||
.set(
|
||||
'Authorization',
|
||||
auth === 'jwt'
|
||||
? 'Bearer ' + secret[auth].token
|
||||
: 'Basic ' +
|
||||
new Buffer(`${secret[auth].apikey.key}:${secret[auth].apikey.secret}`).toString(
|
||||
'base64'
|
||||
)
|
||||
)
|
||||
.send({
|
||||
mfa: false,
|
||||
password: secret[auth].password,
|
||||
token: authenticator.generate(secret[auth].mfaSecret),
|
||||
})
|
||||
.end((err, res) => {
|
||||
expect(err === null).to.equal(true)
|
||||
expect(res.status).to.equal(200)
|
||||
expect(res.body.result).to.equal(`success`)
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -91,6 +91,21 @@ export const userTests = async (chai, config, expect, store) => {
|
|||
done()
|
||||
})
|
||||
})
|
||||
step(`${store.icon('user')} Should set the password (altaccount)`, (done) => {
|
||||
chai
|
||||
.request(config.api)
|
||||
.put('/account/jwt')
|
||||
.set('Authorization', 'Bearer ' + store.altaccount.token)
|
||||
.send({
|
||||
password: store.altaccount.password,
|
||||
})
|
||||
.end((err, res) => {
|
||||
expect(res.status).to.equal(200)
|
||||
expect(res.type).to.equal('application/json')
|
||||
expect(res.charset).to.equal('utf-8')
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
step(`${store.icon('user')} Should login with username and password`, (done) => {
|
||||
chai
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue