diff --git a/src/boot/after_store.js b/src/boot/after_store.js
index 9e97918fdf..8219ce778d 100644
--- a/src/boot/after_store.js
+++ b/src/boot/after_store.js
@@ -280,6 +280,8 @@ const afterStoreSetup = async ({ store, i18n }) => {
}
})
+ store.commit('authFlow/setRouter', router)
+
/* eslint-disable no-new */
return new Vue({
router,
diff --git a/src/boot/routes.js b/src/boot/routes.js
index 1a17909911..4c66c8950b 100644
--- a/src/boot/routes.js
+++ b/src/boot/routes.js
@@ -14,6 +14,8 @@ import OAuthCallback from 'components/oauth_callback/oauth_callback.vue'
import UserSearch from 'components/user_search/user_search.vue'
import Notifications from 'components/notifications/notifications.vue'
import LoginForm from 'components/login_form/login_form.vue'
+import TOTPForm from 'components/mfa_form/totp_form.vue'
+import RecoveryForm from 'components/mfa_form/recovery_form.vue'
import ChatPanel from 'components/chat_panel/chat_panel.vue'
import WhoToFollow from 'components/who_to_follow/who_to_follow.vue'
import About from 'components/about/about.vue'
@@ -43,6 +45,8 @@ export default (store) => {
{ name: 'user-settings', path: '/user-settings', component: UserSettings },
{ name: 'notifications', path: '/:username/notifications', component: Notifications },
{ name: 'login', path: '/login', component: LoginForm },
+ { name: 'mfa-totp', path: '/sessions/two-factor', component: TOTPForm },
+ { name: 'mfa-recovery', path: '/sessions/two-factor/recovery', component: RecoveryForm },
{ name: 'chat', path: '/chat', component: ChatPanel, props: () => ({ floating: false }) },
{ name: 'oauth-callback', path: '/oauth-callback', component: OAuthCallback, props: (route) => ({ code: route.query.code }) },
{ name: 'user-search', path: '/user-search', component: UserSearch, props: (route) => ({ query: route.query.query }) },
diff --git a/src/components/mfa_form/recovery_form.js b/src/components/mfa_form/recovery_form.js
index fbe9b437a9..630bda2808 100644
--- a/src/components/mfa_form/recovery_form.js
+++ b/src/components/mfa_form/recovery_form.js
@@ -13,6 +13,11 @@ export default {
}),
...mapState({ instance: 'instance' })
},
+ beforeRouteEnter (to, from, next) {
+ next(vm => {
+ if (!vm.authApp) { vm.$router.push({name: 'login'}) }
+ })
+ },
methods: {
...mapMutations('authFlow', ['requireTOTP', 'abortMFA']),
...mapActions({ login: 'authFlow/login' }),
diff --git a/src/components/mfa_form/recovery_form.vue b/src/components/mfa_form/recovery_form.vue
index 806746f476..e0e2d65bd5 100644
--- a/src/components/mfa_form/recovery_form.vue
+++ b/src/components/mfa_form/recovery_form.vue
@@ -17,6 +17,7 @@
{{$t('login.enter_two_factor_code')}}
+
{{$t('general.cancel')}}
diff --git a/src/components/mfa_form/totp_form.js b/src/components/mfa_form/totp_form.js
index 6c94fe52bf..e235eecc14 100644
--- a/src/components/mfa_form/totp_form.js
+++ b/src/components/mfa_form/totp_form.js
@@ -12,6 +12,11 @@ export default {
}),
...mapState({ instance: 'instance' })
},
+ beforeRouteEnter (to, from, next) {
+ next(vm => {
+ if (!vm.authApp) { vm.$router.push({name: 'login'}) }
+ })
+ },
methods: {
...mapMutations('authFlow', ['requireRecovery', 'abortMFA']),
...mapActions({ login: 'authFlow/login' }),
diff --git a/src/components/mfa_form/totp_form.vue b/src/components/mfa_form/totp_form.vue
index d39caa6d1a..c547785e2b 100644
--- a/src/components/mfa_form/totp_form.vue
+++ b/src/components/mfa_form/totp_form.vue
@@ -21,6 +21,7 @@
{{$t('login.enter_recovery_code')}}
+
{{$t('general.cancel')}}
diff --git a/src/modules/auth_flow.js b/src/modules/auth_flow.js
index 86328cf3f6..5fb50efe53 100644
--- a/src/modules/auth_flow.js
+++ b/src/modules/auth_flow.js
@@ -7,6 +7,7 @@ const RECOVERY_STRATEGY = 'recovery'
// initial state
const state = {
+ router: null,
app: null,
settings: {},
strategy: PASSWORD_STRATEGY,
@@ -43,6 +44,9 @@ const getters = {
// mutations
const mutations = {
+ setRouter (state, router) {
+ if (router) { state.router = router }
+ },
setInitialStrategy (state, strategy) {
if (strategy) {
state.initStrategy = strategy
@@ -59,15 +63,29 @@ const mutations = {
state.settings = settings
state.app = app
state.strategy = TOTP_STRATEGY // default strategy of MFA
+
+ if (state.router.currentRoute.name === 'login') {
+ state.router.push({name: 'mfa-totp'})
+ }
},
requireRecovery (state) {
state.strategy = RECOVERY_STRATEGY
+ if (state.router.currentRoute.name === 'mfa-totp') {
+ state.router.push({name: 'mfa-recovery'})
+ }
},
requireTOTP (state) {
state.strategy = TOTP_STRATEGY
+ if (state.router.currentRoute.name === 'mfa-recovery') {
+ state.router.push({name: 'mfa-totp'})
+ }
},
abortMFA (state) {
resetState(state)
+ if (state.router.currentRoute.name === 'mfa-totp' ||
+ state.router.currentRoute.name === 'mfa-recovery') {
+ state.router.push({name: 'login'})
+ }
}
}