diff --git a/docs/docs/apis/openidoauth/scopes.md b/docs/docs/apis/openidoauth/scopes.md
index 163b1f6b4..7208cfc19 100644
--- a/docs/docs/apis/openidoauth/scopes.md
+++ b/docs/docs/apis/openidoauth/scopes.md
@@ -30,5 +30,6 @@ In addition to the standard compliant scopes we utilize the following scopes.
| `urn:zitadel:iam:org:project:id:{projectid}:aud` | ZITADEL's Project id is `urn:zitadel:iam:org:project:id:69234237810729019:aud` | By adding this scope, the requested projectid will be added to the audience of the access and id token |
| urn:zitadel:iam:user:metadata | `urn:zitadel:iam:user:metadata` | By adding this scope, the metadata of the user will be included in the token. The values are base64 encoded. |
| urn:zitadel:iam:user:resourceowner | `urn:zitadel:iam:user:resourceowner` | By adding this scope, the resourceowner (id, name, primary_domain) of the user will be included in the token. |
+| urn:zitadel:iam:org:idp:id:{idp_id} | `urn:zitadel:iam:org:idp:id:76625965177954913` | By adding this scope the user will directly be redirected to the identity provider to authenticate. Make sure you also send the primary domain scope if a custom login policy is configured. Otherwise the system will not be able to identify the identity provider. |
> If access to ZITADEL's API's is needed with a service user the scope `urn:zitadel:iam:org:project:id:69234237810729019:aud` needs to be used with the JWT Profile request
diff --git a/docs/docs/apis/proto/admin.md b/docs/docs/apis/proto/admin.md
index 907b186e4..57e0fbe72 100644
--- a/docs/docs/apis/proto/admin.md
+++ b/docs/docs/apis/proto/admin.md
@@ -1201,6 +1201,7 @@ This is an empty request
| scopes | repeated string | - | |
| display_name_mapping | zitadel.idp.v1.OIDCMappingField | - | enum.defined_only: true
|
| username_mapping | zitadel.idp.v1.OIDCMappingField | - | enum.defined_only: true
|
+| auto_register | bool | - | |
@@ -2887,6 +2888,7 @@ This is an empty request
| idp_id | string | - | string.min_len: 1
string.max_len: 200
|
| name | string | - | string.min_len: 1
string.max_len: 200
|
| styling_type | zitadel.idp.v1.IDPStylingType | - | enum.defined_only: true
|
+| auto_register | bool | - | |
diff --git a/docs/docs/apis/proto/idp.md b/docs/docs/apis/proto/idp.md
index de646c887..8a23275ca 100644
--- a/docs/docs/apis/proto/idp.md
+++ b/docs/docs/apis/proto/idp.md
@@ -22,6 +22,7 @@ title: zitadel/idp.proto
| styling_type | IDPStylingType | - | |
| owner | IDPOwnerType | - | |
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) config.oidc_config | OIDCConfig | - | |
+| auto_register | bool | - | |
diff --git a/docs/docs/apis/proto/management.md b/docs/docs/apis/proto/management.md
index bb12511c7..a59153af5 100644
--- a/docs/docs/apis/proto/management.md
+++ b/docs/docs/apis/proto/management.md
@@ -3154,6 +3154,7 @@ This is an empty request
| scopes | repeated string | - | |
| display_name_mapping | zitadel.idp.v1.OIDCMappingField | - | enum.defined_only: true
|
| username_mapping | zitadel.idp.v1.OIDCMappingField | - | enum.defined_only: true
|
+| auto_register | bool | - | |
@@ -7379,6 +7380,7 @@ This is an empty request
| idp_id | string | - | string.min_len: 1
string.max_len: 200
|
| name | string | - | string.min_len: 1
string.max_len: 200
|
| styling_type | zitadel.idp.v1.IDPStylingType | - | enum.defined_only: true
|
+| auto_register | bool | - | |
diff --git a/internal/api/grpc/admin/idp_converter.go b/internal/api/grpc/admin/idp_converter.go
index c74eff76d..98c918acf 100644
--- a/internal/api/grpc/admin/idp_converter.go
+++ b/internal/api/grpc/admin/idp_converter.go
@@ -12,10 +12,11 @@ import (
func addOIDCIDPRequestToDomain(req *admin_pb.AddOIDCIDPRequest) *domain.IDPConfig {
return &domain.IDPConfig{
- Name: req.Name,
- OIDCConfig: addOIDCIDPRequestToDomainOIDCIDPConfig(req),
- StylingType: idp_grpc.IDPStylingTypeToDomain(req.StylingType),
- Type: domain.IDPConfigTypeOIDC,
+ Name: req.Name,
+ OIDCConfig: addOIDCIDPRequestToDomainOIDCIDPConfig(req),
+ StylingType: idp_grpc.IDPStylingTypeToDomain(req.StylingType),
+ Type: domain.IDPConfigTypeOIDC,
+ AutoRegister: req.AutoRegister,
}
}
@@ -32,9 +33,10 @@ func addOIDCIDPRequestToDomainOIDCIDPConfig(req *admin_pb.AddOIDCIDPRequest) *do
func updateIDPToDomain(req *admin_pb.UpdateIDPRequest) *domain.IDPConfig {
return &domain.IDPConfig{
- IDPConfigID: req.IdpId,
- Name: req.Name,
- StylingType: idp_grpc.IDPStylingTypeToDomain(req.StylingType),
+ IDPConfigID: req.IdpId,
+ Name: req.Name,
+ StylingType: idp_grpc.IDPStylingTypeToDomain(req.StylingType),
+ AutoRegister: req.AutoRegister,
}
}
diff --git a/internal/api/grpc/admin/idp_converter_test.go b/internal/api/grpc/admin/idp_converter_test.go
index 19b990dc1..d4f625542 100644
--- a/internal/api/grpc/admin/idp_converter_test.go
+++ b/internal/api/grpc/admin/idp_converter_test.go
@@ -28,6 +28,7 @@ func Test_addOIDCIDPRequestToDomain(t *testing.T) {
Scopes: []string{"email", "profile"},
DisplayNameMapping: idp.OIDCMappingField_OIDC_MAPPING_FIELD_EMAIL,
UsernameMapping: idp.OIDCMappingField_OIDC_MAPPING_FIELD_PREFERRED_USERNAME,
+ AutoRegister: true,
},
},
},
@@ -98,9 +99,10 @@ func Test_updateIDPToDomain(t *testing.T) {
name: "all fields filled",
args: args{
req: &admin_pb.UpdateIDPRequest{
- IdpId: "13523",
- Name: "new name",
- StylingType: idp.IDPStylingType_STYLING_TYPE_GOOGLE,
+ IdpId: "13523",
+ Name: "new name",
+ StylingType: idp.IDPStylingType_STYLING_TYPE_GOOGLE,
+ AutoRegister: true,
},
},
},
diff --git a/internal/api/grpc/idp/converter.go b/internal/api/grpc/idp/converter.go
index 79778d674..a7974a9b2 100644
--- a/internal/api/grpc/idp/converter.go
+++ b/internal/api/grpc/idp/converter.go
@@ -18,29 +18,31 @@ func IDPViewsToPb(idps []*iam_model.IDPConfigView) []*idp_pb.IDP {
func ModelIDPViewToPb(idp *iam_model.IDPConfigView) *idp_pb.IDP {
return &idp_pb.IDP{
- Id: idp.IDPConfigID,
- State: ModelIDPStateToPb(idp.State),
- Name: idp.Name,
- StylingType: ModelIDPStylingTypeToPb(idp.StylingType),
- Owner: ModelIDPProviderTypeToPb(idp.IDPProviderType),
- Config: ModelIDPViewToConfigPb(idp),
+ Id: idp.IDPConfigID,
+ State: ModelIDPStateToPb(idp.State),
+ Name: idp.Name,
+ StylingType: ModelIDPStylingTypeToPb(idp.StylingType),
+ AutoRegister: idp.AutoRegister,
+ Owner: ModelIDPProviderTypeToPb(idp.IDPProviderType),
+ Config: ModelIDPViewToConfigPb(idp),
Details: obj_grpc.ToViewDetailsPb(
idp.Sequence,
idp.CreationDate,
idp.ChangeDate,
- "", //TODO: backend
+ idp.AggregateID,
),
}
}
func IDPViewToPb(idp *domain.IDPConfigView) *idp_pb.IDP {
mapped := &idp_pb.IDP{
- Id: idp.AggregateID,
- State: IDPStateToPb(idp.State),
- Name: idp.Name,
- StylingType: IDPStylingTypeToPb(idp.StylingType),
- Config: IDPViewToConfigPb(idp),
- Details: obj_grpc.ToViewDetailsPb(idp.Sequence, idp.CreationDate, idp.ChangeDate, ""), //TODO: resource owner in view
+ Id: idp.AggregateID,
+ State: IDPStateToPb(idp.State),
+ Name: idp.Name,
+ StylingType: IDPStylingTypeToPb(idp.StylingType),
+ AutoRegister: idp.AutoRegister,
+ Config: IDPViewToConfigPb(idp),
+ Details: obj_grpc.ToViewDetailsPb(idp.Sequence, idp.CreationDate, idp.ChangeDate, idp.AggregateID),
}
return mapped
}
diff --git a/internal/api/grpc/management/idp_converter.go b/internal/api/grpc/management/idp_converter.go
index 241e3be77..e90009a20 100644
--- a/internal/api/grpc/management/idp_converter.go
+++ b/internal/api/grpc/management/idp_converter.go
@@ -32,9 +32,10 @@ func addOIDCIDPRequestToDomainOIDCIDPConfig(req *mgmt_pb.AddOrgOIDCIDPRequest) *
func updateIDPToDomain(req *mgmt_pb.UpdateOrgIDPRequest) *domain.IDPConfig {
return &domain.IDPConfig{
- IDPConfigID: req.IdpId,
- Name: req.Name,
- StylingType: idp_grpc.IDPStylingTypeToDomain(req.StylingType),
+ IDPConfigID: req.IdpId,
+ Name: req.Name,
+ StylingType: idp_grpc.IDPStylingTypeToDomain(req.StylingType),
+ AutoRegister: req.AutoRegister,
}
}
diff --git a/internal/api/grpc/management/idp_converter_test.go b/internal/api/grpc/management/idp_converter_test.go
index 254808d74..fe5547aea 100644
--- a/internal/api/grpc/management/idp_converter_test.go
+++ b/internal/api/grpc/management/idp_converter_test.go
@@ -28,6 +28,7 @@ func Test_addOIDCIDPRequestToDomain(t *testing.T) {
Scopes: []string{"email", "profile"},
DisplayNameMapping: idp.OIDCMappingField_OIDC_MAPPING_FIELD_EMAIL,
UsernameMapping: idp.OIDCMappingField_OIDC_MAPPING_FIELD_PREFERRED_USERNAME,
+ AutoRegister: true,
},
},
},
@@ -45,6 +46,7 @@ func Test_addOIDCIDPRequestToDomain(t *testing.T) {
"OIDCConfig.AuthorizationEndpoint",
"OIDCConfig.TokenEndpoint",
"Type", //TODO: default (0) is oidc
+ "AutoRegister",
)
})
}
@@ -98,9 +100,10 @@ func Test_updateIDPToDomain(t *testing.T) {
name: "all fields filled",
args: args{
req: &mgmt_pb.UpdateOrgIDPRequest{
- IdpId: "13523",
- Name: "new name",
- StylingType: idp.IDPStylingType_STYLING_TYPE_GOOGLE,
+ IdpId: "13523",
+ Name: "new name",
+ StylingType: idp.IDPStylingType_STYLING_TYPE_GOOGLE,
+ AutoRegister: true,
},
},
},
diff --git a/internal/api/oidc/auth_request_converter.go b/internal/api/oidc/auth_request_converter.go
index 52ca9a063..8af7ef836 100644
--- a/internal/api/oidc/auth_request_converter.go
+++ b/internal/api/oidc/auth_request_converter.go
@@ -3,6 +3,7 @@ package oidc
import (
"context"
"net"
+ "strings"
"time"
"github.com/caos/oidc/pkg/oidc"
@@ -10,6 +11,7 @@ import (
"golang.org/x/text/language"
http_utils "github.com/caos/zitadel/internal/api/http"
+ model2 "github.com/caos/zitadel/internal/auth_request/model"
"github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/user/model"
@@ -113,18 +115,19 @@ func AuthRequestFromBusiness(authReq *domain.AuthRequest) (_ op.AuthRequest, err
func CreateAuthRequestToBusiness(ctx context.Context, authReq *oidc.AuthRequest, userAgentID, userID string) *domain.AuthRequest {
return &domain.AuthRequest{
- CreationDate: time.Now(),
- AgentID: userAgentID,
- BrowserInfo: ParseBrowserInfoFromContext(ctx),
- ApplicationID: authReq.ClientID,
- CallbackURI: authReq.RedirectURI,
- TransferState: authReq.State,
- Prompt: PromptToBusiness(authReq.Prompt),
- PossibleLOAs: ACRValuesToBusiness(authReq.ACRValues),
- UiLocales: UILocalesToBusiness(authReq.UILocales),
- LoginHint: authReq.LoginHint,
- MaxAuthAge: MaxAgeToBusiness(authReq.MaxAge),
- UserID: userID,
+ CreationDate: time.Now(),
+ AgentID: userAgentID,
+ BrowserInfo: ParseBrowserInfoFromContext(ctx),
+ ApplicationID: authReq.ClientID,
+ CallbackURI: authReq.RedirectURI,
+ TransferState: authReq.State,
+ Prompt: PromptToBusiness(authReq.Prompt),
+ PossibleLOAs: ACRValuesToBusiness(authReq.ACRValues),
+ UiLocales: UILocalesToBusiness(authReq.UILocales),
+ LoginHint: authReq.LoginHint,
+ SelectedIDPConfigID: GetSelectedIDPIDFromScopes(authReq.Scopes),
+ MaxAuthAge: MaxAgeToBusiness(authReq.MaxAge),
+ UserID: userID,
Request: &domain.AuthRequestOIDC{
Scopes: authReq.Scopes,
ResponseType: ResponseTypeToBusiness(authReq.ResponseType),
@@ -196,6 +199,15 @@ func UILocalesToBusiness(tags []language.Tag) []string {
return locales
}
+func GetSelectedIDPIDFromScopes(scopes oidc.SpaceDelimitedArray) string {
+ for _, scope := range scopes {
+ if strings.HasPrefix(scope, model2.SelectIDPScope) {
+ return strings.TrimPrefix(scope, model2.SelectIDPScope)
+ }
+ }
+ return ""
+}
+
func MaxAgeToBusiness(maxAge *uint) *time.Duration {
if maxAge == nil {
return nil
diff --git a/internal/api/oidc/client_converter.go b/internal/api/oidc/client_converter.go
index 17e1424be..a285b8017 100644
--- a/internal/api/oidc/client_converter.go
+++ b/internal/api/oidc/client_converter.go
@@ -106,6 +106,9 @@ func (c *Client) IsScopeAllowed(scope string) bool {
if strings.HasPrefix(scope, authreq_model.ProjectIDScope) {
return true
}
+ if strings.HasPrefix(scope, authreq_model.SelectIDPScope) {
+ return true
+ }
if strings.HasPrefix(scope, ScopeUserMetaData) {
return true
}
diff --git a/internal/auth/repository/eventsourcing/eventstore/auth_request.go b/internal/auth/repository/eventsourcing/eventstore/auth_request.go
index df3f41418..eb0d057da 100644
--- a/internal/auth/repository/eventsourcing/eventstore/auth_request.go
+++ b/internal/auth/repository/eventsourcing/eventstore/auth_request.go
@@ -133,6 +133,7 @@ func (repo *AuthRequestRepo) CreateAuthRequest(ctx context.Context, request *dom
err = repo.checkLoginName(ctx, request, request.LoginHint)
logging.LogWithFields("EVENT-aG311", "login name", request.LoginHint, "id", request.ID, "applicationID", request.ApplicationID, "traceID", tracing.TraceIDFromCtx(ctx)).OnError(err).Debug("login hint invalid")
}
+
err = repo.AuthRequests.SaveAuthRequest(ctx, request)
if err != nil {
return nil, err
@@ -642,7 +643,13 @@ func (repo *AuthRequestRepo) nextSteps(ctx context.Context, request *domain.Auth
if err != nil {
return nil, err
}
- if len(users) > 0 || domain.IsPrompt(request.Prompt, domain.PromptSelectAccount) {
+ if domain.IsPrompt(request.Prompt, domain.PromptSelectAccount) {
+ steps = append(steps, &domain.SelectUserStep{Users: users})
+ }
+ if request.SelectedIDPConfigID != "" {
+ steps = append(steps, &domain.RedirectToExternalIDPStep{})
+ }
+ if len(request.Prompt) == 0 && len(users) > 0 {
steps = append(steps, &domain.SelectUserStep{Users: users})
}
}
diff --git a/internal/auth/repository/eventsourcing/eventstore/auth_request_test.go b/internal/auth/repository/eventsourcing/eventstore/auth_request_test.go
index 8f4ee22a2..741eea5f5 100644
--- a/internal/auth/repository/eventsourcing/eventstore/auth_request_test.go
+++ b/internal/auth/repository/eventsourcing/eventstore/auth_request_test.go
@@ -305,6 +305,15 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
[]domain.NextStep{&domain.ExternalNotFoundOptionStep{}},
nil,
},
+ {
+ "user not set no active session selected idp, redirect to external idp step",
+ fields{
+ userSessionViewProvider: &mockViewNoUserSession{},
+ },
+ args{&domain.AuthRequest{SelectedIDPConfigID: "id"}, false},
+ []domain.NextStep{&domain.LoginStep{}, &domain.RedirectToExternalIDPStep{}},
+ nil,
+ },
{
"user not set, prompt select account and internal error, internal error",
fields{
diff --git a/internal/auth_request/model/request.go b/internal/auth_request/model/request.go
index 43f8c93ea..25bf0d31b 100644
--- a/internal/auth_request/model/request.go
+++ b/internal/auth_request/model/request.go
@@ -23,6 +23,7 @@ const (
OrgDomainPrimaryClaim = "urn:zitadel:iam:org:domain:primary"
ProjectIDScope = "urn:zitadel:iam:org:project:id:"
AudSuffix = ":aud"
+ SelectIDPScope = "urn:zitadel:iam:org:idp:id:"
)
type AuthRequestOIDC struct {
diff --git a/internal/command/iam_converter.go b/internal/command/iam_converter.go
index acf5e6e81..eefbb8077 100644
--- a/internal/command/iam_converter.go
+++ b/internal/command/iam_converter.go
@@ -130,11 +130,12 @@ func writeModelToPrivacyPolicy(wm *PrivacyPolicyWriteModel) *domain.PrivacyPolic
func writeModelToIDPConfig(wm *IDPConfigWriteModel) *domain.IDPConfig {
return &domain.IDPConfig{
- ObjectRoot: writeModelToObjectRoot(wm.WriteModel),
- IDPConfigID: wm.ConfigID,
- Name: wm.Name,
- State: wm.State,
- StylingType: wm.StylingType,
+ ObjectRoot: writeModelToObjectRoot(wm.WriteModel),
+ IDPConfigID: wm.ConfigID,
+ Name: wm.Name,
+ State: wm.State,
+ StylingType: wm.StylingType,
+ AutoRegister: wm.AutoRegister,
}
}
diff --git a/internal/command/iam_idp_config.go b/internal/command/iam_idp_config.go
index 3d1cda2c0..86f630222 100644
--- a/internal/command/iam_idp_config.go
+++ b/internal/command/iam_idp_config.go
@@ -37,6 +37,7 @@ func (c *Commands) AddDefaultIDPConfig(ctx context.Context, config *domain.IDPCo
config.Name,
config.Type,
config.StylingType,
+ config.AutoRegister,
),
iam_repo.NewIDPOIDCConfigAddedEvent(
ctx,
@@ -73,11 +74,11 @@ func (c *Commands) ChangeDefaultIDPConfig(ctx context.Context, config *domain.ID
return nil, err
}
if existingIDP.State == domain.IDPConfigStateRemoved || existingIDP.State == domain.IDPConfigStateUnspecified {
- return nil, caos_errs.ThrowNotFound(nil, "IAM-4M9so", "Errors.IDPConfig.NotExisting")
+ return nil, caos_errs.ThrowNotFound(nil, "IAM-m0e3r", "Errors.IDPConfig.NotExisting")
}
iamAgg := IAMAggregateFromWriteModel(&existingIDP.WriteModel)
- changedEvent, hasChanged := existingIDP.NewChangedEvent(ctx, iamAgg, config.IDPConfigID, config.Name, config.StylingType)
+ changedEvent, hasChanged := existingIDP.NewChangedEvent(ctx, iamAgg, config.IDPConfigID, config.Name, config.StylingType, config.AutoRegister)
if !hasChanged {
return nil, caos_errs.ThrowPreconditionFailed(nil, "IAM-4M9vs", "Errors.IAM.LabelPolicy.NotChanged")
}
@@ -98,7 +99,7 @@ func (c *Commands) DeactivateDefaultIDPConfig(ctx context.Context, idpID string)
return nil, err
}
if existingIDP.State != domain.IDPConfigStateActive {
- return nil, caos_errs.ThrowPreconditionFailed(nil, "IAM-4M9so", "Errors.IAM.IDPConfig.NotActive")
+ return nil, caos_errs.ThrowPreconditionFailed(nil, "IAM-2n0fs", "Errors.IAM.IDPConfig.NotActive")
}
iamAgg := IAMAggregateFromWriteModel(&existingIDP.WriteModel)
pushedEvents, err := c.eventstore.PushEvents(ctx, iam_repo.NewIDPConfigDeactivatedEvent(ctx, iamAgg, idpID))
@@ -173,7 +174,7 @@ func (c *Commands) getIAMIDPConfigByID(ctx context.Context, idpID string) (*doma
return nil, err
}
if !config.State.Exists() {
- return nil, caos_errs.ThrowNotFound(nil, "IAM-4M9so", "Errors.IDPConfig.NotExisting")
+ return nil, caos_errs.ThrowNotFound(nil, "IAM-p0pFF", "Errors.IDPConfig.NotExisting")
}
return writeModelToIDPConfig(&config.IDPConfigWriteModel), nil
}
diff --git a/internal/command/iam_idp_config_model.go b/internal/command/iam_idp_config_model.go
index ae4e72de1..2f8994afd 100644
--- a/internal/command/iam_idp_config_model.go
+++ b/internal/command/iam_idp_config_model.go
@@ -100,6 +100,7 @@ func (wm *IAMIDPConfigWriteModel) NewChangedEvent(
configID,
name string,
stylingType domain.IDPConfigStylingType,
+ autoRegister bool,
) (*iam.IDPConfigChangedEvent, bool) {
changes := make([]idpconfig.IDPConfigChanges, 0)
@@ -111,6 +112,9 @@ func (wm *IAMIDPConfigWriteModel) NewChangedEvent(
if stylingType.Valid() && wm.StylingType != stylingType {
changes = append(changes, idpconfig.ChangeStyleType(stylingType))
}
+ if wm.AutoRegister != autoRegister {
+ changes = append(changes, idpconfig.ChangeAutoRegister(autoRegister))
+ }
if len(changes) == 0 {
return nil, false
}
diff --git a/internal/command/iam_idp_config_test.go b/internal/command/iam_idp_config_test.go
index a900b6efa..7aa781e6c 100644
--- a/internal/command/iam_idp_config_test.go
+++ b/internal/command/iam_idp_config_test.go
@@ -68,6 +68,7 @@ func TestCommandSide_AddDefaultIDPConfig(t *testing.T) {
"name1",
domain.IDPConfigTypeOIDC,
domain.IDPConfigStylingTypeGoogle,
+ true,
),
),
eventFromEventPusher(
@@ -99,8 +100,9 @@ func TestCommandSide_AddDefaultIDPConfig(t *testing.T) {
args: args{
ctx: context.Background(),
config: &domain.IDPConfig{
- Name: "name1",
- StylingType: domain.IDPConfigStylingTypeGoogle,
+ Name: "name1",
+ StylingType: domain.IDPConfigStylingTypeGoogle,
+ AutoRegister: true,
OIDCConfig: &domain.OIDCIDPConfig{
ClientID: "clientid1",
Issuer: "issuer",
@@ -119,10 +121,11 @@ func TestCommandSide_AddDefaultIDPConfig(t *testing.T) {
AggregateID: "IAM",
ResourceOwner: "IAM",
},
- IDPConfigID: "config1",
- Name: "name1",
- StylingType: domain.IDPConfigStylingTypeGoogle,
- State: domain.IDPConfigStateActive,
+ IDPConfigID: "config1",
+ Name: "name1",
+ StylingType: domain.IDPConfigStylingTypeGoogle,
+ State: domain.IDPConfigStateActive,
+ AutoRegister: true,
},
},
},
@@ -212,6 +215,7 @@ func TestCommandSide_ChangeDefaultIDPConfig(t *testing.T) {
"name1",
domain.IDPConfigTypeOIDC,
domain.IDPConfigStylingTypeGoogle,
+ true,
),
),
eventFromEventPusher(
@@ -237,7 +241,7 @@ func TestCommandSide_ChangeDefaultIDPConfig(t *testing.T) {
expectPush(
[]*repository.Event{
eventFromEventPusher(
- newDefaultIDPConfigChangedEvent(context.Background(), "config1", "name1", "name2", domain.IDPConfigStylingTypeUnspecified),
+ newDefaultIDPConfigChangedEvent(context.Background(), "config1", "name1", "name2", domain.IDPConfigStylingTypeUnspecified, false),
),
},
uniqueConstraintsFromEventConstraint(idpconfig.NewRemoveIDPConfigNameUniqueConstraint("name1", "IAM")),
@@ -248,9 +252,10 @@ func TestCommandSide_ChangeDefaultIDPConfig(t *testing.T) {
args: args{
ctx: context.Background(),
config: &domain.IDPConfig{
- IDPConfigID: "config1",
- Name: "name2",
- StylingType: domain.IDPConfigStylingTypeUnspecified,
+ IDPConfigID: "config1",
+ Name: "name2",
+ StylingType: domain.IDPConfigStylingTypeUnspecified,
+ AutoRegister: false,
},
},
res: res{
@@ -259,10 +264,11 @@ func TestCommandSide_ChangeDefaultIDPConfig(t *testing.T) {
AggregateID: "IAM",
ResourceOwner: "IAM",
},
- IDPConfigID: "config1",
- Name: "name2",
- StylingType: domain.IDPConfigStylingTypeUnspecified,
- State: domain.IDPConfigStateActive,
+ IDPConfigID: "config1",
+ Name: "name2",
+ StylingType: domain.IDPConfigStylingTypeUnspecified,
+ State: domain.IDPConfigStateActive,
+ AutoRegister: false,
},
},
},
@@ -286,7 +292,7 @@ func TestCommandSide_ChangeDefaultIDPConfig(t *testing.T) {
}
}
-func newDefaultIDPConfigChangedEvent(ctx context.Context, configID, oldName, newName string, stylingType domain.IDPConfigStylingType) *iam.IDPConfigChangedEvent {
+func newDefaultIDPConfigChangedEvent(ctx context.Context, configID, oldName, newName string, stylingType domain.IDPConfigStylingType, autoRegister bool) *iam.IDPConfigChangedEvent {
event, _ := iam.NewIDPConfigChangedEvent(ctx,
&iam.NewAggregate().Aggregate,
configID,
@@ -294,6 +300,7 @@ func newDefaultIDPConfigChangedEvent(ctx context.Context, configID, oldName, new
[]idpconfig.IDPConfigChanges{
idpconfig.ChangeName(newName),
idpconfig.ChangeStyleType(stylingType),
+ idpconfig.ChangeAutoRegister(autoRegister),
},
)
return event
diff --git a/internal/command/iam_idp_oidc_config_test.go b/internal/command/iam_idp_oidc_config_test.go
index 425cfc791..9381935dd 100644
--- a/internal/command/iam_idp_oidc_config_test.go
+++ b/internal/command/iam_idp_oidc_config_test.go
@@ -84,6 +84,7 @@ func TestCommandSide_ChangeDefaultIDPOIDCConfig(t *testing.T) {
"name1",
domain.IDPConfigTypeOIDC,
domain.IDPConfigStylingTypeGoogle,
+ true,
),
),
eventFromEventPusher(
@@ -138,6 +139,7 @@ func TestCommandSide_ChangeDefaultIDPOIDCConfig(t *testing.T) {
"name1",
domain.IDPConfigTypeOIDC,
domain.IDPConfigStylingTypeGoogle,
+ true,
),
),
eventFromEventPusher(
@@ -193,6 +195,7 @@ func TestCommandSide_ChangeDefaultIDPOIDCConfig(t *testing.T) {
"name1",
domain.IDPConfigTypeOIDC,
domain.IDPConfigStylingTypeGoogle,
+ true,
),
),
eventFromEventPusher(
diff --git a/internal/command/iam_policy_login_test.go b/internal/command/iam_policy_login_test.go
index 77119d7b6..b36b7ae16 100644
--- a/internal/command/iam_policy_login_test.go
+++ b/internal/command/iam_policy_login_test.go
@@ -344,6 +344,7 @@ func TestCommandSide_AddIDPProviderDefaultLoginPolicy(t *testing.T) {
"name",
domain.IDPConfigTypeOIDC,
domain.IDPConfigStylingTypeUnspecified,
+ true,
),
),
),
@@ -391,6 +392,7 @@ func TestCommandSide_AddIDPProviderDefaultLoginPolicy(t *testing.T) {
"name",
domain.IDPConfigTypeOIDC,
domain.IDPConfigStylingTypeUnspecified,
+ true,
),
),
),
diff --git a/internal/command/idp_config_model.go b/internal/command/idp_config_model.go
index 133e12047..c41ace1bb 100644
--- a/internal/command/idp_config_model.go
+++ b/internal/command/idp_config_model.go
@@ -11,9 +11,10 @@ type IDPConfigWriteModel struct {
State domain.IDPConfigState
- ConfigID string
- Name string
- StylingType domain.IDPConfigStylingType
+ ConfigID string
+ Name string
+ AutoRegister bool
+ StylingType domain.IDPConfigStylingType
}
func (rm *IDPConfigWriteModel) AppendEvents(events ...eventstore.EventReader) {
@@ -42,6 +43,7 @@ func (rm *IDPConfigWriteModel) reduceConfigAddedEvent(e *idpconfig.IDPConfigAdde
rm.ConfigID = e.ConfigID
rm.Name = e.Name
rm.StylingType = e.StylingType
+ rm.AutoRegister = e.AutoRegister
rm.State = domain.IDPConfigStateActive
}
@@ -52,6 +54,9 @@ func (rm *IDPConfigWriteModel) reduceConfigChangedEvent(e *idpconfig.IDPConfigCh
if e.StylingType != nil && e.StylingType.Valid() {
rm.StylingType = *e.StylingType
}
+ if e.AutoRegister != nil {
+ rm.AutoRegister = *e.AutoRegister
+ }
}
func (rm *IDPConfigWriteModel) reduceConfigStateChanged(configID string, state domain.IDPConfigState) {
diff --git a/internal/command/org_idp_config.go b/internal/command/org_idp_config.go
index 6c9043807..2a15ab907 100644
--- a/internal/command/org_idp_config.go
+++ b/internal/command/org_idp_config.go
@@ -40,6 +40,7 @@ func (c *Commands) AddIDPConfig(ctx context.Context, config *domain.IDPConfig, r
config.Name,
config.Type,
config.StylingType,
+ config.AutoRegister,
),
org_repo.NewIDPOIDCConfigAddedEvent(
ctx,
@@ -69,12 +70,12 @@ func (c *Commands) ChangeIDPConfig(ctx context.Context, config *domain.IDPConfig
if resourceOwner == "" {
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-Gh8ds", "Errors.ResourceOwnerMissing")
}
- existingIDP, err := c.orgIDPConfigWriteModelByID(ctx, config.IDPConfigID, config.AggregateID)
+ existingIDP, err := c.orgIDPConfigWriteModelByID(ctx, config.IDPConfigID, resourceOwner)
if err != nil {
return nil, err
}
if existingIDP.State == domain.IDPConfigStateRemoved || existingIDP.State == domain.IDPConfigStateUnspecified {
- return nil, caos_errs.ThrowNotFound(nil, "Org-4M9so", "Errors.Org.IDPConfig.NotExisting")
+ return nil, caos_errs.ThrowNotFound(nil, "Org-1J9fs", "Errors.Org.IDPConfig.NotExisting")
}
orgAgg := OrgAggregateFromWriteModel(&existingIDP.WriteModel)
@@ -83,7 +84,8 @@ func (c *Commands) ChangeIDPConfig(ctx context.Context, config *domain.IDPConfig
orgAgg,
config.IDPConfigID,
config.Name,
- config.StylingType)
+ config.StylingType,
+ config.AutoRegister)
if !hasChanged {
return nil, caos_errs.ThrowPreconditionFailed(nil, "Org-4M9vs", "Errors.Org.LabelPolicy.NotChanged")
@@ -105,7 +107,7 @@ func (c *Commands) DeactivateIDPConfig(ctx context.Context, idpID, orgID string)
return nil, err
}
if existingIDP.State != domain.IDPConfigStateActive {
- return nil, caos_errs.ThrowPreconditionFailed(nil, "Org-4M9so", "Errors.Org.IDPConfig.NotActive")
+ return nil, caos_errs.ThrowPreconditionFailed(nil, "Org-BBmd0", "Errors.Org.IDPConfig.NotActive")
}
orgAgg := OrgAggregateFromWriteModel(&existingIDP.WriteModel)
pushedEvents, err := c.eventstore.PushEvents(ctx, org_repo.NewIDPConfigDeactivatedEvent(ctx, orgAgg, idpID))
@@ -185,7 +187,7 @@ func (c *Commands) getOrgIDPConfigByID(ctx context.Context, idpID, orgID string)
return nil, err
}
if !config.State.Exists() {
- return nil, caos_errs.ThrowNotFound(nil, "ORG-4M9so", "Errors.Org.IDPConfig.NotExisting")
+ return nil, caos_errs.ThrowNotFound(nil, "ORG-2m90f", "Errors.Org.IDPConfig.NotExisting")
}
return writeModelToIDPConfig(&config.IDPConfigWriteModel), nil
}
diff --git a/internal/command/org_idp_config_model.go b/internal/command/org_idp_config_model.go
index b3c466bfa..2c5599da1 100644
--- a/internal/command/org_idp_config_model.go
+++ b/internal/command/org_idp_config_model.go
@@ -100,6 +100,7 @@ func (wm *OrgIDPConfigWriteModel) NewChangedEvent(
configID,
name string,
stylingType domain.IDPConfigStylingType,
+ autoRegister bool,
) (*org.IDPConfigChangedEvent, bool) {
changes := make([]idpconfig.IDPConfigChanges, 0)
@@ -111,6 +112,9 @@ func (wm *OrgIDPConfigWriteModel) NewChangedEvent(
if stylingType.Valid() && wm.StylingType != stylingType {
changes = append(changes, idpconfig.ChangeStyleType(stylingType))
}
+ if wm.AutoRegister != autoRegister {
+ changes = append(changes, idpconfig.ChangeAutoRegister(autoRegister))
+ }
if len(changes) == 0 {
return nil, false
}
diff --git a/internal/command/org_idp_config_test.go b/internal/command/org_idp_config_test.go
index 781849ea4..ffef7899d 100644
--- a/internal/command/org_idp_config_test.go
+++ b/internal/command/org_idp_config_test.go
@@ -50,8 +50,9 @@ func TestCommandSide_AddIDPConfig(t *testing.T) {
args: args{
ctx: context.Background(),
config: &domain.IDPConfig{
- Name: "name1",
- StylingType: domain.IDPConfigStylingTypeGoogle,
+ Name: "name1",
+ StylingType: domain.IDPConfigStylingTypeGoogle,
+ AutoRegister: true,
OIDCConfig: &domain.OIDCIDPConfig{
ClientID: "clientid1",
Issuer: "issuer",
@@ -96,6 +97,7 @@ func TestCommandSide_AddIDPConfig(t *testing.T) {
"name1",
domain.IDPConfigTypeOIDC,
domain.IDPConfigStylingTypeGoogle,
+ true,
),
),
eventFromEventPusher(
@@ -128,8 +130,9 @@ func TestCommandSide_AddIDPConfig(t *testing.T) {
ctx: context.Background(),
resourceOwner: "org1",
config: &domain.IDPConfig{
- Name: "name1",
- StylingType: domain.IDPConfigStylingTypeGoogle,
+ Name: "name1",
+ StylingType: domain.IDPConfigStylingTypeGoogle,
+ AutoRegister: true,
OIDCConfig: &domain.OIDCIDPConfig{
ClientID: "clientid1",
Issuer: "issuer",
@@ -148,10 +151,11 @@ func TestCommandSide_AddIDPConfig(t *testing.T) {
AggregateID: "org1",
ResourceOwner: "org1",
},
- IDPConfigID: "config1",
- Name: "name1",
- StylingType: domain.IDPConfigStylingTypeGoogle,
- State: domain.IDPConfigStateActive,
+ IDPConfigID: "config1",
+ Name: "name1",
+ StylingType: domain.IDPConfigStylingTypeGoogle,
+ State: domain.IDPConfigStateActive,
+ AutoRegister: true,
},
},
},
@@ -260,6 +264,7 @@ func TestCommandSide_ChangeIDPConfig(t *testing.T) {
"name1",
domain.IDPConfigTypeOIDC,
domain.IDPConfigStylingTypeGoogle,
+ true,
),
),
eventFromEventPusher(
@@ -297,9 +302,10 @@ func TestCommandSide_ChangeIDPConfig(t *testing.T) {
ctx: context.Background(),
resourceOwner: "org1",
config: &domain.IDPConfig{
- IDPConfigID: "config1",
- Name: "name2",
- StylingType: domain.IDPConfigStylingTypeUnspecified,
+ IDPConfigID: "config1",
+ Name: "name2",
+ StylingType: domain.IDPConfigStylingTypeUnspecified,
+ AutoRegister: true,
},
},
res: res{
@@ -308,10 +314,11 @@ func TestCommandSide_ChangeIDPConfig(t *testing.T) {
AggregateID: "org1",
ResourceOwner: "org1",
},
- IDPConfigID: "config1",
- Name: "name2",
- StylingType: domain.IDPConfigStylingTypeUnspecified,
- State: domain.IDPConfigStateActive,
+ IDPConfigID: "config1",
+ Name: "name2",
+ StylingType: domain.IDPConfigStylingTypeUnspecified,
+ State: domain.IDPConfigStateActive,
+ AutoRegister: true,
},
},
},
diff --git a/internal/command/org_idp_oidc_config_test.go b/internal/command/org_idp_oidc_config_test.go
index ec835de87..214a841f0 100644
--- a/internal/command/org_idp_oidc_config_test.go
+++ b/internal/command/org_idp_oidc_config_test.go
@@ -104,6 +104,7 @@ func TestCommandSide_ChangeIDPOIDCConfig(t *testing.T) {
"name1",
domain.IDPConfigTypeOIDC,
domain.IDPConfigStylingTypeGoogle,
+ true,
),
),
eventFromEventPusher(
@@ -159,6 +160,7 @@ func TestCommandSide_ChangeIDPOIDCConfig(t *testing.T) {
"name1",
domain.IDPConfigTypeOIDC,
domain.IDPConfigStylingTypeGoogle,
+ true,
),
),
eventFromEventPusher(
@@ -215,6 +217,7 @@ func TestCommandSide_ChangeIDPOIDCConfig(t *testing.T) {
"name1",
domain.IDPConfigTypeOIDC,
domain.IDPConfigStylingTypeGoogle,
+ true,
),
),
eventFromEventPusher(
diff --git a/internal/command/org_policy_login_test.go b/internal/command/org_policy_login_test.go
index ddd73b38a..ff4cbb471 100644
--- a/internal/command/org_policy_login_test.go
+++ b/internal/command/org_policy_login_test.go
@@ -659,6 +659,7 @@ func TestCommandSide_AddIDPProviderLoginPolicy(t *testing.T) {
"name",
domain.IDPConfigTypeOIDC,
domain.IDPConfigStylingTypeUnspecified,
+ true,
),
),
),
@@ -710,6 +711,7 @@ func TestCommandSide_AddIDPProviderLoginPolicy(t *testing.T) {
"name",
domain.IDPConfigTypeOIDC,
domain.IDPConfigStylingTypeUnspecified,
+ true,
),
),
),
diff --git a/internal/command/user_human_externalidp_test.go b/internal/command/user_human_externalidp_test.go
index d08b30d22..a74e575be 100644
--- a/internal/command/user_human_externalidp_test.go
+++ b/internal/command/user_human_externalidp_test.go
@@ -164,6 +164,7 @@ func TestCommandSide_BulkAddExternalIDPs(t *testing.T) {
"name",
domain.IDPConfigTypeOIDC,
domain.IDPConfigStylingTypeUnspecified,
+ true,
),
),
),
@@ -213,6 +214,7 @@ func TestCommandSide_BulkAddExternalIDPs(t *testing.T) {
"name",
domain.IDPConfigTypeOIDC,
domain.IDPConfigStylingTypeUnspecified,
+ true,
),
),
),
diff --git a/internal/domain/idp_config.go b/internal/domain/idp_config.go
index 063eb17fe..b1ce3178a 100644
--- a/internal/domain/idp_config.go
+++ b/internal/domain/idp_config.go
@@ -9,12 +9,13 @@ import (
type IDPConfig struct {
es_models.ObjectRoot
- IDPConfigID string
- Type IDPConfigType
- Name string
- StylingType IDPConfigStylingType
- State IDPConfigState
- OIDCConfig *OIDCIDPConfig
+ IDPConfigID string
+ Type IDPConfigType
+ Name string
+ StylingType IDPConfigStylingType
+ State IDPConfigState
+ OIDCConfig *OIDCIDPConfig
+ AutoRegister bool
}
type IDPConfigView struct {
@@ -27,6 +28,7 @@ type IDPConfigView struct {
ChangeDate time.Time
Sequence uint64
IDPProviderType IdentityProviderType
+ AutoRegister bool
IsOIDC bool
OIDCClientID string
diff --git a/internal/domain/next_step.go b/internal/domain/next_step.go
index 57eaa3304..01083b0d1 100644
--- a/internal/domain/next_step.go
+++ b/internal/domain/next_step.go
@@ -27,6 +27,7 @@ const (
NextStepPasswordlessRegistrationPrompt
NextStepRegistration
NextStepProjectRequired
+ NextStepRedirectToExternalIDP
)
type LoginStep struct{}
@@ -67,6 +68,12 @@ const (
UserSessionStateTerminated
)
+type RedirectToExternalIDPStep struct{}
+
+func (s *RedirectToExternalIDPStep) Type() NextStepType {
+ return NextStepRedirectToExternalIDP
+}
+
type InitUserStep struct {
PasswordSet bool
}
diff --git a/internal/iam/model/idp_config_view.go b/internal/iam/model/idp_config_view.go
index 37de4feed..9c7cbb25a 100644
--- a/internal/iam/model/idp_config_view.go
+++ b/internal/iam/model/idp_config_view.go
@@ -13,6 +13,7 @@ type IDPConfigView struct {
IDPConfigID string
Name string
StylingType IDPStylingType
+ AutoRegister bool
State IDPConfigState
CreationDate time.Time
ChangeDate time.Time
diff --git a/internal/iam/repository/view/model/idp_config.go b/internal/iam/repository/view/model/idp_config.go
index 5470dc634..78cfab6cb 100644
--- a/internal/iam/repository/view/model/idp_config.go
+++ b/internal/iam/repository/view/model/idp_config.go
@@ -33,6 +33,7 @@ type IDPConfigView struct {
ChangeDate time.Time `json:"-" gorm:"column:change_date"`
IDPState int32 `json:"-" gorm:"column:idp_state"`
IDPProviderType int32 `json:"-" gorm:"column:idp_provider_type"`
+ AutoRegister bool `json:"autoRegister" gorm:"column:auto_register"`
IsOIDC bool `json:"-" gorm:"column:is_oidc"`
OIDCClientID string `json:"clientId" gorm:"column:oidc_client_id"`
@@ -54,6 +55,7 @@ func IDPConfigViewToModel(idp *IDPConfigView) *model.IDPConfigView {
State: model.IDPConfigState(idp.IDPState),
Name: idp.Name,
StylingType: model.IDPStylingType(idp.StylingType),
+ AutoRegister: idp.AutoRegister,
Sequence: idp.Sequence,
CreationDate: idp.CreationDate,
ChangeDate: idp.ChangeDate,
diff --git a/internal/query/idp_config_model.go b/internal/query/idp_config_model.go
index 35ffecdd9..044069900 100644
--- a/internal/query/idp_config_model.go
+++ b/internal/query/idp_config_model.go
@@ -12,6 +12,7 @@ type IDPConfigReadModel struct {
State domain.IDPConfigState
ConfigID string
Name string
+ AutoRegister bool
StylingType domain.IDPConfigStylingType
ProviderType domain.IdentityProviderType
@@ -77,6 +78,7 @@ func (rm *IDPConfigReadModel) reduceConfigAddedEvent(e *idpconfig.IDPConfigAdded
rm.Name = e.Name
rm.StylingType = e.StylingType
rm.State = domain.IDPConfigStateActive
+ rm.AutoRegister = e.AutoRegister
}
func (rm *IDPConfigReadModel) reduceConfigChangedEvent(e *idpconfig.IDPConfigChangedEvent) {
@@ -86,6 +88,9 @@ func (rm *IDPConfigReadModel) reduceConfigChangedEvent(e *idpconfig.IDPConfigCha
if e.StylingType != nil && e.StylingType.Valid() {
rm.StylingType = *e.StylingType
}
+ if e.AutoRegister != nil {
+ rm.AutoRegister = *e.AutoRegister
+ }
}
func (rm *IDPConfigReadModel) reduceConfigStateChanged(configID string, state domain.IDPConfigState) {
diff --git a/internal/repository/iam/idp_config.go b/internal/repository/iam/idp_config.go
index 1f2226ece..6152c13ad 100644
--- a/internal/repository/iam/idp_config.go
+++ b/internal/repository/iam/idp_config.go
@@ -28,6 +28,7 @@ func NewIDPConfigAddedEvent(
name string,
configType domain.IDPConfigType,
stylingType domain.IDPConfigStylingType,
+ autoRegister bool,
) *IDPConfigAddedEvent {
return &IDPConfigAddedEvent{
@@ -41,6 +42,7 @@ func NewIDPConfigAddedEvent(
name,
configType,
stylingType,
+ autoRegister,
),
}
}
diff --git a/internal/repository/idpconfig/idp_config.go b/internal/repository/idpconfig/idp_config.go
index e022503e6..5ce71f4ba 100644
--- a/internal/repository/idpconfig/idp_config.go
+++ b/internal/repository/idpconfig/idp_config.go
@@ -29,10 +29,11 @@ func NewRemoveIDPConfigNameUniqueConstraint(idpConfigName, resourceOwner string)
type IDPConfigAddedEvent struct {
eventstore.BaseEvent `json:"-"`
- ConfigID string `json:"idpConfigId"`
- Name string `json:"name,omitempty"`
- Typ domain.IDPConfigType `json:"idpType,omitempty"`
- StylingType domain.IDPConfigStylingType `json:"stylingType,omitempty"`
+ ConfigID string `json:"idpConfigId"`
+ Name string `json:"name,omitempty"`
+ Typ domain.IDPConfigType `json:"idpType,omitempty"`
+ StylingType domain.IDPConfigStylingType `json:"stylingType,omitempty"`
+ AutoRegister bool `json:"autoRegister,omitempty"`
}
func NewIDPConfigAddedEvent(
@@ -41,13 +42,15 @@ func NewIDPConfigAddedEvent(
name string,
configType domain.IDPConfigType,
stylingType domain.IDPConfigStylingType,
+ autoRegister bool,
) *IDPConfigAddedEvent {
return &IDPConfigAddedEvent{
- BaseEvent: *base,
- ConfigID: configID,
- Name: name,
- StylingType: stylingType,
- Typ: configType,
+ BaseEvent: *base,
+ ConfigID: configID,
+ Name: name,
+ StylingType: stylingType,
+ Typ: configType,
+ AutoRegister: autoRegister,
}
}
@@ -75,10 +78,11 @@ func IDPConfigAddedEventMapper(event *repository.Event) (eventstore.EventReader,
type IDPConfigChangedEvent struct {
eventstore.BaseEvent `json:"-"`
- ConfigID string `json:"idpConfigId"`
- Name *string `json:"name,omitempty"`
- StylingType *domain.IDPConfigStylingType `json:"stylingType,omitempty"`
- oldName string `json:"-"`
+ ConfigID string `json:"idpConfigId"`
+ Name *string `json:"name,omitempty"`
+ StylingType *domain.IDPConfigStylingType `json:"stylingType,omitempty"`
+ AutoRegister *bool `json:"autoRegister,omitempty"`
+ oldName string `json:"-"`
}
func (e *IDPConfigChangedEvent) Data() interface{} {
@@ -129,6 +133,12 @@ func ChangeStyleType(styleType domain.IDPConfigStylingType) func(*IDPConfigChang
}
}
+func ChangeAutoRegister(autoRegister bool) func(*IDPConfigChangedEvent) {
+ return func(e *IDPConfigChangedEvent) {
+ e.AutoRegister = &autoRegister
+ }
+}
+
func IDPConfigChangedEventMapper(event *repository.Event) (eventstore.EventReader, error) {
e := &IDPConfigChangedEvent{
BaseEvent: *eventstore.BaseEventFromRepo(event),
diff --git a/internal/repository/org/idp_config.go b/internal/repository/org/idp_config.go
index 0e787872f..9b17c5eb1 100644
--- a/internal/repository/org/idp_config.go
+++ b/internal/repository/org/idp_config.go
@@ -28,6 +28,7 @@ func NewIDPConfigAddedEvent(
name string,
configType domain.IDPConfigType,
stylingType domain.IDPConfigStylingType,
+ autoRegister bool,
) *IDPConfigAddedEvent {
return &IDPConfigAddedEvent{
@@ -41,6 +42,7 @@ func NewIDPConfigAddedEvent(
name,
configType,
stylingType,
+ autoRegister,
),
}
}
diff --git a/internal/ui/login/handler/external_login_handler.go b/internal/ui/login/handler/external_login_handler.go
index 472cf8dc6..2221d58a2 100644
--- a/internal/ui/login/handler/external_login_handler.go
+++ b/internal/ui/login/handler/external_login_handler.go
@@ -160,7 +160,16 @@ func (l *Login) handleExternalUserAuthenticated(w http.ResponseWriter, r *http.R
if errors.IsNotFound(err) {
err = nil
}
- l.renderExternalNotFoundOption(w, r, authReq, err)
+ if !idpConfig.AutoRegister {
+ l.renderExternalNotFoundOption(w, r, authReq, err)
+ return
+ }
+ authReq, err = l.authRepo.AuthRequestByID(r.Context(), authReq.ID, userAgentID)
+ if err != nil {
+ l.renderExternalNotFoundOption(w, r, authReq, err)
+ return
+ }
+ l.handleAutoRegister(w, r, authReq)
return
}
l.renderNextStep(w, r, authReq)
diff --git a/internal/ui/login/handler/external_register_handler.go b/internal/ui/login/handler/external_register_handler.go
index 6ff1c0211..bd49fc939 100644
--- a/internal/ui/login/handler/external_register_handler.go
+++ b/internal/ui/login/handler/external_register_handler.go
@@ -119,7 +119,27 @@ func (l *Login) handleExternalUserRegister(w http.ResponseWriter, r *http.Reques
return
}
user, externalIDP := l.mapTokenToLoginHumanAndExternalIDP(orgIamPolicy, tokens, idpConfig)
- l.renderExternalRegisterOverview(w, r, authReq, orgIamPolicy, user, externalIDP, nil)
+ if !idpConfig.AutoRegister {
+ l.renderExternalRegisterOverview(w, r, authReq, orgIamPolicy, user, externalIDP, nil)
+ return
+ }
+ l.registerExternalUser(w, r, authReq, iam, user, externalIDP)
+}
+
+func (l *Login) registerExternalUser(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, iam *iam_model.IAM, user *domain.Human, externalIDP *domain.ExternalIDP) {
+ resourceOwner := iam.GlobalOrgID
+ memberRoles := []string{domain.RoleOrgProjectCreator}
+
+ if authReq.RequestedOrgID != "" && authReq.RequestedOrgID != resourceOwner {
+ memberRoles = nil
+ resourceOwner = authReq.RequestedOrgID
+ }
+ _, err := l.command.RegisterHuman(setContext(r.Context(), resourceOwner), resourceOwner, user, externalIDP, memberRoles)
+ if err != nil {
+ l.renderRegisterOption(w, r, authReq, err)
+ return
+ }
+ l.renderNextStep(w, r, authReq)
}
func (l *Login) renderExternalRegisterOverview(w http.ResponseWriter, r *http.Request, authReq *domain.AuthRequest, orgIAMPolicy *iam_model.OrgIAMPolicyView, human *domain.Human, idp *domain.ExternalIDP, err error) {
diff --git a/internal/ui/login/handler/renderer.go b/internal/ui/login/handler/renderer.go
index 43b3891d9..ff24ca187 100644
--- a/internal/ui/login/handler/renderer.go
+++ b/internal/ui/login/handler/renderer.go
@@ -223,7 +223,7 @@ func (l *Login) renderNextStep(w http.ResponseWriter, r *http.Request, authReq *
userAgentID, _ := http_mw.UserAgentIDFromCtx(r.Context())
authReq, err := l.authRepo.AuthRequestByID(r.Context(), authReq.ID, userAgentID)
if err != nil {
- l.renderInternalError(w, r, authReq, caos_errs.ThrowInternal(err, "APP-sio0W", "could not get authreq"))
+ l.renderInternalError(w, r, authReq, err)
return
}
if len(authReq.PossibleSteps) == 0 {
@@ -257,6 +257,8 @@ func (l *Login) chooseNextStep(w http.ResponseWriter, r *http.Request, authReq *
l.renderRegisterOption(w, r, authReq, nil)
case *domain.SelectUserStep:
l.renderUserSelection(w, r, authReq, step)
+ case *domain.RedirectToExternalIDPStep:
+ l.handleIDP(w, r, authReq, authReq.SelectedIDPConfigID)
case *domain.InitPasswordStep:
l.renderInitPassword(w, r, authReq, authReq.UserID, "", err)
case *domain.PasswordStep:
diff --git a/migrations/cockroach/V1.70__idp.sql b/migrations/cockroach/V1.70__idp.sql
new file mode 100644
index 000000000..798c2696a
--- /dev/null
+++ b/migrations/cockroach/V1.70__idp.sql
@@ -0,0 +1,3 @@
+ALTER TABLE management.idp_configs ADD COLUMN auto_register BOOLEAN;
+ALTER TABLE adminapi.idp_configs ADD COLUMN auto_register BOOLEAN;
+ALTER TABLE auth.idp_configs ADD COLUMN auto_register BOOLEAN;
diff --git a/proto/zitadel/admin.proto b/proto/zitadel/admin.proto
index 6407ef007..c1b105865 100644
--- a/proto/zitadel/admin.proto
+++ b/proto/zitadel/admin.proto
@@ -2428,6 +2428,7 @@ message AddOIDCIDPRequest {
description: "definition which field is mapped to the email of the user";
}
];
+ bool auto_register = 9;
}
message AddOIDCIDPResponse {
@@ -2458,6 +2459,7 @@ message UpdateIDPRequest {
description: "some identity providers specify the styling of the button to their login";
}
];
+ bool auto_register = 4;
}
message UpdateIDPResponse {
diff --git a/proto/zitadel/idp.proto b/proto/zitadel/idp.proto
index d6fb1a688..2d7b28c4d 100644
--- a/proto/zitadel/idp.proto
+++ b/proto/zitadel/idp.proto
@@ -38,6 +38,7 @@ message IDP {
oneof config {
OIDCConfig oidc_config = 7;
}
+ bool auto_register = 8;
}
message IDPUserLink {
diff --git a/proto/zitadel/management.proto b/proto/zitadel/management.proto
index ffe0ab1e2..c9a90e01d 100644
--- a/proto/zitadel/management.proto
+++ b/proto/zitadel/management.proto
@@ -4884,6 +4884,7 @@ message AddOrgOIDCIDPRequest {
description: "definition which field is mapped to the email of the user";
}
];
+ bool auto_register = 9;
}
message AddOrgOIDCIDPResponse {
@@ -4928,6 +4929,7 @@ message UpdateOrgIDPRequest {
description: "some identity providers specify the styling of the button to their login";
}
];
+ bool auto_register = 4;
}
message UpdateOrgIDPResponse {