update authFlow

This commit is contained in:
Maksim Pechnikov 2019-06-06 12:58:08 +03:00
parent d0a23df5db
commit 4e1e6d9cc8
7 changed files with 74 additions and 52 deletions

View File

@ -7,21 +7,23 @@ const LoginForm = {
error: false
}),
computed: {
isPasswordAuth () { return this.authMethod === 'password' },
isTokenAuth () { return this.authMethod === 'token' },
isPasswordAuth () { return this.requiredPassword },
isTokenAuth () { return this.requiredToken },
...mapState({
registrationOpen: state => state.instance.registrationOpen,
instance: state => state.instance,
loggingIn: state => state.users.loggingIn,
oauth: state => state.oauth
}),
...mapGetters({ authMethod: 'authFlow/authMethod' })
...mapGetters(
'authFlow', ['requiredPassword', 'requiredToken', 'requiredMFA']
)
},
methods: {
...mapMutations({ setStage: 'authFlow/setStage' }),
...mapMutations('authFlow', ['requireMFA', 'setupMFA']),
...mapActions({ login: 'authFlow/login' }),
submit () {
this.tokenMethod ? this.submitToken() : this.submitPassword()
this.isTokenMethod ? this.submitToken() : this.submitPassword()
},
submitToken () {
oauthApi.login({
@ -48,18 +50,13 @@ const LoginForm = {
).then((result) => {
if (result.error) {
if (result.error === 'mfa_required') {
this.setStage({
app: app,
settings: result,
stage: 'second',
authMethod: 'totp'
})
return
this.setupMFA({app: app, settings: result})
this.requireMFA()
} else {
this.error = result.error
this.focusOnPasswordInput()
return
}
return
}
this.login(result).then(() => {
this.$router.push({name: 'friends'})

View File

@ -14,7 +14,7 @@ export default {
...mapState({ instance: 'instance' })
},
methods: {
...mapMutations({ setMethod: 'authFlow/setMethod' }),
...mapMutations({ requireTOTP: 'authFlow/requireTOTP' }),
...mapActions({ login: 'authFlow/login' }),
clearError () { this.error = false },
submit () {

View File

@ -14,12 +14,12 @@
<div class='form-group'>
<div class='login-bottom'>
<div>
<a href="#" @click.prevent="setMethod('totp')">
<a href="#" @click.prevent="requireTOTP">
{{$t('login.enter_two_factor_code')}}
</a>
</div>
<button type='submit' class='btn btn-default'>
{{$t('login.verify')}}
{{$t('general.verify')}}
</button>
</div>
</div>

View File

@ -13,7 +13,7 @@ export default {
...mapState({ instance: 'instance' })
},
methods: {
...mapMutations({ setMethod: 'authFlow/setMethod' }),
...mapMutations({ requireRecovery: 'authFlow/requireRecovery' }),
...mapActions({ login: 'authFlow/login' }),
clearError () { this.error = false },
submit () {

View File

@ -18,7 +18,7 @@
<div class='form-group'>
<div class='login-bottom'>
<div>
<a href="#" @click.prevent="setMethod('recovery')">
<a href="#" @click.prevent="requireRecovery">
{{$t('login.enter_recovery_code')}}
</a>
</div>

View File

@ -9,21 +9,12 @@ const UserPanel = {
computed: {
signedIn () { return this.user },
authForm () {
switch (this.authMethod) {
case 'totp':
return 'MFATOTPForm'
case 'recovery':
return 'MFARecoveryForm'
default:
return 'LoginForm'
}
if (this.requiredTOTP) { return 'MFATOTPForm' }
if (this.requiredRecovery) { return 'MFARecoveryForm' }
return 'LoginForm'
},
...mapGetters({
authMethod: 'authFlow/authMethod'
}),
...mapState({
user: state => state.users.currentUser
})
...mapGetters('authFlow', ['requiredTOTP', 'requiredRecovery']),
...mapState({ user: state => state.users.currentUser })
},
components: {
MFARecoveryForm,

View File

@ -1,9 +1,25 @@
const PASSWORD_STRATEGY = 'password'
const TOKEN_STRATEGY = 'token'
// MFA strategies
const TOTP_STRATEGY = 'totp'
const RECOVERY_STRATEGY = 'recovery'
const fetchStrategy = (state, rootState) => {
if (state.requiredMFA) {
return state.strategy || TOTP_STRATEGY
} else {
return state.strategy || rootState.instance.loginMethod || PASSWORD_STRATEGY
}
}
// initial state
const state = {
app: null,
settings: {},
stage: 'first', // first, second
authMethod: 'password' // first (password, token), second (recovery, totp)
requiredMFA: false,
strategy: null
}
// getters
@ -14,27 +30,49 @@ const getters = {
settings: (state, getters) => {
return state.settings
},
authMethod: (state, getters, rootState) => {
if (state.stage === 'first') {
return state.authMethod || rootState.instance.loginMethod
}
if (state.stage === 'second') {
return state.authMethod || 'totp'
}
requiredPassword: (state, getters, rootState) => {
return fetchStrategy(state, rootState) === PASSWORD_STRATEGY
},
requiredToken: (state, getters, rootState) => {
return fetchStrategy(state, rootState) === TOKEN_STRATEGY
},
requiredTOTP: (state, getters, rootState) => {
return state.requiredMFA &&
fetchStrategy(state, rootState) === TOTP_STRATEGY
},
requiredRecovery: (state, getters, rootState) => {
return state.requiredMFA &&
fetchStrategy(state, rootState) === RECOVERY_STRATEGY
}
}
// mutations
const mutations = {
setStage (state, {stage, authMethod, app, settings}) {
state.stage = stage
state.authMethod = authMethod
requirePassword (state) {
state.strategy = PASSWORD_STRATEGY
},
requireToken (state) {
state.strategy = TOKEN_STRATEGY
},
setupMFA (state, {app, settings}) { // sets MFA settings (token, supported strategies)
state.settings = settings
state.app = app
},
setMethod (state, authMethod) {
state.authMethod = authMethod
requireMFA (state) {
state.requiredMFA = true
state.strategy = TOTP_STRATEGY
},
requireRecovery (state) {
state.strategy = RECOVERY_STRATEGY
},
requireTOTP (state) {
state.strategy = TOTP_STRATEGY
},
reset (state) {
state.requiredMFA = false
state.strategy = null
state.settings = {}
state.app = null
}
}
@ -43,11 +81,7 @@ const actions = {
async login ({state, dispatch, commit}, {access_token}) {
commit('setToken', access_token, { root: true })
await dispatch('loginUser', access_token, { root: true })
state.stage = 'first'
state.authMethod = 'password'
state.settings = {}
state.app = null
commit('reset')
}
}