mirror of
https://github.com/casdoor/casdoor.git
synced 2025-05-23 10:45:47 +08:00
feat: allow user use other mfaType in mfa step and skip redundant MFA verification (#3499)
* feat: allow user use other mfaType in mfa step and skip redundant MFA verification * feat: improve format
This commit is contained in:
parent
4b0a2fdbfc
commit
e1b3b0ac6a
@ -331,6 +331,8 @@ func (c *ApiController) Login() {
|
||||
return
|
||||
}
|
||||
|
||||
verificationType := ""
|
||||
|
||||
if authForm.Username != "" {
|
||||
if authForm.Type == ResponseTypeLogin {
|
||||
if c.GetSessionUsername() != "" {
|
||||
@ -425,6 +427,12 @@ func (c *ApiController) Login() {
|
||||
c.ResponseError(err.Error(), nil)
|
||||
return
|
||||
}
|
||||
|
||||
if verificationCodeType == object.VerifyTypePhone {
|
||||
verificationType = "sms"
|
||||
} else {
|
||||
verificationType = "email"
|
||||
}
|
||||
} else {
|
||||
var application *object.Application
|
||||
application, err = object.GetApplication(fmt.Sprintf("admin/%s", authForm.Application))
|
||||
@ -524,8 +532,19 @@ func (c *ApiController) Login() {
|
||||
|
||||
if user.IsMfaEnabled() {
|
||||
c.setMfaUserSession(user.GetId())
|
||||
c.ResponseOk(object.NextMfa, user.GetPreferredMfaProps(true))
|
||||
return
|
||||
mfaList := object.GetAllMfaProps(user, true)
|
||||
mfaAllowList := []*object.MfaProps{}
|
||||
for _, prop := range mfaList {
|
||||
if prop.MfaType == verificationType || !prop.Enabled {
|
||||
continue
|
||||
}
|
||||
mfaAllowList = append(mfaAllowList, prop)
|
||||
}
|
||||
if len(mfaAllowList) >= 1 {
|
||||
c.SetSession("verificationCodeType", verificationType)
|
||||
c.ResponseOk(object.NextMfa, mfaAllowList)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
resp = c.HandleLoggedIn(application, user, &authForm)
|
||||
@ -866,8 +885,12 @@ func (c *ApiController) Login() {
|
||||
}
|
||||
|
||||
if authForm.Passcode != "" {
|
||||
if authForm.MfaType == c.GetSession("verificationCodeType") {
|
||||
c.ResponseError("Invalid multi-factor authentication type")
|
||||
return
|
||||
}
|
||||
user.CountryCode = user.GetCountryCode(user.CountryCode)
|
||||
mfaUtil := object.GetMfaUtil(authForm.MfaType, user.GetPreferredMfaProps(false))
|
||||
mfaUtil := object.GetMfaUtil(authForm.MfaType, user.GetMfaProps(authForm.MfaType, false))
|
||||
if mfaUtil == nil {
|
||||
c.ResponseError("Invalid multi-factor authentication type")
|
||||
return
|
||||
@ -878,6 +901,7 @@ func (c *ApiController) Login() {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
c.SetSession("verificationCodeType", "")
|
||||
} else if authForm.RecoveryCode != "" {
|
||||
err = object.MfaRecover(user, authForm.RecoveryCode)
|
||||
if err != nil {
|
||||
|
@ -37,6 +37,7 @@ import RedirectForm from "../common/RedirectForm";
|
||||
import {MfaAuthVerifyForm, NextMfa, RequiredMfa} from "./mfa/MfaAuthVerifyForm";
|
||||
import {GoogleOneTapLoginVirtualButton} from "./GoogleLoginButton";
|
||||
import * as ProviderButton from "./ProviderButton";
|
||||
import {EmailMfaType, SmsMfaType, TotpMfaType} from "./MfaSetupPage";
|
||||
const FaceRecognitionModal = lazy(() => import("../common/modal/FaceRecognitionModal"));
|
||||
|
||||
class LoginPage extends React.Component {
|
||||
@ -440,19 +441,12 @@ class LoginPage extends React.Component {
|
||||
if (res.status === "ok") {
|
||||
if (res.data === NextMfa) {
|
||||
this.setState({
|
||||
getVerifyTotp: () => {
|
||||
return (
|
||||
<MfaAuthVerifyForm
|
||||
mfaProps={res.data2}
|
||||
formValues={values}
|
||||
authParams={casParams}
|
||||
application={this.getApplicationObj()}
|
||||
onFail={(errorMessage) => {
|
||||
Setting.showMessage("error", errorMessage);
|
||||
}}
|
||||
onSuccess={(res) => loginHandler(res)}
|
||||
/>);
|
||||
},
|
||||
mfaProps: res.data2,
|
||||
selectedMfaProp: this.getPreferredMfaProp(res.data2),
|
||||
}, () => {
|
||||
this.setState({
|
||||
getVerifyTotp: () => this.renderMfaAuthVerifyForm(values, casParams, loginHandler),
|
||||
});
|
||||
});
|
||||
} else {
|
||||
loginHandler(res);
|
||||
@ -513,19 +507,12 @@ class LoginPage extends React.Component {
|
||||
if (res.status === "ok") {
|
||||
if (res.data === NextMfa) {
|
||||
this.setState({
|
||||
getVerifyTotp: () => {
|
||||
return (
|
||||
<MfaAuthVerifyForm
|
||||
mfaProps={res.data2}
|
||||
formValues={values}
|
||||
authParams={oAuthParams}
|
||||
application={this.getApplicationObj()}
|
||||
onFail={(errorMessage) => {
|
||||
Setting.showMessage("error", errorMessage);
|
||||
}}
|
||||
onSuccess={(res) => loginHandler(res)}
|
||||
/>);
|
||||
},
|
||||
mfaProps: res.data2,
|
||||
selectedMfaProp: this.getPreferredMfaProp(res.data2),
|
||||
}, () => {
|
||||
this.setState({
|
||||
getVerifyTotp: () => this.renderMfaAuthVerifyForm(values, oAuthParams, loginHandler),
|
||||
});
|
||||
});
|
||||
} else if (res.data === "SelectPlan") {
|
||||
// paid-user does not have active or pending subscription, go to application default pricing page to select-plan
|
||||
@ -545,6 +532,49 @@ class LoginPage extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
renderMfaAuthVerifyForm(values, authParams, onSuccess) {
|
||||
return (
|
||||
<div>
|
||||
<MfaAuthVerifyForm
|
||||
mfaProps={this.state.selectedMfaProp}
|
||||
formValues={values}
|
||||
authParams={authParams}
|
||||
application={this.getApplicationObj()}
|
||||
onFail={(errorMessage) => {
|
||||
Setting.showMessage("error", errorMessage);
|
||||
}}
|
||||
onSuccess={(res) => onSuccess(res)}
|
||||
/>
|
||||
<div>
|
||||
{
|
||||
this.state.mfaProps.map((mfa) => {
|
||||
if (this.state.selectedMfaProp.mfaType === mfa.mfaType) {return null;}
|
||||
let mfaI18n = "";
|
||||
switch (mfa.mfaType) {
|
||||
case SmsMfaType: mfaI18n = i18next.t("mfa:Use SMS"); break;
|
||||
case TotpMfaType: mfaI18n = i18next.t("mfa:Use Authenticator App"); break ;
|
||||
case EmailMfaType: mfaI18n = i18next.t("mfa:Use Email") ;break;
|
||||
}
|
||||
return <div key={mfa.mfaType}><Button type={"link"} onClick={() => {
|
||||
this.setState({
|
||||
selectedMfaProp: mfa,
|
||||
});
|
||||
}}>{mfaI18n}</Button></div>;
|
||||
})
|
||||
}
|
||||
</div>
|
||||
</div>);
|
||||
}
|
||||
|
||||
getPreferredMfaProp(mfaProps) {
|
||||
for (const i in mfaProps) {
|
||||
if (mfaProps[i].isPreffered) {
|
||||
return mfaProps[i];
|
||||
}
|
||||
}
|
||||
return mfaProps[0];
|
||||
}
|
||||
|
||||
isProviderVisible(providerItem) {
|
||||
if (this.state.mode === "signup") {
|
||||
return Setting.isProviderVisibleForSignUp(providerItem);
|
||||
|
@ -33,7 +33,8 @@ export function MfaAuthVerifyForm({formValues, authParams, mfaProps, application
|
||||
|
||||
const verify = ({passcode}) => {
|
||||
setLoading(true);
|
||||
const values = {...formValues, passcode, mfaType};
|
||||
const values = {...formValues, passcode};
|
||||
values["mfaType"] = mfaProps.mfaType;
|
||||
const loginFunction = formValues.type === "cas" ? AuthBackend.loginCas : AuthBackend.login;
|
||||
loginFunction(values, authParams).then((res) => {
|
||||
if (res.status === "ok") {
|
||||
@ -71,7 +72,7 @@ export function MfaAuthVerifyForm({formValues, authParams, mfaProps, application
|
||||
<div style={{marginBottom: 24, textAlign: "center", fontSize: "24px"}}>
|
||||
{i18next.t("mfa:Multi-factor authentication")}
|
||||
</div>
|
||||
{mfaType === SmsMfaType || mfaType === EmailMfaType ? (
|
||||
{mfaProps.mfaType === SmsMfaType || mfaProps.mfaType === EmailMfaType ? (
|
||||
<Fragment>
|
||||
<div style={{marginBottom: 24}}>
|
||||
{i18next.t("mfa:You have enabled Multi-Factor Authentication, Please click 'Send Code' to continue")}
|
||||
|
Loading…
x
Reference in New Issue
Block a user