From 4ee26c6041adef94105f306697a9eb4fa2f0ba1c Mon Sep 17 00:00:00 2001 From: Sean King Date: Thu, 15 May 2025 23:33:49 -0600 Subject: [PATCH] Migrate auth flow module to pinia store --- src/boot/after_store.js | 3 +- src/components/auth_form/auth_form.js | 5 +- src/components/login_form/login_form.js | 12 ++-- src/components/mfa_form/recovery_form.js | 12 ++-- src/components/mfa_form/totp_form.js | 12 ++-- src/modules/auth_flow.js | 86 ------------------------ src/modules/index.js | 2 - src/stores/auth_flow.js | 69 +++++++++++++++++++ 8 files changed, 91 insertions(+), 110 deletions(-) delete mode 100644 src/modules/auth_flow.js create mode 100644 src/stores/auth_flow.js diff --git a/src/boot/after_store.js b/src/boot/after_store.js index 1b133a089..eb31f2418 100644 --- a/src/boot/after_store.js +++ b/src/boot/after_store.js @@ -21,6 +21,7 @@ import { useOAuthStore } from 'src/stores/oauth' import { useI18nStore } from 'src/stores/i18n' import { useInterfaceStore } from 'src/stores/interface' import { useAnnouncementsStore } from 'src/stores/announcements' +import { useAuthFlowStore } from 'src/stores/auth_flow' let staticInitialResults = null @@ -153,7 +154,7 @@ const setSettings = async ({ apiConfig, staticConfig, store }) => { : config.logoMargin }) copyInstanceOption('logoLeft') - store.commit('authFlow/setInitialStrategy', config.loginMethod) + useAuthFlowStore().setInitialStrategy(config.loginMethod) copyInstanceOption('redirectRootNoLogin') copyInstanceOption('redirectRootLogin') diff --git a/src/components/auth_form/auth_form.js b/src/components/auth_form/auth_form.js index a86a3dca2..243cbf574 100644 --- a/src/components/auth_form/auth_form.js +++ b/src/components/auth_form/auth_form.js @@ -2,7 +2,8 @@ import { h, resolveComponent } from 'vue' import LoginForm from '../login_form/login_form.vue' import MFARecoveryForm from '../mfa_form/recovery_form.vue' import MFATOTPForm from '../mfa_form/totp_form.vue' -import { mapGetters } from 'vuex' +import { mapState } from 'pinia' +import { useAuthFlowStore } from 'src/stores/auth_flow' const AuthForm = { name: 'AuthForm', @@ -15,7 +16,7 @@ const AuthForm = { if (this.requiredRecovery) { return 'MFARecoveryForm' } return 'LoginForm' }, - ...mapGetters('authFlow', ['requiredTOTP', 'requiredRecovery']) + ...mapState(useAuthFlowStore, ['requiredTOTP', 'requiredRecovery']) }, components: { MFARecoveryForm, diff --git a/src/components/login_form/login_form.js b/src/components/login_form/login_form.js index ed3f5dfc6..9566aa903 100644 --- a/src/components/login_form/login_form.js +++ b/src/components/login_form/login_form.js @@ -1,7 +1,8 @@ -import { mapState, mapGetters, mapActions, mapMutations } from 'vuex' -import { mapStores } from 'pinia' +import { mapState } from 'vuex' +import { mapStores, mapActions, mapState as mapPiniaState } from 'pinia' import oauthApi from '../../services/new_api/oauth.js' import { useOAuthStore } from 'src/stores/oauth.js' +import { useAuthFlowStore } from 'src/stores/auth_flow.js' import { library } from '@fortawesome/fontawesome-svg-core' import { faTimes @@ -25,13 +26,10 @@ const LoginForm = { instance: state => state.instance, loggingIn: state => state.users.loggingIn, }), - ...mapGetters( - 'authFlow', ['requiredPassword', 'requiredToken', 'requiredMFA'] - ) + ...mapPiniaState(useAuthFlowStore, ['requiredPassword', 'requiredToken', 'requiredMFA']) }, methods: { - ...mapMutations('authFlow', ['requireMFA']), - ...mapActions({ login: 'authFlow/login' }), + ...mapActions(useAuthFlowStore, ['requireMFA', 'login']), submit () { this.isTokenAuth ? this.submitToken() : this.submitPassword() }, diff --git a/src/components/mfa_form/recovery_form.js b/src/components/mfa_form/recovery_form.js index 2d0f4fdff..84479b1ec 100644 --- a/src/components/mfa_form/recovery_form.js +++ b/src/components/mfa_form/recovery_form.js @@ -1,7 +1,8 @@ import mfaApi from '../../services/new_api/mfa.js' -import { mapState, mapGetters, mapActions, mapMutations } from 'vuex' -import { mapStores } from 'pinia' +import { mapState } from 'vuex' +import { mapStores, mapActions, mapState as mapPiniaState } from 'pinia' import { useOAuthStore } from 'src/stores/oauth.js' +import { useAuthFlowStore } from 'src/stores/auth_flow.js' import { library } from '@fortawesome/fontawesome-svg-core' import { faTimes @@ -17,8 +18,8 @@ export default { error: false }), computed: { - ...mapGetters({ - authSettings: 'authFlow/settings' + ...mapPiniaState(useAuthFlowStore, { + authSettings: store => store.settings }), ...mapStores(useOAuthStore), ...mapState({ @@ -26,8 +27,7 @@ export default { }) }, methods: { - ...mapMutations('authFlow', ['requireTOTP', 'abortMFA']), - ...mapActions({ login: 'authFlow/login' }), + ...mapActions(useAuthFlowStore, ['requireTOTP', 'abortMFA', 'login']), clearError () { this.error = false }, focusOnCodeInput () { diff --git a/src/components/mfa_form/totp_form.js b/src/components/mfa_form/totp_form.js index 857d055ff..e369d8a5d 100644 --- a/src/components/mfa_form/totp_form.js +++ b/src/components/mfa_form/totp_form.js @@ -1,7 +1,8 @@ import mfaApi from '../../services/new_api/mfa.js' -import { mapState, mapGetters, mapActions, mapMutations } from 'vuex' -import { mapStores } from 'pinia' +import { mapState } from 'vuex' +import { mapStores, mapActions, mapState as mapPiniaState } from 'pinia' import { useOAuthStore } from 'src/stores/oauth.js' +import { useAuthFlowStore } from 'src/stores/auth_flow.js' import { library } from '@fortawesome/fontawesome-svg-core' import { faTimes @@ -17,8 +18,8 @@ export default { error: false }), computed: { - ...mapGetters({ - authSettings: 'authFlow/settings' + ...mapPiniaState(useAuthFlowStore, { + authSettings: store => store.settings }), ...mapStores(useOAuthStore), ...mapState({ @@ -26,8 +27,7 @@ export default { }) }, methods: { - ...mapMutations('authFlow', ['requireRecovery', 'abortMFA']), - ...mapActions({ login: 'authFlow/login' }), + ...mapActions(useAuthFlowStore, ['requireRecovery', 'abortMFA', 'login']), clearError () { this.error = false }, focusOnCodeInput () { diff --git a/src/modules/auth_flow.js b/src/modules/auth_flow.js deleted file mode 100644 index dfa673db9..000000000 --- a/src/modules/auth_flow.js +++ /dev/null @@ -1,86 +0,0 @@ -import { useOAuthStore } from 'src/stores/oauth.js' - -const PASSWORD_STRATEGY = 'password' -const TOKEN_STRATEGY = 'token' - -// MFA strategies -const TOTP_STRATEGY = 'totp' -const RECOVERY_STRATEGY = 'recovery' - -// initial state -const state = { - settings: {}, - strategy: PASSWORD_STRATEGY, - initStrategy: PASSWORD_STRATEGY // default strategy from config -} - -const resetState = (state) => { - state.strategy = state.initStrategy - state.settings = {} -} - -// getters -const getters = { - settings: (state) => { - return state.settings - }, - requiredPassword: (state) => { - return state.strategy === PASSWORD_STRATEGY - }, - requiredToken: (state) => { - return state.strategy === TOKEN_STRATEGY - }, - requiredTOTP: (state) => { - return state.strategy === TOTP_STRATEGY - }, - requiredRecovery: (state) => { - return state.strategy === RECOVERY_STRATEGY - } -} - -// mutations -const mutations = { - setInitialStrategy (state, strategy) { - if (strategy) { - state.initStrategy = strategy - state.strategy = strategy - } - }, - requirePassword (state) { - state.strategy = PASSWORD_STRATEGY - }, - requireToken (state) { - state.strategy = TOKEN_STRATEGY - }, - requireMFA (state, { settings }) { - state.settings = settings - state.strategy = TOTP_STRATEGY // default strategy of MFA - }, - requireRecovery (state) { - state.strategy = RECOVERY_STRATEGY - }, - requireTOTP (state) { - state.strategy = TOTP_STRATEGY - }, - abortMFA (state) { - resetState(state) - } -} - -// actions -const actions = { - - async login ({ state, dispatch }, { access_token: accessToken }) { - useOAuthStore().setToken(accessToken) - await dispatch('loginUser', accessToken, { root: true }) - resetState(state) - } -} - -export default { - namespaced: true, - state, - getters, - mutations, - actions -} diff --git a/src/modules/index.js b/src/modules/index.js index 7f0aa3706..fd233585d 100644 --- a/src/modules/index.js +++ b/src/modules/index.js @@ -6,7 +6,6 @@ import api from './api.js' import config from './config.js' import profileConfig from './profileConfig.js' import adminSettings from './adminSettings.js' -import authFlow from './auth_flow.js' import drafts from './drafts.js' import chats from './chats.js' @@ -19,7 +18,6 @@ export default { config, profileConfig, adminSettings, - authFlow, drafts, chats } diff --git a/src/stores/auth_flow.js b/src/stores/auth_flow.js new file mode 100644 index 000000000..e41a9bc5e --- /dev/null +++ b/src/stores/auth_flow.js @@ -0,0 +1,69 @@ +import { useOAuthStore } from 'src/stores/oauth.js' +import { defineStore } from 'pinia' + +const PASSWORD_STRATEGY = 'password' +const TOKEN_STRATEGY = 'token' + +// MFA strategies +const TOTP_STRATEGY = 'totp' +const RECOVERY_STRATEGY = 'recovery' + +export const useAuthFlowStore = defineStore('authFlow', { + // initial state + state: () => ({ + settings: {}, + strategy: PASSWORD_STRATEGY, + initStrategy: PASSWORD_STRATEGY // default strategy from config + }), + // getters + getters: { + requiredPassword: (state) => { + return state.strategy === PASSWORD_STRATEGY + }, + requiredToken: (state) => { + return state.strategy === TOKEN_STRATEGY + }, + requiredTOTP: (state) => { + return state.strategy === TOTP_STRATEGY + }, + requiredRecovery: (state) => { + return state.strategy === RECOVERY_STRATEGY + }, + }, + actions: { + setInitialStrategy (strategy) { + if (strategy) { + this.initStrategy = strategy + this.strategy = strategy + } + }, + requirePassword () { + this.strategy = PASSWORD_STRATEGY + }, + requireToken () { + this.strategy = TOKEN_STRATEGY + }, + requireMFA ({ settings }) { + this.settings = settings + this.strategy = TOTP_STRATEGY // default strategy of MFA + }, + requireRecovery () { + this.strategy = RECOVERY_STRATEGY + }, + requireTOTP () { + this.strategy = TOTP_STRATEGY + }, + abortMFA () { + this.resetState() + }, + resetState () { + this.strategy = this.initStrategy + this.settings = {} + }, + async login ({ access_token: accessToken }) { + useOAuthStore().setToken(accessToken) + await window.vuex.dispatch('loginUser', accessToken, { root: true }) + this.resetState() + } + } +}) \ No newline at end of file