mirror of
https://github.com/casdoor/casdoor.git
synced 2025-05-23 02:35:49 +08:00

* feat: add two-factor authentication interface and api * merge * feat: add Two-factor authentication accountItem and two-factor api in frontend * feat: add basic 2fa setup UI * rebase * feat: finish the two-factor authentication * rebase * feat: support recover code * chore: fix eslint error * feat: support multiple sms account * fix: client application login * fix: lint * Update authz.go * Update mfa.go * fix: support phone * fix: i18n * fix: i18n * fix: support preferred mfa methods --------- Co-authored-by: hsluoyz <hsluoyz@qq.com>
108 lines
2.6 KiB
Go
108 lines
2.6 KiB
Go
// Copyright 2023 The Casdoor Authors. All Rights Reserved.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package object
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/casdoor/casdoor/util"
|
|
|
|
"github.com/beego/beego/context"
|
|
)
|
|
|
|
type MfaSessionData struct {
|
|
UserId string
|
|
}
|
|
|
|
type MfaProps struct {
|
|
Id string `json:"id"`
|
|
IsPreferred bool `json:"isPreferred"`
|
|
AuthType string `json:"type" form:"type"`
|
|
Secret string `json:"secret,omitempty"`
|
|
CountryCode string `json:"countryCode,omitempty"`
|
|
URL string `json:"url,omitempty"`
|
|
RecoveryCodes []string `json:"recoveryCodes,omitempty"`
|
|
}
|
|
|
|
type MfaInterface interface {
|
|
SetupVerify(ctx *context.Context, passCode string) error
|
|
Verify(passCode string) error
|
|
Initiate(ctx *context.Context, name1 string, name2 string) (*MfaProps, error)
|
|
Enable(ctx *context.Context, user *User) error
|
|
}
|
|
|
|
const (
|
|
SmsType = "sms"
|
|
TotpType = "app"
|
|
)
|
|
|
|
const (
|
|
MfaSessionUserId = "MfaSessionUserId"
|
|
NextMfa = "NextMfa"
|
|
)
|
|
|
|
func GetMfaUtil(providerType string, config *MfaProps) MfaInterface {
|
|
switch providerType {
|
|
case SmsType:
|
|
return NewSmsTwoFactor(config)
|
|
case TotpType:
|
|
return nil
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func RecoverTfs(user *User, recoveryCode string) error {
|
|
hit := false
|
|
|
|
twoFactor := user.GetPreferMfa(false)
|
|
if len(twoFactor.RecoveryCodes) == 0 {
|
|
return fmt.Errorf("do not have recovery codes")
|
|
}
|
|
|
|
for _, code := range twoFactor.RecoveryCodes {
|
|
if code == recoveryCode {
|
|
hit = true
|
|
break
|
|
}
|
|
}
|
|
if !hit {
|
|
return fmt.Errorf("recovery code not found")
|
|
}
|
|
|
|
affected := UpdateUser(user.GetId(), user, []string{"two_factor_auth"}, user.IsAdminUser())
|
|
if !affected {
|
|
return fmt.Errorf("")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func GetMaskedProps(props *MfaProps) *MfaProps {
|
|
maskedProps := &MfaProps{
|
|
AuthType: props.AuthType,
|
|
Id: props.Id,
|
|
IsPreferred: props.IsPreferred,
|
|
}
|
|
|
|
if props.AuthType == SmsType {
|
|
if !util.IsEmailValid(props.Secret) {
|
|
maskedProps.Secret = util.GetMaskedPhone(props.Secret)
|
|
} else {
|
|
maskedProps.Secret = util.GetMaskedEmail(props.Secret)
|
|
}
|
|
}
|
|
return maskedProps
|
|
}
|