fix: enable login with password when passwordless set up (#1120)

* fix: enable login with password when passwordless set up

* enable only it allowed
This commit is contained in:
Livio Amstutz 2020-12-18 13:42:21 +01:00 committed by GitHub
parent 40ced9154e
commit 410a53f15b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 72 additions and 13 deletions

View File

@ -643,24 +643,28 @@ func (repo *AuthRequestRepo) firstFactorChecked(request *model.AuthRequest, user
return &model.InitUserStep{PasswordSet: user.PasswordSet} return &model.InitUserStep{PasswordSet: user.PasswordSet}
} }
var step model.NextStep
if request.LoginPolicy.PasswordlessType != iam_model.PasswordlessTypeNotAllowed && user.IsPasswordlessReady() { if request.LoginPolicy.PasswordlessType != iam_model.PasswordlessTypeNotAllowed && user.IsPasswordlessReady() {
if !checkVerificationTime(userSession.PasswordlessVerification, repo.MultiFactorCheckLifeTime) { if checkVerificationTime(userSession.PasswordlessVerification, repo.MultiFactorCheckLifeTime) {
return &model.PasswordlessStep{}
}
request.AuthTime = userSession.PasswordlessVerification request.AuthTime = userSession.PasswordlessVerification
return nil return nil
} }
step = &model.PasswordlessStep{}
}
if !user.PasswordSet { if !user.PasswordSet {
return &model.InitPasswordStep{} return &model.InitPasswordStep{}
} }
if !checkVerificationTime(userSession.PasswordVerification, repo.PasswordCheckLifeTime) { if checkVerificationTime(userSession.PasswordVerification, repo.PasswordCheckLifeTime) {
return &model.PasswordStep{}
}
request.PasswordVerified = true request.PasswordVerified = true
request.AuthTime = userSession.PasswordVerification request.AuthTime = userSession.PasswordVerification
return nil return nil
}
if step != nil {
return step
}
return &model.PasswordStep{}
} }
func (repo *AuthRequestRepo) mfaChecked(userSession *user_model.UserSessionView, request *model.AuthRequest, user *user_model.UserView) (model.NextStep, bool, error) { func (repo *AuthRequestRepo) mfaChecked(userSession *user_model.UserSessionView, request *model.AuthRequest, user *user_model.UserView) (model.NextStep, bool, error) {

View File

@ -611,6 +611,35 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
[]model.NextStep{&model.RedirectToCallbackStep{}}, []model.NextStep{&model.RedirectToCallbackStep{}},
nil, nil,
}, },
{
"password verified, passwordless set up, mfa not verified, mfa check step",
fields{
userSessionViewProvider: &mockViewUserSession{
PasswordVerification: time.Now().UTC().Add(-5 * time.Minute),
},
userViewProvider: &mockViewUser{
PasswordSet: true,
PasswordlessTokens: user_view_model.WebAuthNTokens{&user_view_model.WebAuthNView{ID: "id", State: int32(user_model.MFAStateReady)}},
OTPState: int32(user_model.MFAStateReady),
MFAMaxSetUp: int32(model.MFALevelMultiFactor),
},
userEventProvider: &mockEventUser{},
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
PasswordCheckLifeTime: 10 * 24 * time.Hour,
SecondFactorCheckLifeTime: 18 * time.Hour,
},
args{
&model.AuthRequest{
UserID: "UserID",
LoginPolicy: &iam_model.LoginPolicyView{
SecondFactors: []iam_model.SecondFactorType{iam_model.SecondFactorTypeOTP},
},
}, false},
[]model.NextStep{&model.MFAVerificationStep{
MFAProviders: []model.MFAType{model.MFATypeOTP},
}},
nil,
},
{ {
"mfa not verified, mfa check step", "mfa not verified, mfa check step",
fields{ fields{

View File

@ -13,6 +13,16 @@ const (
tmplPasswordlessVerification = "passwordlessverification" tmplPasswordlessVerification = "passwordlessverification"
) )
type passwordlessData struct {
webAuthNData
PasswordLogin bool
}
type passwordlessFormData struct {
webAuthNFormData
PasswordLogin bool `schema:"passwordlogin"`
}
func (l *Login) renderPasswordlessVerification(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, err error) { func (l *Login) renderPasswordlessVerification(w http.ResponseWriter, r *http.Request, authReq *model.AuthRequest, err error) {
var errType, errMessage, credentialData string var errType, errMessage, credentialData string
var webAuthNLogin *user_model.WebAuthNLogin var webAuthNLogin *user_model.WebAuthNLogin
@ -26,20 +36,31 @@ func (l *Login) renderPasswordlessVerification(w http.ResponseWriter, r *http.Re
if webAuthNLogin != nil { if webAuthNLogin != nil {
credentialData = base64.RawURLEncoding.EncodeToString(webAuthNLogin.CredentialAssertionData) credentialData = base64.RawURLEncoding.EncodeToString(webAuthNLogin.CredentialAssertionData)
} }
data := &webAuthNData{ var passwordLogin bool
if authReq.LoginPolicy != nil {
passwordLogin = authReq.LoginPolicy.AllowUsernamePassword
}
data := &passwordlessData{
webAuthNData{
userData: l.getUserData(r, authReq, "Login Passwordless", errType, errMessage), userData: l.getUserData(r, authReq, "Login Passwordless", errType, errMessage),
CredentialCreationData: credentialData, CredentialCreationData: credentialData,
},
passwordLogin,
} }
l.renderer.RenderTemplate(w, r, l.renderer.Templates[tmplPasswordlessVerification], data, nil) l.renderer.RenderTemplate(w, r, l.renderer.Templates[tmplPasswordlessVerification], data, nil)
} }
func (l *Login) handlePasswordlessVerification(w http.ResponseWriter, r *http.Request) { func (l *Login) handlePasswordlessVerification(w http.ResponseWriter, r *http.Request) {
formData := new(webAuthNFormData) formData := new(passwordlessFormData)
authReq, err := l.getAuthRequestAndParseData(r, formData) authReq, err := l.getAuthRequestAndParseData(r, formData)
if err != nil { if err != nil {
l.renderError(w, r, authReq, err) l.renderError(w, r, authReq, err)
return return
} }
if formData.PasswordLogin {
l.renderPassword(w, r, authReq, nil)
return
}
credData, err := base64.URLEncoding.DecodeString(formData.CredentialData) credData, err := base64.URLEncoding.DecodeString(formData.CredentialData)
if err != nil { if err != nil {
l.renderPasswordlessVerification(w, r, authReq, err) l.renderPasswordlessVerification(w, r, authReq, err)

View File

@ -209,6 +209,7 @@ Actions:
RegisterToken: Token registrieren RegisterToken: Token registrieren
ValidateToken: Token validieren ValidateToken: Token validieren
Recreate: erneut erstellen Recreate: erneut erstellen
PasswordLogin: Mit Passwort anmelden
Errors: Errors:
Internal: Es ist ein interner Fehler aufgetreten Internal: Es ist ein interner Fehler aufgetreten

View File

@ -209,6 +209,7 @@ Actions:
RegisterToken: Register Token RegisterToken: Register Token
ValidateToken: Validate Token ValidateToken: Validate Token
Recreate: recreate Recreate: recreate
PasswordLogin: Login with password
Errors: Errors:
Internal: An internal error occured Internal: An internal error occured

View File

@ -26,6 +26,9 @@
{{ template "error-message" .}} {{ template "error-message" .}}
<div class="actions"> <div class="actions">
{{if .PasswordLogin}}
<button class="secondary" name="passwordlogin" value="true" type="submit">{{t "Actions.PasswordLogin"}}</button>
{{end}}
<a href="{{ loginNameChangeUrl .AuthReqID }}"> <a href="{{ loginNameChangeUrl .AuthReqID }}">
<button class="secondary" type="button">{{t "Actions.Back"}}</button> <button class="secondary" type="button">{{t "Actions.Back"}}</button>
</a> </a>