From 410a53f15b029e6b5e1c8d80dd6ee2db4a3f727f Mon Sep 17 00:00:00 2001 From: Livio Amstutz Date: Fri, 18 Dec 2020 13:42:21 +0100 Subject: [PATCH] fix: enable login with password when passwordless set up (#1120) * fix: enable login with password when passwordless set up * enable only it allowed --- .../eventsourcing/eventstore/auth_request.go | 22 ++++++++------ .../eventstore/auth_request_test.go | 29 +++++++++++++++++++ .../handler/passwordless_login_handler.go | 29 ++++++++++++++++--- internal/ui/login/static/i18n/de.yaml | 1 + internal/ui/login/static/i18n/en.yaml | 1 + .../login/static/templates/passwordless.html | 3 ++ 6 files changed, 72 insertions(+), 13 deletions(-) diff --git a/internal/auth/repository/eventsourcing/eventstore/auth_request.go b/internal/auth/repository/eventsourcing/eventstore/auth_request.go index bc38d00575..92ef6aada6 100644 --- a/internal/auth/repository/eventsourcing/eventstore/auth_request.go +++ b/internal/auth/repository/eventsourcing/eventstore/auth_request.go @@ -643,24 +643,28 @@ func (repo *AuthRequestRepo) firstFactorChecked(request *model.AuthRequest, user return &model.InitUserStep{PasswordSet: user.PasswordSet} } + var step model.NextStep if request.LoginPolicy.PasswordlessType != iam_model.PasswordlessTypeNotAllowed && user.IsPasswordlessReady() { - if !checkVerificationTime(userSession.PasswordlessVerification, repo.MultiFactorCheckLifeTime) { - return &model.PasswordlessStep{} + if checkVerificationTime(userSession.PasswordlessVerification, repo.MultiFactorCheckLifeTime) { + request.AuthTime = userSession.PasswordlessVerification + return nil } - request.AuthTime = userSession.PasswordlessVerification - return nil + step = &model.PasswordlessStep{} } if !user.PasswordSet { return &model.InitPasswordStep{} } - if !checkVerificationTime(userSession.PasswordVerification, repo.PasswordCheckLifeTime) { - return &model.PasswordStep{} + if checkVerificationTime(userSession.PasswordVerification, repo.PasswordCheckLifeTime) { + request.PasswordVerified = true + request.AuthTime = userSession.PasswordVerification + return nil } - request.PasswordVerified = true - request.AuthTime = userSession.PasswordVerification - 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) { diff --git a/internal/auth/repository/eventsourcing/eventstore/auth_request_test.go b/internal/auth/repository/eventsourcing/eventstore/auth_request_test.go index 67fef8b588..f6085c1ebf 100644 --- a/internal/auth/repository/eventsourcing/eventstore/auth_request_test.go +++ b/internal/auth/repository/eventsourcing/eventstore/auth_request_test.go @@ -611,6 +611,35 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) { []model.NextStep{&model.RedirectToCallbackStep{}}, 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", fields{ diff --git a/internal/ui/login/handler/passwordless_login_handler.go b/internal/ui/login/handler/passwordless_login_handler.go index ee0ae68579..7523e3d1f6 100644 --- a/internal/ui/login/handler/passwordless_login_handler.go +++ b/internal/ui/login/handler/passwordless_login_handler.go @@ -13,6 +13,16 @@ const ( 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) { var errType, errMessage, credentialData string var webAuthNLogin *user_model.WebAuthNLogin @@ -26,20 +36,31 @@ func (l *Login) renderPasswordlessVerification(w http.ResponseWriter, r *http.Re if webAuthNLogin != nil { credentialData = base64.RawURLEncoding.EncodeToString(webAuthNLogin.CredentialAssertionData) } - data := &webAuthNData{ - userData: l.getUserData(r, authReq, "Login Passwordless", errType, errMessage), - CredentialCreationData: credentialData, + var passwordLogin bool + if authReq.LoginPolicy != nil { + passwordLogin = authReq.LoginPolicy.AllowUsernamePassword + } + data := &passwordlessData{ + webAuthNData{ + userData: l.getUserData(r, authReq, "Login Passwordless", errType, errMessage), + CredentialCreationData: credentialData, + }, + passwordLogin, } l.renderer.RenderTemplate(w, r, l.renderer.Templates[tmplPasswordlessVerification], data, nil) } func (l *Login) handlePasswordlessVerification(w http.ResponseWriter, r *http.Request) { - formData := new(webAuthNFormData) + formData := new(passwordlessFormData) authReq, err := l.getAuthRequestAndParseData(r, formData) if err != nil { l.renderError(w, r, authReq, err) return } + if formData.PasswordLogin { + l.renderPassword(w, r, authReq, nil) + return + } credData, err := base64.URLEncoding.DecodeString(formData.CredentialData) if err != nil { l.renderPasswordlessVerification(w, r, authReq, err) diff --git a/internal/ui/login/static/i18n/de.yaml b/internal/ui/login/static/i18n/de.yaml index 8a4fe747fd..fc86aaa0ad 100644 --- a/internal/ui/login/static/i18n/de.yaml +++ b/internal/ui/login/static/i18n/de.yaml @@ -209,6 +209,7 @@ Actions: RegisterToken: Token registrieren ValidateToken: Token validieren Recreate: erneut erstellen + PasswordLogin: Mit Passwort anmelden Errors: Internal: Es ist ein interner Fehler aufgetreten diff --git a/internal/ui/login/static/i18n/en.yaml b/internal/ui/login/static/i18n/en.yaml index 67793d2045..73ae3d217a 100644 --- a/internal/ui/login/static/i18n/en.yaml +++ b/internal/ui/login/static/i18n/en.yaml @@ -209,6 +209,7 @@ Actions: RegisterToken: Register Token ValidateToken: Validate Token Recreate: recreate + PasswordLogin: Login with password Errors: Internal: An internal error occured diff --git a/internal/ui/login/static/templates/passwordless.html b/internal/ui/login/static/templates/passwordless.html index 120889be6d..70276780e2 100644 --- a/internal/ui/login/static/templates/passwordless.html +++ b/internal/ui/login/static/templates/passwordless.html @@ -26,6 +26,9 @@ {{ template "error-message" .}}
+ {{if .PasswordLogin}} + + {{end}}