fix: check if pw login allowed (#8584)

# Which Problems Are Solved

When checking for the next step for the login UI and a user did not yet
have an IdP linked, they would always be presented the password check
screen, even if the local authentication was disabled.

# How the Problems Are Solved

- Correctly check the login policy for the `Allow Username Password`
option
- In case the user has no IdP linked yet, fallback to the organizations
configuration (and redirect if possible)
- the user can be auto-linked based on the username / email after
successfully authenticating at the IdP

# Additional Changes

None

# Additional Context

- closes https://github.com/zitadel/zitadel/issues/5106
- closes https://github.com/zitadel/zitadel/issues/7502
This commit is contained in:
Livio Spring
2024-09-10 12:55:32 +02:00
committed by GitHub
parent ccf222f0f6
commit 650c21f18a
2 changed files with 141 additions and 26 deletions

View File

@@ -1060,8 +1060,12 @@ func (repo *AuthRequestRepo) nextSteps(ctx context.Context, request *domain.Auth
if err != nil {
return nil, err
}
if (!isInternalLogin || len(idps.Links) > 0) && len(request.LinkingUsers) == 0 {
step := repo.idpChecked(request, idps.Links, userSession)
noLocalAuth := request.LoginPolicy != nil && !request.LoginPolicy.AllowUsernamePassword
if (!isInternalLogin || len(idps.Links) > 0 || noLocalAuth) && len(request.LinkingUsers) == 0 {
step, err := repo.idpChecked(request, idps.Links, userSession)
if err != nil {
return nil, err
}
if step != nil {
return append(steps, step), nil
}
@@ -1272,20 +1276,29 @@ func (repo *AuthRequestRepo) firstFactorChecked(ctx context.Context, request *do
return &domain.PasswordStep{}
}
func (repo *AuthRequestRepo) idpChecked(request *domain.AuthRequest, idps []*query.IDPUserLink, userSession *user_model.UserSessionView) domain.NextStep {
func (repo *AuthRequestRepo) idpChecked(request *domain.AuthRequest, idps []*query.IDPUserLink, userSession *user_model.UserSessionView) (domain.NextStep, error) {
if checkVerificationTimeMaxAge(userSession.ExternalLoginVerification, request.LoginPolicy.ExternalLoginCheckLifetime, request) {
request.IDPLoginChecked = true
request.AuthTime = userSession.ExternalLoginVerification
return nil
return nil, nil
}
selectedIDPConfigID := request.SelectedIDPConfigID
if selectedIDPConfigID == "" {
selectedIDPConfigID = userSession.SelectedIDPConfigID
// use the explicitly set IdP first
if request.SelectedIDPConfigID != "" {
return &domain.ExternalLoginStep{SelectedIDPConfigID: request.SelectedIDPConfigID}, nil
}
if selectedIDPConfigID == "" && len(idps) > 0 {
selectedIDPConfigID = idps[0].IDPID
// reuse the previously used IdP from the session
if userSession.SelectedIDPConfigID != "" {
return &domain.ExternalLoginStep{SelectedIDPConfigID: userSession.SelectedIDPConfigID}, nil
}
return &domain.ExternalLoginStep{SelectedIDPConfigID: selectedIDPConfigID}
// then use an existing linked IdP of the user
if len(idps) > 0 {
return &domain.ExternalLoginStep{SelectedIDPConfigID: idps[0].IDPID}, nil
}
// if the user did not link one, then just use one of the configured IdPs of the org
if len(request.AllowedExternalIDPs) > 0 {
return &domain.ExternalLoginStep{SelectedIDPConfigID: request.AllowedExternalIDPs[0].IDPConfigID}, nil
}
return nil, zerrors.ThrowPreconditionFailed(nil, "LOGIN-5Hm8s", "Errors.Org.IdpNotExisting")
}
func (repo *AuthRequestRepo) mfaChecked(userSession *user_model.UserSessionView, request *domain.AuthRequest, user *user_model.UserView, isInternalAuthentication bool) (domain.NextStep, bool, error) {