diff --git a/pkg/api/authorizations.go b/pkg/api/authorizations.go index 8189716e..e6989e9e 100644 --- a/pkg/api/authorizations.go +++ b/pkg/api/authorizations.go @@ -190,5 +190,11 @@ func (a *AuthorizationsApi) TwoFactorAuthorizeByRecoveryCodeHandler(c *core.Cont c.SetTokenClaims(claims) log.InfofWithRequestId(c, "[authorizations.TwoFactorAuthorizeByRecoveryCodeHandler] user \"uid:%d\" has authorized two factor via recovery code \"%s\", token will be expired at %d", user.Uid, credential.RecoveryCode, claims.ExpiresAt) - return token, nil + + authResp := &models.AuthResponse{ + Token : token, + Need2FA: false, + } + + return authResp, nil } diff --git a/pkg/errs/twofactor_authorization.go b/pkg/errs/twofactor_authorization.go index 6511c44d..368228e8 100644 --- a/pkg/errs/twofactor_authorization.go +++ b/pkg/errs/twofactor_authorization.go @@ -4,8 +4,8 @@ import "net/http" var ( ErrPasscodeInvalid = NewNormalError(NORMAL_SUBCATEGORY_TWOFACTOR, 0, http.StatusUnauthorized, "passcode is invalid") - ErrTwoFactorRecoveryCodeInvalid = NewNormalError(NORMAL_SUBCATEGORY_TWOFACTOR, 1, http.StatusUnauthorized, "two factor recovery code is invalid") + ErrTwoFactorRecoveryCodeInvalid = NewNormalError(NORMAL_SUBCATEGORY_TWOFACTOR, 1, http.StatusUnauthorized, "two factor backup code is invalid") ErrTwoFactorIsNotEnabled = NewNormalError(NORMAL_SUBCATEGORY_TWOFACTOR, 2, http.StatusBadRequest, "two factor is not enabled") ErrTwoFactorAlreadyEnabled = NewNormalError(NORMAL_SUBCATEGORY_TWOFACTOR, 3, http.StatusBadRequest, "two factor has already been enabled") - ErrTwoFactorRecoveryCodeNotExist = NewNormalError(NORMAL_SUBCATEGORY_TWOFACTOR, 4, http.StatusUnauthorized, "two factor recovery code does not exist") + ErrTwoFactorRecoveryCodeNotExist = NewNormalError(NORMAL_SUBCATEGORY_TWOFACTOR, 4, http.StatusUnauthorized, "two factor backup code does not exist") ) diff --git a/src/lib/services.js b/src/lib/services.js index b4cfed65..bb6e3f73 100644 --- a/src/lib/services.js +++ b/src/lib/services.js @@ -49,6 +49,15 @@ export default { } }); }, + authorize2FAByBackupCode: ({ recoveryCode, token }) => { + return axios.post('2fa/recovery.json', { + recoveryCode + }, { + headers: { + Authorization: `Bearer ${token}` + } + }); + }, logout: () => { return axios.get('v1/logout.json'); }, diff --git a/src/locales/en.js b/src/locales/en.js index c783f552..952bb681 100644 --- a/src/locales/en.js +++ b/src/locales/en.js @@ -5,6 +5,7 @@ export default { } }, 'error': { + 'system error': 'System Error', 'unauthorized access': 'Unauthorized access', 'token is expired': 'Token is expired', 'token is invalid': 'Token is invalid', @@ -26,10 +27,10 @@ export default { 'login name or password is invalid': 'Login name or password is invalid', 'login name or password is wrong': 'Login name or password is wrong', 'passcode is invalid': 'Passcode is invalid', - 'two factor recovery code is invalid': 'Two factor recovery code is invalid', + 'two factor backup code is invalid': 'Two factor backup code is invalid', 'two factor is not enabled': 'Two factor is not enabled', 'two factor has already been enabled': 'Two factor has already been enabled', - 'two factor recovery code does not exist': 'Two factor recovery code does not exist', + 'two factor backup code does not exist': 'Two factor backup code does not exist', }, 'OK': 'OK', 'Cancel': 'Cancel', @@ -53,10 +54,13 @@ export default { 'Unable to login': 'Unable to login', 'Two-Factor Authentication': 'Two-Factor Authentication', 'Passcode': 'Passcode', + 'Backup Code': 'Backup Code', 'Verify': 'Verify', 'Please input passcode': 'Please input passcode', + 'Please input backup code': 'Please input backup code', 'Unable to verify': 'Unable to verify', 'Use a backup code': 'Use a backup code', + 'Use a passcode': 'Use a passcode', 'Language': 'Language', 'Log Out': 'Log Out', 'Are you sure you want to log out?': 'Are you sure you want to log out?', diff --git a/src/locales/zh_Hans.js b/src/locales/zh_Hans.js index 8f3d0f22..8a1a1fff 100644 --- a/src/locales/zh_Hans.js +++ b/src/locales/zh_Hans.js @@ -5,6 +5,7 @@ export default { } }, 'error': { + 'system error': '系统错误', 'unauthorized access': '未授权的登录', 'token is expired': '认证令牌已过期', 'token is invalid': '认证令牌无效', @@ -26,10 +27,10 @@ export default { 'login name or password is invalid': '登录名或密码无效', 'login name or password is wrong': '登录名或密码错误', 'passcode is invalid': '验证码无效', - 'two factor recovery code is invalid': '两步验证恢复口令无效', + 'two factor backup code is invalid': '两步验证备用码无效', 'two factor is not enabled': '两步验证没有启用', 'two factor has already been enabled': '两步验证已经启用', - 'two factor recovery code does not exist': '两步验证恢复口令不存在', + 'two factor backup code does not exist': '两步验证备用码不存在', }, 'OK': '确定', 'Cancel': '取消', @@ -53,10 +54,13 @@ export default { 'Unable to login': '无法登录', 'Two-Factor Authentication': '两步验证', 'Passcode': '验证码', + 'Backup Code': '备用码', 'Verify': '验证', 'Please input passcode': '请输入验证码', + 'Please input backup code': '请输入备用码', 'Unable to verify': '无法验证', 'Use a backup code': '使用备用码', + 'Use a passcode': '使用验证码', 'Language': '语言', 'Log Out': '退出登录', 'Are you sure you want to log out?': '您确定是否要退出登录?', diff --git a/src/views/mobile/Login.vue b/src/views/mobile/Login.vue index f9b805d9..ceb9aae4 100644 --- a/src/views/mobile/Login.vue +++ b/src/views/mobile/Login.vue @@ -57,14 +57,23 @@ type="number" outline clear-button + v-if="twoFAVerifyType === 'passcode'" :placeholder="$t('Passcode')" :value="passcode" @input="passcode = $event.target.value" > + {{ $t('Verify') }}
- {{ $t('Use a backup code') }} + {{ $t(twoFAVerifyTypeSwitchName) }}
@@ -81,7 +90,10 @@ export default { username: '', password: '', passcode: '', + backupCode: '', tempToken: '', + twoFAVerifyType: 'passcode', + twoFAVerifyTypeSwitchName: 'Use a backup code', allLanguages: self.$getAllLanguages() }; }, @@ -90,7 +102,11 @@ export default { return !this.username || !this.password; }, twoFAInputIsEmpty() { - return !this.passcode; + if (this.twoFAVerifyType === 'backupcode') { + return !this.backupCode; + } else { + return !this.passcode; + } }, currentLanguageName() { const currentLocale = this.$i18n.locale; @@ -173,17 +189,31 @@ export default { const app = self.$f7; const router = self.$f7router; - if (!this.passcode) { + if (this.twoFAVerifyType === 'passcode' && !this.passcode) { self.$alert('Please input passcode'); return; + } else if (this.twoFAVerifyType === 'backupcode' && !this.backupCode) { + self.$alert('Please input backup code'); + return; } app.preloader.show(); - self.$services.authorize2FA({ - passcode: self.passcode, - token: self.tempToken - }).then(response => { + let promise = null; + + if (self.twoFAVerifyType === 'backupcode') { + promise = self.$services.authorize2FAByBackupCode({ + recoveryCode: self.backupCode, + token: self.tempToken + }); + } else { + promise = self.$services.authorize2FA({ + passcode: self.passcode, + token: self.tempToken + }); + } + + promise.then(response => { app.preloader.hide(); const data = response.data; @@ -205,6 +235,15 @@ export default { } }) }, + switch2FAVerifyType() { + if (this.twoFAVerifyType === 'passcode') { + this.twoFAVerifyType = 'backupcode'; + this.twoFAVerifyTypeSwitchName = 'Use a passcode'; + } else { + this.twoFAVerifyType = 'passcode'; + this.twoFAVerifyTypeSwitchName = 'Use a backup code'; + } + }, changeLanguage(locale) { this.$setLanguage(locale); }