diff --git a/cmd/zitadel/setup.yaml b/cmd/zitadel/setup.yaml
index 79611a5022..67910af8a5 100644
--- a/cmd/zitadel/setup.yaml
+++ b/cmd/zitadel/setup.yaml
@@ -98,4 +98,6 @@ SetUp:
Step7:
DefaultSecondFactor: 1 #SecondFactorTypeOTP
Step8:
- DefaultSecondFactor: 2 #SecondFactorTypeU2F
\ No newline at end of file
+ DefaultSecondFactor: 2 #SecondFactorTypeU2F
+ Step9:
+ Passwordless: true
\ No newline at end of file
diff --git a/console/src/app/modules/changes/changes.component.ts b/console/src/app/modules/changes/changes.component.ts
index eeee5de055..58001fd7ae 100644
--- a/console/src/app/modules/changes/changes.component.ts
+++ b/console/src/app/modules/changes/changes.component.ts
@@ -42,7 +42,6 @@ export class ChangesComponent implements OnInit, OnDestroy {
this.init();
if (this.refresh) {
this.refresh.pipe(takeUntil(this.destroyed$), debounceTime(2000)).subscribe(() => {
- console.log('asdf');
this.init();
});
}
diff --git a/console/src/app/modules/policies/login-policy/login-policy.component.html b/console/src/app/modules/policies/login-policy/login-policy.component.html
index f76da7bc9c..e88404bf39 100644
--- a/console/src/app/modules/policies/login-policy/login-policy.component.html
+++ b/console/src/app/modules/policies/login-policy/login-policy.component.html
@@ -53,7 +53,7 @@
- {{'MFA.TYPE' | translate}}
+ {{'LOGINPOLICY.PASSWORDLESS' | translate}}
{{'LOGINPOLICY.PASSWORDLESSTYPE.'+pt | translate}}
diff --git a/console/src/app/pages/users/user-detail/auth-user-detail/auth-passwordless/auth-passwordless.component.ts b/console/src/app/pages/users/user-detail/auth-user-detail/auth-passwordless/auth-passwordless.component.ts
index 2bde75405e..eefdb0efe1 100644
--- a/console/src/app/pages/users/user-detail/auth-user-detail/auth-passwordless/auth-passwordless.component.ts
+++ b/console/src/app/pages/users/user-detail/auth-user-detail/auth-passwordless/auth-passwordless.component.ts
@@ -58,6 +58,13 @@ export class AuthPasswordlessComponent implements OnInit, OnDestroy {
if (credOptions.publicKey?.challenge) {
credOptions.publicKey.challenge = _base64ToArrayBuffer(credOptions.publicKey.challenge as any);
credOptions.publicKey.user.id = _base64ToArrayBuffer(credOptions.publicKey.user.id as any);
+ if (credOptions.publicKey.excludeCredentials) {
+ credOptions.publicKey.excludeCredentials.map(cred => {
+ cred.id = _base64ToArrayBuffer(cred.id as any);
+ return cred;
+ });
+ }
+ console.log(credOptions);
const dialogRef = this.dialog.open(DialogU2FComponent, {
width: '400px',
data: {
diff --git a/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-mfa/auth-user-mfa.component.ts b/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-mfa/auth-user-mfa.component.ts
index a8d8d7f935..2926ce8e22 100644
--- a/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-mfa/auth-user-mfa.component.ts
+++ b/console/src/app/pages/users/user-detail/auth-user-detail/auth-user-mfa/auth-user-mfa.component.ts
@@ -83,8 +83,12 @@ export class AuthUserMfaComponent implements OnInit, OnDestroy {
credOptions.publicKey.challenge = _base64ToArrayBuffer(credOptions.publicKey.challenge as any);
credOptions.publicKey.user.id = _base64ToArrayBuffer(credOptions.publicKey.user.id as any);
if (credOptions.publicKey.excludeCredentials) {
- credOptions.publicKey.excludeCredentials.map(cred => cred.id = _base64ToArrayBuffer(cred.id as any));
+ credOptions.publicKey.excludeCredentials.map(cred => {
+ cred.id = _base64ToArrayBuffer(cred.id as any);
+ return cred;
+ });
}
+ console.log(credOptions);
const dialogRef = this.dialog.open(DialogU2FComponent, {
width: '400px',
data: {
diff --git a/console/src/app/pages/users/user-detail/auth-user-detail/dialog-u2f/dialog-u2f.component.html b/console/src/app/pages/users/user-detail/auth-user-detail/dialog-u2f/dialog-u2f.component.html
index b649faae0c..fa9a136f1e 100644
--- a/console/src/app/pages/users/user-detail/auth-user-detail/dialog-u2f/dialog-u2f.component.html
+++ b/console/src/app/pages/users/user-detail/auth-user-detail/dialog-u2f/dialog-u2f.component.html
@@ -4,7 +4,7 @@
{{'USER.MFA.U2F_NAME' | translate}}
-
+
@@ -13,6 +13,7 @@
{{'ACTIONS.CLOSE' | translate}}
- {{'ACTIONS.VERIFY' | translate}}
+ {{'ACTIONS.VERIFY' | translate}}
-
+
\ No newline at end of file
diff --git a/console/src/app/pages/users/user-detail/auth-user-detail/edit-dialog/edit-dialog.component.html b/console/src/app/pages/users/user-detail/auth-user-detail/edit-dialog/edit-dialog.component.html
index 37056e74fe..81ded0f7ef 100644
--- a/console/src/app/pages/users/user-detail/auth-user-detail/edit-dialog/edit-dialog.component.html
+++ b/console/src/app/pages/users/user-detail/auth-user-detail/edit-dialog/edit-dialog.component.html
@@ -5,7 +5,7 @@
{{data.labelKey | translate }}
-
+
diff --git a/console/src/assets/i18n/de.json b/console/src/assets/i18n/de.json
index 41254071f1..7f12d7b93f 100644
--- a/console/src/assets/i18n/de.json
+++ b/console/src/assets/i18n/de.json
@@ -909,6 +909,7 @@
"DESCRIPTION":"Sie können vordefinierte oder selbsterstellten Provider auswählen",
"SELECTIDPS":"Identity Provider"
},
+ "PASSWORDLESS":"Passwordloser Login",
"PASSWORDLESSTYPE": {
"0":"Nicht erlaubt",
"1":"Erlaubt"
diff --git a/console/src/assets/i18n/en.json b/console/src/assets/i18n/en.json
index dd79f04a8a..ee90a3b6c8 100644
--- a/console/src/assets/i18n/en.json
+++ b/console/src/assets/i18n/en.json
@@ -909,6 +909,7 @@
"DESCRIPTION":"You can select predefined or selfcreated providers for authentication.",
"SELECTIDPS":"Identity providers"
},
+ "PASSWORDLESS":"Passwordless Login",
"PASSWORDLESSTYPE": {
"0":"Not allowed",
"1":"Allowed"
diff --git a/internal/auth/repository/eventsourcing/eventstore/auth_request.go b/internal/auth/repository/eventsourcing/eventstore/auth_request.go
index 0c456b1b5e..bc38d00575 100644
--- a/internal/auth/repository/eventsourcing/eventstore/auth_request.go
+++ b/internal/auth/repository/eventsourcing/eventstore/auth_request.go
@@ -643,7 +643,7 @@ func (repo *AuthRequestRepo) firstFactorChecked(request *model.AuthRequest, user
return &model.InitUserStep{PasswordSet: user.PasswordSet}
}
- if user.IsPasswordlessReady() {
+ if request.LoginPolicy.PasswordlessType != iam_model.PasswordlessTypeNotAllowed && user.IsPasswordlessReady() {
if !checkVerificationTime(userSession.PasswordlessVerification, repo.MultiFactorCheckLifeTime) {
return &model.PasswordlessStep{}
}
@@ -815,9 +815,8 @@ func userSessionByIDs(ctx context.Context, provider userSessionViewProvider, eve
case es_model.UserRemoved:
return nil, errors.ThrowPreconditionFailed(nil, "EVENT-dG2fe", "Errors.User.NotActive")
}
- if err := sessionCopy.AppendEvent(event); err != nil {
- return user_view_model.UserSessionToModel(&sessionCopy), nil
- }
+ err := sessionCopy.AppendEvent(event)
+ logging.Log("EVENT-qbhj3").OnError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Warn("error appending event")
}
return user_view_model.UserSessionToModel(&sessionCopy), nil
}
diff --git a/internal/auth/repository/eventsourcing/eventstore/auth_request_test.go b/internal/auth/repository/eventsourcing/eventstore/auth_request_test.go
index 32883f97f7..67fef8b588 100644
--- a/internal/auth/repository/eventsourcing/eventstore/auth_request_test.go
+++ b/internal/auth/repository/eventsourcing/eventstore/auth_request_test.go
@@ -430,7 +430,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
userEventProvider: &mockEventUser{},
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
},
- args{&model.AuthRequest{UserID: "UserID"}, false},
+ args{&model.AuthRequest{UserID: "UserID", LoginPolicy: &iam_model.LoginPolicyView{}}, false},
[]model.NextStep{&model.PasswordStep{}},
nil,
},
@@ -475,7 +475,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
MultiFactorCheckLifeTime: 10 * time.Hour,
},
- args{&model.AuthRequest{UserID: "UserID"}, false},
+ args{&model.AuthRequest{UserID: "UserID", LoginPolicy: &iam_model.LoginPolicyView{PasswordlessType: iam_model.PasswordlessTypeAllowed}}, false},
[]model.NextStep{&model.PasswordlessStep{}},
nil,
},
@@ -500,7 +500,8 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
args{&model.AuthRequest{
UserID: "UserID",
LoginPolicy: &iam_model.LoginPolicyView{
- MultiFactors: []iam_model.MultiFactorType{iam_model.MultiFactorTypeU2FWithPIN},
+ PasswordlessType: iam_model.PasswordlessTypeAllowed,
+ MultiFactors: []iam_model.MultiFactorType{iam_model.MultiFactorTypeU2FWithPIN},
},
}, false},
[]model.NextStep{&model.VerifyEMailStep{}},
@@ -514,7 +515,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
userEventProvider: &mockEventUser{},
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
},
- args{&model.AuthRequest{UserID: "UserID"}, false},
+ args{&model.AuthRequest{UserID: "UserID", LoginPolicy: &iam_model.LoginPolicyView{}}, false},
[]model.NextStep{&model.InitPasswordStep{}},
nil,
},
@@ -578,7 +579,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
PasswordCheckLifeTime: 10 * 24 * time.Hour,
},
- args{&model.AuthRequest{UserID: "UserID"}, false},
+ args{&model.AuthRequest{UserID: "UserID", LoginPolicy: &iam_model.LoginPolicyView{}}, false},
[]model.NextStep{&model.PasswordStep{}},
nil,
},
@@ -887,6 +888,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
args{
&model.AuthRequest{
UserID: "UserID",
+ LoginPolicy: &iam_model.LoginPolicyView{},
SelectedIDPConfigID: "IDPConfigID",
LinkingUsers: []*model.ExternalUser{{IDPConfigID: "IDPConfigID", ExternalUserID: "UserID", DisplayName: "DisplayName"}},
}, false},
diff --git a/internal/auth/repository/eventsourcing/eventstore/user.go b/internal/auth/repository/eventsourcing/eventstore/user.go
index 1421b97929..d4e96c32e3 100644
--- a/internal/auth/repository/eventsourcing/eventstore/user.go
+++ b/internal/auth/repository/eventsourcing/eventstore/user.go
@@ -303,11 +303,26 @@ func (repo *UserRepo) RemoveMyMFAOTP(ctx context.Context) error {
}
func (repo *UserRepo) AddMFAU2F(ctx context.Context, userID string) (*model.WebAuthNToken, error) {
- return repo.UserEvents.AddU2F(ctx, userID, true)
+ accountName := ""
+ user, err := repo.UserByID(ctx, userID)
+ if err != nil {
+ logging.Log("EVENT-DAqe1").WithError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Debug("unable to get user for loginname")
+ } else {
+ accountName = user.PreferredLoginName
+ }
+ return repo.UserEvents.AddU2F(ctx, userID, accountName, true)
}
func (repo *UserRepo) AddMyMFAU2F(ctx context.Context) (*model.WebAuthNToken, error) {
- return repo.UserEvents.AddU2F(ctx, authz.GetCtxData(ctx).UserID, false)
+ userID := authz.GetCtxData(ctx).UserID
+ accountName := ""
+ user, err := repo.UserByID(ctx, userID)
+ if err != nil {
+ logging.Log("EVENT-Ghwl1").WithError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Debug("unable to get user for loginname")
+ } else {
+ accountName = user.PreferredLoginName
+ }
+ return repo.UserEvents.AddU2F(ctx, userID, accountName, false)
}
func (repo *UserRepo) VerifyMFAU2FSetup(ctx context.Context, userID, tokenName, userAgentID string, credentialData []byte) error {
@@ -331,7 +346,14 @@ func (repo *UserRepo) GetPasswordless(ctx context.Context, userID string) ([]*mo
}
func (repo *UserRepo) AddPasswordless(ctx context.Context, userID string) (*model.WebAuthNToken, error) {
- return repo.UserEvents.AddPasswordless(ctx, userID, true)
+ accountName := ""
+ user, err := repo.UserByID(ctx, userID)
+ if err != nil {
+ logging.Log("EVENT-Vj2k1").WithError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Debug("unable to get user for loginname")
+ } else {
+ accountName = user.PreferredLoginName
+ }
+ return repo.UserEvents.AddPasswordless(ctx, userID, accountName, true)
}
func (repo *UserRepo) GetMyPasswordless(ctx context.Context) ([]*model.WebAuthNToken, error) {
@@ -339,7 +361,15 @@ func (repo *UserRepo) GetMyPasswordless(ctx context.Context) ([]*model.WebAuthNT
}
func (repo *UserRepo) AddMyPasswordless(ctx context.Context) (*model.WebAuthNToken, error) {
- return repo.UserEvents.AddPasswordless(ctx, authz.GetCtxData(ctx).UserID, false)
+ userID := authz.GetCtxData(ctx).UserID
+ accountName := ""
+ user, err := repo.UserByID(ctx, userID)
+ if err != nil {
+ logging.Log("EVENT-AEq21").WithError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Debug("unable to get user for loginname")
+ } else {
+ accountName = user.PreferredLoginName
+ }
+ return repo.UserEvents.AddPasswordless(ctx, authz.GetCtxData(ctx).UserID, accountName, false)
}
func (repo *UserRepo) VerifyPasswordlessSetup(ctx context.Context, userID, tokenName, userAgentID string, credentialData []byte) error {
diff --git a/internal/iam/model/iam.go b/internal/iam/model/iam.go
index f80ea3b400..41ba7a6509 100644
--- a/internal/iam/model/iam.go
+++ b/internal/iam/model/iam.go
@@ -15,6 +15,7 @@ const (
Step6
Step7
Step8
+ Step9
//StepCount marks the the length of possible steps (StepCount-1 == last possible step)
StepCount
)
diff --git a/internal/iam/repository/eventsourcing/eventstore.go b/internal/iam/repository/eventsourcing/eventstore.go
index e1ed4d2d26..cad7dc09c0 100644
--- a/internal/iam/repository/eventsourcing/eventstore.go
+++ b/internal/iam/repository/eventsourcing/eventstore.go
@@ -525,20 +525,31 @@ func (es *IAMEventstore) AddLoginPolicy(ctx context.Context, policy *iam_model.L
return model.LoginPolicyToModel(repoIam.DefaultLoginPolicy), nil
}
-func (es *IAMEventstore) ChangeLoginPolicy(ctx context.Context, policy *iam_model.LoginPolicy) (*iam_model.LoginPolicy, error) {
+func (es *IAMEventstore) PrepareChangeLoginPolicy(ctx context.Context, policy *iam_model.LoginPolicy) (*model.IAM, *models.Aggregate, error) {
if policy == nil || !policy.IsValid() {
- return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-3M0so", "Errors.IAM.LoginPolicyInvalid")
+ return nil, nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-3M0so", "Errors.IAM.LoginPolicyInvalid")
}
iam, err := es.IAMByID(ctx, policy.AggregateID)
if err != nil {
- return nil, err
+ return nil, nil, err
}
repoIam := model.IAMFromModel(iam)
repoLoginPolicy := model.LoginPolicyFromModel(policy)
- addAggregate := LoginPolicyChangedAggregate(es.Eventstore.AggregateCreator(), repoIam, repoLoginPolicy)
- err = es_sdk.Push(ctx, es.PushAggregates, repoIam.AppendEvents, addAggregate)
+ changeAgg, err := LoginPolicyChangedAggregate(es.Eventstore.AggregateCreator(), repoIam, repoLoginPolicy)(ctx)
+ if err != nil {
+ return nil, nil, err
+ }
+ return repoIam, changeAgg, nil
+}
+
+func (es *IAMEventstore) ChangeLoginPolicy(ctx context.Context, policy *iam_model.LoginPolicy) (*iam_model.LoginPolicy, error) {
+ repoIam, changeAggregate, err := es.PrepareChangeLoginPolicy(ctx, policy)
+ if err != nil {
+ return nil, err
+ }
+ err = es_sdk.PushAggregates(ctx, es.PushAggregates, repoIam.AppendEvents, changeAggregate)
if err != nil {
return nil, err
}
@@ -665,27 +676,38 @@ func (es *IAMEventstore) RemoveSecondFactorFromLoginPolicy(ctx context.Context,
return nil
}
-func (es *IAMEventstore) AddMultiFactorToLoginPolicy(ctx context.Context, aggregateID string, mfa iam_model.MultiFactorType) (iam_model.MultiFactorType, error) {
+func (es *IAMEventstore) PrepareAddMultiFactorToLoginPolicy(ctx context.Context, aggregateID string, mfa iam_model.MultiFactorType) (*model.IAM, *models.Aggregate, error) {
if mfa == iam_model.MultiFactorTypeUnspecified {
- return 0, caos_errs.ThrowPreconditionFailed(nil, "EVENT-2Dh7J", "Errors.IAM.LoginPolicy.MFA.Unspecified")
+ return nil, nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-2Dh7J", "Errors.IAM.LoginPolicy.MFA.Unspecified")
}
iam, err := es.IAMByID(ctx, aggregateID)
if err != nil {
- return 0, err
+ return nil, nil, err
}
if _, m := iam.DefaultLoginPolicy.GetMultiFactor(mfa); m != 0 {
- return 0, caos_errs.ThrowAlreadyExists(nil, "EVENT-4Rk09", "Errors.IAM.LoginPolicy.MFA.AlreadyExists")
+ return nil, nil, caos_errs.ThrowAlreadyExists(nil, "EVENT-4Rk09", "Errors.IAM.LoginPolicy.MFA.AlreadyExists")
}
repoIam := model.IAMFromModel(iam)
repoMFA := model.MultiFactorFromModel(mfa)
- addAggregate := LoginPolicyMultiFactorAddedAggregate(es.Eventstore.AggregateCreator(), repoIam, repoMFA)
- err = es_sdk.Push(ctx, es.PushAggregates, repoIam.AppendEvents, addAggregate)
+ addAggregate, err := LoginPolicyMultiFactorAddedAggregate(es.Eventstore.AggregateCreator(), repoIam, repoMFA)(ctx)
+ if err != nil {
+ return nil, nil, err
+ }
+ return repoIam, addAggregate, nil
+}
+
+func (es *IAMEventstore) AddMultiFactorToLoginPolicy(ctx context.Context, aggregateID string, mfa iam_model.MultiFactorType) (iam_model.MultiFactorType, error) {
+ repoIAM, addAggregate, err := es.PrepareAddMultiFactorToLoginPolicy(ctx, aggregateID, mfa)
if err != nil {
return 0, err
}
- es.iamCache.cacheIAM(repoIam)
- if _, m := model.GetMFA(repoIam.DefaultLoginPolicy.MultiFactors, int32(mfa)); m != 0 {
+ err = es_sdk.PushAggregates(ctx, es.PushAggregates, repoIAM.AppendEvents, addAggregate)
+ if err != nil {
+ return 0, err
+ }
+ es.iamCache.cacheIAM(repoIAM)
+ if _, m := model.GetMFA(repoIAM.DefaultLoginPolicy.MultiFactors, int32(mfa)); m != 0 {
return iam_model.MultiFactorType(m), nil
}
return 0, caos_errs.ThrowInternal(nil, "EVENT-5N9so", "Errors.Internal")
diff --git a/internal/setup/config.go b/internal/setup/config.go
index 93e0d4926b..c4e3149a4c 100644
--- a/internal/setup/config.go
+++ b/internal/setup/config.go
@@ -14,6 +14,7 @@ type IAMSetUp struct {
Step6 *Step6
Step7 *Step7
Step8 *Step8
+ Step9 *Step9
}
func (setup *IAMSetUp) steps(currentDone iam_model.Step) ([]step, error) {
@@ -29,6 +30,7 @@ func (setup *IAMSetUp) steps(currentDone iam_model.Step) ([]step, error) {
setup.Step6,
setup.Step7,
setup.Step8,
+ setup.Step9,
} {
if step.step() <= currentDone {
continue
diff --git a/internal/setup/step9.go b/internal/setup/step9.go
new file mode 100644
index 0000000000..2733dbb51a
--- /dev/null
+++ b/internal/setup/step9.go
@@ -0,0 +1,74 @@
+package setup
+
+import (
+ "context"
+
+ "github.com/caos/logging"
+
+ "github.com/caos/zitadel/internal/eventstore/models"
+ es_sdk "github.com/caos/zitadel/internal/eventstore/sdk"
+ iam_model "github.com/caos/zitadel/internal/iam/model"
+ iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model"
+)
+
+type Step9 struct {
+ Passwordless bool
+
+ setup *Setup
+}
+
+func (step *Step9) isNil() bool {
+ return step == nil
+}
+
+func (step *Step9) step() iam_model.Step {
+ return iam_model.Step9
+}
+
+func (step *Step9) init(setup *Setup) {
+ step.setup = setup
+}
+
+func (step *Step9) execute(ctx context.Context) (*iam_model.IAM, error) {
+ if !step.Passwordless {
+ return step.setup.IamEvents.IAMByID(ctx, step.setup.iamID)
+ }
+ iam, agg, err := step.setPasswordlessAllowedInPolicy(ctx)
+ if err != nil {
+ logging.Log("SETUP-Gdbjq").WithField("step", step.step()).WithError(err).Error("unable to finish setup (add default mfa to login policy)")
+ return nil, err
+ }
+ iam, agg2, err := step.addMFAToPolicy(ctx)
+ if err != nil {
+ logging.Log("SETUP-Gdbjq").WithField("step", step.step()).WithError(err).Error("unable to finish setup (add default mfa to login policy)")
+ return nil, err
+ }
+ agg.Events = append(agg.Events, agg2.Events...)
+ iam, agg, push, err := step.setup.IamEvents.PrepareSetupDone(ctx, iam, agg, step.step())
+ if err != nil {
+ logging.Log("SETUP-Cnf21").WithField("step", step.step()).WithError(err).Error("unable to finish setup (prepare setup done)")
+ return nil, err
+ }
+ err = es_sdk.PushAggregates(ctx, push, iam.AppendEvents, agg)
+ if err != nil {
+ logging.Log("SETUP-NFq21").WithField("step", step.step()).WithError(err).Error("unable to finish setup")
+ return nil, err
+ }
+ return iam_es_model.IAMToModel(iam), nil
+}
+
+func (step *Step9) setPasswordlessAllowedInPolicy(ctx context.Context) (*iam_es_model.IAM, *models.Aggregate, error) {
+ logging.Log("SETUP-DAd1h").Info("enabling passwordless in loginPolicy")
+ iam, err := step.setup.IamEvents.IAMByID(ctx, step.setup.iamID)
+ if err != nil {
+ return nil, nil, err
+ }
+ iam.DefaultLoginPolicy.AggregateID = step.setup.iamID
+ iam.DefaultLoginPolicy.PasswordlessType = iam_model.PasswordlessTypeAllowed
+ return step.setup.IamEvents.PrepareChangeLoginPolicy(ctx, iam.DefaultLoginPolicy)
+}
+
+func (step *Step9) addMFAToPolicy(ctx context.Context) (*iam_es_model.IAM, *models.Aggregate, error) {
+ logging.Log("SETUP-DAd1h").Info("adding MFA to loginPolicy")
+ return step.setup.IamEvents.PrepareAddMultiFactorToLoginPolicy(ctx, step.setup.iamID, iam_model.MultiFactorTypeU2FWithPIN)
+}
diff --git a/internal/user/repository/eventsourcing/eventstore.go b/internal/user/repository/eventsourcing/eventstore.go
index 7e5191d0db..acbf78048b 100644
--- a/internal/user/repository/eventsourcing/eventstore.go
+++ b/internal/user/repository/eventsourcing/eventstore.go
@@ -1302,12 +1302,12 @@ func (es *UserEventstore) verifyMFAOTP(otp *usr_model.OTP, code string) error {
return nil
}
-func (es *UserEventstore) AddU2F(ctx context.Context, userID string, isLoginUI bool) (*usr_model.WebAuthNToken, error) {
+func (es *UserEventstore) AddU2F(ctx context.Context, userID string, accountName string, isLoginUI bool) (*usr_model.WebAuthNToken, error) {
user, err := es.HumanByID(ctx, userID)
if err != nil {
return nil, err
}
- webAuthN, err := es.webauthn.BeginRegistration(user, usr_model.AuthenticatorAttachmentUnspecified, usr_model.UserVerificationRequirementDiscouraged, isLoginUI, user.U2FTokens...)
+ webAuthN, err := es.webauthn.BeginRegistration(user, accountName, usr_model.AuthenticatorAttachmentUnspecified, usr_model.UserVerificationRequirementDiscouraged, isLoginUI, user.U2FTokens...)
if err != nil {
return nil, err
}
@@ -1418,12 +1418,12 @@ func (es *UserEventstore) GetPasswordless(ctx context.Context, userID string) ([
return user.PasswordlessTokens, nil
}
-func (es *UserEventstore) AddPasswordless(ctx context.Context, userID string, isLoginUI bool) (*usr_model.WebAuthNToken, error) {
+func (es *UserEventstore) AddPasswordless(ctx context.Context, userID, accountName string, isLoginUI bool) (*usr_model.WebAuthNToken, error) {
user, err := es.HumanByID(ctx, userID)
if err != nil {
return nil, err
}
- webAuthN, err := es.webauthn.BeginRegistration(user, usr_model.AuthenticatorAttachmentUnspecified, usr_model.UserVerificationRequirementRequired, isLoginUI, user.PasswordlessTokens...)
+ webAuthN, err := es.webauthn.BeginRegistration(user, accountName, usr_model.AuthenticatorAttachmentUnspecified, usr_model.UserVerificationRequirementRequired, isLoginUI, user.PasswordlessTokens...)
if err != nil {
return nil, err
}
diff --git a/internal/user/repository/view/model/user_session.go b/internal/user/repository/view/model/user_session.go
index 514c829cf6..b64fb1b303 100644
--- a/internal/user/repository/view/model/user_session.go
+++ b/internal/user/repository/view/model/user_session.go
@@ -155,8 +155,12 @@ func (v *UserSessionView) AppendEvent(event *models.Event) error {
es_model.HumanSignedOut,
es_model.UserLocked,
es_model.UserDeactivated:
+ v.PasswordlessVerification = time.Time{}
v.PasswordVerification = time.Time{}
v.SecondFactorVerification = time.Time{}
+ v.SecondFactorVerificationType = int32(req_model.MFALevelNotSetUp)
+ v.MultiFactorVerification = time.Time{}
+ v.MultiFactorVerificationType = int32(req_model.MFALevelNotSetUp)
v.ExternalLoginVerification = time.Time{}
v.State = int32(req_model.UserSessionStateTerminated)
case es_model.HumanExternalIDPRemoved, es_model.HumanExternalIDPCascadeRemoved:
diff --git a/internal/webauthn/webauthn.go b/internal/webauthn/webauthn.go
index 8c1fa0093f..b003b3d5f6 100644
--- a/internal/webauthn/webauthn.go
+++ b/internal/webauthn/webauthn.go
@@ -42,6 +42,7 @@ func StartServer(sd systemdefaults.WebAuthN) (*WebAuthN, error) {
type webUser struct {
*usr_model.User
+ accountName string
credentials []webauthn.Credential
}
@@ -50,7 +51,10 @@ func (u *webUser) WebAuthnID() []byte {
}
func (u *webUser) WebAuthnName() string {
- return u.PreferredLoginName
+ if u.accountName != "" {
+ return u.accountName
+ }
+ return u.UserName
}
func (u *webUser) WebAuthnDisplayName() string {
@@ -65,7 +69,7 @@ func (u *webUser) WebAuthnCredentials() []webauthn.Credential {
return u.credentials
}
-func (w *WebAuthN) BeginRegistration(user *usr_model.User, authType usr_model.AuthenticatorAttachment, userVerification usr_model.UserVerificationRequirement, isLoginUI bool, webAuthNs ...*usr_model.WebAuthNToken) (*usr_model.WebAuthNToken, error) {
+func (w *WebAuthN) BeginRegistration(user *usr_model.User, accountName string, authType usr_model.AuthenticatorAttachment, userVerification usr_model.UserVerificationRequirement, isLoginUI bool, webAuthNs ...*usr_model.WebAuthNToken) (*usr_model.WebAuthNToken, error) {
creds := WebAuthNsToCredentials(webAuthNs)
existing := make([]protocol.CredentialDescriptor, len(creds))
for i, cred := range creds {
@@ -77,6 +81,7 @@ func (w *WebAuthN) BeginRegistration(user *usr_model.User, authType usr_model.Au
credentialOptions, sessionData, err := w.web(isLoginUI).BeginRegistration(
&webUser{
User: user,
+ accountName: accountName,
credentials: creds,
},
webauthn.WithAuthenticatorSelection(protocol.AuthenticatorSelection{