Compare commits

...

9 Commits

60 changed files with 317 additions and 146 deletions

View File

@ -66,7 +66,11 @@ func GetConfigBool(key string) bool {
func GetConfigInt64(key string) (int64, error) {
value := GetConfigString(key)
num, err := strconv.ParseInt(value, 10, 64)
return num, err
if err != nil {
return 0, fmt.Errorf("GetConfigInt64(%s) error, %s", key, err.Error())
}
return num, nil
}
func GetConfigDataSourceName() string {

View File

@ -42,6 +42,7 @@ type Response struct {
Name string `json:"name"`
Data interface{} `json:"data"`
Data2 interface{} `json:"data2"`
Data3 interface{} `json:"data3"`
}
type Captcha struct {

View File

@ -132,7 +132,7 @@ func (c *ApiController) HandleLoggedIn(application *object.Application, user *ob
if form.Type == ResponseTypeLogin {
c.SetSessionUsername(userId)
util.LogInfo(c.Ctx, "API: [%s] signed in", userId)
resp = &Response{Status: "ok", Msg: "", Data: userId, Data2: user.NeedUpdatePassword}
resp = &Response{Status: "ok", Msg: "", Data: userId, Data3: user.NeedUpdatePassword}
} else if form.Type == ResponseTypeCode {
clientId := c.Input().Get("clientId")
responseType := c.Input().Get("responseType")
@ -154,7 +154,7 @@ func (c *ApiController) HandleLoggedIn(application *object.Application, user *ob
}
resp = codeToResponse(code)
resp.Data2 = user.NeedUpdatePassword
resp.Data3 = user.NeedUpdatePassword
if application.EnableSigninSession || application.HasPromptPage() {
// The prompt page needs the user to be signed in
c.SetSessionUsername(userId)
@ -168,7 +168,7 @@ func (c *ApiController) HandleLoggedIn(application *object.Application, user *ob
token, _ := object.GetTokenByUser(application, user, scope, nonce, c.Ctx.Request.Host)
resp = tokenToResponse(token)
resp.Data2 = user.NeedUpdatePassword
resp.Data3 = user.NeedUpdatePassword
}
} else if form.Type == ResponseTypeDevice {
authCache, ok := object.DeviceAuthMap.LoadAndDelete(form.UserCode)
@ -195,14 +195,14 @@ func (c *ApiController) HandleLoggedIn(application *object.Application, user *ob
object.DeviceAuthMap.Store(authCacheCast.UserName, deviceAuthCacheDeviceCodeCast)
resp = &Response{Status: "ok", Msg: "", Data: userId, Data2: user.NeedUpdatePassword}
resp = &Response{Status: "ok", Msg: "", Data: userId, Data3: user.NeedUpdatePassword}
} else if form.Type == ResponseTypeSaml { // saml flow
res, redirectUrl, method, err := object.GetSamlResponse(application, user, form.SamlRequest, c.Ctx.Request.Host)
if err != nil {
c.ResponseError(err.Error(), nil)
return
}
resp = &Response{Status: "ok", Msg: "", Data: res, Data2: map[string]interface{}{"redirectUrl": redirectUrl, "method": method, "needUpdatePassword": user.NeedUpdatePassword}}
resp = &Response{Status: "ok", Msg: "", Data: res, Data2: map[string]interface{}{"redirectUrl": redirectUrl, "method": method}, Data3: user.NeedUpdatePassword}
if application.EnableSigninSession || application.HasPromptPage() {
// The prompt page needs the user to be signed in
@ -355,20 +355,27 @@ func isProxyProviderType(providerType string) bool {
func checkMfaEnable(c *ApiController, user *object.User, organization *object.Organization, verificationType string) bool {
if object.IsNeedPromptMfa(organization, user) {
// The prompt page needs the user to be srigned in
// The prompt page needs the user to be signed in
c.SetSessionUsername(user.GetId())
c.ResponseOk(object.RequiredMfa)
return true
}
if user.IsMfaEnabled() {
currentTime := util.String2Time(util.GetCurrentTime())
mfaRememberDeadline := util.String2Time(user.MfaRememberDeadline)
if user.MfaRememberDeadline != "" && mfaRememberDeadline.After(currentTime) {
return false
}
c.setMfaUserSession(user.GetId())
mfaList := object.GetAllMfaProps(user, true)
mfaAllowList := []*object.MfaProps{}
mfaRememberInHours := organization.MfaRememberInHours
for _, prop := range mfaList {
if prop.MfaType == verificationType || !prop.Enabled {
continue
}
prop.MfaRememberInHours = mfaRememberInHours
mfaAllowList = append(mfaAllowList, prop)
}
if len(mfaAllowList) >= 1 {
@ -973,6 +980,28 @@ func (c *ApiController) Login() {
return
}
var application *object.Application
if authForm.ClientId == "" {
application, err = object.GetApplication(fmt.Sprintf("admin/%s", authForm.Application))
} else {
application, err = object.GetApplicationByClientId(authForm.ClientId)
}
if err != nil {
c.ResponseError(err.Error())
return
}
if application == nil {
c.ResponseError(fmt.Sprintf(c.T("auth:The application: %s does not exist"), authForm.Application))
return
}
var organization *object.Organization
organization, err = object.GetOrganization(util.GetId("admin", application.Organization))
if err != nil {
c.ResponseError(c.T(err.Error()))
}
if authForm.Passcode != "" {
if authForm.MfaType == c.GetSession("verificationCodeType") {
c.ResponseError("Invalid multi-factor authentication type")
@ -999,6 +1028,17 @@ func (c *ApiController) Login() {
}
}
if authForm.EnableMfaRemember {
mfaRememberInSeconds := organization.MfaRememberInHours * 3600
currentTime := util.String2Time(util.GetCurrentTime())
duration := time.Duration(mfaRememberInSeconds) * time.Second
user.MfaRememberDeadline = util.Time2String(currentTime.Add(duration))
_, err = object.UpdateUser(user.GetId(), user, []string{"mfa_remember_deadline"}, user.IsAdmin)
if err != nil {
c.ResponseError(err.Error())
return
}
}
c.SetSession("verificationCodeType", "")
} else if authForm.RecoveryCode != "" {
err = object.MfaRecover(user, authForm.RecoveryCode)
@ -1011,22 +1051,6 @@ func (c *ApiController) Login() {
return
}
var application *object.Application
if authForm.ClientId == "" {
application, err = object.GetApplication(fmt.Sprintf("admin/%s", authForm.Application))
} else {
application, err = object.GetApplicationByClientId(authForm.ClientId)
}
if err != nil {
c.ResponseError(err.Error())
return
}
if application == nil {
c.ResponseError(fmt.Sprintf(c.T("auth:The application: %s does not exist"), authForm.Application))
return
}
resp = c.HandleLoggedIn(application, user, &authForm)
c.setMfaUserSession("")

View File

@ -58,6 +58,12 @@ func (c *ApiController) MfaSetupInitiate() {
return
}
organization, err := object.GetOrganizationByUser(user)
if err != nil {
c.ResponseError(err.Error())
return
}
mfaProps, err := MfaUtil.Initiate(user.GetId())
if err != nil {
c.ResponseError(err.Error())
@ -66,6 +72,7 @@ func (c *ApiController) MfaSetupInitiate() {
recoveryCode := uuid.NewString()
mfaProps.RecoveryCodes = []string{recoveryCode}
mfaProps.MfaRememberInHours = organization.MfaRememberInHours
resp := mfaProps
c.ResponseOk(resp)

View File

@ -98,6 +98,10 @@ func (c *ApiController) GetOrganization() {
return
}
if organization != nil && organization.MfaRememberInHours == 0 {
organization.MfaRememberInHours = 12
}
c.ResponseOk(organization)
}

View File

@ -16,6 +16,7 @@ package controllers
import (
"encoding/json"
"fmt"
"time"
"github.com/beego/beego/utils/pagination"
@ -460,7 +461,18 @@ func (c *ApiController) IntrospectToken() {
}
if token != nil {
application, err = object.GetApplication(fmt.Sprintf("%s/%s", token.Owner, token.Application))
if err != nil {
c.ResponseTokenError(err.Error())
return
}
if application == nil {
c.ResponseError(fmt.Sprintf(c.T("auth:The application: %s does not exist"), token.Application))
return
}
introspectionResponse.TokenType = token.TokenType
introspectionResponse.ClientId = application.ClientId
}
c.Data["json"] = introspectionResponse

View File

@ -31,7 +31,7 @@ func (cm *Argon2idCredManager) GetHashedPassword(password string, salt string) s
return hash
}
func (cm *Argon2idCredManager) IsPasswordCorrect(plainPwd string, hashedPwd string, userSalt string, organizationSalt string) bool {
func (cm *Argon2idCredManager) IsPasswordCorrect(plainPwd string, hashedPwd string, salt string) bool {
match, _ := argon2id.ComparePasswordAndHash(plainPwd, hashedPwd)
return match
}

View File

@ -17,7 +17,7 @@ func (cm *BcryptCredManager) GetHashedPassword(password string, salt string) str
return string(bytes)
}
func (cm *BcryptCredManager) IsPasswordCorrect(plainPwd string, hashedPwd string, userSalt string, organizationSalt string) bool {
func (cm *BcryptCredManager) IsPasswordCorrect(plainPwd string, hashedPwd string, salt string) bool {
err := bcrypt.CompareHashAndPassword([]byte(hashedPwd), []byte(plainPwd))
return err == nil
}

View File

@ -16,7 +16,7 @@ package cred
type CredManager interface {
GetHashedPassword(password string, salt string) string
IsPasswordCorrect(password string, passwordHash string, userSalt string, organizationSalt string) bool
IsPasswordCorrect(password string, passwordHash string, salt string) bool
}
func GetCredManager(passwordType string) CredManager {

View File

@ -41,9 +41,6 @@ func (cm *Md5UserSaltCredManager) GetHashedPassword(password string, salt string
return getMd5HexDigest(getMd5HexDigest(password) + salt)
}
func (cm *Md5UserSaltCredManager) IsPasswordCorrect(plainPwd string, hashedPwd string, userSalt string, organizationSalt string) bool {
if hashedPwd == cm.GetHashedPassword(plainPwd, organizationSalt) {
return true
}
return hashedPwd == cm.GetHashedPassword(plainPwd, userSalt)
func (cm *Md5UserSaltCredManager) IsPasswordCorrect(plainPwd string, hashedPwd string, salt string) bool {
return hashedPwd == cm.GetHashedPassword(plainPwd, salt)
}

View File

@ -35,9 +35,6 @@ func (cm *Pbkdf2SaltCredManager) GetHashedPassword(password string, salt string)
return base64.StdEncoding.EncodeToString(res)
}
func (cm *Pbkdf2SaltCredManager) IsPasswordCorrect(plainPwd string, hashedPwd string, userSalt string, organizationSalt string) bool {
if hashedPwd == cm.GetHashedPassword(plainPwd, organizationSalt) {
return true
}
return hashedPwd == cm.GetHashedPassword(plainPwd, userSalt)
func (cm *Pbkdf2SaltCredManager) IsPasswordCorrect(plainPwd string, hashedPwd string, salt string) bool {
return hashedPwd == cm.GetHashedPassword(plainPwd, salt)
}

View File

@ -42,7 +42,7 @@ func (m *Pbkdf2DjangoCredManager) GetHashedPassword(password string, salt string
return "pbkdf2_sha256$" + strconv.Itoa(iterations) + "$" + salt + "$" + hashBase64
}
func (m *Pbkdf2DjangoCredManager) IsPasswordCorrect(password string, passwordHash string, userSalt string, organizationSalt string) bool {
func (m *Pbkdf2DjangoCredManager) IsPasswordCorrect(password string, passwordHash string, _salt string) bool {
parts := strings.Split(passwordHash, "$")
if len(parts) != 4 {
return false

View File

@ -25,6 +25,6 @@ func (cm *PlainCredManager) GetHashedPassword(password string, salt string) stri
return password
}
func (cm *PlainCredManager) IsPasswordCorrect(plainPwd string, hashedPwd string, userSalt string, organizationSalt string) bool {
func (cm *PlainCredManager) IsPasswordCorrect(plainPwd string, hashedPwd string, salt string) bool {
return hashedPwd == plainPwd
}

View File

@ -41,9 +41,6 @@ func (cm *Sha256SaltCredManager) GetHashedPassword(password string, salt string)
return getSha256HexDigest(getSha256HexDigest(password) + salt)
}
func (cm *Sha256SaltCredManager) IsPasswordCorrect(plainPwd string, hashedPwd string, userSalt string, organizationSalt string) bool {
if hashedPwd == cm.GetHashedPassword(plainPwd, organizationSalt) {
return true
}
return hashedPwd == cm.GetHashedPassword(plainPwd, userSalt)
func (cm *Sha256SaltCredManager) IsPasswordCorrect(plainPwd string, hashedPwd string, salt string) bool {
return hashedPwd == cm.GetHashedPassword(plainPwd, salt)
}

View File

@ -41,9 +41,6 @@ func (cm *Sha512SaltCredManager) GetHashedPassword(password string, salt string)
return getSha512HexDigest(getSha512HexDigest(password) + salt)
}
func (cm *Sha512SaltCredManager) IsPasswordCorrect(plainPwd string, hashedPwd string, userSalt string, organizationSalt string) bool {
if hashedPwd == cm.GetHashedPassword(plainPwd, organizationSalt) {
return true
}
return hashedPwd == cm.GetHashedPassword(plainPwd, userSalt)
func (cm *Sha512SaltCredManager) IsPasswordCorrect(plainPwd string, hashedPwd string, salt string) bool {
return hashedPwd == cm.GetHashedPassword(plainPwd, salt)
}

View File

@ -61,9 +61,10 @@ type AuthForm struct {
CaptchaToken string `json:"captchaToken"`
ClientSecret string `json:"clientSecret"`
MfaType string `json:"mfaType"`
Passcode string `json:"passcode"`
RecoveryCode string `json:"recoveryCode"`
MfaType string `json:"mfaType"`
Passcode string `json:"passcode"`
RecoveryCode string `json:"recoveryCode"`
EnableMfaRemember bool `json:"enableMfaRemember"`
Plan string `json:"plan"`
Pricing string `json:"pricing"`

View File

@ -220,10 +220,15 @@ func checkSigninErrorTimes(user *User, lang string) error {
}
func CheckPassword(user *User, password string, lang string, options ...bool) error {
if password == "" {
return fmt.Errorf(i18n.Translate(lang, "check:Password cannot be empty"))
}
enableCaptcha := false
if len(options) > 0 {
enableCaptcha = options[0]
}
// check the login error times
if !enableCaptcha {
err := checkSigninErrorTimes(user, lang)
@ -236,35 +241,31 @@ func CheckPassword(user *User, password string, lang string, options ...bool) er
if err != nil {
return err
}
if organization == nil {
return fmt.Errorf(i18n.Translate(lang, "check:Organization does not exist"))
}
if password == "" {
return fmt.Errorf(i18n.Translate(lang, "check:Password cannot be empty"))
}
passwordType := user.PasswordType
if passwordType == "" {
passwordType = organization.PasswordType
}
credManager := cred.GetCredManager(passwordType)
if credManager != nil {
if organization.MasterPassword != "" {
if password == organization.MasterPassword || credManager.IsPasswordCorrect(password, organization.MasterPassword, "", organization.PasswordSalt) {
return resetUserSigninErrorTimes(user)
}
}
if credManager.IsPasswordCorrect(password, user.Password, user.PasswordSalt, organization.PasswordSalt) {
return resetUserSigninErrorTimes(user)
}
return recordSigninErrorInfo(user, lang, enableCaptcha)
} else {
if credManager == nil {
return fmt.Errorf(i18n.Translate(lang, "check:unsupported password type: %s"), organization.PasswordType)
}
if organization.MasterPassword != "" {
if password == organization.MasterPassword || credManager.IsPasswordCorrect(password, organization.MasterPassword, organization.PasswordSalt) {
return resetUserSigninErrorTimes(user)
}
}
if !credManager.IsPasswordCorrect(password, user.Password, organization.PasswordSalt) && !credManager.IsPasswordCorrect(password, user.Password, user.PasswordSalt) {
return recordSigninErrorInfo(user, lang, enableCaptcha)
}
return resetUserSigninErrorTimes(user)
}
func CheckPasswordComplexityByOrg(organization *Organization, password string) string {

View File

@ -103,7 +103,7 @@ func GetDashboard(owner string) (*map[string][]int64, error) {
func countCreatedBefore(dashboardMapItem DashboardMapItem, before time.Time) int64 {
count := dashboardMapItem.itemCount
for _, e := range dashboardMapItem.dashboardDateItems {
createdTime, _ := time.Parse("2006-01-02T15:04:05-07:00", e.CreatedTime)
createdTime, _ := time.Parse(time.RFC3339, e.CreatedTime)
if createdTime.Before(before) {
count++
}

View File

@ -21,13 +21,14 @@ import (
)
type MfaProps struct {
Enabled bool `json:"enabled"`
IsPreferred bool `json:"isPreferred"`
MfaType string `json:"mfaType" form:"mfaType"`
Secret string `json:"secret,omitempty"`
CountryCode string `json:"countryCode,omitempty"`
URL string `json:"url,omitempty"`
RecoveryCodes []string `json:"recoveryCodes,omitempty"`
Enabled bool `json:"enabled"`
IsPreferred bool `json:"isPreferred"`
MfaType string `json:"mfaType" form:"mfaType"`
Secret string `json:"secret,omitempty"`
CountryCode string `json:"countryCode,omitempty"`
URL string `json:"url,omitempty"`
RecoveryCodes []string `json:"recoveryCodes,omitempty"`
MfaRememberInHours int `json:"mfaRememberInHours"`
}
type MfaInterface interface {

View File

@ -84,8 +84,9 @@ type Organization struct {
NavItems []string `xorm:"varchar(1000)" json:"navItems"`
WidgetItems []string `xorm:"varchar(1000)" json:"widgetItems"`
MfaItems []*MfaItem `xorm:"varchar(300)" json:"mfaItems"`
AccountItems []*AccountItem `xorm:"varchar(5000)" json:"accountItems"`
MfaItems []*MfaItem `xorm:"varchar(300)" json:"mfaItems"`
MfaRememberInHours int `json:"mfaRememberInHours"`
AccountItems []*AccountItem `xorm:"varchar(5000)" json:"accountItems"`
}
func GetOrganizationCount(owner, name, field, value string) (int64, error) {

View File

@ -49,17 +49,21 @@ func (plan *Plan) GetId() string {
return fmt.Sprintf("%s/%s", plan.Owner, plan.Name)
}
func GetDuration(period string) (startTime time.Time, endTime time.Time) {
func getDuration(period string) (string, string, error) {
startTime := time.Now()
var endTime time.Time
if period == PeriodYearly {
startTime = time.Now()
endTime = startTime.AddDate(1, 0, 0)
} else if period == PeriodMonthly {
startTime = time.Now()
endTime = startTime.AddDate(0, 1, 0)
} else {
panic(fmt.Sprintf("invalid period: %s", period))
return "", "", fmt.Errorf("invalid period: %s", period)
}
return
startTimeString := startTime.Format(time.RFC3339)
endTimeString := endTime.Format(time.RFC3339)
return startTimeString, endTimeString, nil
}
func GetPlanCount(owner, field, value string) (int64, error) {

View File

@ -206,11 +206,17 @@ func BuyProduct(id string, user *User, providerName, pricingName, planName, host
if plan == nil {
return nil, nil, fmt.Errorf("the plan: %s does not exist", planName)
}
sub := NewSubscription(owner, user.Name, plan.Name, paymentName, plan.Period)
sub, err := NewSubscription(owner, user.Name, plan.Name, paymentName, plan.Period)
if err != nil {
return nil, nil, err
}
_, err = AddSubscription(sub)
if err != nil {
return nil, nil, err
}
returnUrl = fmt.Sprintf("%s/buy-plan/%s/%s/result?subscription=%s", originFrontend, owner, pricingName, sub.Name)
}
}

View File

@ -48,8 +48,8 @@ type Subscription struct {
Plan string `xorm:"varchar(100)" json:"plan"`
Payment string `xorm:"varchar(100)" json:"payment"`
StartTime time.Time `json:"startTime"`
EndTime time.Time `json:"endTime"`
StartTime string `xorm:"varchar(100)" json:"startTime"`
EndTime string `xorm:"varchar(100)" json:"endTime"`
Period string `xorm:"varchar(100)" json:"period"`
State SubscriptionState `xorm:"varchar(100)" json:"state"`
}
@ -84,9 +84,19 @@ func (sub *Subscription) UpdateState() error {
}
if sub.State == SubStateActive || sub.State == SubStateUpcoming || sub.State == SubStateExpired {
if sub.EndTime.Before(time.Now()) {
startTime, err := time.Parse(time.RFC3339, sub.StartTime)
if err != nil {
return err
}
endTime, err := time.Parse(time.RFC3339, sub.EndTime)
if err != nil {
return err
}
if endTime.Before(time.Now()) {
sub.State = SubStateExpired
} else if sub.StartTime.After(time.Now()) {
} else if startTime.After(time.Now()) {
sub.State = SubStateUpcoming
} else {
sub.State = SubStateActive
@ -103,10 +113,15 @@ func (sub *Subscription) UpdateState() error {
return nil
}
func NewSubscription(owner, userName, planName, paymentName, period string) *Subscription {
startTime, endTime := GetDuration(period)
func NewSubscription(owner, userName, planName, paymentName, period string) (*Subscription, error) {
startTime, endTime, err := getDuration(period)
if err != nil {
return nil, err
}
id := util.GenerateId()[:6]
return &Subscription{
res := &Subscription{
Owner: owner,
Name: "sub_" + id,
DisplayName: "New Subscription - " + id,
@ -121,6 +136,7 @@ func NewSubscription(owner, userName, planName, paymentName, period string) *Sub
Period: period,
State: SubStatePending, // waiting for payment complete
}
return res, nil
}
func GetSubscriptionCount(owner, field, value string) (int64, error) {

View File

@ -210,11 +210,12 @@ type User struct {
LastSigninWrongTime string `xorm:"varchar(100)" json:"lastSigninWrongTime"`
SigninWrongTimes int `json:"signinWrongTimes"`
ManagedAccounts []ManagedAccount `xorm:"managedAccounts blob" json:"managedAccounts"`
MfaAccounts []MfaAccount `xorm:"mfaAccounts blob" json:"mfaAccounts"`
MfaItems []*MfaItem `xorm:"varchar(300)" json:"mfaItems"`
NeedUpdatePassword bool `json:"needUpdatePassword"`
IpWhitelist string `xorm:"varchar(200)" json:"ipWhitelist"`
ManagedAccounts []ManagedAccount `xorm:"managedAccounts blob" json:"managedAccounts"`
MfaAccounts []MfaAccount `xorm:"mfaAccounts blob" json:"mfaAccounts"`
MfaItems []*MfaItem `xorm:"varchar(300)" json:"mfaItems"`
MfaRememberDeadline string `xorm:"varchar(100)" json:"mfaRememberDeadline"`
NeedUpdatePassword bool `json:"needUpdatePassword"`
IpWhitelist string `xorm:"varchar(200)" json:"ipWhitelist"`
}
type Userinfo struct {
@ -792,7 +793,7 @@ func UpdateUser(id string, user *User, columns []string, isAdmin bool) (bool, er
"eveonline", "fitbit", "gitea", "heroku", "influxcloud", "instagram", "intercom", "kakao", "lastfm", "mailru", "meetup",
"microsoftonline", "naver", "nextcloud", "onedrive", "oura", "patreon", "paypal", "salesforce", "shopify", "soundcloud",
"spotify", "strava", "stripe", "type", "tiktok", "tumblr", "twitch", "twitter", "typetalk", "uber", "vk", "wepay", "xero", "yahoo",
"yammer", "yandex", "zoom", "custom", "need_update_password", "ip_whitelist",
"yammer", "yandex", "zoom", "custom", "need_update_password", "ip_whitelist", "mfa_items", "mfa_remember_deadline",
}
}
if isAdmin {

View File

@ -66,6 +66,10 @@ func AutoSigninFilter(ctx *context.Context) {
responseError(ctx, err.Error())
return
}
if application == nil {
responseError(ctx, fmt.Sprintf("No application is found for userId: app/%s", token.Application))
return
}
setSessionUser(ctx, userId)
setSessionOidc(ctx, token.Scope, application.ClientId)

View File

@ -603,6 +603,16 @@ class OrganizationEditPage extends React.Component {
/>
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("application:MFA remember time"), i18next.t("application:MFA remember time - Tooltip"))} :
</Col>
<Col span={22} >
<InputNumber style={{width: "150px"}} value={this.state.organization.mfaRememberInHours} min={1} step={1} precision={0} addonAfter="Hours" onChange={value => {
this.updateOrganizationField("mfaRememberInHours", value);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("general:MFA items"), i18next.t("general:MFA items - Tooltip"))} :

View File

@ -25,6 +25,7 @@ import PopconfirmModal from "./common/modal/PopconfirmModal";
class OrganizationListPage extends BaseListPage {
newOrganization() {
const randomName = Setting.getRandomName();
const DefaultMfaRememberInHours = 12;
return {
owner: "admin", // this.props.account.organizationname,
name: `organization_${randomName}`,
@ -48,6 +49,7 @@ class OrganizationListPage extends BaseListPage {
enableSoftDeletion: false,
isProfilePublic: true,
enableTour: true,
mfaRememberInHours: DefaultMfaRememberInHours,
accountItems: [
{name: "Organization", visible: true, viewRule: "Public", modifyRule: "Admin"},
{name: "ID", visible: true, viewRule: "Public", modifyRule: "Immutable"},

View File

@ -166,7 +166,7 @@ class AuthCallback extends React.Component {
const responseType = this.getResponseType();
const handleLogin = (res) => {
if (responseType === "login") {
if (res.data2) {
if (res.data3) {
sessionStorage.setItem("signinUrl", signinUrl);
Setting.goToLinkSoft(this, `/forget/${applicationName}`);
return;
@ -176,7 +176,7 @@ class AuthCallback extends React.Component {
const link = Setting.getFromLink();
Setting.goToLink(link);
} else if (responseType === "code") {
if (res.data2) {
if (res.data3) {
sessionStorage.setItem("signinUrl", signinUrl);
Setting.goToLinkSoft(this, `/forget/${applicationName}`);
return;
@ -185,7 +185,7 @@ class AuthCallback extends React.Component {
Setting.goToLink(`${oAuthParams.redirectUri}${concatChar}code=${code}&state=${oAuthParams.state}`);
// Setting.showMessage("success", `Authorization code: ${res.data}`);
} else if (responseType === "token" || responseType === "id_token") {
if (res.data2) {
if (res.data3) {
sessionStorage.setItem("signinUrl", signinUrl);
Setting.goToLinkSoft(this, `/forget/${applicationName}`);
return;
@ -207,7 +207,7 @@ class AuthCallback extends React.Component {
relayState: oAuthParams.relayState,
});
} else {
if (res.data2.needUpdatePassword) {
if (res.data3) {
sessionStorage.setItem("signinUrl", signinUrl);
Setting.goToLinkSoft(this, `/forget/${applicationName}`);
return;

View File

@ -385,7 +385,7 @@ class ForgetPage extends React.Component {
},
]}
/>
<Popover placement="right" content={this.state.passwordPopover} open={this.state.passwordPopoverOpen}>
<Popover placement={window.innerWidth >= 960 ? "right" : "top"} content={this.state.passwordPopover} open={this.state.passwordPopoverOpen}>
<Form.Item
name="newPassword"
hidden={this.state.current !== 2}
@ -415,7 +415,7 @@ class ForgetPage extends React.Component {
}}
onFocus={() => {
this.setState({
passwordPopoverOpen: true,
passwordPopoverOpen: application.organizationObj.passwordOptions?.length > 0,
passwordPopover: PasswordChecker.renderPasswordPopover(application.organizationObj.passwordOptions, this.form.current?.getFieldValue("newPassword") ?? ""),
});
}}

View File

@ -496,9 +496,9 @@ class LoginPage extends React.Component {
const responseType = values["type"];
if (responseType === "login") {
if (res.data2) {
if (res.data3) {
sessionStorage.setItem("signinUrl", window.location.pathname + window.location.search);
Setting.goToLink(this, `/forget/${this.state.applicationName}`);
Setting.goToLinkSoft(this, `/forget/${this.state.applicationName}`);
}
Setting.showMessage("success", i18next.t("application:Logged in successfully"));
this.props.onLoginSuccess();
@ -510,9 +510,9 @@ class LoginPage extends React.Component {
userCodeStatus: "success",
});
} else if (responseType === "token" || responseType === "id_token") {
if (res.data2) {
if (res.data3) {
sessionStorage.setItem("signinUrl", window.location.pathname + window.location.search);
Setting.goToLink(this, `/forget/${this.state.applicationName}`);
Setting.goToLinkSoft(this, `/forget/${this.state.applicationName}`);
}
const amendatoryResponseType = responseType === "token" ? "access_token" : responseType;
const accessToken = res.data;
@ -522,9 +522,9 @@ class LoginPage extends React.Component {
this.props.onLoginSuccess(window.location.href);
return;
}
if (res.data2.needUpdatePassword) {
if (res.data3) {
sessionStorage.setItem("signinUrl", window.location.pathname + window.location.search);
Setting.goToLink(this, `/forget/${this.state.applicationName}`);
Setting.goToLinkSoft(this, `/forget/${this.state.applicationName}`);
}
if (res.data2.method === "POST") {
this.setState({

View File

@ -607,7 +607,7 @@ class SignupPage extends React.Component {
}
} else if (signupItem.name === "Password") {
return (
<Popover placement="right" content={this.state.passwordPopover} open={this.state.passwordPopoverOpen}>
<Popover placement={window.innerWidth >= 960 ? "right" : "top"} content={this.state.passwordPopover} open={this.state.passwordPopoverOpen}>
<Form.Item
name="password"
className="signup-password"
@ -635,7 +635,7 @@ class SignupPage extends React.Component {
}}
onFocus={() => {
this.setState({
passwordPopoverOpen: true,
passwordPopoverOpen: application.organizationObj.passwordOptions?.length > 0,
passwordPopover: PasswordChecker.renderPasswordPopover(application.organizationObj.passwordOptions, this.form.current?.getFieldValue("password") ?? ""),
});
}}

View File

@ -31,9 +31,9 @@ export function MfaAuthVerifyForm({formValues, authParams, mfaProps, application
const [mfaType, setMfaType] = useState(mfaProps.mfaType);
const [recoveryCode, setRecoveryCode] = useState("");
const verify = ({passcode}) => {
const verify = ({passcode, enableMfaRemember}) => {
setLoading(true);
const values = {...formValues, passcode};
const values = {...formValues, passcode, enableMfaRemember};
values["mfaType"] = mfaProps.mfaType;
const loginFunction = formValues.type === "cas" ? AuthBackend.loginCas : AuthBackend.login;
loginFunction(values, authParams).then((res) => {

View File

@ -1,5 +1,5 @@
import {UserOutlined} from "@ant-design/icons";
import {Button, Form, Input, Space} from "antd";
import {Button, Checkbox, Form, Input, Space} from "antd";
import i18next from "i18next";
import React, {useEffect} from "react";
import {CountryCodeSelect} from "../../common/select/CountryCodeSelect";
@ -12,6 +12,13 @@ export const MfaVerifySmsForm = ({mfaProps, application, onFinish, method, user}
const [dest, setDest] = React.useState("");
const [form] = Form.useForm();
const handleFinish = (values) => {
onFinish({
passcode: values.passcode,
enableMfaRemember: values.enableMfaRemember,
});
};
useEffect(() => {
if (method === mfaAuth) {
setDest(mfaProps.secret);
@ -51,9 +58,10 @@ export const MfaVerifySmsForm = ({mfaProps, application, onFinish, method, user}
<Form
form={form}
style={{width: "300px"}}
onFinish={onFinish}
onFinish={handleFinish}
initialValues={{
countryCode: mfaProps.countryCode,
enableMfaRemember: false,
}}
>
{isShowText() ?
@ -109,6 +117,14 @@ export const MfaVerifySmsForm = ({mfaProps, application, onFinish, method, user}
application={application}
/>
</Form.Item>
<Form.Item
name="enableMfaRemember"
valuePropName="checked"
>
<Checkbox>
{i18next.t("mfa:Remember this account for {hour} hours").replace("{hour}", mfaProps?.mfaRememberInHours)}
</Checkbox>
</Form.Item>
<Form.Item>
<Button
style={{marginTop: 24}}

View File

@ -1,5 +1,5 @@
import {CopyOutlined} from "@ant-design/icons";
import {Button, Col, Form, Input, QRCode, Space} from "antd";
import {Button, Checkbox, Col, Form, Input, QRCode, Space} from "antd";
import copy from "copy-to-clipboard";
import i18next from "i18next";
import React from "react";
@ -8,6 +8,13 @@ import * as Setting from "../../Setting";
export const MfaVerifyTotpForm = ({mfaProps, onFinish}) => {
const [form] = Form.useForm();
const handleFinish = (values) => {
onFinish({
passcode: values.passcode,
enableMfaRemember: values.enableMfaRemember,
});
};
const renderSecret = () => {
if (!mfaProps.secret) {
return null;
@ -40,7 +47,10 @@ export const MfaVerifyTotpForm = ({mfaProps, onFinish}) => {
<Form
form={form}
style={{width: "300px"}}
onFinish={onFinish}
onFinish={handleFinish}
initialValues={{
enableMfaRemember: false,
}}
>
{renderSecret()}
<Form.Item
@ -54,6 +64,14 @@ export const MfaVerifyTotpForm = ({mfaProps, onFinish}) => {
}}
/>
</Form.Item>
<Form.Item
name="enableMfaRemember"
valuePropName="checked"
>
<Checkbox>
{i18next.t("mfa:Remember this account for {hour} hours").replace("{hour}", mfaProps?.mfaRememberInHours)}
</Checkbox>
</Form.Item>
<Form.Item>
<Button
style={{marginTop: 24}}

View File

@ -132,18 +132,18 @@ export const PasswordModal = (props) => {
</Row>
) : null}
<Row style={{width: "100%", marginBottom: "20px"}}>
<Popover placement="right" content={passwordPopover} open={passwordPopoverOpen}>
<Popover placement={window.innerWidth >= 960 ? "right" : "top"} content={passwordPopover} open={passwordPopoverOpen}>
<Input.Password
addonBefore={i18next.t("user:New Password")}
placeholder={i18next.t("user:input password")}
onChange={(e) => {
handleNewPassword(e.target.value);
setPasswordPopoverOpen(true);
setPasswordPopoverOpen(passwordOptions?.length > 0);
setPasswordPopover(PasswordChecker.renderPasswordPopover(passwordOptions, e.target.value));
}}
onFocus={() => {
setPasswordPopoverOpen(true);
setPasswordPopoverOpen(passwordOptions?.length > 0);
setPasswordPopover(PasswordChecker.renderPasswordPopover(passwordOptions, newPassword));
}}
onBlur={() => {

View File

@ -82,6 +82,8 @@
"Left": "Left",
"Logged in successfully": "Logged in successfully",
"Logged out successfully": "Logged out successfully",
"MFA remember time": "MFA remember time",
"MFA remember time - Tooltip": "MFA remember time - Tooltip",
"Multiple Choices": "Multiple Choices",
"New Application": "New Application",
"No verification": "No verification",
@ -580,13 +582,13 @@
"Multi-factor recover": "Multi-factor recover",
"Multi-factor recover description": "Multi-factor recover description",
"Or copy the secret to your Authenticator App": "Or copy the secret to your Authenticator App",
"Passcode": "Passcode",
"Please bind your email first, the system will automatically uses the mail for multi-factor authentication": "Please bind your email first, the system will automatically uses the mail for multi-factor authentication",
"Please bind your phone first, the system automatically uses the phone for multi-factor authentication": "Please bind your phone first, the system automatically uses the phone for multi-factor authentication",
"Please confirm the information below": "Please confirm the information below",
"Please save this recovery code. Once your device cannot provide an authentication code, you can reset mfa authentication by this recovery code": "Please save this recovery code. Once your device cannot provide an authentication code, you can reset mfa authentication by this recovery code",
"Protect your account with Multi-factor authentication": "Protect your account with Multi-factor authentication",
"Recovery code": "Recovery code",
"Remember this account for {hour} hours": "Remember this account for {hour} hours",
"Scan the QR code with your Authenticator App": "Scan the QR code with your Authenticator App",
"Set preferred": "Set preferred",
"Setup": "Setup",

View File

@ -82,6 +82,8 @@
"Left": "Vlevo",
"Logged in successfully": "Úspěšně přihlášen",
"Logged out successfully": "Úspěšně odhlášen",
"MFA remember time": "MFA remember time",
"MFA remember time - Tooltip": "MFA remember time - Tooltip",
"Multiple Choices": "Multiple Choices",
"New Application": "Nová aplikace",
"No verification": "Bez ověření",
@ -580,13 +582,13 @@
"Multi-factor recover": "Obnovení dvoufaktorového ověřování",
"Multi-factor recover description": "Popis obnovení dvoufaktorového ověřování",
"Or copy the secret to your Authenticator App": "Nebo zkopírujte tajný kód do své aplikace Authenticator",
"Passcode": "Přístupový kód",
"Please bind your email first, the system will automatically uses the mail for multi-factor authentication": "Nejprve prosím spojte svůj email, systém automaticky použije tento email pro dvoufaktorové ověřování",
"Please bind your phone first, the system automatically uses the phone for multi-factor authentication": "Nejprve prosím spojte svůj telefon, systém automaticky použije tento telefon pro dvoufaktorové ověřování",
"Please confirm the information below": "Potvrďte prosím níže uvedené informace",
"Please save this recovery code. Once your device cannot provide an authentication code, you can reset mfa authentication by this recovery code": "Uložte si tento obnovovací kód. Pokud vaše zařízení nemůže poskytnout ověřovací kód, můžete resetovat dvoufaktorové ověřování pomocí tohoto kódu",
"Protect your account with Multi-factor authentication": "Chraňte svůj účet pomocí dvoufaktorového ověřování",
"Recovery code": "Obnovovací kód",
"Remember this account for {hour} hours": "Remember this account for {hour} hours",
"Scan the QR code with your Authenticator App": "Naskenujte QR kód pomocí aplikace Authenticator",
"Set preferred": "Nastavit jako preferované",
"Setup": "Nastavení",

View File

@ -82,6 +82,8 @@
"Left": "Links",
"Logged in successfully": "Erfolgreich eingeloggt",
"Logged out successfully": "Erfolgreich ausgeloggt",
"MFA remember time": "MFA remember time",
"MFA remember time - Tooltip": "MFA remember time - Tooltip",
"Multiple Choices": "Multiple Choices",
"New Application": "Neue Anwendung",
"No verification": "No verification",
@ -580,13 +582,13 @@
"Multi-factor recover": "Multi-factor recover",
"Multi-factor recover description": "Multi-factor recover description",
"Or copy the secret to your Authenticator App": "Or copy the secret to your Authenticator App",
"Passcode": "Passcode",
"Please bind your email first, the system will automatically uses the mail for multi-factor authentication": "Please bind your email first, the system will automatically uses the mail for multi-factor authentication",
"Please bind your phone first, the system automatically uses the phone for multi-factor authentication": "Please bind your phone first, the system automatically uses the phone for multi-factor authentication",
"Please confirm the information below": "Please confirm the information below",
"Please save this recovery code. Once your device cannot provide an authentication code, you can reset mfa authentication by this recovery code": "Please save this recovery code. Once your device cannot provide an authentication code, you can reset mfa authentication by this recovery code",
"Protect your account with Multi-factor authentication": "Protect your account with Multi-factor authentication",
"Recovery code": "Recovery code",
"Remember this account for {hour} hours": "Remember this account for {hour} hours",
"Scan the QR code with your Authenticator App": "Scan the QR code with your Authenticator App",
"Set preferred": "Set preferred",
"Setup": "Setup",

View File

@ -82,6 +82,8 @@
"Left": "Left",
"Logged in successfully": "Logged in successfully",
"Logged out successfully": "Logged out successfully",
"MFA remember time": "MFA remember time",
"MFA remember time - Tooltip": "Configures the duration that a account is remembered as trusted after a successful MFA login",
"Multiple Choices": "Multiple Choices",
"New Application": "New Application",
"No verification": "No verification",
@ -580,13 +582,13 @@
"Multi-factor recover": "Multi-factor recover",
"Multi-factor recover description": "Multi-factor recover description",
"Or copy the secret to your Authenticator App": "Or copy the secret to your Authenticator App",
"Passcode": "Passcode",
"Please bind your email first, the system will automatically uses the mail for multi-factor authentication": "Please bind your email first, the system will automatically uses the mail for multi-factor authentication",
"Please bind your phone first, the system automatically uses the phone for multi-factor authentication": "Please bind your phone first, the system automatically uses the phone for multi-factor authentication",
"Please confirm the information below": "Please confirm the information below",
"Please save this recovery code. Once your device cannot provide an authentication code, you can reset mfa authentication by this recovery code": "Please save this recovery code. Once your device cannot provide an authentication code, you can reset mfa authentication by this recovery code",
"Protect your account with Multi-factor authentication": "Protect your account with Multi-factor authentication",
"Recovery code": "Recovery code",
"Remember this account for {hour} hours": "Remember this account for {hour} hours",
"Scan the QR code with your Authenticator App": "Scan the QR code with your Authenticator App",
"Set preferred": "Set preferred",
"Setup": "Setup",

View File

@ -82,6 +82,8 @@
"Left": "Izquierda",
"Logged in successfully": "Acceso satisfactorio",
"Logged out successfully": "Cerró sesión exitosamente",
"MFA remember time": "MFA remember time",
"MFA remember time - Tooltip": "MFA remember time - Tooltip",
"Multiple Choices": "Multiple Choices",
"New Application": "Nueva aplicación",
"No verification": "No verification",
@ -580,13 +582,13 @@
"Multi-factor recover": "Multi-factor recover",
"Multi-factor recover description": "Multi-factor recover description",
"Or copy the secret to your Authenticator App": "Or copy the secret to your Authenticator App",
"Passcode": "Passcode",
"Please bind your email first, the system will automatically uses the mail for multi-factor authentication": "Please bind your email first, the system will automatically uses the mail for multi-factor authentication",
"Please bind your phone first, the system automatically uses the phone for multi-factor authentication": "Please bind your phone first, the system automatically uses the phone for multi-factor authentication",
"Please confirm the information below": "Please confirm the information below",
"Please save this recovery code. Once your device cannot provide an authentication code, you can reset mfa authentication by this recovery code": "Please save this recovery code. Once your device cannot provide an authentication code, you can reset mfa authentication by this recovery code",
"Protect your account with Multi-factor authentication": "Protect your account with Multi-factor authentication",
"Recovery code": "Recovery code",
"Remember this account for {hour} hours": "Remember this account for {hour} hours",
"Scan the QR code with your Authenticator App": "Scan the QR code with your Authenticator App",
"Set preferred": "Set preferred",
"Setup": "Setup",

View File

@ -82,6 +82,8 @@
"Left": "چپ",
"Logged in successfully": "با موفقیت وارد شدید",
"Logged out successfully": "با موفقیت خارج شدید",
"MFA remember time": "MFA remember time",
"MFA remember time - Tooltip": "MFA remember time - Tooltip",
"Multiple Choices": "انتخاب‌های متعدد",
"New Application": "برنامه جدید",
"No verification": "بدون تأیید",
@ -580,13 +582,13 @@
"Multi-factor recover": "بازیابی چندعاملی",
"Multi-factor recover description": "توضیح بازیابی چندعاملی",
"Or copy the secret to your Authenticator App": "یا راز را به برنامه تأیید هویت خود کپی کنید",
"Passcode": "کد عبور",
"Please bind your email first, the system will automatically uses the mail for multi-factor authentication": "لطفاً ابتدا ایمیل خود را متصل کنید، سیستم به‌طور خودکار از ایمیل برای احراز هویت چندعاملی استفاده می‌کند",
"Please bind your phone first, the system automatically uses the phone for multi-factor authentication": "لطفاً ابتدا تلفن خود را متصل کنید، سیستم به‌طور خودکار از تلفن برای احراز هویت چندعاملی استفاده می‌کند",
"Please confirm the information below": "لطفاً اطلاعات زیر را تأیید کنید",
"Please save this recovery code. Once your device cannot provide an authentication code, you can reset mfa authentication by this recovery code": "لطفاً این کد بازیابی را ذخیره کنید. هنگامی که دستگاه شما نتواند کد تأیید ارائه دهد، می‌توانید احراز هویت mfa را با این کد بازیابی تنظیم مجدد کنید",
"Protect your account with Multi-factor authentication": "حساب خود را با احراز هویت چندعاملی محافظت کنید",
"Recovery code": "کد بازیابی",
"Remember this account for {hour} hours": "Remember this account for {hour} hours",
"Scan the QR code with your Authenticator App": "کد QR را با برنامه تأیید هویت خود اسکن کنید",
"Set preferred": "تنظیم به‌عنوان مورد علاقه",
"Setup": "راه‌اندازی",

View File

@ -82,6 +82,8 @@
"Left": "Left",
"Logged in successfully": "Logged in successfully",
"Logged out successfully": "Logged out successfully",
"MFA remember time": "MFA remember time",
"MFA remember time - Tooltip": "MFA remember time - Tooltip",
"Multiple Choices": "Multiple Choices",
"New Application": "New Application",
"No verification": "No verification",
@ -580,13 +582,13 @@
"Multi-factor recover": "Multi-factor recover",
"Multi-factor recover description": "Multi-factor recover description",
"Or copy the secret to your Authenticator App": "Or copy the secret to your Authenticator App",
"Passcode": "Passcode",
"Please bind your email first, the system will automatically uses the mail for multi-factor authentication": "Please bind your email first, the system will automatically uses the mail for multi-factor authentication",
"Please bind your phone first, the system automatically uses the phone for multi-factor authentication": "Please bind your phone first, the system automatically uses the phone for multi-factor authentication",
"Please confirm the information below": "Please confirm the information below",
"Please save this recovery code. Once your device cannot provide an authentication code, you can reset mfa authentication by this recovery code": "Please save this recovery code. Once your device cannot provide an authentication code, you can reset mfa authentication by this recovery code",
"Protect your account with Multi-factor authentication": "Protect your account with Multi-factor authentication",
"Recovery code": "Recovery code",
"Remember this account for {hour} hours": "Remember this account for {hour} hours",
"Scan the QR code with your Authenticator App": "Scan the QR code with your Authenticator App",
"Set preferred": "Set preferred",
"Setup": "Setup",

View File

@ -82,6 +82,8 @@
"Left": "Gauche",
"Logged in successfully": "Connexion réussie",
"Logged out successfully": "Déconnexion réussie",
"MFA remember time": "MFA remember time",
"MFA remember time - Tooltip": "MFA remember time - Tooltip",
"Multiple Choices": "Multiple Choices",
"New Application": "Nouvelle application",
"No verification": "Aucune vérification",
@ -580,13 +582,13 @@
"Multi-factor recover": "Restauration de l'authentification multifacteur",
"Multi-factor recover description": "Description de la restauration de l'authentification multifacteur",
"Or copy the secret to your Authenticator App": "Ou copiez la clé secrète dans votre application d'authentification",
"Passcode": "Code d'accès",
"Please bind your email first, the system will automatically uses the mail for multi-factor authentication": "Veuillez lier votre e-mail en premier, le système l'utilisera automatiquement pour l'authentification multifacteur",
"Please bind your phone first, the system automatically uses the phone for multi-factor authentication": "Veuillez lier votre numéro de téléphone en premier, le système l'utilisera automatiquement pour l'authentification multifacteur",
"Please confirm the information below": "Veuillez confirmer les informations ci-dessous",
"Please save this recovery code. Once your device cannot provide an authentication code, you can reset mfa authentication by this recovery code": "Veuillez enregistrer ce code de récupération. Si votre appareil ne peut pas vous fournir un code d'authentification, vous pourrez réinitialiser l'authentification multifacteur avec ce code de récupération",
"Protect your account with Multi-factor authentication": "Protégez votre compte avec l'authentification multifacteur",
"Recovery code": "Code de récupération",
"Remember this account for {hour} hours": "Remember this account for {hour} hours",
"Scan the QR code with your Authenticator App": "Scannez le QR code avec votre application d'authentification",
"Set preferred": "Définir comme préféré",
"Setup": "Configurer",

View File

@ -82,6 +82,8 @@
"Left": "Left",
"Logged in successfully": "Logged in successfully",
"Logged out successfully": "Logged out successfully",
"MFA remember time": "MFA remember time",
"MFA remember time - Tooltip": "MFA remember time - Tooltip",
"Multiple Choices": "Multiple Choices",
"New Application": "New Application",
"No verification": "No verification",
@ -580,13 +582,13 @@
"Multi-factor recover": "Multi-factor recover",
"Multi-factor recover description": "Multi-factor recover description",
"Or copy the secret to your Authenticator App": "Or copy the secret to your Authenticator App",
"Passcode": "Passcode",
"Please bind your email first, the system will automatically uses the mail for multi-factor authentication": "Please bind your email first, the system will automatically uses the mail for multi-factor authentication",
"Please bind your phone first, the system automatically uses the phone for multi-factor authentication": "Please bind your phone first, the system automatically uses the phone for multi-factor authentication",
"Please confirm the information below": "Please confirm the information below",
"Please save this recovery code. Once your device cannot provide an authentication code, you can reset mfa authentication by this recovery code": "Please save this recovery code. Once your device cannot provide an authentication code, you can reset mfa authentication by this recovery code",
"Protect your account with Multi-factor authentication": "Protect your account with Multi-factor authentication",
"Recovery code": "Recovery code",
"Remember this account for {hour} hours": "Remember this account for {hour} hours",
"Scan the QR code with your Authenticator App": "Scan the QR code with your Authenticator App",
"Set preferred": "Set preferred",
"Setup": "Setup",

View File

@ -82,6 +82,8 @@
"Left": "Kiri",
"Logged in successfully": "Berhasil masuk",
"Logged out successfully": "Berhasil keluar dari sistem",
"MFA remember time": "MFA remember time",
"MFA remember time - Tooltip": "The duration for which the account is remembered as trusted after a successful MFA login",
"Multiple Choices": "Multiple Choices",
"New Application": "Aplikasi Baru",
"No verification": "No verification",
@ -580,13 +582,13 @@
"Multi-factor recover": "Multi-factor recover",
"Multi-factor recover description": "Multi-factor recover description",
"Or copy the secret to your Authenticator App": "Or copy the secret to your Authenticator App",
"Passcode": "Passcode",
"Please bind your email first, the system will automatically uses the mail for multi-factor authentication": "Please bind your email first, the system will automatically uses the mail for multi-factor authentication",
"Please bind your phone first, the system automatically uses the phone for multi-factor authentication": "Please bind your phone first, the system automatically uses the phone for multi-factor authentication",
"Please confirm the information below": "Please confirm the information below",
"Please save this recovery code. Once your device cannot provide an authentication code, you can reset mfa authentication by this recovery code": "Please save this recovery code. Once your device cannot provide an authentication code, you can reset mfa authentication by this recovery code",
"Protect your account with Multi-factor authentication": "Protect your account with Multi-factor authentication",
"Recovery code": "Recovery code",
"Remember this account for {hour} hours": "Remember this account for {hour} hours",
"Scan the QR code with your Authenticator App": "Scan the QR code with your Authenticator App",
"Set preferred": "Set preferred",
"Setup": "Setup",

View File

@ -82,6 +82,8 @@
"Left": "Left",
"Logged in successfully": "Logged in successfully",
"Logged out successfully": "Logged out successfully",
"MFA remember time": "MFA remember time",
"MFA remember time - Tooltip": "MFA remember time - Tooltip",
"Multiple Choices": "Multiple Choices",
"New Application": "New Application",
"No verification": "No verification",
@ -580,13 +582,13 @@
"Multi-factor recover": "Multi-factor recover",
"Multi-factor recover description": "Multi-factor recover description",
"Or copy the secret to your Authenticator App": "Or copy the secret to your Authenticator App",
"Passcode": "Passcode",
"Please bind your email first, the system will automatically uses the mail for multi-factor authentication": "Please bind your email first, the system will automatically uses the mail for multi-factor authentication",
"Please bind your phone first, the system automatically uses the phone for multi-factor authentication": "Please bind your phone first, the system automatically uses the phone for multi-factor authentication",
"Please confirm the information below": "Please confirm the information below",
"Please save this recovery code. Once your device cannot provide an authentication code, you can reset mfa authentication by this recovery code": "Please save this recovery code. Once your device cannot provide an authentication code, you can reset mfa authentication by this recovery code",
"Protect your account with Multi-factor authentication": "Protect your account with Multi-factor authentication",
"Recovery code": "Recovery code",
"Remember this account for {hour} hours": "Remember this account for {hour} hours",
"Scan the QR code with your Authenticator App": "Scan the QR code with your Authenticator App",
"Set preferred": "Set preferred",
"Setup": "Setup",

View File

@ -82,6 +82,8 @@
"Left": "左",
"Logged in successfully": "正常にログインしました",
"Logged out successfully": "正常にログアウトしました",
"MFA remember time": "MFA remember time",
"MFA remember time - Tooltip": "MFA remember time - Tooltip",
"Multiple Choices": "Multiple Choices",
"New Application": "新しいアプリケーション",
"No verification": "No verification",
@ -580,13 +582,13 @@
"Multi-factor recover": "Multi-factor recover",
"Multi-factor recover description": "Multi-factor recover description",
"Or copy the secret to your Authenticator App": "Or copy the secret to your Authenticator App",
"Passcode": "Passcode",
"Please bind your email first, the system will automatically uses the mail for multi-factor authentication": "Please bind your email first, the system will automatically uses the mail for multi-factor authentication",
"Please bind your phone first, the system automatically uses the phone for multi-factor authentication": "Please bind your phone first, the system automatically uses the phone for multi-factor authentication",
"Please confirm the information below": "Please confirm the information below",
"Please save this recovery code. Once your device cannot provide an authentication code, you can reset mfa authentication by this recovery code": "Please save this recovery code. Once your device cannot provide an authentication code, you can reset mfa authentication by this recovery code",
"Protect your account with Multi-factor authentication": "Protect your account with Multi-factor authentication",
"Recovery code": "Recovery code",
"Remember this account for {hour} hours": "Remember this account for {hour} hours",
"Scan the QR code with your Authenticator App": "Scan the QR code with your Authenticator App",
"Set preferred": "Set preferred",
"Setup": "Setup",

View File

@ -82,6 +82,8 @@
"Left": "Left",
"Logged in successfully": "Logged in successfully",
"Logged out successfully": "Logged out successfully",
"MFA remember time": "MFA remember time",
"MFA remember time - Tooltip": "MFA remember time - Tooltip",
"Multiple Choices": "Multiple Choices",
"New Application": "New Application",
"No verification": "No verification",
@ -580,13 +582,13 @@
"Multi-factor recover": "Multi-factor recover",
"Multi-factor recover description": "Multi-factor recover description",
"Or copy the secret to your Authenticator App": "Or copy the secret to your Authenticator App",
"Passcode": "Passcode",
"Please bind your email first, the system will automatically uses the mail for multi-factor authentication": "Please bind your email first, the system will automatically uses the mail for multi-factor authentication",
"Please bind your phone first, the system automatically uses the phone for multi-factor authentication": "Please bind your phone first, the system automatically uses the phone for multi-factor authentication",
"Please confirm the information below": "Please confirm the information below",
"Please save this recovery code. Once your device cannot provide an authentication code, you can reset mfa authentication by this recovery code": "Please save this recovery code. Once your device cannot provide an authentication code, you can reset mfa authentication by this recovery code",
"Protect your account with Multi-factor authentication": "Protect your account with Multi-factor authentication",
"Recovery code": "Recovery code",
"Remember this account for {hour} hours": "Remember this account for {hour} hours",
"Scan the QR code with your Authenticator App": "Scan the QR code with your Authenticator App",
"Set preferred": "Set preferred",
"Setup": "Setup",

View File

@ -82,6 +82,8 @@
"Left": "왼쪽",
"Logged in successfully": "성공적으로 로그인했습니다",
"Logged out successfully": "로그아웃이 성공적으로 되었습니다",
"MFA remember time": "MFA remember time",
"MFA remember time - Tooltip": "MFA remember time - Tooltip",
"Multiple Choices": "Multiple Choices",
"New Application": "새로운 응용 프로그램",
"No verification": "No verification",
@ -580,13 +582,13 @@
"Multi-factor recover": "Multi-factor recover",
"Multi-factor recover description": "Multi-factor recover description",
"Or copy the secret to your Authenticator App": "Or copy the secret to your Authenticator App",
"Passcode": "Passcode",
"Please bind your email first, the system will automatically uses the mail for multi-factor authentication": "Please bind your email first, the system will automatically uses the mail for multi-factor authentication",
"Please bind your phone first, the system automatically uses the phone for multi-factor authentication": "Please bind your phone first, the system automatically uses the phone for multi-factor authentication",
"Please confirm the information below": "Please confirm the information below",
"Please save this recovery code. Once your device cannot provide an authentication code, you can reset mfa authentication by this recovery code": "Please save this recovery code. Once your device cannot provide an authentication code, you can reset mfa authentication by this recovery code",
"Protect your account with Multi-factor authentication": "Protect your account with Multi-factor authentication",
"Recovery code": "Recovery code",
"Remember this account for {hour} hours": "Remember this account for {hour} hours",
"Scan the QR code with your Authenticator App": "Scan the QR code with your Authenticator App",
"Set preferred": "Set preferred",
"Setup": "Setup",

View File

@ -82,6 +82,8 @@
"Left": "Left",
"Logged in successfully": "Logged in successfully",
"Logged out successfully": "Logged out successfully",
"MFA remember time": "MFA remember time",
"MFA remember time - Tooltip": "MFA remember time - Tooltip",
"Multiple Choices": "Multiple Choices",
"New Application": "New Application",
"No verification": "No verification",
@ -580,13 +582,13 @@
"Multi-factor recover": "Multi-factor recover",
"Multi-factor recover description": "Multi-factor recover description",
"Or copy the secret to your Authenticator App": "Or copy the secret to your Authenticator App",
"Passcode": "Passcode",
"Please bind your email first, the system will automatically uses the mail for multi-factor authentication": "Please bind your email first, the system will automatically uses the mail for multi-factor authentication",
"Please bind your phone first, the system automatically uses the phone for multi-factor authentication": "Please bind your phone first, the system automatically uses the phone for multi-factor authentication",
"Please confirm the information below": "Please confirm the information below",
"Please save this recovery code. Once your device cannot provide an authentication code, you can reset mfa authentication by this recovery code": "Please save this recovery code. Once your device cannot provide an authentication code, you can reset mfa authentication by this recovery code",
"Protect your account with Multi-factor authentication": "Protect your account with Multi-factor authentication",
"Recovery code": "Recovery code",
"Remember this account for {hour} hours": "Remember this account for {hour} hours",
"Scan the QR code with your Authenticator App": "Scan the QR code with your Authenticator App",
"Set preferred": "Set preferred",
"Setup": "Setup",

View File

@ -82,6 +82,8 @@
"Left": "Left",
"Logged in successfully": "Logged in successfully",
"Logged out successfully": "Logged out successfully",
"MFA remember time": "MFA remember time",
"MFA remember time - Tooltip": "MFA remember time - Tooltip",
"Multiple Choices": "Multiple Choices",
"New Application": "New Application",
"No verification": "No verification",
@ -580,13 +582,13 @@
"Multi-factor recover": "Multi-factor recover",
"Multi-factor recover description": "Multi-factor recover description",
"Or copy the secret to your Authenticator App": "Or copy the secret to your Authenticator App",
"Passcode": "Passcode",
"Please bind your email first, the system will automatically uses the mail for multi-factor authentication": "Please bind your email first, the system will automatically uses the mail for multi-factor authentication",
"Please bind your phone first, the system automatically uses the phone for multi-factor authentication": "Please bind your phone first, the system automatically uses the phone for multi-factor authentication",
"Please confirm the information below": "Please confirm the information below",
"Please save this recovery code. Once your device cannot provide an authentication code, you can reset mfa authentication by this recovery code": "Please save this recovery code. Once your device cannot provide an authentication code, you can reset mfa authentication by this recovery code",
"Protect your account with Multi-factor authentication": "Protect your account with Multi-factor authentication",
"Recovery code": "Recovery code",
"Remember this account for {hour} hours": "Remember this account for {hour} hours",
"Scan the QR code with your Authenticator App": "Scan the QR code with your Authenticator App",
"Set preferred": "Set preferred",
"Setup": "Setup",

View File

@ -82,6 +82,8 @@
"Left": "Left",
"Logged in successfully": "Logged in successfully",
"Logged out successfully": "Logged out successfully",
"MFA remember time": "MFA remember time",
"MFA remember time - Tooltip": "MFA remember time - Tooltip",
"Multiple Choices": "Multiple Choices",
"New Application": "New Application",
"No verification": "No verification",
@ -580,13 +582,13 @@
"Multi-factor recover": "Multi-factor recover",
"Multi-factor recover description": "Multi-factor recover description",
"Or copy the secret to your Authenticator App": "Or copy the secret to your Authenticator App",
"Passcode": "Passcode",
"Please bind your email first, the system will automatically uses the mail for multi-factor authentication": "Please bind your email first, the system will automatically uses the mail for multi-factor authentication",
"Please bind your phone first, the system automatically uses the phone for multi-factor authentication": "Please bind your phone first, the system automatically uses the phone for multi-factor authentication",
"Please confirm the information below": "Please confirm the information below",
"Please save this recovery code. Once your device cannot provide an authentication code, you can reset mfa authentication by this recovery code": "Please save this recovery code. Once your device cannot provide an authentication code, you can reset mfa authentication by this recovery code",
"Protect your account with Multi-factor authentication": "Protect your account with Multi-factor authentication",
"Recovery code": "Recovery code",
"Remember this account for {hour} hours": "Remember this account for {hour} hours",
"Scan the QR code with your Authenticator App": "Scan the QR code with your Authenticator App",
"Set preferred": "Set preferred",
"Setup": "Setup",

View File

@ -82,6 +82,8 @@
"Left": "Esquerda",
"Logged in successfully": "Login realizado com sucesso",
"Logged out successfully": "Logout realizado com sucesso",
"MFA remember time": "MFA remember time",
"MFA remember time - Tooltip": "MFA remember time - Tooltip",
"Multiple Choices": "Multiple Choices",
"New Application": "Nova Aplicação",
"No verification": "Sem verificação",
@ -580,13 +582,13 @@
"Multi-factor recover": "Multi-factor recover",
"Multi-factor recover description": "Multi-factor recover description",
"Or copy the secret to your Authenticator App": "Or copy the secret to your Authenticator App",
"Passcode": "Código de acesso",
"Please bind your email first, the system will automatically uses the mail for multi-factor authentication": "Please bind your email first, the system will automatically uses the mail for multi-factor authentication",
"Please bind your phone first, the system automatically uses the phone for multi-factor authentication": "Please bind your phone first, the system automatically uses the phone for multi-factor authentication",
"Please confirm the information below": "Please confirm the information below",
"Please save this recovery code. Once your device cannot provide an authentication code, you can reset mfa authentication by this recovery code": "Please save this recovery code. Once your device cannot provide an authentication code, you can reset mfa authentication by this recovery code",
"Protect your account with Multi-factor authentication": "Protect your account with Multi-factor authentication",
"Recovery code": "Recovery code",
"Remember this account for {hour} hours": "Remember this account for {hour} hours",
"Scan the QR code with your Authenticator App": "Scan the QR code with your Authenticator App",
"Set preferred": "Set preferred",
"Setup": "Setup",

View File

@ -82,6 +82,8 @@
"Left": "Левый",
"Logged in successfully": "Успешный вход в систему",
"Logged out successfully": "Успешный выход из системы",
"MFA remember time": "MFA remember time",
"MFA remember time - Tooltip": "MFA remember time - Tooltip",
"Multiple Choices": "Multiple Choices",
"New Application": "Новое приложение",
"No verification": "Нет верификации",
@ -580,13 +582,13 @@
"Multi-factor recover": "Multi-factor recover",
"Multi-factor recover description": "Multi-factor recover description",
"Or copy the secret to your Authenticator App": "Or copy the secret to your Authenticator App",
"Passcode": "Passcode",
"Please bind your email first, the system will automatically uses the mail for multi-factor authentication": "Please bind your email first, the system will automatically uses the mail for multi-factor authentication",
"Please bind your phone first, the system automatically uses the phone for multi-factor authentication": "Please bind your phone first, the system automatically uses the phone for multi-factor authentication",
"Please confirm the information below": "Please confirm the information below",
"Please save this recovery code. Once your device cannot provide an authentication code, you can reset mfa authentication by this recovery code": "Please save this recovery code. Once your device cannot provide an authentication code, you can reset mfa authentication by this recovery code",
"Protect your account with Multi-factor authentication": "Protect your account with Multi-factor authentication",
"Recovery code": "Recovery code",
"Remember this account for {hour} hours": "Remember this account for {hour} hours",
"Scan the QR code with your Authenticator App": "Scan the QR code with your Authenticator App",
"Set preferred": "Set preferred",
"Setup": "Setup",

View File

@ -82,6 +82,8 @@
"Left": "Vľavo",
"Logged in successfully": "Úspešne prihlásený",
"Logged out successfully": "Úspešne odhlásený",
"MFA remember time": "MFA remember time",
"MFA remember time - Tooltip": "MFA remember time - Tooltip",
"Multiple Choices": "Multiple Choices",
"New Application": "Nová aplikácia",
"No verification": "Bez overenia",
@ -580,13 +582,13 @@
"Multi-factor recover": "Obnova viacfaktorovej autentifikácie",
"Multi-factor recover description": "Popis obnovy viacfaktorovej autentifikácie",
"Or copy the secret to your Authenticator App": "Alebo skopírujte tajomstvo do svojej aplikácie na autentifikáciu",
"Passcode": "Kód",
"Please bind your email first, the system will automatically uses the mail for multi-factor authentication": "Najskôr pripojte svoj email, systém automaticky použije mail na viacfaktorovú autentifikáciu",
"Please bind your phone first, the system automatically uses the phone for multi-factor authentication": "Najskôr pripojte svoj telefón, systém automaticky použije telefón na viacfaktorovú autentifikáciu",
"Please confirm the information below": "Potvrďte informácie nižšie",
"Please save this recovery code. Once your device cannot provide an authentication code, you can reset mfa authentication by this recovery code": "Uložte si tento obnovovací kód. Keď vaše zariadenie nebude schopné poskytnúť overovací kód, môžete obnoviť MFA autentifikáciu pomocou tohto obnovovacieho kódu",
"Protect your account with Multi-factor authentication": "Chráňte svoj účet pomocou viacfaktorovej autentifikácie",
"Recovery code": "Obnovovací kód",
"Remember this account for {hour} hours": "Remember this account for {hour} hours",
"Scan the QR code with your Authenticator App": "Naskenujte QR kód pomocou svojej aplikácie na autentifikáciu",
"Set preferred": "Nastaviť ako preferované",
"Setup": "Nastaviť",

View File

@ -82,6 +82,8 @@
"Left": "Left",
"Logged in successfully": "Logged in successfully",
"Logged out successfully": "Logged out successfully",
"MFA remember time": "MFA remember time",
"MFA remember time - Tooltip": "MFA remember time - Tooltip",
"Multiple Choices": "Multiple Choices",
"New Application": "New Application",
"No verification": "No verification",
@ -580,13 +582,13 @@
"Multi-factor recover": "Multi-factor recover",
"Multi-factor recover description": "Multi-factor recover description",
"Or copy the secret to your Authenticator App": "Or copy the secret to your Authenticator App",
"Passcode": "Passcode",
"Please bind your email first, the system will automatically uses the mail for multi-factor authentication": "Please bind your email first, the system will automatically uses the mail for multi-factor authentication",
"Please bind your phone first, the system automatically uses the phone for multi-factor authentication": "Please bind your phone first, the system automatically uses the phone for multi-factor authentication",
"Please confirm the information below": "Please confirm the information below",
"Please save this recovery code. Once your device cannot provide an authentication code, you can reset mfa authentication by this recovery code": "Please save this recovery code. Once your device cannot provide an authentication code, you can reset mfa authentication by this recovery code",
"Protect your account with Multi-factor authentication": "Protect your account with Multi-factor authentication",
"Recovery code": "Recovery code",
"Remember this account for {hour} hours": "Remember this account for {hour} hours",
"Scan the QR code with your Authenticator App": "Scan the QR code with your Authenticator App",
"Set preferred": "Set preferred",
"Setup": "Setup",

View File

@ -82,6 +82,8 @@
"Left": "Sol",
"Logged in successfully": "Başarıyla giriş yapıldı",
"Logged out successfully": "Başarıyla çıkış yapıldı",
"MFA remember time": "MFA remember time",
"MFA remember time - Tooltip": "MFA remember time - Tooltip",
"Multiple Choices": "Multiple Choices",
"New Application": "New Application",
"No verification": "No verification",
@ -580,13 +582,13 @@
"Multi-factor recover": "Multi-factor recover",
"Multi-factor recover description": "Multi-factor recover description",
"Or copy the secret to your Authenticator App": "Veya kod 'u Authenticator uygulamasından kopyalayın",
"Passcode": "Parola",
"Please bind your email first, the system will automatically uses the mail for multi-factor authentication": "Please bind your email first, the system will automatically uses the mail for multi-factor authentication",
"Please bind your phone first, the system automatically uses the phone for multi-factor authentication": "Please bind your phone first, the system automatically uses the phone for multi-factor authentication",
"Please confirm the information below": "Lütfen aşağıdaki bilgileri doğrulayın",
"Please save this recovery code. Once your device cannot provide an authentication code, you can reset mfa authentication by this recovery code": "Lütfen bu kurtarma kodlarını kaydedin. Cihazınızdan yetkilendirme kodları oluşturamazsanız bu kodları kullanarak sorunu çözebilirsiniz",
"Protect your account with Multi-factor authentication": "Protect your account with Multi-factor authentication",
"Recovery code": "Kurtarma kodu",
"Remember this account for {hour} hours": "Remember this account for {hour} hours",
"Scan the QR code with your Authenticator App": "Bu QR kodunu kimlik doğrulama uygulamanızla tarayın",
"Set preferred": "Set preferred",
"Setup": "Setup",

View File

@ -82,6 +82,8 @@
"Left": "Ліворуч",
"Logged in successfully": "Успішно ввійшли",
"Logged out successfully": "Успішно вийшов",
"MFA remember time": "MFA remember time",
"MFA remember time - Tooltip": "MFA remember time - Tooltip",
"Multiple Choices": "Multiple Choices",
"New Application": "Нова заявка",
"No verification": "Без підтвердження",
@ -580,13 +582,13 @@
"Multi-factor recover": "Багатофакторне відновлення",
"Multi-factor recover description": "Опис багатофакторного відновлення",
"Or copy the secret to your Authenticator App": "Або скопіюйте секрет у програму Authenticator",
"Passcode": "Пароль",
"Please bind your email first, the system will automatically uses the mail for multi-factor authentication": "Спочатку прив’яжіть свою електронну адресу, система автоматично використовуватиме її для багатофакторної автентифікації",
"Please bind your phone first, the system automatically uses the phone for multi-factor authentication": "Спочатку прив’яжіть свій телефон, система автоматично використовує телефон для багатофакторної автентифікації",
"Please confirm the information below": "Підтвердьте інформацію нижче",
"Please save this recovery code. Once your device cannot provide an authentication code, you can reset mfa authentication by this recovery code": "Будь ласка, збережіть цей код відновлення. ",
"Protect your account with Multi-factor authentication": "Захистіть свій обліковий запис за допомогою багатофакторної автентифікації",
"Recovery code": "Код відновлення",
"Remember this account for {hour} hours": "Remember this account for {hour} hours",
"Scan the QR code with your Authenticator App": "Відскануйте QR-код за допомогою програми Authenticator",
"Set preferred": "Встановити перевагу",
"Setup": "Налаштування",

View File

@ -82,6 +82,8 @@
"Left": "Trái",
"Logged in successfully": "Đăng nhập thành công",
"Logged out successfully": "Đã đăng xuất thành công",
"MFA remember time": "MFA remember time",
"MFA remember time - Tooltip": "MFA remember time - Tooltip",
"Multiple Choices": "Multiple Choices",
"New Application": "Ứng dụng mới",
"No verification": "Không xác minh",
@ -580,13 +582,13 @@
"Multi-factor recover": "Multi-factor recover",
"Multi-factor recover description": "Multi-factor recover description",
"Or copy the secret to your Authenticator App": "Or copy the secret to your Authenticator App",
"Passcode": "Passcode",
"Please bind your email first, the system will automatically uses the mail for multi-factor authentication": "Please bind your email first, the system will automatically uses the mail for multi-factor authentication",
"Please bind your phone first, the system automatically uses the phone for multi-factor authentication": "Please bind your phone first, the system automatically uses the phone for multi-factor authentication",
"Please confirm the information below": "Please confirm the information below",
"Please save this recovery code. Once your device cannot provide an authentication code, you can reset mfa authentication by this recovery code": "Please save this recovery code. Once your device cannot provide an authentication code, you can reset mfa authentication by this recovery code",
"Protect your account with Multi-factor authentication": "Protect your account with Multi-factor authentication",
"Recovery code": "Recovery code",
"Remember this account for {hour} hours": "Remember this account for {hour} hours",
"Scan the QR code with your Authenticator App": "Scan the QR code with your Authenticator App",
"Set preferred": "Set preferred",
"Setup": "Setup",

View File

@ -82,6 +82,8 @@
"Left": "居左",
"Logged in successfully": "登录成功",
"Logged out successfully": "登出成功",
"MFA remember time": "MFA记住时间",
"MFA remember time - Tooltip": "配置MFA登录成功后帐户被记住为受信任的持续时间",
"Multiple Choices": "多选",
"New Application": "添加应用",
"No verification": "不校验",
@ -580,13 +582,13 @@
"Multi-factor recover": "重置多因素认证",
"Multi-factor recover description": "如果您无法访问您的设备,输入您的多因素认证恢复代码来确认您的身份",
"Or copy the secret to your Authenticator App": "或者将这个密钥复制到你的身份验证应用中",
"Passcode": "认证码",
"Please bind your email first, the system will automatically uses the mail for multi-factor authentication": "请先绑定邮箱,之后会自动使用该邮箱作为多因素认证的方式",
"Please bind your phone first, the system automatically uses the phone for multi-factor authentication": "请先绑定手机号,之后会自动使用该手机号作为多因素认证的方式",
"Please confirm the information below": "请确认以下信息",
"Please save this recovery code. Once your device cannot provide an authentication code, you can reset mfa authentication by this recovery code": "请保存此恢复代码。一旦您的设备无法提供身份验证码,您可以通过此恢复码重置多因素认证",
"Protect your account with Multi-factor authentication": "通过多因素认证保护您的帐户",
"Recovery code": "恢复码",
"Remember this account for {hour} hours": "记住这个账户 {hour} 小时",
"Scan the QR code with your Authenticator App": "用你的身份验证应用扫描二维码",
"Set preferred": "设为首选",
"Setup": "设置",