mirror of
https://github.com/Bubka/2FAuth.git
synced 2024-12-04 22:31:58 +01:00
134 lines
5.0 KiB
Vue
134 lines
5.0 KiB
Vue
<template>
|
|
<form-wrapper :title="$t('auth.webauthn.register_a_new_device')" :punchline="$t('auth.webauthn.recover_account_instructions')" >
|
|
<div v-if="deviceRegistered" class="field">
|
|
<label class="label mb-5">{{ $t('auth.webauthn.device_successfully_registered') }} <font-awesome-icon :icon="['fas', 'check']" /></label>
|
|
<form @submit.prevent="handleSubmit" @keydown="form.onKeydown($event)">
|
|
<form-field :form="form" fieldName="name" inputType="text" placeholder="iPhone 12, TouchID, Yubikey 5C" :label="$t('auth.forms.name_this_device')" />
|
|
<form-buttons :isBusy="form.isBusy" :isDisabled="form.isDisabled" :caption="$t('commons.continue')" />
|
|
</form>
|
|
</div>
|
|
<div v-else>
|
|
<div class="field">
|
|
<input id="unique" name="unique" type="checkbox" class="is-checkradio is-info" v-model="unique" >
|
|
<label for="unique" class="label">{{ $t('auth.webauthn.disable_all_other_devices') }}</label>
|
|
</div>
|
|
<div class="field is-grouped">
|
|
<div class="control">
|
|
<button class="button is-link" @click="register()">{{ $t('auth.webauthn.register_a_new_device')}}</button>
|
|
</div>
|
|
<div class="control">
|
|
<router-link :to="{ name: 'login' }" class="button is-text">{{ $t('commons.cancel') }}</router-link>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- footer -->
|
|
<vue-footer></vue-footer>
|
|
</form-wrapper>
|
|
</template>
|
|
|
|
<script>
|
|
|
|
import Form from './../../../components/Form'
|
|
|
|
export default {
|
|
data(){
|
|
return {
|
|
email : '',
|
|
token: '',
|
|
unique: false,
|
|
deviceRegistered: false,
|
|
deviceId : null,
|
|
form: new Form({
|
|
name : '',
|
|
}),
|
|
}
|
|
},
|
|
|
|
created () {
|
|
this.email = this.$route.query.email
|
|
this.token = this.$route.query.token
|
|
},
|
|
|
|
methods : {
|
|
|
|
/**
|
|
* Register a new security device
|
|
*/
|
|
async register() {
|
|
// Check https context
|
|
if (!window.isSecureContext) {
|
|
this.$notify({ type: 'is-danger', text: this.$t('errors.https_required') })
|
|
return false
|
|
}
|
|
|
|
// Check browser support
|
|
if (!window.PublicKeyCredential) {
|
|
this.$notify({ type: 'is-danger', text: this.$t('errors.browser_does_not_support_webauthn') })
|
|
return false
|
|
}
|
|
|
|
const registerOptions = await this.axios.post('/webauthn/recover/options',
|
|
{
|
|
email : this.email,
|
|
token: this.token
|
|
},
|
|
{ returnError: true })
|
|
.then(res => res.data)
|
|
.catch(error => {
|
|
this.$notify({ type: 'is-danger', text: error.response.data.message })
|
|
});
|
|
|
|
const publicKey = this.parseIncomingServerOptions(registerOptions)
|
|
let bufferedCredentials
|
|
|
|
try {
|
|
bufferedCredentials = await navigator.credentials.create({ publicKey })
|
|
}
|
|
catch (error) {
|
|
if (error.name == 'AbortError') {
|
|
this.$notify({ type: 'is-warning', text: this.$t('errors.aborted_by_user') })
|
|
}
|
|
else if (error.name == 'NotAllowedError' || 'InvalidStateError') {
|
|
this.$notify({ type: 'is-danger', text: this.$t('errors.security_device_unsupported') })
|
|
}
|
|
return false
|
|
}
|
|
|
|
const publicKeyCredential = this.parseOutgoingCredentials(bufferedCredentials);
|
|
|
|
this.axios.post('/webauthn/recover', publicKeyCredential, {
|
|
headers: {
|
|
email : this.email,
|
|
token: this.token,
|
|
unique: this.unique,
|
|
}
|
|
}).then(response => {
|
|
this.$notify({ type: 'is-success', text: this.$t('auth.webauthn.device_successfully_registered') })
|
|
this.deviceId = publicKeyCredential.id
|
|
this.deviceRegistered = true
|
|
})
|
|
},
|
|
|
|
|
|
/**
|
|
* Rename the registered device
|
|
*/
|
|
async handleSubmit(e) {
|
|
|
|
await this.form.patch('/webauthn/credentials/' + this.deviceId + '/name')
|
|
|
|
if( this.form.errors.any() === false ) {
|
|
this.$router.push({name: 'accounts', params: { toRefresh: true }})
|
|
}
|
|
},
|
|
},
|
|
|
|
beforeRouteLeave (to, from, next) {
|
|
this.$notify({
|
|
clean: true
|
|
})
|
|
|
|
next()
|
|
}
|
|
}
|
|
</script> |