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

@@ -35,7 +35,7 @@ const template = `
</button>
</div>
<div class="modal-body">
{{ modal.message }}
<span v-html="modal.message"></span>
<div v-if="Array.isArray(modal.inputs)" class="mt-4 mb-3">
<span v-for="input of modal.inputs">
<input

View File

@@ -133,8 +133,14 @@ const template = `
<h6 class="pad-top">Recovery Codes</h6>
<p>Recovery codes can be used to regain access to your account in the event that you lose access to the device that generates your MFA codes.</p>
<p class="font-italic">No recovery codes have been generated for your account.</p>
<button class="btn btn-sm btn-success">Generate Recovery Codes</button>
<span v-if="!has_mfa_recovery">
<p class="font-italic">No recovery codes have been generated for your account.</p>
<button class="btn btn-sm btn-success" @click="on_mfa_recovery_generate">Generate Recovery Codes</button>
</span>
<span v-if="has_mfa_recovery">
<p class="font-italic">Recovery codes were generate for your account on {{ mfa_recovery_date }}. <span v-if="mfa_recovery_codes === 1">There is only 1 recovery code remaining.</span><span v-if="mfa_recovery_codes !== 1">There are {{ mfa_recovery_codes }} recovery codes remaining.</span></p>
<button class="btn btn-sm btn-success" @click="on_mfa_recovery_generate">Re-generate Recovery Codes</button>
</span>
</li>
</ul>
</div>
@@ -163,6 +169,10 @@ export default class EditProfileComponent extends Component {
last_reset = ''
mfa_enable_date = ''
has_mfa_recovery = false
mfa_recovery_date = ''
mfa_recovery_codes = 0
form_message = 'No changes.'
has_mfa = false
@@ -248,7 +258,16 @@ export default class EditProfileComponent extends Component {
const mfa = await auth_api.has_mfa()
this.has_mfa = mfa && mfa.mfa_enabled
if (this.has_mfa) this.mfa_enable_date = (new Date(mfa.mfa_enable_date)).toLocaleDateString()
if (this.has_mfa) {
this.mfa_enable_date = (new Date(mfa.mfa_enable_date)).toLocaleDateString()
const result = await auth_api.has_mfa_recovery()
if ( result && result.has_recovery ) {
this.has_mfa_recovery = true
this.mfa_recovery_date = (new Date(result.generated)).toLocaleDateString()
this.mfa_recovery_codes = result.remaining_codes
}
}
await this.load_app_passwords()
}
@@ -301,5 +320,65 @@ export default class EditProfileComponent extends Component {
],
})
}
async on_mfa_recovery_generate($event) {
if ( !this.has_mfa ) return
if ( !this.has_mfa_recovery ) {
await this.generate_mfa_recovery()
} else {
message_service.modal({
title: 'Are you sure?',
message: 'There are already MFA recovery codes associated with your account. If you re-generate them, you will be unable to use the old ones. Continue?',
buttons: [
{
text: 'Cancel',
type: 'close',
},
{
text: 'Re-generate',
type: 'close',
class: ['btn', 'btn-warning'],
on_click: async () => {
await this.generate_mfa_recovery()
},
},
],
})
}
}
async generate_mfa_recovery() {
const codes = await auth_api.generate_mfa_recovery()
if ( codes ) {
this.display_mfa_recovery_modal(codes)
} else {
message_service.alert({
type: 'error',
message: 'An unknown error occurred while attempting to generate MFA recovery codes.'
})
}
await this.load()
}
display_mfa_recovery_modal(codes) {
const code_display = codes.map(x => `<li><code>${x}</code></li>`).join('\n')
message_service.modal({
title: 'MFA Recovery Codes',
message: `We've generated recovery codes for your account. You can use these to recover access to your account in the event that you lose your MFA device.
<br><br>
Be sure to put these somewhere safe. After you close this modal, they will disappear:
<br><br>
<ul>
${code_display}
</ul>`,
buttons: [
{
text: 'Close',
type: 'close',
},
],
})
}
}