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}
}
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) {

View File

@ -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{

View File

@ -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)

View File

@ -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

View File

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

View File

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