How to Implement Two-Step Login Verification in Vue-Element-Admin
You can implement two-step login verification by extending the existing Vuex authentication module in src/store/modules/user.js to handle temporary tokens and adding a verification step in the login view component.
Vue-Element-Admin provides a robust authentication system built on Vuex that handles JWT tokens through src/store/modules/user.js. To add two-step login verification (2FA) to this existing architecture, you need to intercept the initial login response, store a temporary token when 2FA is required, and create a secondary verification action that exchanges the OTP code for the final authentication token.
Understanding the Existing Authentication Flow
Before extending the system, you must understand how Vue-Element-Admin currently handles authentication across its three core layers.
Login UI Component
The entry point for authentication is src/views/login/index.vue, which collects user credentials and dispatches the user/login action. The handleLogin method validates the form and triggers the authentication process.
// src/views/login/index.vue
handleLogin() {
this.$refs.loginForm.validate(valid => {
if (valid) {
this.$store.dispatch('user/login', this.loginForm).then(() => {
this.$router.push({ path: this.redirect || '/' })
})
}
})
}
Vuex Store Authentication Logic
The src/store/modules/user.js file contains the central authentication state management. The login action sends credentials to src/api/user.js and stores the resulting JWT using setToken from src/utils/auth.js.
// src/store/modules/user.js
login({ commit }, userInfo) {
const { username, password } = userInfo
return new Promise((resolve, reject) => {
login({ username: username.trim(), password }).then(response => {
const { data } = response
commit('SET_TOKEN', data.token)
setToken(data.token)
resolve()
}).catch(error => {
reject(error)
})
})
}
Token Persistence Layer
The src/utils/auth.js utility abstracts token storage, typically using cookies or localStorage. This layer remains unchanged when implementing two-step verification, as it ultimately stores the final JWT after successful 2FA completion.
Extending the System for Two-Step Login Verification
To implement two-step login verification, you need to modify the authentication flow to handle an intermediate state where the server requires an additional verification code.
Step 1: Modify the Login Action to Handle 2FA Flags
Update src/store/modules/user.js to check for a need2FA flag in the login response. When this flag is true, store the temporary token and resolve with a status indicator instead of completing the authentication.
// src/store/modules/user.js
const mutations = {
SET_TOKEN: (state, token) => {
state.token = token
},
SET_TEMP_TOKEN: (state, token) => {
state.tempToken = token
}
}
const state = {
token: getToken(),
tempToken: '',
name: '',
avatar: ''
}
login({ commit }, userInfo) {
const { username, password } = userInfo
return new Promise((resolve, reject) => {
login({ username: username.trim(), password }).then(res => {
const { data } = res
if (data.need2FA) {
commit('SET_TEMP_TOKEN', data.tempToken)
resolve({ need2FA: true })
} else {
commit('SET_TOKEN', data.token)
setToken(data.token)
resolve({ need2FA: false })
}
}).catch(err => reject(err))
})
}
Step 2: Create the Verification Action
Add a new verify2FA action in src/store/modules/user.js that sends the OTP code and temporary token to the server. Upon successful verification, store the final JWT and clear the temporary token.
// src/store/modules/user.js
verify2FA({ commit, state }, code) {
return new Promise((resolve, reject) => {
verify2FA({ token: state.tempToken, code }).then(res => {
const { token } = res.data
commit('SET_TOKEN', token)
setToken(token)
commit('SET_TEMP_TOKEN', '')
resolve()
}).catch(err => reject(err))
})
}
Step 3: Add the API Endpoint Wrapper
Extend src/api/user.js to include the new verification endpoint that communicates with your backend.
// src/api/user.js
export function verify2FA(data) {
return request({
url: '/vue-element-admin/user/verify-2fa',
method: 'post',
data
})
}
Step 4: Update the Login View Component
Modify src/views/login/index.vue to conditionally render a two-factor input form when the initial login response indicates 2FA is required.
<!-- src/views/login/index.vue -->
<template>
<div class="login-container">
<!-- Standard Login Form -->
<el-form v-if="!show2FA" ref="loginForm" :model="loginForm" :rules="loginRules">
<el-form-item prop="username">
<el-input v-model="loginForm.username" placeholder="Username" />
</el-form-item>
<el-form-item prop="password">
<el-input v-model="loginForm.password" placeholder="Password" type="password" />
</el-form-item>
<el-button type="primary" @click="handleLogin">Login</el-button>
</el-form>
<!-- Two-Factor Verification Form -->
<el-form v-else ref="faForm" @submit.native.prevent="handleVerify2FA">
<el-form-item prop="code">
<el-input v-model="twoFactorCode" placeholder="Enter OTP Code" maxlength="6" />
</el-form-item>
<el-button type="primary" @click="handleVerify2FA">Verify</el-button>
</el-form>
</div>
</template>
<script>
export default {
data() {
return {
loginForm: {
username: '',
password: ''
},
show2FA: false,
twoFactorCode: ''
}
},
methods: {
handleLogin() {
this.$refs.loginForm.validate(valid => {
if (valid) {
this.$store.dispatch('user/login', this.loginForm).then(response => {
if (response.need2FA) {
this.show2FA = true
} else {
this.$router.push({ path: this.redirect || '/' })
}
})
}
})
},
handleVerify2FA() {
this.$store.dispatch('user/verify2FA', this.twoFactorCode).then(() => {
this.$router.push({ path: this.redirect || '/' })
})
}
}
}
</script>
Summary
Implementing two-step login verification in Vue-Element-Admin requires minimal changes to the existing authentication architecture:
- Extend the Vuex store (
src/store/modules/user.js) with aSET_TEMP_TOKENmutation and modify theloginaction to detectneed2FAresponses. - Add a verification action (
verify2FA) that exchanges the temporary token and OTP for the final JWT. - Create the API wrapper (
src/api/user.js) for the/user/verify-2faendpoint. - Update the login view (
src/views/login/index.vue) to conditionally render the OTP input form and handle the two-step flow.
This approach preserves the existing token persistence layer (src/utils/auth.js) and permission system while adding robust two-factor authentication capabilities.
Frequently Asked Questions
How does the temporary token differ from the final JWT in the two-step verification flow?
The temporary token (stored via SET_TEMP_TOKEN) is a short-lived credential returned by the server during the first authentication step when need2FA is true. It cannot access protected routes or APIs; it exists solely to validate the OTP code in the second step. The final JWT (stored via SET_TOKEN and setToken) is the full authentication token issued only after successful two-step verification, which grants access to the application and is persisted in cookies or localStorage by src/utils/auth.js.
Can I use this two-step verification implementation with external 2FA providers like Google Authenticator or Authy?
Yes, the implementation in src/store/modules/user.js and src/views/login/index.vue is provider-agnostic. The frontend only handles the OTP code input and passes it to your backend via the verify2FA action. Your backend (/vue-element-admin/user/verify-2fa endpoint) can validate the code against TOTP algorithms used by Google Authenticator, Authy, or SMS-based verification. The frontend changes remain identical regardless of which 2FA method your backend implements.
What happens if a user refreshes the page during the two-step verification process?
If a user refreshes the page after completing the first step (when show2FA is true in src/views/login/index.vue) but before entering the OTP, the application state resets because tempToken is stored in Vuex state (memory) by default. To prevent users from restarting the login process, you can persist tempToken to sessionStorage in the SET_TEMP_TOKEN mutation within src/store/modules/user.js, similar to how the final token uses src/utils/auth.js. However, ensure the temporary token has a short expiration on the backend to maintain security if persisted client-side.
Do I need to modify the permission and routing guards when adding two-step verification?
No, the existing permission system in Vue-Element-Admin continues to function without modification. The routing guards in src/permission.js check for the presence of a valid token using getToken() from src/utils/auth.js. Since the verify2FA action in src/store/modules/user.js only sets the final token (via setToken) after successful two-step verification, the permission system naturally blocks access until the full authentication is complete. The temporary token used during the 2FA process is stored separately in Vuex state and is never checked by the routing guards.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →