feat: login + TFA authentication
This commit is contained in:
@@ -1,20 +1,29 @@
|
||||
<template lang="pug">
|
||||
.login(:class='{ "is-error": error }')
|
||||
.login-container(:class='{ "is-expanded": strategies.length > 1 }')
|
||||
.login-container(:class='{ "is-expanded": strategies.length > 1, "is-loading": isLoading }')
|
||||
.login-providers(v-show='strategies.length > 1')
|
||||
button(v-for='strategy in strategies', :class='{ "is-active": strategy.key === selectedStrategy }', @click='selectStrategy(strategy.key, strategy.useForm)', :title='strategy.title')
|
||||
em(v-html='strategy.icon')
|
||||
span {{ strategy.title }}
|
||||
.login-providers-fill
|
||||
.login-frame
|
||||
.login-frame(v-show='screen === "login"')
|
||||
h1 {{ siteTitle }}
|
||||
h2 {{ $t('auth:loginrequired') }}
|
||||
input(type='text', ref='iptEmail', :placeholder='$t("auth:fields.emailuser")')
|
||||
input(type='password', ref='iptPassword', :placeholder='$t("auth:fields.password")')
|
||||
h2 {{ $t('auth:loginRequired') }}
|
||||
input(type='text', ref='iptEmail', v-model='username', :placeholder='$t("auth:fields.emailUser")')
|
||||
input(type='password', ref='iptPassword', v-model='password', :placeholder='$t("auth:fields.password")', @keyup.enter='login')
|
||||
button.button.is-blue.is-fullwidth(@click='login')
|
||||
span {{ $t('auth:actions.login') }}
|
||||
.login-frame(v-show='screen === "tfa"')
|
||||
.login-frame-icon
|
||||
svg.icons.is-48(role='img')
|
||||
title {{ $t('auth:tfa.title') }}
|
||||
use(xlink:href='#nc-key')
|
||||
h2 {{ $t('auth:tfa.subtitle') }}
|
||||
input(type='text', ref='iptTFA', v-model='securityCode', :placeholder='$t("auth:tfa.placeholder")', @keyup.enter='verifySecurityCode')
|
||||
button.button.is-blue.is-fullwidth(@click='verifySecurityCode')
|
||||
span {{ $t('auth:tfa.verifyToken') }}
|
||||
.login-copyright
|
||||
span {{ $t('footer.poweredby') }}
|
||||
span {{ $t('footer.poweredBy') }}
|
||||
a(href='https://wiki.js.org', rel='external', title='Wiki.js') Wiki.js
|
||||
</template>
|
||||
|
||||
@@ -23,26 +32,36 @@
|
||||
|
||||
export default {
|
||||
name: 'login',
|
||||
data() {
|
||||
data () {
|
||||
return {
|
||||
error: false,
|
||||
strategies: [],
|
||||
selectedStrategy: 'local'
|
||||
selectedStrategy: 'local',
|
||||
screen: 'login',
|
||||
username: '',
|
||||
password: '',
|
||||
securityCode: '',
|
||||
loginToken: '',
|
||||
isLoading: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
siteTitle() {
|
||||
siteTitle () {
|
||||
return siteConfig.title
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
selectStrategy(key, useForm) {
|
||||
selectStrategy (key, useForm) {
|
||||
this.selectedStrategy = key
|
||||
this.screen = 'login'
|
||||
if (!useForm) {
|
||||
window.location.assign(siteConfig.path + '/login/' + key)
|
||||
window.location.assign(siteConfig.path + 'login/' + key)
|
||||
} else {
|
||||
this.$refs.iptEmail.focus()
|
||||
}
|
||||
},
|
||||
refreshStrategies() {
|
||||
refreshStrategies () {
|
||||
this.isLoading = true
|
||||
graphQL.query({
|
||||
query: CONSTANTS.GRAPHQL.GQL_QUERY_AUTHENTICATION,
|
||||
variables: {
|
||||
@@ -54,19 +73,122 @@ export default {
|
||||
} else {
|
||||
throw new Error('No authentication providers available!')
|
||||
}
|
||||
this.isLoading = false
|
||||
}).catch(err => {
|
||||
console.error(err)
|
||||
this.$store.dispatch('alert', {
|
||||
style: 'error',
|
||||
icon: 'gg-warning',
|
||||
msg: err.message
|
||||
})
|
||||
this.isLoading = false
|
||||
})
|
||||
},
|
||||
login() {
|
||||
this.$store.dispatch('alert', {
|
||||
style: 'error',
|
||||
icon: 'gg-warning',
|
||||
msg: 'Email or password is invalid'
|
||||
})
|
||||
login () {
|
||||
if (this.username.length < 2) {
|
||||
this.$store.dispatch('alert', {
|
||||
style: 'error',
|
||||
icon: 'gg-warning',
|
||||
msg: 'Enter a valid email / username.'
|
||||
})
|
||||
this.$refs.iptEmail.focus()
|
||||
} else if (this.password.length < 2) {
|
||||
this.$store.dispatch('alert', {
|
||||
style: 'error',
|
||||
icon: 'gg-warning',
|
||||
msg: 'Enter a valid password.'
|
||||
})
|
||||
this.$refs.iptPassword.focus()
|
||||
} else {
|
||||
this.isLoading = true
|
||||
graphQL.mutate({
|
||||
mutation: CONSTANTS.GRAPHQL.GQL_MUTATION_LOGIN,
|
||||
variables: {
|
||||
username: this.username,
|
||||
password: this.password,
|
||||
provider: this.selectedStrategy
|
||||
}
|
||||
}).then(resp => {
|
||||
if (resp.data.login) {
|
||||
let respObj = resp.data.login
|
||||
if (respObj.succeeded === true) {
|
||||
if (respObj.tfaRequired === true) {
|
||||
this.screen = 'tfa'
|
||||
this.securityCode = ''
|
||||
this.loginToken = respObj.tfaLoginToken
|
||||
this.$nextTick(() => {
|
||||
this.$refs.iptTFA.focus()
|
||||
})
|
||||
} else {
|
||||
this.$store.dispatch('alert', {
|
||||
style: 'success',
|
||||
icon: 'gg-check',
|
||||
msg: 'Login successful!'
|
||||
})
|
||||
}
|
||||
this.isLoading = false
|
||||
} else {
|
||||
throw new Error(respObj.message)
|
||||
}
|
||||
} else {
|
||||
throw new Error('Authentication is unavailable.')
|
||||
}
|
||||
}).catch(err => {
|
||||
console.error(err)
|
||||
this.$store.dispatch('alert', {
|
||||
style: 'error',
|
||||
icon: 'gg-warning',
|
||||
msg: err.message
|
||||
})
|
||||
this.isLoading = false
|
||||
})
|
||||
}
|
||||
},
|
||||
verifySecurityCode () {
|
||||
if (this.securityCode.length !== 6) {
|
||||
this.$store.dispatch('alert', {
|
||||
style: 'error',
|
||||
icon: 'gg-warning',
|
||||
msg: 'Enter a valid security code.'
|
||||
})
|
||||
this.$refs.iptTFA.focus()
|
||||
} else {
|
||||
this.isLoading = true
|
||||
graphQL.mutate({
|
||||
mutation: CONSTANTS.GRAPHQL.GQL_MUTATION_LOGINTFA,
|
||||
variables: {
|
||||
loginToken: this.loginToken,
|
||||
securityCode: this.securityCode
|
||||
}
|
||||
}).then(resp => {
|
||||
if (resp.data.loginTFA) {
|
||||
let respObj = resp.data.loginTFA
|
||||
if (respObj.succeeded === true) {
|
||||
this.$store.dispatch('alert', {
|
||||
style: 'success',
|
||||
icon: 'gg-check',
|
||||
msg: 'Login successful!'
|
||||
})
|
||||
this.isLoading = false
|
||||
} else {
|
||||
throw new Error(respObj.message)
|
||||
}
|
||||
} else {
|
||||
throw new Error('Authentication is unavailable.')
|
||||
}
|
||||
}).catch(err => {
|
||||
console.error(err)
|
||||
this.$store.dispatch('alert', {
|
||||
style: 'error',
|
||||
icon: 'gg-warning',
|
||||
msg: err.message
|
||||
})
|
||||
this.isLoading = false
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
mounted () {
|
||||
this.$store.commit('navigator/subtitleStatic', 'Login')
|
||||
this.refreshStrategies()
|
||||
this.$refs.iptEmail.focus()
|
||||
|
Reference in New Issue
Block a user