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'}) + } } }