diff --git a/cmd/setup/61.go b/cmd/setup/61.go
new file mode 100644
index 00000000000..cf353b5e2f8
--- /dev/null
+++ b/cmd/setup/61.go
@@ -0,0 +1,27 @@
+package setup
+
+import (
+ "context"
+ _ "embed"
+
+ "github.com/zitadel/zitadel/internal/database"
+ "github.com/zitadel/zitadel/internal/eventstore"
+)
+
+var (
+ //go:embed 61.sql
+ addSAMLSignatureAlgorithm string
+)
+
+type IDPTemplate6SAMLSignatureAlgorithm struct {
+ dbClient *database.DB
+}
+
+func (mig *IDPTemplate6SAMLSignatureAlgorithm) Execute(ctx context.Context, _ eventstore.Event) error {
+ _, err := mig.dbClient.ExecContext(ctx, addSAMLSignatureAlgorithm)
+ return err
+}
+
+func (mig *IDPTemplate6SAMLSignatureAlgorithm) String() string {
+ return "61_idp_templates6_add_saml_signature_algorithm"
+}
diff --git a/cmd/setup/61.sql b/cmd/setup/61.sql
new file mode 100644
index 00000000000..79372549337
--- /dev/null
+++ b/cmd/setup/61.sql
@@ -0,0 +1 @@
+ALTER TABLE IF EXISTS projections.idp_templates6_saml ADD COLUMN IF NOT EXISTS signature_algorithm TEXT;
diff --git a/cmd/setup/config.go b/cmd/setup/config.go
index bac73b0ae58..2a7f4cf41c1 100644
--- a/cmd/setup/config.go
+++ b/cmd/setup/config.go
@@ -157,6 +157,7 @@ type Steps struct {
s58ReplaceLoginNames3View *ReplaceLoginNames3View
s59SetupWebkeys *SetupWebkeys
s60GenerateSystemID *GenerateSystemID
+ s61IDPTemplate6SAMLSignatureAlgorithm *IDPTemplate6SAMLSignatureAlgorithm
}
func MustNewSteps(v *viper.Viper) *Steps {
diff --git a/cmd/setup/setup.go b/cmd/setup/setup.go
index 896e0a4ac47..73489578f89 100644
--- a/cmd/setup/setup.go
+++ b/cmd/setup/setup.go
@@ -218,6 +218,7 @@ func Setup(ctx context.Context, config *Config, steps *Steps, masterKey string)
steps.s57CreateResourceCounts = &CreateResourceCounts{dbClient: dbClient}
steps.s58ReplaceLoginNames3View = &ReplaceLoginNames3View{dbClient: dbClient}
steps.s60GenerateSystemID = &GenerateSystemID{eventstore: eventstoreClient}
+ steps.s61IDPTemplate6SAMLSignatureAlgorithm = &IDPTemplate6SAMLSignatureAlgorithm{dbClient: dbClient}
err = projection.Create(ctx, dbClient, eventstoreClient, config.Projections, nil, nil, nil)
logging.OnError(err).Fatal("unable to start projections")
@@ -266,6 +267,7 @@ func Setup(ctx context.Context, config *Config, steps *Steps, masterKey string)
steps.s57CreateResourceCounts,
steps.s58ReplaceLoginNames3View,
steps.s60GenerateSystemID,
+ steps.s61IDPTemplate6SAMLSignatureAlgorithm,
} {
setupErr = executeMigration(ctx, eventstoreClient, step, "migration failed")
if setupErr != nil {
diff --git a/console/src/app/modules/providers/provider-saml-sp/provider-saml-sp.component.html b/console/src/app/modules/providers/provider-saml-sp/provider-saml-sp.component.html
index c5f9d3905b3..833a44231de 100644
--- a/console/src/app/modules/providers/provider-saml-sp/provider-saml-sp.component.html
+++ b/console/src/app/modules/providers/provider-saml-sp/provider-saml-sp.component.html
@@ -71,6 +71,15 @@
+
+ {{ 'IDP.SAML.SIGNATUREALGORITHM' | translate }}
+
+ {{
+ signatureAlgorithm
+ }}
+
+
+
{{ 'IDP.SAML.NAMEIDFORMAT' | translate }}
diff --git a/console/src/app/modules/providers/provider-saml-sp/provider-saml-sp.component.ts b/console/src/app/modules/providers/provider-saml-sp/provider-saml-sp.component.ts
index cd17633112a..9db3f1a5682 100644
--- a/console/src/app/modules/providers/provider-saml-sp/provider-saml-sp.component.ts
+++ b/console/src/app/modules/providers/provider-saml-sp/provider-saml-sp.component.ts
@@ -6,6 +6,7 @@ import {
Provider,
SAMLBinding,
SAMLNameIDFormat,
+ SAMLSignatureAlgorithm,
} from '../../../proto/generated/zitadel/idp_pb';
import { AbstractControl, FormControl, FormGroup } from '@angular/forms';
import { PolicyComponentServiceType } from '../../policies/policy-component-types.enum';
@@ -38,6 +39,7 @@ interface SAMLProviderForm {
metadataUrl: FormControl;
binding: FormControl;
withSignedRequest: FormControl;
+ signatureAlgorithm: FormControl;
nameIdFormat: FormControl;
transientMappingAttributeName: FormControl;
federatedLogoutEnabled: FormControl;
@@ -65,6 +67,7 @@ export class ProviderSamlSpComponent {
private service!: ManagementService | AdminService;
bindingValues: string[] = getEnumKeys(SAMLBinding);
nameIDFormatValues: string[] = getEnumKeys(SAMLNameIDFormat);
+ signatureAlgorithmValues: string[] = Object.keys(SAMLSignatureAlgorithm);
public justCreated$: BehaviorSubject = new BehaviorSubject('');
public justActivated$ = new BehaviorSubject(false);
@@ -131,6 +134,9 @@ export class ProviderSamlSpComponent {
private _initializeForm(): void {
const defaultBinding = getEnumKeyFromValue(SAMLBinding, SAMLBinding.SAML_BINDING_POST) || this.bindingValues[0];
+ const defaultSignatureAlgorithm =
+ getEnumKeyFromValue(SAMLSignatureAlgorithm, SAMLSignatureAlgorithm.SAML_SIGNATURE_UNSPECIFIED) ||
+ this.signatureAlgorithmValues[0];
const defaultNameIdFormat =
getEnumKeyFromValue(SAMLNameIDFormat, SAMLNameIDFormat.SAML_NAME_ID_FORMAT_PERSISTENT) || this.nameIDFormatValues[0];
@@ -141,6 +147,7 @@ export class ProviderSamlSpComponent {
metadataUrl: new FormControl('', { nonNullable: true }),
binding: new FormControl(defaultBinding, { nonNullable: true, validators: [requiredValidator] }),
withSignedRequest: new FormControl(true, { nonNullable: true, validators: [requiredValidator] }),
+ signatureAlgorithm: new FormControl(defaultSignatureAlgorithm, { nonNullable: true }),
nameIdFormat: new FormControl(defaultNameIdFormat, { nonNullable: true }),
transientMappingAttributeName: new FormControl('', { nonNullable: true }),
federatedLogoutEnabled: new FormControl(false, { nonNullable: true }),
@@ -223,6 +230,10 @@ export class ProviderSamlSpComponent {
}
req.setWithSignedRequest(this.withSignedRequest.value);
req.setBinding(SAMLBinding[this.binding.value as keyof typeof SAMLBinding]);
+ req.setSignatureAlgorithm(
+ SAMLSignatureAlgorithm[this.signatureAlgorithm.value as keyof typeof SAMLSignatureAlgorithm],
+ );
+ // @ts-ignore
req.setNameIdFormat(SAMLNameIDFormat[this.nameIDFormat.value as keyof typeof SAMLNameIDFormat]);
req.setTransientMappingAttributeName(this.transientMapping.value);
req.setFederatedLogoutEnabled(this.federatedLogoutEnabled.value);
@@ -260,6 +271,12 @@ export class ProviderSamlSpComponent {
req.setProviderOptions(this.options);
req.setBinding(SAMLBinding[this.binding.value as keyof typeof SAMLBinding]);
req.setWithSignedRequest(this.withSignedRequest.value);
+ if (this.signatureAlgorithm) {
+ // @ts-ignore
+ req.setSignatureAlgorithm(
+ SAMLSignatureAlgorithm[this.signatureAlgorithm.value as keyof typeof SAMLSignatureAlgorithm],
+ );
+ }
if (this.nameIDFormat) {
req.setNameIdFormat(SAMLNameIDFormat[this.nameIDFormat.value as keyof typeof SAMLNameIDFormat]);
}
@@ -343,6 +360,10 @@ export class ProviderSamlSpComponent {
return this.form.controls.nameIdFormat;
}
+ private get signatureAlgorithm(): FormControl {
+ return this.form.controls.signatureAlgorithm;
+ }
+
private get transientMapping(): FormControl {
return this.form.controls.transientMappingAttributeName;
}
diff --git a/console/src/assets/i18n/en.json b/console/src/assets/i18n/en.json
index 3caac2c1b2a..61fd24a21d4 100644
--- a/console/src/assets/i18n/en.json
+++ b/console/src/assets/i18n/en.json
@@ -2373,6 +2373,7 @@
"METADATAURL": "Metadata URL",
"BINDING": "Binding",
"SIGNEDREQUEST": "Signed Request",
+ "SIGNATUREALGORITHM": "Signature Algorithm",
"NAMEIDFORMAT": "NameID Format",
"TRANSIENTMAPPINGATTRIBUTENAME": "Custom Mapping Attribute Name",
"TRANSIENTMAPPINGATTRIBUTENAME_DESC": "Alternative attribute name to map the user in case the `nameid-format` returned is `transient`, e.g. `http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress`"
diff --git a/console/src/assets/i18n/hu.json b/console/src/assets/i18n/hu.json
index dca3b1e839a..c505d82b63d 100644
--- a/console/src/assets/i18n/hu.json
+++ b/console/src/assets/i18n/hu.json
@@ -2368,6 +2368,7 @@
"METADATAURL": "Metaadatok URL",
"BINDING": "Kötés",
"SIGNEDREQUEST": "Aláírt kérés",
+ "SIGNATUREALGORITHM": "Aláírási algoritmus",
"NAMEIDFORMAT": "NameID formátum",
"TRANSIENTMAPPINGATTRIBUTENAME": "Egyéni leképezési attribútumnév",
"TRANSIENTMAPPINGATTRIBUTENAME_DESC": "Alternatív attribútumnév a felhasználó leképezéséhez, ha a visszakapott `nameid-format` `transient`, pl. `http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress`"
diff --git a/console/src/assets/i18n/id.json b/console/src/assets/i18n/id.json
index d7d5b6dda4a..9be2153bb5e 100644
--- a/console/src/assets/i18n/id.json
+++ b/console/src/assets/i18n/id.json
@@ -2150,6 +2150,7 @@
"METADATAURL": "URL metadata",
"BINDING": "Mengikat",
"SIGNEDREQUEST": "Permintaan yang Ditandatangani",
+ "SIGNATUREALGORITHM": "Algoritma tanda tangan",
"NAMEIDFORMAT": "Format ID Nama",
"TRANSIENTMAPPINGATTRIBUTENAME": "Nama Atribut Pemetaan Khusus",
"TRANSIENTMAPPINGATTRIBUTENAME_DESC": "Nama atribut alternatif untuk memetakan pengguna jika `format nameid` yang dikembalikan adalah `sementara`, mis. `http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress`"
diff --git a/console/src/assets/i18n/ja.json b/console/src/assets/i18n/ja.json
index 07423e451f8..2a4dea9ff19 100644
--- a/console/src/assets/i18n/ja.json
+++ b/console/src/assets/i18n/ja.json
@@ -2370,6 +2370,7 @@
"METADATAURL": "Metadata URL",
"BINDING": "Binding",
"SIGNEDREQUEST": "署名付きリクエスト",
+ "SIGNATUREALGORITHM": "署名アルゴリズム",
"NAMEIDFORMAT": "NameIDフォーマット",
"TRANSIENTMAPPINGATTRIBUTENAME": "カスタム属性名",
"TRANSIENTMAPPINGATTRIBUTENAME_DESC": "NameIDフォーマットが`transient`の場合にユーザーをマッピングするためのカスタム属性名 (例: `http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress`)"
diff --git a/console/src/assets/i18n/ko.json b/console/src/assets/i18n/ko.json
index 19f9e35403e..8ebbfef3b2d 100644
--- a/console/src/assets/i18n/ko.json
+++ b/console/src/assets/i18n/ko.json
@@ -2370,6 +2370,7 @@
"METADATAURL": "메타데이터 URL",
"BINDING": "바인딩",
"SIGNEDREQUEST": "서명된 요청",
+ "SIGNATUREALGORITHM": "서명 알고리즘",
"NAMEIDFORMAT": "NameID 형식",
"TRANSIENTMAPPINGATTRIBUTENAME": "사용자 매핑 속성 이름",
"TRANSIENTMAPPINGATTRIBUTENAME_DESC": "`nameid-format`이 `transient`인 경우 사용자 매핑에 사용할 대체 속성 이름, 예: `http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress`"
diff --git a/console/src/assets/i18n/nl.json b/console/src/assets/i18n/nl.json
index a15488b090c..1a176feeb01 100644
--- a/console/src/assets/i18n/nl.json
+++ b/console/src/assets/i18n/nl.json
@@ -2369,7 +2369,8 @@
"METADATAXML": "Metadata Xml",
"METADATAURL": "Metadata URL",
"BINDING": "Binding",
- "SIGNEDREQUEST": "Ondertekend Verzoek"
+ "SIGNEDREQUEST": "Ondertekend Verzoek",
+ "SIGNATUREALGORITHM": "Handtekeningalgoritme"
},
"TOAST": {
"SAVED": "Succesvol opgeslagen.",
diff --git a/console/src/assets/i18n/ro.json b/console/src/assets/i18n/ro.json
index 9555246294e..f8926f0e2b8 100644
--- a/console/src/assets/i18n/ro.json
+++ b/console/src/assets/i18n/ro.json
@@ -2368,6 +2368,7 @@
"METADATAURL": "URL metadata",
"BINDING": "Legare",
"SIGNEDREQUEST": "Solicitare semnată",
+ "SIGNATUREALGORITHM": "Algoritm de semnătură",
"NAMEIDFORMAT": "Format ID nume",
"TRANSIENTMAPPINGATTRIBUTENAME": "Nume atribut mapare personalizat",
"TRANSIENTMAPPINGATTRIBUTENAME_DESC": "Nume alternativ de atribut pentru a mapa utilizatorul în cazul în care nameid-format returnat este tranzitoriu, de exemplu, http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"
diff --git a/console/src/assets/i18n/tr.json b/console/src/assets/i18n/tr.json
index 39c8a73f8d5..73b360f6312 100644
--- a/console/src/assets/i18n/tr.json
+++ b/console/src/assets/i18n/tr.json
@@ -2373,6 +2373,7 @@
"METADATAURL": "Metadata URL",
"BINDING": "Binding",
"SIGNEDREQUEST": "İmzalı İstek",
+ "SIGNATUREALGORITHM": "İmza algoritması",
"NAMEIDFORMAT": "NameID Formatı",
"TRANSIENTMAPPINGATTRIBUTENAME": "Özel Eşleme Özellik Adı",
"TRANSIENTMAPPINGATTRIBUTENAME_DESC": "`nameid-format` `transient` döndürülmesi durumunda kullanıcıyı eşlemek için alternatif özellik adı, örn. `http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress`"
diff --git a/internal/api/grpc/admin/idp_converter.go b/internal/api/grpc/admin/idp_converter.go
index fd7749d6647..f158f128bdd 100644
--- a/internal/api/grpc/admin/idp_converter.go
+++ b/internal/api/grpc/admin/idp_converter.go
@@ -3,6 +3,7 @@ package admin
import (
"github.com/crewjam/saml"
"github.com/muhlemmer/gu"
+ dsig "github.com/russellhaering/goxmldsig"
idp_grpc "github.com/zitadel/zitadel/internal/api/grpc/idp"
"github.com/zitadel/zitadel/internal/api/grpc/object"
@@ -480,12 +481,14 @@ func addSAMLProviderToCommand(req *admin_pb.AddSAMLProviderRequest) *command.SAM
if req.NameIdFormat != nil {
nameIDFormat = gu.Ptr(idp_grpc.SAMLNameIDFormatToDomain(req.GetNameIdFormat()))
}
+
return &command.SAMLProvider{
Name: req.Name,
Metadata: req.GetMetadataXml(),
MetadataURL: req.GetMetadataUrl(),
Binding: bindingToCommand(req.Binding),
WithSignedRequest: req.WithSignedRequest,
+ SignatureAlgorithm: signatureAlgorithmToCommand(req.GetSignatureAlgorithm()),
NameIDFormat: nameIDFormat,
TransientMappingAttributeName: req.GetTransientMappingAttributeName(),
FederatedLogoutEnabled: req.GetFederatedLogoutEnabled(),
@@ -498,12 +501,14 @@ func updateSAMLProviderToCommand(req *admin_pb.UpdateSAMLProviderRequest) *comma
if req.NameIdFormat != nil {
nameIDFormat = gu.Ptr(idp_grpc.SAMLNameIDFormatToDomain(req.GetNameIdFormat()))
}
+
return &command.SAMLProvider{
Name: req.Name,
Metadata: req.GetMetadataXml(),
MetadataURL: req.GetMetadataUrl(),
Binding: bindingToCommand(req.Binding),
WithSignedRequest: req.WithSignedRequest,
+ SignatureAlgorithm: signatureAlgorithmToCommand(req.GetSignatureAlgorithm()),
NameIDFormat: nameIDFormat,
TransientMappingAttributeName: req.GetTransientMappingAttributeName(),
FederatedLogoutEnabled: req.GetFederatedLogoutEnabled(),
@@ -525,3 +530,18 @@ func bindingToCommand(binding idp_pb.SAMLBinding) string {
return ""
}
}
+
+func signatureAlgorithmToCommand(signatureAlgorithm idp_pb.SAMLSignatureAlgorithm) string {
+ switch signatureAlgorithm {
+ case idp_pb.SAMLSignatureAlgorithm_SAML_SIGNATURE_UNSPECIFIED:
+ return ""
+ case idp_pb.SAMLSignatureAlgorithm_SAML_SIGNATURE_RSA_SHA1:
+ return dsig.RSASHA1SignatureMethod
+ case idp_pb.SAMLSignatureAlgorithm_SAML_SIGNATURE_RSA_SHA256:
+ return dsig.RSASHA256SignatureMethod
+ case idp_pb.SAMLSignatureAlgorithm_SAML_SIGNATURE_RSA_SHA512:
+ return dsig.RSASHA512SignatureMethod
+ default:
+ return ""
+ }
+}
diff --git a/internal/api/grpc/admin/idp_converter_test.go b/internal/api/grpc/admin/idp_converter_test.go
index 23d378b8883..adad5f348dd 100644
--- a/internal/api/grpc/admin/idp_converter_test.go
+++ b/internal/api/grpc/admin/idp_converter_test.go
@@ -3,6 +3,8 @@ package admin
import (
"testing"
+ "github.com/stretchr/testify/require"
+
"github.com/zitadel/zitadel/internal/test"
admin_pb "github.com/zitadel/zitadel/pkg/grpc/admin"
"github.com/zitadel/zitadel/pkg/grpc/idp"
@@ -157,3 +159,40 @@ func Test_updateOIDCConfigToDomain(t *testing.T) {
})
}
}
+
+func Test_signatureAlgorithmToCommand(t *testing.T) {
+ t.Parallel()
+ tests := []struct {
+ name string
+ signatureAlgorithm idp.SAMLSignatureAlgorithm
+ wantSignatureAlgorithm string
+ }{
+ {
+ name: "signature algorithm default value",
+ signatureAlgorithm: 11,
+ wantSignatureAlgorithm: "",
+ },
+ {
+ name: "RSA_SHA1",
+ signatureAlgorithm: idp.SAMLSignatureAlgorithm_SAML_SIGNATURE_RSA_SHA1,
+ wantSignatureAlgorithm: "http://www.w3.org/2000/09/xmldsig#rsa-sha1",
+ },
+ {
+ name: "RSA_SHA256",
+ signatureAlgorithm: idp.SAMLSignatureAlgorithm_SAML_SIGNATURE_RSA_SHA256,
+ wantSignatureAlgorithm: "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256",
+ },
+ {
+ name: "RSA_SHA512",
+ signatureAlgorithm: idp.SAMLSignatureAlgorithm_SAML_SIGNATURE_RSA_SHA512,
+ wantSignatureAlgorithm: "http://www.w3.org/2001/04/xmldsig-more#rsa-sha512",
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ t.Parallel()
+ got := signatureAlgorithmToCommand(tt.signatureAlgorithm)
+ require.Equal(t, tt.wantSignatureAlgorithm, got)
+ })
+ }
+}
diff --git a/internal/api/grpc/idp/converter.go b/internal/api/grpc/idp/converter.go
index caab5121b8f..6e8f14359ce 100644
--- a/internal/api/grpc/idp/converter.go
+++ b/internal/api/grpc/idp/converter.go
@@ -3,6 +3,7 @@ package idp
import (
"github.com/crewjam/saml"
"github.com/muhlemmer/gu"
+ dsig "github.com/russellhaering/goxmldsig"
"google.golang.org/protobuf/types/known/durationpb"
obj_grpc "github.com/zitadel/zitadel/internal/api/grpc/object"
@@ -667,6 +668,7 @@ func samlConfigToPb(providerConfig *idp_pb.ProviderConfig, template *query.SAMLI
MetadataXml: template.Metadata,
Binding: bindingToPb(template.Binding),
WithSignedRequest: template.WithSignedRequest,
+ SignatureAlgorithm: gu.Ptr(signatureAlgorithmToPb(template.SignatureAlgorithm)),
NameIdFormat: nameIDFormat,
TransientMappingAttributeName: gu.Ptr(template.TransientMappingAttributeName),
FederatedLogoutEnabled: gu.Ptr(template.FederatedLogoutEnabled),
@@ -703,3 +705,16 @@ func nameIDToPb(format domain.SAMLNameIDFormat) idp_pb.SAMLNameIDFormat {
return idp_pb.SAMLNameIDFormat_SAML_NAME_ID_FORMAT_UNSPECIFIED
}
}
+
+func signatureAlgorithmToPb(signatureAlgorithm string) idp_pb.SAMLSignatureAlgorithm {
+ switch signatureAlgorithm {
+ case dsig.RSASHA1SignatureMethod:
+ return idp_pb.SAMLSignatureAlgorithm_SAML_SIGNATURE_RSA_SHA1
+ case dsig.RSASHA256SignatureMethod:
+ return idp_pb.SAMLSignatureAlgorithm_SAML_SIGNATURE_RSA_SHA256
+ case dsig.RSASHA512SignatureMethod:
+ return idp_pb.SAMLSignatureAlgorithm_SAML_SIGNATURE_RSA_SHA512
+ default:
+ return idp_pb.SAMLSignatureAlgorithm_SAML_SIGNATURE_UNSPECIFIED
+ }
+}
diff --git a/internal/api/grpc/idp/v2/query.go b/internal/api/grpc/idp/v2/query.go
index 587b1687b91..c9c763cb96b 100644
--- a/internal/api/grpc/idp/v2/query.go
+++ b/internal/api/grpc/idp/v2/query.go
@@ -6,6 +6,7 @@ import (
"connectrpc.com/connect"
"github.com/crewjam/saml"
"github.com/muhlemmer/gu"
+ dsig "github.com/russellhaering/goxmldsig"
"google.golang.org/protobuf/types/known/durationpb"
"github.com/zitadel/zitadel/internal/api/grpc/object/v2"
@@ -335,6 +336,7 @@ func samlConfigToPb(idpConfig *idp_pb.IDPConfig, template *query.SAMLIDPTemplate
MetadataXml: template.Metadata,
Binding: bindingToPb(template.Binding),
WithSignedRequest: template.WithSignedRequest,
+ SignatureAlgorithm: signatureAlgorithmToPb(template.SignatureAlgorithm),
NameIdFormat: nameIDFormat,
TransientMappingAttributeName: gu.Ptr(template.TransientMappingAttributeName),
FederatedLogoutEnabled: gu.Ptr(template.FederatedLogoutEnabled),
@@ -371,3 +373,16 @@ func nameIDToPb(format domain.SAMLNameIDFormat) idp_pb.SAMLNameIDFormat {
return idp_pb.SAMLNameIDFormat_SAML_NAME_ID_FORMAT_UNSPECIFIED
}
}
+
+func signatureAlgorithmToPb(signatureAlgorithm string) idp_pb.SAMLSignatureAlgorithm {
+ switch signatureAlgorithm {
+ case dsig.RSASHA1SignatureMethod:
+ return idp_pb.SAMLSignatureAlgorithm_SAML_SIGNATURE_RSA_SHA1
+ case dsig.RSASHA256SignatureMethod:
+ return idp_pb.SAMLSignatureAlgorithm_SAML_SIGNATURE_RSA_SHA256
+ case dsig.RSASHA512SignatureMethod:
+ return idp_pb.SAMLSignatureAlgorithm_SAML_SIGNATURE_RSA_SHA512
+ default:
+ return idp_pb.SAMLSignatureAlgorithm_SAML_SIGNATURE_UNSPECIFIED
+ }
+}
diff --git a/internal/api/grpc/management/idp_converter.go b/internal/api/grpc/management/idp_converter.go
index 37169dfc6d1..6fa49d081f0 100644
--- a/internal/api/grpc/management/idp_converter.go
+++ b/internal/api/grpc/management/idp_converter.go
@@ -5,6 +5,7 @@ import (
"github.com/crewjam/saml"
"github.com/muhlemmer/gu"
+ dsig "github.com/russellhaering/goxmldsig"
"github.com/zitadel/zitadel/internal/api/authz"
idp_grpc "github.com/zitadel/zitadel/internal/api/grpc/idp"
@@ -473,12 +474,14 @@ func addSAMLProviderToCommand(req *mgmt_pb.AddSAMLProviderRequest) *command.SAML
if req.NameIdFormat != nil {
nameIDFormat = gu.Ptr(idp_grpc.SAMLNameIDFormatToDomain(req.GetNameIdFormat()))
}
+
return &command.SAMLProvider{
Name: req.Name,
Metadata: req.GetMetadataXml(),
MetadataURL: req.GetMetadataUrl(),
Binding: bindingToCommand(req.Binding),
WithSignedRequest: req.WithSignedRequest,
+ SignatureAlgorithm: signatureAlgorithmToCommand(req.GetSignatureAlgorithm()),
NameIDFormat: nameIDFormat,
TransientMappingAttributeName: req.GetTransientMappingAttributeName(),
FederatedLogoutEnabled: req.GetFederatedLogoutEnabled(),
@@ -491,12 +494,14 @@ func updateSAMLProviderToCommand(req *mgmt_pb.UpdateSAMLProviderRequest) *comman
if req.NameIdFormat != nil {
nameIDFormat = gu.Ptr(idp_grpc.SAMLNameIDFormatToDomain(req.GetNameIdFormat()))
}
+
return &command.SAMLProvider{
Name: req.Name,
Metadata: req.GetMetadataXml(),
MetadataURL: req.GetMetadataUrl(),
Binding: bindingToCommand(req.Binding),
WithSignedRequest: req.WithSignedRequest,
+ SignatureAlgorithm: signatureAlgorithmToCommand(req.GetSignatureAlgorithm()),
NameIDFormat: nameIDFormat,
TransientMappingAttributeName: req.GetTransientMappingAttributeName(),
FederatedLogoutEnabled: req.GetFederatedLogoutEnabled(),
@@ -518,3 +523,18 @@ func bindingToCommand(binding idp_pb.SAMLBinding) string {
return ""
}
}
+
+func signatureAlgorithmToCommand(signatureAlgorithm idp_pb.SAMLSignatureAlgorithm) string {
+ switch signatureAlgorithm {
+ case idp_pb.SAMLSignatureAlgorithm_SAML_SIGNATURE_UNSPECIFIED:
+ return ""
+ case idp_pb.SAMLSignatureAlgorithm_SAML_SIGNATURE_RSA_SHA1:
+ return dsig.RSASHA1SignatureMethod
+ case idp_pb.SAMLSignatureAlgorithm_SAML_SIGNATURE_RSA_SHA256:
+ return dsig.RSASHA256SignatureMethod
+ case idp_pb.SAMLSignatureAlgorithm_SAML_SIGNATURE_RSA_SHA512:
+ return dsig.RSASHA512SignatureMethod
+ default:
+ return ""
+ }
+}
diff --git a/internal/api/grpc/management/idp_converter_test.go b/internal/api/grpc/management/idp_converter_test.go
index 999be2ea069..78640aa959f 100644
--- a/internal/api/grpc/management/idp_converter_test.go
+++ b/internal/api/grpc/management/idp_converter_test.go
@@ -3,6 +3,8 @@ package management
import (
"testing"
+ "github.com/stretchr/testify/require"
+
"github.com/zitadel/zitadel/internal/test"
"github.com/zitadel/zitadel/pkg/grpc/idp"
mgmt_pb "github.com/zitadel/zitadel/pkg/grpc/management"
@@ -157,3 +159,40 @@ func Test_updateOIDCConfigToDomain(t *testing.T) {
})
}
}
+
+func Test_signatureAlgorithmToCommand(t *testing.T) {
+ t.Parallel()
+ tests := []struct {
+ name string
+ signatureAlgorithm idp.SAMLSignatureAlgorithm
+ wantSignatureAlgorithm string
+ }{
+ {
+ name: "signature algorithm default value",
+ signatureAlgorithm: 11,
+ wantSignatureAlgorithm: "",
+ },
+ {
+ name: "RSA_SHA1",
+ signatureAlgorithm: idp.SAMLSignatureAlgorithm_SAML_SIGNATURE_RSA_SHA1,
+ wantSignatureAlgorithm: "http://www.w3.org/2000/09/xmldsig#rsa-sha1",
+ },
+ {
+ name: "RSA_SHA256",
+ signatureAlgorithm: idp.SAMLSignatureAlgorithm_SAML_SIGNATURE_RSA_SHA256,
+ wantSignatureAlgorithm: "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256",
+ },
+ {
+ name: "RSA_SHA512",
+ signatureAlgorithm: idp.SAMLSignatureAlgorithm_SAML_SIGNATURE_RSA_SHA512,
+ wantSignatureAlgorithm: "http://www.w3.org/2001/04/xmldsig-more#rsa-sha512",
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ t.Parallel()
+ got := signatureAlgorithmToCommand(tt.signatureAlgorithm)
+ require.Equal(t, tt.wantSignatureAlgorithm, got)
+ })
+ }
+}
diff --git a/internal/command/idp.go b/internal/command/idp.go
index 06d8f473c09..b53a6610e0c 100644
--- a/internal/command/idp.go
+++ b/internal/command/idp.go
@@ -120,6 +120,7 @@ type SAMLProvider struct {
MetadataURL string
Binding string
WithSignedRequest bool
+ SignatureAlgorithm string
NameIDFormat *domain.SAMLNameIDFormat
TransientMappingAttributeName string
FederatedLogoutEnabled bool
diff --git a/internal/command/idp_intent_test.go b/internal/command/idp_intent_test.go
index e0f4e2ffdb7..df976559465 100644
--- a/internal/command/idp_intent_test.go
+++ b/internal/command/idp_intent_test.go
@@ -737,6 +737,7 @@ func TestCommands_AuthFromProvider_SAML(t *testing.T) {
[]byte("certificate"),
"",
false,
+ "",
gu.Ptr(domain.SAMLNameIDFormatUnspecified),
"",
false,
@@ -758,6 +759,7 @@ func TestCommands_AuthFromProvider_SAML(t *testing.T) {
}, []byte("-----BEGIN CERTIFICATE-----\nMIIC2zCCAcOgAwIBAgIIAy/jm1gAAdEwDQYJKoZIhvcNAQELBQAwEjEQMA4GA1UE\nChMHWklUQURFTDAeFw0yMzA4MzAwNzExMTVaFw0yNDA4MjkwNzExMTVaMBIxEDAO\nBgNVBAoTB1pJVEFERUwwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDE\nd3TztGgSb3LBVZn8f60NbFCyZW+F9HPiMCr9F9T45Zc0fgmMwxId0WzRD5Y/3yc1\ndHJzt+Bsxvw12aUHbIPiothqk3lINoFzl2H/cSfIW3nehKyNOUqdBQ8B4mvaqH81\njTjoJ/JTJAwzglHk6JAWjhOyx9aep1yBqYa3QASeTaW9sxkpB0Co1L2UPNhuMwZq\n8RA9NkTfmYVcVBeNqihler5MhruFtqrv+J0ftwc1stw8uCN89ADyr4Ni+e+FeWar\nQs9Bkfc6KLF/5IXa9HCsHNPaaoYPY6I6RSaG4/DKoSKIEe1/GSVG1FTpZ8trUZxv\nU+xXS6gEalXcrJsiX8aXAgMBAAGjNTAzMA4GA1UdDwEB/wQEAwIFoDATBgNVHSUE\nDDAKBggrBgEFBQcDATAMBgNVHRMBAf8EAjAAMA0GCSqGSIb3DQEBCwUAA4IBAQCx\n/dRNIj0N/16zJhZR/ahkc2AkvDXYxyr4JRT5wK9GQDNl/oaX3debRuSi/tfaXFIX\naJA6PxM4J49ZaiEpLrKfxMz5kAhjKchCBEMcH3mGt+iNZH7EOyTvHjpGrP2OZrsh\nO17yrvN3HuQxIU6roJlqtZz2iAADsoPtwOO4D7hupm9XTMkSnAmlMWOo/q46Jz89\n1sMxB+dXmH/zV0wgwh0omZfLV0u89mvdq269VhcjNBpBYSnN1ccqYWd5iwziob3I\nvaavGHGfkbvRUn/tKftYuTK30q03R+e9YbmlWZ0v695owh2e/apCzowQsCKfSVC8\nOxVyt5XkHq1tWwVyBmFp\n-----END CERTIFICATE-----\n"),
"",
false,
+ "",
gu.Ptr(domain.SAMLNameIDFormatUnspecified),
"",
false,
@@ -828,6 +830,7 @@ func TestCommands_AuthFromProvider_SAML(t *testing.T) {
[]byte("certificate"),
"",
false,
+ "",
gu.Ptr(domain.SAMLNameIDFormatUnspecified),
"",
false,
@@ -849,6 +852,7 @@ func TestCommands_AuthFromProvider_SAML(t *testing.T) {
}, []byte("-----BEGIN CERTIFICATE-----\nMIIC2zCCAcOgAwIBAgIIAy/jm1gAAdEwDQYJKoZIhvcNAQELBQAwEjEQMA4GA1UE\nChMHWklUQURFTDAeFw0yMzA4MzAwNzExMTVaFw0yNDA4MjkwNzExMTVaMBIxEDAO\nBgNVBAoTB1pJVEFERUwwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDE\nd3TztGgSb3LBVZn8f60NbFCyZW+F9HPiMCr9F9T45Zc0fgmMwxId0WzRD5Y/3yc1\ndHJzt+Bsxvw12aUHbIPiothqk3lINoFzl2H/cSfIW3nehKyNOUqdBQ8B4mvaqH81\njTjoJ/JTJAwzglHk6JAWjhOyx9aep1yBqYa3QASeTaW9sxkpB0Co1L2UPNhuMwZq\n8RA9NkTfmYVcVBeNqihler5MhruFtqrv+J0ftwc1stw8uCN89ADyr4Ni+e+FeWar\nQs9Bkfc6KLF/5IXa9HCsHNPaaoYPY6I6RSaG4/DKoSKIEe1/GSVG1FTpZ8trUZxv\nU+xXS6gEalXcrJsiX8aXAgMBAAGjNTAzMA4GA1UdDwEB/wQEAwIFoDATBgNVHSUE\nDDAKBggrBgEFBQcDATAMBgNVHRMBAf8EAjAAMA0GCSqGSIb3DQEBCwUAA4IBAQCx\n/dRNIj0N/16zJhZR/ahkc2AkvDXYxyr4JRT5wK9GQDNl/oaX3debRuSi/tfaXFIX\naJA6PxM4J49ZaiEpLrKfxMz5kAhjKchCBEMcH3mGt+iNZH7EOyTvHjpGrP2OZrsh\nO17yrvN3HuQxIU6roJlqtZz2iAADsoPtwOO4D7hupm9XTMkSnAmlMWOo/q46Jz89\n1sMxB+dXmH/zV0wgwh0omZfLV0u89mvdq269VhcjNBpBYSnN1ccqYWd5iwziob3I\nvaavGHGfkbvRUn/tKftYuTK30q03R+e9YbmlWZ0v695owh2e/apCzowQsCKfSVC8\nOxVyt5XkHq1tWwVyBmFp\n-----END CERTIFICATE-----\n"),
"",
false,
+ "",
gu.Ptr(domain.SAMLNameIDFormatUnspecified),
"",
false,
diff --git a/internal/command/idp_model.go b/internal/command/idp_model.go
index 188d45e6ec4..d1cb0d592b5 100644
--- a/internal/command/idp_model.go
+++ b/internal/command/idp_model.go
@@ -1758,6 +1758,7 @@ type SAMLIDPWriteModel struct {
Certificate []byte
Binding string
WithSignedRequest bool
+ SignatureAlgorithm string
NameIDFormat *domain.SAMLNameIDFormat
TransientMappingAttributeName string
FederatedLogoutEnabled bool
@@ -1787,6 +1788,7 @@ func (wm *SAMLIDPWriteModel) reduceAddedEvent(e *idp.SAMLIDPAddedEvent) {
wm.Certificate = e.Certificate
wm.Binding = e.Binding
wm.WithSignedRequest = e.WithSignedRequest
+ wm.SignatureAlgorithm = e.SignatureAlgorithm
wm.NameIDFormat = e.NameIDFormat
wm.TransientMappingAttributeName = e.TransientMappingAttributeName
wm.FederatedLogoutEnabled = e.FederatedLogoutEnabled
@@ -1813,6 +1815,9 @@ func (wm *SAMLIDPWriteModel) reduceChangedEvent(e *idp.SAMLIDPChangedEvent) {
if e.WithSignedRequest != nil {
wm.WithSignedRequest = *e.WithSignedRequest
}
+ if e.SignatureAlgorithm != nil {
+ wm.SignatureAlgorithm = *e.SignatureAlgorithm
+ }
if e.NameIDFormat != nil {
wm.NameIDFormat = e.NameIDFormat
}
@@ -1833,6 +1838,7 @@ func (wm *SAMLIDPWriteModel) NewChanges(
secretCrypto crypto.EncryptionAlgorithm,
binding string,
withSignedRequest bool,
+ signatureAlgorithm string,
nameIDFormat *domain.SAMLNameIDFormat,
transientMappingAttributeName string,
federatedLogoutEnabled bool,
@@ -1861,6 +1867,9 @@ func (wm *SAMLIDPWriteModel) NewChanges(
if wm.WithSignedRequest != withSignedRequest {
changes = append(changes, idp.ChangeSAMLWithSignedRequest(withSignedRequest))
}
+ if wm.SignatureAlgorithm != signatureAlgorithm {
+ changes = append(changes, idp.ChangeSAMLSignatureAlgorithm(signatureAlgorithm))
+ }
if wm.NameIDFormat != nameIDFormat {
changes = append(changes, idp.ChangeSAMLNameIDFormat(nameIDFormat))
}
@@ -1902,6 +1911,9 @@ func (wm *SAMLIDPWriteModel) ToProvider(callbackURL string, idpAlg crypto.Encryp
if wm.Binding != "" {
opts = append(opts, saml2.WithBinding(wm.Binding))
}
+ if wm.WithSignedRequest && wm.SignatureAlgorithm != "" {
+ opts = append(opts, saml2.WithSignatureAlgorithm(wm.SignatureAlgorithm))
+ }
if wm.NameIDFormat != nil {
opts = append(opts, saml2.WithNameIDFormat(*wm.NameIDFormat))
}
diff --git a/internal/command/instance_idp.go b/internal/command/instance_idp.go
index 90efb3edd41..1f6536ef8c8 100644
--- a/internal/command/instance_idp.go
+++ b/internal/command/instance_idp.go
@@ -1793,6 +1793,7 @@ func (c *Commands) prepareAddInstanceSAMLProvider(a *instance.Aggregate, writeMo
cert,
provider.Binding,
provider.WithSignedRequest,
+ provider.SignatureAlgorithm,
provider.NameIDFormat,
provider.TransientMappingAttributeName,
provider.FederatedLogoutEnabled,
@@ -1847,6 +1848,7 @@ func (c *Commands) prepareUpdateInstanceSAMLProvider(a *instance.Aggregate, writ
c.idpConfigEncryption,
provider.Binding,
provider.WithSignedRequest,
+ provider.SignatureAlgorithm,
provider.NameIDFormat,
provider.TransientMappingAttributeName,
provider.FederatedLogoutEnabled,
@@ -1893,6 +1895,7 @@ func (c *Commands) prepareRegenerateInstanceSAMLProviderCertificate(a *instance.
c.idpConfigEncryption,
writeModel.Binding,
writeModel.WithSignedRequest,
+ writeModel.SignatureAlgorithm,
writeModel.NameIDFormat,
writeModel.TransientMappingAttributeName,
writeModel.FederatedLogoutEnabled,
diff --git a/internal/command/instance_idp_model.go b/internal/command/instance_idp_model.go
index 03d9cd9c367..262b01655bd 100644
--- a/internal/command/instance_idp_model.go
+++ b/internal/command/instance_idp_model.go
@@ -921,6 +921,7 @@ func (wm *InstanceSAMLIDPWriteModel) NewChangedEvent(
secretCrypto crypto.EncryptionAlgorithm,
binding string,
withSignedRequest bool,
+ signatureAlgorithm string,
nameIDFormat *domain.SAMLNameIDFormat,
transientMappingAttributeName string,
federatedLogoutEnabled bool,
@@ -934,6 +935,7 @@ func (wm *InstanceSAMLIDPWriteModel) NewChangedEvent(
secretCrypto,
binding,
withSignedRequest,
+ signatureAlgorithm,
nameIDFormat,
transientMappingAttributeName,
federatedLogoutEnabled,
diff --git a/internal/command/instance_idp_test.go b/internal/command/instance_idp_test.go
index 4805d075dd7..8e6b92bcf0f 100644
--- a/internal/command/instance_idp_test.go
+++ b/internal/command/instance_idp_test.go
@@ -5435,6 +5435,7 @@ func TestCommandSide_AddInstanceSAMLIDP(t *testing.T) {
[]byte("certificate"),
"",
false,
+ "",
nil,
"",
false,
@@ -5477,6 +5478,7 @@ func TestCommandSide_AddInstanceSAMLIDP(t *testing.T) {
[]byte("certificate"),
"binding",
true,
+ "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256",
gu.Ptr(domain.SAMLNameIDFormatTransient),
"customAttribute",
true,
@@ -5500,6 +5502,7 @@ func TestCommandSide_AddInstanceSAMLIDP(t *testing.T) {
Metadata: validSAMLMetadata,
Binding: "binding",
WithSignedRequest: true,
+ SignatureAlgorithm: "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256",
NameIDFormat: gu.Ptr(domain.SAMLNameIDFormatTransient),
TransientMappingAttributeName: "customAttribute",
FederatedLogoutEnabled: true,
@@ -5666,6 +5669,7 @@ func TestCommandSide_UpdateInstanceGenericSAMLIDP(t *testing.T) {
[]byte("certificate"),
"",
false,
+ "",
nil,
"",
false,
@@ -5705,6 +5709,7 @@ func TestCommandSide_UpdateInstanceGenericSAMLIDP(t *testing.T) {
[]byte("certificate"),
"binding",
false,
+ "",
gu.Ptr(domain.SAMLNameIDFormatUnspecified),
"",
false,
@@ -5850,6 +5855,7 @@ func TestCommandSide_RegenerateInstanceSAMLProviderCertificate(t *testing.T) {
[]byte("certificate"),
"binding",
false,
+ "",
gu.Ptr(domain.SAMLNameIDFormatUnspecified),
"",
false,
diff --git a/internal/command/org_idp.go b/internal/command/org_idp.go
index 6cae78acc75..20e7436ad41 100644
--- a/internal/command/org_idp.go
+++ b/internal/command/org_idp.go
@@ -1766,6 +1766,7 @@ func (c *Commands) prepareAddOrgSAMLProvider(a *org.Aggregate, writeModel *OrgSA
cert,
provider.Binding,
provider.WithSignedRequest,
+ provider.SignatureAlgorithm,
provider.NameIDFormat,
provider.TransientMappingAttributeName,
provider.FederatedLogoutEnabled,
@@ -1820,6 +1821,7 @@ func (c *Commands) prepareUpdateOrgSAMLProvider(a *org.Aggregate, writeModel *Or
c.idpConfigEncryption,
provider.Binding,
provider.WithSignedRequest,
+ provider.SignatureAlgorithm,
provider.NameIDFormat,
provider.TransientMappingAttributeName,
provider.FederatedLogoutEnabled,
@@ -1866,6 +1868,7 @@ func (c *Commands) prepareRegenerateOrgSAMLProviderCertificate(a *org.Aggregate,
c.idpConfigEncryption,
writeModel.Binding,
writeModel.WithSignedRequest,
+ writeModel.SignatureAlgorithm,
writeModel.NameIDFormat,
writeModel.TransientMappingAttributeName,
writeModel.FederatedLogoutEnabled,
diff --git a/internal/command/org_idp_model.go b/internal/command/org_idp_model.go
index fdafb7f0871..9a7fea93c63 100644
--- a/internal/command/org_idp_model.go
+++ b/internal/command/org_idp_model.go
@@ -933,6 +933,7 @@ func (wm *OrgSAMLIDPWriteModel) NewChangedEvent(
secretCrypto crypto.EncryptionAlgorithm,
binding string,
withSignedRequest bool,
+ signatureAlgorithm string,
nameIDFormat *domain.SAMLNameIDFormat,
transientMappingAttributeName string,
federatedLogoutEnabled bool,
@@ -946,6 +947,7 @@ func (wm *OrgSAMLIDPWriteModel) NewChangedEvent(
secretCrypto,
binding,
withSignedRequest,
+ signatureAlgorithm,
nameIDFormat,
transientMappingAttributeName,
federatedLogoutEnabled,
diff --git a/internal/command/org_idp_test.go b/internal/command/org_idp_test.go
index 54f508da305..152b72a0abd 100644
--- a/internal/command/org_idp_test.go
+++ b/internal/command/org_idp_test.go
@@ -5517,6 +5517,7 @@ func TestCommandSide_AddOrgSAMLIDP(t *testing.T) {
[]byte("certificate"),
"",
false,
+ "",
nil,
"",
false,
@@ -5559,6 +5560,7 @@ func TestCommandSide_AddOrgSAMLIDP(t *testing.T) {
[]byte("certificate"),
"binding",
true,
+ "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256",
gu.Ptr(domain.SAMLNameIDFormatTransient),
"customAttribute",
true,
@@ -5583,6 +5585,7 @@ func TestCommandSide_AddOrgSAMLIDP(t *testing.T) {
Metadata: validSAMLMetadata,
Binding: "binding",
WithSignedRequest: true,
+ SignatureAlgorithm: "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256",
NameIDFormat: gu.Ptr(domain.SAMLNameIDFormatTransient),
TransientMappingAttributeName: "customAttribute",
FederatedLogoutEnabled: true,
@@ -5757,6 +5760,7 @@ func TestCommandSide_UpdateOrgSAMLIDP(t *testing.T) {
[]byte("certificate"),
"",
false,
+ "",
nil,
"",
false,
@@ -5797,6 +5801,7 @@ func TestCommandSide_UpdateOrgSAMLIDP(t *testing.T) {
[]byte("certificate"),
"binding",
false,
+ "",
gu.Ptr(domain.SAMLNameIDFormatUnspecified),
"",
false,
@@ -5948,6 +5953,7 @@ func TestCommandSide_RegenerateOrgSAMLProviderCertificate(t *testing.T) {
[]byte("certificate"),
"binding",
false,
+ "",
gu.Ptr(domain.SAMLNameIDFormatUnspecified),
"",
false,
diff --git a/internal/idp/providers/saml/saml.go b/internal/idp/providers/saml/saml.go
index 11b1d36da2d..aa138a476b9 100644
--- a/internal/idp/providers/saml/saml.go
+++ b/internal/idp/providers/saml/saml.go
@@ -32,6 +32,7 @@ type Provider struct {
spOptions *samlsp.Options
binding string
+ signatureAlgorithm string
nameIDFormat saml.NameIDFormat
transientMappingAttributeName string
@@ -84,6 +85,12 @@ func WithBinding(binding string) ProviderOpts {
}
}
+func WithSignatureAlgorithm(signatureAlgorithm string) ProviderOpts {
+ return func(p *Provider) {
+ p.signatureAlgorithm = signatureAlgorithm
+ }
+}
+
func WithNameIDFormat(format domain.SAMLNameIDFormat) ProviderOpts {
return func(p *Provider) {
p.nameIDFormat = nameIDFormatFromDomain(format)
@@ -219,6 +226,9 @@ func (p *Provider) GetSP() (*samlsp.Middleware, error) {
if p.binding != "" {
sp.Binding = p.binding
}
+ if p.signatureAlgorithm != "" {
+ sp.ServiceProvider.SignatureMethod = p.signatureAlgorithm
+ }
sp.ServiceProvider.MetadataValidDuration = time.Until(sp.ServiceProvider.Certificate.NotAfter)
return sp, nil
}
diff --git a/internal/idp/providers/saml/saml_test.go b/internal/idp/providers/saml/saml_test.go
index 5e76e6dcaa2..eeb9a58d6ce 100644
--- a/internal/idp/providers/saml/saml_test.go
+++ b/internal/idp/providers/saml/saml_test.go
@@ -165,6 +165,7 @@ func TestProvider_Options(t *testing.T) {
nameIDFormat saml.NameIDFormat
transientMappingAttributeName string
withSignedRequest bool
+ signatureAlgorithm string
requesttracker samlsp.RequestTracker
entityID string
}
@@ -257,6 +258,7 @@ func TestProvider_Options(t *testing.T) {
WithAutoUpdate(),
WithBinding("binding"),
WithSignedRequest(),
+ WithSignatureAlgorithm("http://www.w3.org/2001/04/xmldsig-more#rsa-sha512"),
WithCustomRequestTracker(&requesttracker.RequestTracker{}),
WithEntityID("entityID"),
WithNameIDFormat(domain.SAMLNameIDFormatTransient),
@@ -274,6 +276,7 @@ func TestProvider_Options(t *testing.T) {
nameIDFormat: saml.TransientNameIDFormat,
transientMappingAttributeName: "attribute",
withSignedRequest: true,
+ signatureAlgorithm: "http://www.w3.org/2001/04/xmldsig-more#rsa-sha512",
requesttracker: &requesttracker.RequestTracker{},
},
},
@@ -297,6 +300,7 @@ func TestProvider_Options(t *testing.T) {
a.Equal(tt.want.nameIDFormat, provider.nameIDFormat)
a.Equal(tt.want.transientMappingAttributeName, provider.transientMappingAttributeName)
a.Equal(tt.want.withSignedRequest, provider.spOptions.SignRequest)
+ a.Equal(tt.want.signatureAlgorithm, provider.signatureAlgorithm)
a.Equal(tt.want.requesttracker, provider.requestTracker)
a.Equal(tt.want.entityID, provider.spOptions.EntityID)
}
diff --git a/internal/query/idp_template.go b/internal/query/idp_template.go
index c1c47e73bc5..25c32a14021 100644
--- a/internal/query/idp_template.go
+++ b/internal/query/idp_template.go
@@ -163,6 +163,7 @@ type SAMLIDPTemplate struct {
Certificate []byte
Binding string
WithSignedRequest bool
+ SignatureAlgorithm string
NameIDFormat sql.Null[domain.SAMLNameIDFormat]
TransientMappingAttributeName string
FederatedLogoutEnabled bool
@@ -717,6 +718,10 @@ var (
name: projection.SAMLWithSignedRequestCol,
table: samlIdpTemplateTable,
}
+ SAMLSignatureAlgorithmCol = Column{
+ name: projection.SAMLSignatureAlgorithmCol,
+ table: samlIdpTemplateTable,
+ }
SAMLNameIDFormatCol = Column{
name: projection.SAMLNameIDFormatCol,
table: samlIdpTemplateTable,
@@ -951,6 +956,7 @@ func prepareIDPTemplateByIDQuery() (sq.SelectBuilder, func(*sql.Row) (*IDPTempla
SAMLCertificateCol.identifier(),
SAMLBindingCol.identifier(),
SAMLWithSignedRequestCol.identifier(),
+ SAMLSignatureAlgorithmCol.identifier(),
SAMLNameIDFormatCol.identifier(),
SAMLTransientMappingAttributeNameCol.identifier(),
SAMLFederatedLogoutEnabledCol.identifier(),
@@ -1071,6 +1077,7 @@ func prepareIDPTemplateByIDQuery() (sq.SelectBuilder, func(*sql.Row) (*IDPTempla
var samlCertificate []byte
samlBinding := sql.NullString{}
samlWithSignedRequest := sql.NullBool{}
+ samlSignatureAlgorithm := sql.NullString{}
samlNameIDFormat := sql.Null[domain.SAMLNameIDFormat]{}
samlTransientMappingAttributeName := sql.NullString{}
samlFederatedLogoutEnabled := sql.NullBool{}
@@ -1189,6 +1196,7 @@ func prepareIDPTemplateByIDQuery() (sq.SelectBuilder, func(*sql.Row) (*IDPTempla
&samlCertificate,
&samlBinding,
&samlWithSignedRequest,
+ &samlSignatureAlgorithm,
&samlNameIDFormat,
&samlTransientMappingAttributeName,
&samlFederatedLogoutEnabled,
@@ -1329,6 +1337,7 @@ func prepareIDPTemplateByIDQuery() (sq.SelectBuilder, func(*sql.Row) (*IDPTempla
Certificate: samlCertificate,
Binding: samlBinding.String,
WithSignedRequest: samlWithSignedRequest.Bool,
+ SignatureAlgorithm: samlSignatureAlgorithm.String,
NameIDFormat: samlNameIDFormat,
TransientMappingAttributeName: samlTransientMappingAttributeName.String,
FederatedLogoutEnabled: samlFederatedLogoutEnabled.Bool,
@@ -1463,6 +1472,7 @@ func prepareIDPTemplatesQuery() (sq.SelectBuilder, func(*sql.Rows) (*IDPTemplate
SAMLCertificateCol.identifier(),
SAMLBindingCol.identifier(),
SAMLWithSignedRequestCol.identifier(),
+ SAMLSignatureAlgorithmCol.identifier(),
SAMLNameIDFormatCol.identifier(),
SAMLTransientMappingAttributeNameCol.identifier(),
SAMLFederatedLogoutEnabledCol.identifier(),
@@ -1588,6 +1598,7 @@ func prepareIDPTemplatesQuery() (sq.SelectBuilder, func(*sql.Rows) (*IDPTemplate
var samlCertificate []byte
samlBinding := sql.NullString{}
samlWithSignedRequest := sql.NullBool{}
+ samlSignatureAlgorithm := sql.NullString{}
samlNameIDFormat := sql.Null[domain.SAMLNameIDFormat]{}
samlTransientMappingAttributeName := sql.NullString{}
samlFederatedLogoutEnabled := sql.NullBool{}
@@ -1706,6 +1717,7 @@ func prepareIDPTemplatesQuery() (sq.SelectBuilder, func(*sql.Rows) (*IDPTemplate
&samlCertificate,
&samlBinding,
&samlWithSignedRequest,
+ &samlSignatureAlgorithm,
&samlNameIDFormat,
&samlTransientMappingAttributeName,
&samlFederatedLogoutEnabled,
@@ -1845,6 +1857,7 @@ func prepareIDPTemplatesQuery() (sq.SelectBuilder, func(*sql.Rows) (*IDPTemplate
Certificate: samlCertificate,
Binding: samlBinding.String,
WithSignedRequest: samlWithSignedRequest.Bool,
+ SignatureAlgorithm: samlSignatureAlgorithm.String,
NameIDFormat: samlNameIDFormat,
TransientMappingAttributeName: samlTransientMappingAttributeName.String,
FederatedLogoutEnabled: samlFederatedLogoutEnabled.Bool,
diff --git a/internal/query/idp_template_test.go b/internal/query/idp_template_test.go
index aed243c61e3..e730a765b7d 100644
--- a/internal/query/idp_template_test.go
+++ b/internal/query/idp_template_test.go
@@ -97,6 +97,7 @@ var (
` projections.idp_templates6_saml.certificate,` +
` projections.idp_templates6_saml.binding,` +
` projections.idp_templates6_saml.with_signed_request,` +
+ ` projections.idp_templates6_saml.signature_algorithm,` +
` projections.idp_templates6_saml.name_id_format,` +
` projections.idp_templates6_saml.transient_mapping_attribute_name,` +
` projections.idp_templates6_saml.federated_logout_enabled,` +
@@ -227,6 +228,7 @@ var (
"certificate",
"binding",
"with_signed_request",
+ "signature_algorithm",
"name_id_format",
"transient_mapping_attribute_name",
"federated_logout_enabled",
@@ -344,6 +346,7 @@ var (
` projections.idp_templates6_saml.certificate,` +
` projections.idp_templates6_saml.binding,` +
` projections.idp_templates6_saml.with_signed_request,` +
+ ` projections.idp_templates6_saml.signature_algorithm,` +
` projections.idp_templates6_saml.name_id_format,` +
` projections.idp_templates6_saml.transient_mapping_attribute_name,` +
` projections.idp_templates6_saml.federated_logout_enabled,` +
@@ -475,6 +478,7 @@ var (
"certificate",
"binding",
"with_signed_request",
+ "signature_algorithm",
"name_id_format",
"transient_mapping_attribute_name",
"federated_logout_enabled",
@@ -635,6 +639,7 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) {
nil,
nil,
nil,
+ nil,
// ldap config
nil,
nil,
@@ -790,6 +795,7 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) {
nil,
nil,
nil,
+ nil,
// ldap config
nil,
nil,
@@ -943,6 +949,7 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) {
nil,
nil,
nil,
+ nil,
// ldap config
nil,
nil,
@@ -1094,6 +1101,7 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) {
nil,
nil,
nil,
+ nil,
// ldap config
nil,
nil,
@@ -1244,6 +1252,7 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) {
nil,
nil,
nil,
+ nil,
// ldap config
nil,
nil,
@@ -1394,6 +1403,7 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) {
nil,
nil,
nil,
+ nil,
// ldap config
nil,
nil,
@@ -1545,6 +1555,7 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) {
nil,
nil,
nil,
+ nil,
// ldap config
nil,
nil,
@@ -1691,7 +1702,8 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) {
nil,
nil,
"binding",
- false,
+ true,
+ "http://www.w3.org/2001/04/xmldsig-more#rsa-sha512",
domain.SAMLNameIDFormatTransient,
"customAttribute",
true,
@@ -1751,7 +1763,8 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) {
Key: nil,
Certificate: nil,
Binding: "binding",
- WithSignedRequest: false,
+ WithSignedRequest: true,
+ SignatureAlgorithm: "http://www.w3.org/2001/04/xmldsig-more#rsa-sha512",
NameIDFormat: sql.Null[domain.SAMLNameIDFormat]{V: domain.SAMLNameIDFormatTransient, Valid: true},
TransientMappingAttributeName: "customAttribute",
FederatedLogoutEnabled: true,
@@ -1850,6 +1863,7 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) {
nil,
nil,
nil,
+ nil,
// ldap config
"idp-id",
database.TextArray[string]{"server"},
@@ -2021,6 +2035,7 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) {
nil,
nil,
nil,
+ nil,
// ldap config
nil,
nil,
@@ -2173,6 +2188,7 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) {
nil,
nil,
nil,
+ nil,
// ldap config
nil,
nil,
@@ -2353,6 +2369,7 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) {
nil,
nil,
nil,
+ nil,
// ldap config
"idp-id",
database.TextArray[string]{"server"},
@@ -2533,6 +2550,7 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) {
nil,
nil,
nil,
+ nil,
// ldap config
nil,
nil,
@@ -2686,6 +2704,7 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) {
nil,
nil,
nil,
+ nil,
// ldap config
"idp-id-ldap",
database.TextArray[string]{"server"},
@@ -2801,6 +2820,7 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) {
nil,
"binding",
false,
+ nil,
domain.SAMLNameIDFormatTransient,
"customAttribute",
true,
@@ -2922,6 +2942,7 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) {
nil,
nil,
nil,
+ nil,
// ldap config
nil,
nil,
@@ -3040,6 +3061,7 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) {
nil,
nil,
nil,
+ nil,
// ldap config
nil,
nil,
@@ -3158,6 +3180,7 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) {
nil,
nil,
nil,
+ nil,
// ldap config
nil,
nil,
@@ -3276,6 +3299,7 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) {
nil,
nil,
nil,
+ nil,
// ldap config
nil,
nil,
@@ -3382,6 +3406,7 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) {
Certificate: nil,
Binding: "binding",
WithSignedRequest: false,
+ SignatureAlgorithm: "",
NameIDFormat: sql.Null[domain.SAMLNameIDFormat]{V: domain.SAMLNameIDFormatTransient, Valid: true},
TransientMappingAttributeName: "customAttribute",
FederatedLogoutEnabled: true,
diff --git a/internal/query/projection/idp_template.go b/internal/query/projection/idp_template.go
index 11eeb8c6137..f6fb7db3007 100644
--- a/internal/query/projection/idp_template.go
+++ b/internal/query/projection/idp_template.go
@@ -171,6 +171,7 @@ const (
SAMLCertificateCol = "certificate"
SAMLBindingCol = "binding"
SAMLWithSignedRequestCol = "with_signed_request"
+ SAMLSignatureAlgorithmCol = "signature_algorithm"
SAMLNameIDFormatCol = "name_id_format"
SAMLTransientMappingAttributeName = "transient_mapping_attribute_name"
SAMLFederatedLogoutEnabled = "federated_logout_enabled"
@@ -376,6 +377,7 @@ func (*idpTemplateProjection) Init() *old_handler.Check {
handler.NewColumn(SAMLCertificateCol, handler.ColumnTypeBytes),
handler.NewColumn(SAMLBindingCol, handler.ColumnTypeText, handler.Nullable()),
handler.NewColumn(SAMLWithSignedRequestCol, handler.ColumnTypeBool, handler.Nullable()),
+ handler.NewColumn(SAMLSignatureAlgorithmCol, handler.ColumnTypeText, handler.Default("")),
handler.NewColumn(SAMLNameIDFormatCol, handler.ColumnTypeEnum, handler.Nullable()),
handler.NewColumn(SAMLTransientMappingAttributeName, handler.ColumnTypeText, handler.Nullable()),
handler.NewColumn(SAMLFederatedLogoutEnabled, handler.ColumnTypeBool, handler.Default(false)),
@@ -1991,6 +1993,7 @@ func (p *idpTemplateProjection) reduceSAMLIDPAdded(event eventstore.Event) (*han
handler.NewCol(SAMLCertificateCol, idpEvent.Certificate),
handler.NewCol(SAMLBindingCol, idpEvent.Binding),
handler.NewCol(SAMLWithSignedRequestCol, idpEvent.WithSignedRequest),
+ handler.NewCol(SAMLSignatureAlgorithmCol, idpEvent.SignatureAlgorithm),
handler.NewCol(SAMLTransientMappingAttributeName, idpEvent.TransientMappingAttributeName),
handler.NewCol(SAMLFederatedLogoutEnabled, idpEvent.FederatedLogoutEnabled),
}
@@ -2522,6 +2525,9 @@ func reduceSAMLIDPChangedColumns(idpEvent idp.SAMLIDPChangedEvent) []handler.Col
if idpEvent.WithSignedRequest != nil {
SAMLCols = append(SAMLCols, handler.NewCol(SAMLWithSignedRequestCol, *idpEvent.WithSignedRequest))
}
+ if idpEvent.SignatureAlgorithm != nil {
+ SAMLCols = append(SAMLCols, handler.NewCol(SAMLSignatureAlgorithmCol, *idpEvent.SignatureAlgorithm))
+ }
if idpEvent.NameIDFormat != nil {
SAMLCols = append(SAMLCols, handler.NewCol(SAMLNameIDFormatCol, *idpEvent.NameIDFormat))
}
diff --git a/internal/query/projection/idp_template_test.go b/internal/query/projection/idp_template_test.go
index 6ba6dabd471..d127e9b8475 100644
--- a/internal/query/projection/idp_template_test.go
+++ b/internal/query/projection/idp_template_test.go
@@ -2789,6 +2789,7 @@ func TestIDPTemplateProjection_reducesSAML(t *testing.T) {
"nameIDFormat": 3,
"transientMappingAttributeName": "customAttribute",
"withSignedRequest": true,
+ "signatureAlgorithm": "",
"isCreationAllowed": true,
"isLinkingAllowed": true,
"isAutoCreation": true,
@@ -2825,7 +2826,7 @@ func TestIDPTemplateProjection_reducesSAML(t *testing.T) {
},
},
{
- expectedStmt: "INSERT INTO projections.idp_templates6_saml (idp_id, instance_id, metadata, key, certificate, binding, with_signed_request, transient_mapping_attribute_name, federated_logout_enabled, name_id_format) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)",
+ expectedStmt: "INSERT INTO projections.idp_templates6_saml (idp_id, instance_id, metadata, key, certificate, binding, with_signed_request, signature_algorithm, transient_mapping_attribute_name, federated_logout_enabled, name_id_format) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)",
expectedArgs: []interface{}{
"idp-id",
"instance-id",
@@ -2834,6 +2835,7 @@ func TestIDPTemplateProjection_reducesSAML(t *testing.T) {
anyArg{},
"binding",
true,
+ "",
"customAttribute",
true,
domain.SAMLNameIDFormatTransient,
@@ -2863,6 +2865,7 @@ func TestIDPTemplateProjection_reducesSAML(t *testing.T) {
"nameIDFormat": 3,
"transientMappingAttributeName": "customAttribute",
"withSignedRequest": true,
+ "signatureAlgorithm": "http://www.w3.org/2001/04/xmldsig-more#rsa-sha512",
"isCreationAllowed": true,
"isLinkingAllowed": true,
"isAutoCreation": true,
@@ -2899,7 +2902,7 @@ func TestIDPTemplateProjection_reducesSAML(t *testing.T) {
},
},
{
- expectedStmt: "INSERT INTO projections.idp_templates6_saml (idp_id, instance_id, metadata, key, certificate, binding, with_signed_request, transient_mapping_attribute_name, federated_logout_enabled, name_id_format) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)",
+ expectedStmt: "INSERT INTO projections.idp_templates6_saml (idp_id, instance_id, metadata, key, certificate, binding, with_signed_request, signature_algorithm, transient_mapping_attribute_name, federated_logout_enabled, name_id_format) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)",
expectedArgs: []interface{}{
"idp-id",
"instance-id",
@@ -2908,6 +2911,7 @@ func TestIDPTemplateProjection_reducesSAML(t *testing.T) {
anyArg{},
"binding",
true,
+ "http://www.w3.org/2001/04/xmldsig-more#rsa-sha512",
"customAttribute",
true,
domain.SAMLNameIDFormatTransient,
@@ -2958,6 +2962,47 @@ func TestIDPTemplateProjection_reducesSAML(t *testing.T) {
},
},
},
+ {
+ name: "instance reduceSAMLIDPChanged - signature algorithm",
+ args: args{
+ event: getEvent(testEvent(
+ instance.SAMLIDPChangedEventType,
+ instance.AggregateType,
+ []byte(`{
+ "id": "idp-id",
+ "name": "custom-zitadel-instance",
+ "signatureAlgorithm": "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"
+}`),
+ ), instance.SAMLIDPChangedEventMapper),
+ },
+ reduce: (&idpTemplateProjection{}).reduceSAMLIDPChanged,
+ want: wantReduce{
+ aggregateType: eventstore.AggregateType("instance"),
+ sequence: 15,
+ executer: &testExecuter{
+ executions: []execution{
+ {
+ expectedStmt: "UPDATE projections.idp_templates6 SET (name, change_date, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)",
+ expectedArgs: []interface{}{
+ "custom-zitadel-instance",
+ anyArg{},
+ uint64(15),
+ "idp-id",
+ "instance-id",
+ },
+ },
+ {
+ expectedStmt: "UPDATE projections.idp_templates6_saml SET signature_algorithm = $1 WHERE (idp_id = $2) AND (instance_id = $3)",
+ expectedArgs: []interface{}{
+ "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256",
+ "idp-id",
+ "instance-id",
+ },
+ },
+ },
+ },
+ },
+ },
{
name: "instance reduceSAMLIDPChanged",
args: args{
@@ -2976,6 +3021,7 @@ func TestIDPTemplateProjection_reducesSAML(t *testing.T) {
"certificate": `+stringToJSONByte("certificate")+`,
"binding": "binding",
"withSignedRequest": true,
+ "signatureAlgorithm": "http://www.w3.org/2001/04/xmldsig-more#rsa-sha512",
"isCreationAllowed": true,
"isLinkingAllowed": true,
"isAutoCreation": true,
@@ -3007,13 +3053,14 @@ func TestIDPTemplateProjection_reducesSAML(t *testing.T) {
},
},
{
- expectedStmt: "UPDATE projections.idp_templates6_saml SET (metadata, key, certificate, binding, with_signed_request, federated_logout_enabled) = ($1, $2, $3, $4, $5, $6) WHERE (idp_id = $7) AND (instance_id = $8)",
+ expectedStmt: "UPDATE projections.idp_templates6_saml SET (metadata, key, certificate, binding, with_signed_request, signature_algorithm, federated_logout_enabled) = ($1, $2, $3, $4, $5, $6, $7) WHERE (idp_id = $8) AND (instance_id = $9)",
expectedArgs: []interface{}{
[]byte("metadata"),
anyArg{},
anyArg{},
"binding",
true,
+ "http://www.w3.org/2001/04/xmldsig-more#rsa-sha512",
true,
"idp-id",
"instance-id",
diff --git a/internal/repository/idp/saml.go b/internal/repository/idp/saml.go
index 209d8371b34..b932454d927 100644
--- a/internal/repository/idp/saml.go
+++ b/internal/repository/idp/saml.go
@@ -16,6 +16,7 @@ type SAMLIDPAddedEvent struct {
Key *crypto.CryptoValue `json:"key,omitempty"`
Certificate []byte `json:"certificate,omitempty"`
Binding string `json:"binding,omitempty"`
+ SignatureAlgorithm string `json:"signatureAlgorithm,omitempty"`
WithSignedRequest bool `json:"withSignedRequest,omitempty"`
NameIDFormat *domain.SAMLNameIDFormat `json:"nameIDFormat,omitempty"`
TransientMappingAttributeName string `json:"transientMappingAttributeName,omitempty"`
@@ -32,6 +33,7 @@ func NewSAMLIDPAddedEvent(
certificate []byte,
binding string,
withSignedRequest bool,
+ signatureAlgorithm string,
nameIDFormat *domain.SAMLNameIDFormat,
transientMappingAttributeName string,
federatedLogoutEnabled bool,
@@ -46,6 +48,7 @@ func NewSAMLIDPAddedEvent(
Certificate: certificate,
Binding: binding,
WithSignedRequest: withSignedRequest,
+ SignatureAlgorithm: signatureAlgorithm,
NameIDFormat: nameIDFormat,
TransientMappingAttributeName: transientMappingAttributeName,
FederatedLogoutEnabled: federatedLogoutEnabled,
@@ -84,6 +87,7 @@ type SAMLIDPChangedEvent struct {
Certificate []byte `json:"certificate,omitempty"`
Binding *string `json:"binding,omitempty"`
WithSignedRequest *bool `json:"withSignedRequest,omitempty"`
+ SignatureAlgorithm *string `json:"signatureAlgorithm,omitempty"`
NameIDFormat *domain.SAMLNameIDFormat `json:"nameIDFormat,omitempty"`
TransientMappingAttributeName *string `json:"transientMappingAttributeName,omitempty"`
FederatedLogoutEnabled *bool `json:"federatedLogoutEnabled,omitempty"`
@@ -146,6 +150,12 @@ func ChangeSAMLWithSignedRequest(withSignedRequest bool) func(*SAMLIDPChangedEve
}
}
+func ChangeSAMLSignatureAlgorithm(signatureAlgorithm string) func(*SAMLIDPChangedEvent) {
+ return func(e *SAMLIDPChangedEvent) {
+ e.SignatureAlgorithm = &signatureAlgorithm
+ }
+}
+
func ChangeSAMLNameIDFormat(nameIDFormat *domain.SAMLNameIDFormat) func(*SAMLIDPChangedEvent) {
return func(e *SAMLIDPChangedEvent) {
e.NameIDFormat = nameIDFormat
diff --git a/internal/repository/instance/idp.go b/internal/repository/instance/idp.go
index f0f324cb7d3..95ffb017c86 100644
--- a/internal/repository/instance/idp.go
+++ b/internal/repository/instance/idp.go
@@ -1022,6 +1022,7 @@ func NewSAMLIDPAddedEvent(
certificate []byte,
binding string,
withSignedRequest bool,
+ signatureAlgorithm string,
nameIDFormat *domain.SAMLNameIDFormat,
transientMappingAttributeName string,
federatedLogoutEnabled bool,
@@ -1041,6 +1042,7 @@ func NewSAMLIDPAddedEvent(
certificate,
binding,
withSignedRequest,
+ signatureAlgorithm,
nameIDFormat,
transientMappingAttributeName,
federatedLogoutEnabled,
diff --git a/internal/repository/org/idp.go b/internal/repository/org/idp.go
index 5f061370f17..dc402e36e85 100644
--- a/internal/repository/org/idp.go
+++ b/internal/repository/org/idp.go
@@ -1023,6 +1023,7 @@ func NewSAMLIDPAddedEvent(
certificate []byte,
binding string,
withSignedRequest bool,
+ signatureAlgorithm string,
nameIDFormat *domain.SAMLNameIDFormat,
transientMappingAttributeName string,
federatedLogoutEnabled bool,
@@ -1043,6 +1044,7 @@ func NewSAMLIDPAddedEvent(
certificate,
binding,
withSignedRequest,
+ signatureAlgorithm,
nameIDFormat,
transientMappingAttributeName,
federatedLogoutEnabled,
diff --git a/proto/zitadel/admin.proto b/proto/zitadel/admin.proto
index 5bcdefe3598..ff5e1e5f846 100644
--- a/proto/zitadel/admin.proto
+++ b/proto/zitadel/admin.proto
@@ -7087,6 +7087,9 @@ message AddSAMLProviderRequest {
// Optionally enable federated logout. If enabled, ZITADEL will send a logout request to the identity provider,
// if the user terminates the session in ZITADEL. Be sure to provide a SLO endpoint as part of the metadata.
optional bool federated_logout_enabled = 9;
+ // Specify a Signature Algorithm that should be used to sign SAML requests and responses.
+ // Can be used only if the `with_signed_request` option is set to true.
+ zitadel.idp.v1.SAMLSignatureAlgorithm signature_algorithm = 10;
}
message AddSAMLProviderResponse {
@@ -7124,6 +7127,9 @@ message UpdateSAMLProviderRequest {
// Optionally enable federated logout. If enabled, ZITADEL will send a logout request to the identity provider,
// if the user terminates the session in ZITADEL. Be sure to provide a SLO endpoint as part of the metadata.
optional bool federated_logout_enabled = 10;
+ // Specify a Signature Algorithm that should be used to sign SAML requests and responses.
+ // Can be used only if the `with_signed_request` option is set to true.
+ zitadel.idp.v1.SAMLSignatureAlgorithm signature_algorithm = 11;
}
message UpdateSAMLProviderResponse {
diff --git a/proto/zitadel/idp.proto b/proto/zitadel/idp.proto
index de497d8c937..e75022e5345 100644
--- a/proto/zitadel/idp.proto
+++ b/proto/zitadel/idp.proto
@@ -276,6 +276,13 @@ enum SAMLBinding {
SAML_BINDING_ARTIFACT = 3;
}
+enum SAMLSignatureAlgorithm {
+ SAML_SIGNATURE_UNSPECIFIED = 0;
+ SAML_SIGNATURE_RSA_SHA1 = 1;
+ SAML_SIGNATURE_RSA_SHA256 = 2;
+ SAML_SIGNATURE_RSA_SHA512 = 3;
+}
+
enum SAMLNameIDFormat {
SAML_NAME_ID_FORMAT_UNSPECIFIED = 0;
SAML_NAME_ID_FORMAT_EMAIL_ADDRESS = 1;
@@ -479,9 +486,11 @@ message SAMLConfig {
// Optional name of the attribute, which will be used to map the user
// in case the nameid-format returned is `urn:oasis:names:tc:SAML:2.0:nameid-format:transient`.
optional string transient_mapping_attribute_name = 5;
- // Boolean weather federated logout is enabled. If enabled, ZITADEL will send a logout request to the identity provider,
+ // Boolean value to indicate whether federated logout is enabled. If enabled, ZITADEL will send a logout request to the identity provider,
// if the user terminates the session in ZITADEL. Be sure to provide a SLO endpoint as part of the metadata.
optional bool federated_logout_enabled = 6;
+ // Optional value to indicate the configured Signing Algorithm used to sign SAML requests and responses.
+ optional zitadel.idp.v1.SAMLSignatureAlgorithm signature_algorithm = 7;
}
message AzureADConfig {
diff --git a/proto/zitadel/idp/v2/idp.proto b/proto/zitadel/idp/v2/idp.proto
index 663581a6591..450c28ebc6c 100644
--- a/proto/zitadel/idp/v2/idp.proto
+++ b/proto/zitadel/idp/v2/idp.proto
@@ -64,6 +64,13 @@ enum SAMLBinding {
SAML_BINDING_ARTIFACT = 3;
}
+enum SAMLSignatureAlgorithm {
+ SAML_SIGNATURE_UNSPECIFIED = 0;
+ SAML_SIGNATURE_RSA_SHA1 = 1;
+ SAML_SIGNATURE_RSA_SHA256 = 2;
+ SAML_SIGNATURE_RSA_SHA512 = 3;
+}
+
enum SAMLNameIDFormat {
SAML_NAME_ID_FORMAT_UNSPECIFIED = 0;
SAML_NAME_ID_FORMAT_EMAIL_ADDRESS = 1;
@@ -306,6 +313,9 @@ message SAMLConfig {
// Boolean weather federated logout is enabled. If enabled, ZITADEL will send a logout request to the identity provider,
// if the user terminates the session in ZITADEL. Be sure to provide a SLO endpoint as part of the metadata.
optional bool federated_logout_enabled = 6;
+ // Specify a Signature Algorithm that should used to sign SAML requests and responses.
+ // Can be set only if `with_signed_request` is set.
+ SAMLSignatureAlgorithm signature_algorithm = 7;
}
message AzureADConfig {
diff --git a/proto/zitadel/management.proto b/proto/zitadel/management.proto
index edaf03cfae8..19dfa9153a7 100644
--- a/proto/zitadel/management.proto
+++ b/proto/zitadel/management.proto
@@ -13794,6 +13794,9 @@ message AddSAMLProviderRequest {
// Optionally enable federated logout. If enabled, ZITADEL will send a logout request to the identity provider,
// if the user terminates the session in ZITADEL. Be sure to provide a SLO endpoint as part of the metadata.
optional bool federated_logout_enabled = 9;
+ // Specify a Signature Algorithm that should be used to sign SAML requests and responses.
+ // Can be used only if the `with_signed_request` option is set to true.
+ zitadel.idp.v1.SAMLSignatureAlgorithm signature_algorithm = 10;
}
message AddSAMLProviderResponse {
@@ -13831,6 +13834,9 @@ message UpdateSAMLProviderRequest {
// Optionally enable federated logout. If enabled, ZITADEL will send a logout request to the identity provider,
// if the user terminates the session in ZITADEL. Be sure to provide a SLO endpoint as part of the metadata.
optional bool federated_logout_enabled = 10;
+ // Specify a Signature Algorithm that should be used to sign SAML requests and responses.
+ // Can be used only if the `with_signed_request` option is set to true.
+ zitadel.idp.v1.SAMLSignatureAlgorithm signature_algorithm = 11;
}
message UpdateSAMLProviderResponse {