feat: added xoauth for smtp (#11239)

# Which Problems Are Solved

- Some mail providers (e.g.: MS Exchange) have deprecated plain auth

# How the Problems Are Solved

Adds XOauth2 auth option to SMPT
- SMTP config now can contain XOAuth2 auth config
- Proto files are updated to have a oneof for configuration selection.
Old fields for configuring plain auth are still available not to create
a breaking change. These can be removed in the future.
- Columns are added to the database to persist the config

# Additional Changes

none

# Additional Context

Replace this example with links to related issues, discussions, discord
threads, or other sources with more context.
Use the Closing #issue syntax for issues that are resolved with this PR.
- Closes #8042

---------

Co-authored-by: Copilot <[email protected]>
Co-authored-by: Tim Möhlmann <[email protected]>
This commit is contained in:
Wim Van Laer
2026-01-27 15:11:56 +01:00
committed by GitHub
co-authored by Copilot Tim Möhlmann
parent 96bbd6a65c
commit 3fc0ef2506
31 changed files with 2990 additions and 1059 deletions
@@ -165,7 +165,7 @@ func TestListInstances(t *testing.T) {
orgOwnerCtx := inst.WithAuthorizationToken(context.Background(), integration.UserTypeOrgOwner)
relTableState := integration.RelationalTablesEnableMatrix()
relTableState := integration.RelationalTablesEnableMatrix(t, ctx, ctxWithSysAuthZ)
tt := []struct {
testName string
@@ -222,16 +222,13 @@ func TestListInstances(t *testing.T) {
}
for _, stateCase := range relTableState {
integration.EnsureInstanceFeature(t, ctx, inst, stateCase.FeatureSet, func(tCollect *assert.CollectT, got *feature.GetInstanceFeaturesResponse) {
assert.Equal(tCollect, stateCase.FeatureSet.GetEnableRelationalTables(), got.EnableRelationalTables.GetEnabled())
})
for _, tc := range tt {
// TODO(IAM-Marco): Fix this test for relational case when permission checks are in place (see https://github.com/zitadel/zitadel/issues/10917)
if tc.testName == "when unauthZ context should return unauthZ error" && stateCase.State == "when relational tables are enabled" {
if tc.testName == "when unauthZ context should return unauthZ error" && stateCase.Name == "when relational tables are enabled" {
continue
}
t.Run(fmt.Sprintf("%s - %s", stateCase.State, tc.testName), func(t *testing.T) {
t.Run(fmt.Sprintf("%s - %s", stateCase.Name, tc.testName), func(t *testing.T) {
// Test
res, err := inst.Client.InstanceV2.ListInstances(tc.inputContext, tc.inputRequest)
@@ -251,7 +248,6 @@ func TestListInstances(t *testing.T) {
})
}
}
}
func TestListCustomDomains(t *testing.T) {
@@ -34,9 +34,12 @@ func TestAddCustomDomain(t *testing.T) {
relationalInst := integration.NewInstance(ctxWithSysAuthZ)
iamOwnerRelationalCtx := relationalInst.WithAuthorizationToken(context.Background(), integration.UserTypeIAMOwner)
integration.EnsureInstanceFeature(t, ctxWithSysAuthZ, relationalInst, &feature.SetInstanceFeaturesRequest{EnableRelationalTables: gu.Ptr(true)}, func(tCollect *assert.CollectT, got *feature.GetInstanceFeaturesResponse) {
assert.True(tCollect, got.EnableRelationalTables.GetEnabled())
})
integration.EnsureInstanceFeature(t, ctxWithSysAuthZ, relationalInst,
&feature.SetInstanceFeaturesRequest{EnableRelationalTables: gu.Ptr(true)},
func(tCollect *assert.CollectT, got *feature.GetInstanceFeaturesResponse) {
assert.True(tCollect, got.EnableRelationalTables.GetEnabled())
},
)
t.Cleanup(func() {
inst.Client.InstanceV2Beta.DeleteInstance(ctxWithSysAuthZ, &instance.DeleteInstanceRequest{InstanceId: inst.ID()})
@@ -140,7 +140,7 @@ func TestListInstances(t *testing.T) {
orgOwnerCtx := inst.WithAuthorizationToken(context.Background(), integration.UserTypeOrgOwner)
relTableState := integration.RelationalTablesEnableMatrix()
relTableState := integration.RelationalTablesEnableMatrix(t, ctx, ctxWithSysAuthZ)
tt := []struct {
testName string
@@ -196,12 +196,8 @@ func TestListInstances(t *testing.T) {
}
for _, stateCase := range relTableState {
integration.EnsureInstanceFeature(t, ctx, inst, stateCase.FeatureSet, func(tCollect *assert.CollectT, got *feature.GetInstanceFeaturesResponse) {
assert.Equal(tCollect, stateCase.FeatureSet.GetEnableRelationalTables(), got.EnableRelationalTables.GetEnabled())
})
for _, tc := range tt {
t.Run(fmt.Sprintf("%s - %s", stateCase.State, tc.testName), func(t *testing.T) {
t.Run(fmt.Sprintf("%s - %s", stateCase.Name, tc.testName), func(t *testing.T) {
// Test
res, err := inst.Client.InstanceV2Beta.ListInstances(tc.inputContext, tc.inputRequest)