Send Signed SAML Response to SP in .net core applications

I have a requirement to send signed SAML response to SP through http post in .net core class library. we were able to do it in .net framework applications but couldn’t make it in .net core applications.
signedXml = signature.Generate(responseXml, certificate?.GetRSAPrivateKey(), SamlConstants.DigestAlgorithms.SHA256, SamlConstants.SignatureAlgorithms.RSA_SHA256, null, certificate);
looking for the equivalent method in .net core for the below code
// IdentityProvider.SendSAMLResponseByHTTPPost(response, destinationURL, signedXml, targetUrl);
Appreciate any help!!

What you were using is part of the low-level SAML API. Our recommendation is to use the high-level API as this is much easier to use, is configuration driven, and requires less application code.

There are two APIs for sending SAML responses. ISamlIdentityProvider.InitiateSsoAsync sends a SAML response as part of IdP-initiated SSO. ISamlIdentityProvider.SendSsoAsync sends a SAML response as part of SP-initiated SSO.

The following code is from the ExampleIdentityProvider’s SamlController.


public async Task InitiateSingleSignOn()
{
// Get the name of the logged in user.
var userName = User?.Identity?.Name;

// For demonstration purposes, include some claims.
var attributes = new List()
{
new SamlAttribute(ClaimTypes.Email, User?.FindFirst(ClaimTypes.Email)?.Value),
new SamlAttribute(ClaimTypes.GivenName, User?.FindFirst(ClaimTypes.GivenName)?.Value),
new SamlAttribute(ClaimTypes.Surname, User?.FindFirst(ClaimTypes.Surname)?.Value),
};

var partnerName = _configuration[“PartnerName”];
var relayState = _configuration[“RelayState”];

// Initiate single sign-on to the service provider (IdP-initiated SSO)
// by sending a SAML response containing a SAML assertion to the SP.
// The optional relay state normally specifies the target URL once SSO completes.
await _samlIdentityProvider.InitiateSsoAsync(partnerName, userName, attributes, relayState);

return new EmptyResult();
}



I recommend taking a look at this example project.

Also, take a look at the Examples Guide in the documentation folder. This walks you through this and the other projects.

The signing of the SAML response is specified through the SAML configuration and handled by the API.

[quote]
ComponentSpace - 9/15/2022
What you were using is part of the low-level SAML API. Our recommendation is to use the high-level API as this is much easier to use, is configuration driven, and requires less application code.

There are two APIs for sending SAML responses. ISamlIdentityProvider.InitiateSsoAsync sends a SAML response as part of IdP-initiated SSO. ISamlIdentityProvider.SendSsoAsync sends a SAML response as part of SP-initiated SSO.

The following code is from the ExampleIdentityProvider's SamlController.


public async Task InitiateSingleSignOn()
{
// Get the name of the logged in user.
var userName = User?.Identity?.Name;

// For demonstration purposes, include some claims.
var attributes = new List()
{
new SamlAttribute(ClaimTypes.Email, User?.FindFirst(ClaimTypes.Email)?.Value),
new SamlAttribute(ClaimTypes.GivenName, User?.FindFirst(ClaimTypes.GivenName)?.Value),
new SamlAttribute(ClaimTypes.Surname, User?.FindFirst(ClaimTypes.Surname)?.Value),
};

var partnerName = _configuration["PartnerName"];
var relayState = _configuration["RelayState"];

// Initiate single sign-on to the service provider (IdP-initiated SSO)
// by sending a SAML response containing a SAML assertion to the SP.
// The optional relay state normally specifies the target URL once SSO completes.
await _samlIdentityProvider.InitiateSsoAsync(partnerName, userName, attributes, relayState);

return new EmptyResult();
}



I recommend taking a look at this example project.

Also, take a look at the Examples Guide in the documentation folder. This walks you through this and the other projects.

The signing of the SAML response is specified through the SAML configuration and handled by the API.
[/quote]

Thank you for quickly responding.
I can make use of high level API as well but currently I have limitation to implement the functionality in class library where I can't make use of DI to inject ISamlIdentityProvider instance variable.
Could you please let us know how to call InitiateSsoAsync method. Any sample code is appreciated!

Thank you!

You could instantiate the SamlIdentityProvider class directly. However, its constructor requires you to construct a number of other instances.

I’m not sure why you can’t use DI in your class library. It would be far easier to make use of DI for the instantiation of these classes.

[quote]
ComponentSpace - 9/15/2022
You could instantiate the SamlIdentityProvider class directly. However, its constructor requires you to construct a number of other instances.

I'm not sure why you can't use DI in your class library. It would be far easier to make use of DI for the instantiation of these classes.

[/quote]

How to validate or deal with the X509Certificate2 certificate here?

The X.509 certificate used to sign the SAML response is specified through the SAML configuration.

I’ve included some of the SAML configuration from the ExampleIdentityProvider’s appsettings.json below.


“SAML”: {
“$schema”: “<a href=“https://www.componentspace.com/schemas/saml-config-schema-v1.0.json",">https://www.componentspace.com/schemas/saml-config-schema-v1.0.json”,
“Configurations”: [
{
“LocalIdentityProviderConfiguration”: {
“Name”: “<a href=“https://ExampleIdentityProvider”,”>https://ExampleIdentityProvider”,
“Description”: “Example Identity Provider”,
“SingleSignOnServiceUrl”: "<a href=“https://localhost:44313/SAML/SingleSignOnService",">https://localhost:44313/SAML/SingleSignOnService”,
“SingleLogoutServiceUrl”: "<a href=“https://localhost:44313/SAML/SingleLogoutService",">https://localhost:44313/SAML/SingleLogoutService”,
“ArtifactResolutionServiceUrl”: “<a href=“https://localhost:44313/SAML/ArtifactResolutionService",">https://localhost:44313/SAML/ArtifactResolutionService”,
“LocalCertificates”: [
{
“FileName”: “certificates/idp.pfx”,
“Password”: “password”
}
]
},
“PartnerServiceProviderConfigurations”: [
{
“Name”: “<a href=“https://ExampleServiceProvider”,”>https://ExampleServiceProvider”,
“Description”: “Example Service Provider”,
“WantAuthnRequestSigned”: true,
“SignSamlResponse”: true,
“EncryptAssertion”: true,
“SignLogoutRequest”: true,
“SignLogoutResponse”: true,
“WantLogoutRequestSigned”: true,
“WantLogoutResponseSigned”: true,
“AssertionConsumerServiceUrl”: "<a href=“https://localhost:44360/SAML/AssertionConsumerService",">https://localhost:44360/SAML/AssertionConsumerService”,
“SingleLogoutServiceUrl”: "<a href=“https://localhost:44360/SAML/SingleLogoutService",">https://localhost:44360/SAML/SingleLogoutService”,
“ArtifactResolutionServiceUrl”: "<a href=“https://localhost:44360/SAML/ArtifactResolutionService",">https://localhost:44360/SAML/ArtifactResolutionService”,
“PartnerCertificates”: [
{
“FileName”: “certificates/sp.cer”
}
]
},



The SignSamlReponse flag specifies that the SAML response will be signed. It’s signed using the LocalCertificate (ie idp.pfx).

[quote]
ComponentSpace - 9/15/2022
The X.509 certificate used to sign the SAML response is specified through the SAML configuration.

I've included some of the SAML configuration from the ExampleIdentityProvider's appsettings.json below.


"SAML": {
"$schema": "https://www.componentspace.com/schemas/saml-config-schema-v1.0.json",
"Configurations": [
{
"LocalIdentityProviderConfiguration": {
"Name": "https://ExampleIdentityProvider",
"Description": "Example Identity Provider",
"SingleSignOnServiceUrl": "https://localhost:44313/SAML/SingleSignOnService",
"SingleLogoutServiceUrl": "https://localhost:44313/SAML/SingleLogoutService",
"ArtifactResolutionServiceUrl": "https://localhost:44313/SAML/ArtifactResolutionService",
"LocalCertificates": [
{
"FileName": "certificates/idp.pfx",
"Password": "password"
}
]
},
"PartnerServiceProviderConfigurations": [
{
"Name": "https://ExampleServiceProvider",
"Description": "Example Service Provider",
"WantAuthnRequestSigned": true,
"SignSamlResponse": true,
"EncryptAssertion": true,
"SignLogoutRequest": true,
"SignLogoutResponse": true,
"WantLogoutRequestSigned": true,
"WantLogoutResponseSigned": true,
"AssertionConsumerServiceUrl": "https://localhost:44360/SAML/AssertionConsumerService",
"SingleLogoutServiceUrl": "https://localhost:44360/SAML/SingleLogoutService",
"ArtifactResolutionServiceUrl": "https://localhost:44360/SAML/ArtifactResolutionService",
"PartnerCertificates": [
{
"FileName": "certificates/sp.cer"
}
]
},



The SignSamlReponse flag specifies that the SAML response will be signed. It's signed using the LocalCertificate (ie idp.pfx).

[/quote]

Can I configure Certificate Name , Partnername and etc in appsettings.json file dynamically through code as end user will provide details?
Thank you

Yes. Please refer to the Configuration Guide. There are several options for specifying configuration dynamically.

We include an implementation where the SAML configuration is stored in an Entity Framework database. You also have the option of storing configuration in a custom database etc.

[quote]
ComponentSpace - 9/15/2022
What you were using is part of the low-level SAML API. Our recommendation is to use the high-level API as this is much easier to use, is configuration driven, and requires less application code.

There are two APIs for sending SAML responses. ISamlIdentityProvider.InitiateSsoAsync sends a SAML response as part of IdP-initiated SSO. ISamlIdentityProvider.SendSsoAsync sends a SAML response as part of SP-initiated SSO.

The following code is from the ExampleIdentityProvider's SamlController.


public async Task InitiateSingleSignOn()
{
// Get the name of the logged in user.
var userName = User?.Identity?.Name;

// For demonstration purposes, include some claims.
var attributes = new List()
{
new SamlAttribute(ClaimTypes.Email, User?.FindFirst(ClaimTypes.Email)?.Value),
new SamlAttribute(ClaimTypes.GivenName, User?.FindFirst(ClaimTypes.GivenName)?.Value),
new SamlAttribute(ClaimTypes.Surname, User?.FindFirst(ClaimTypes.Surname)?.Value),
};

var partnerName = _configuration["PartnerName"];
var relayState = _configuration["RelayState"];

// Initiate single sign-on to the service provider (IdP-initiated SSO)
// by sending a SAML response containing a SAML assertion to the SP.
// The optional relay state normally specifies the target URL once SSO completes.
await _samlIdentityProvider.InitiateSsoAsync(partnerName, userName, attributes, relayState);

return new EmptyResult();
}



I recommend taking a look at this example project.

Also, take a look at the Examples Guide in the documentation folder. This walks you through this and the other projects.

The signing of the SAML response is specified through the SAML configuration and handled by the API.
[/quote]


[quote]
ComponentSpace - 9/15/2022
What you were using is part of the low-level SAML API. Our recommendation is to use the high-level API as this is much easier to use, is configuration driven, and requires less application code.

There are two APIs for sending SAML responses. ISamlIdentityProvider.InitiateSsoAsync sends a SAML response as part of IdP-initiated SSO. ISamlIdentityProvider.SendSsoAsync sends a SAML response as part of SP-initiated SSO.

The following code is from the ExampleIdentityProvider's SamlController.


public async Task InitiateSingleSignOn()
{
// Get the name of the logged in user.
var userName = User?.Identity?.Name;

// For demonstration purposes, include some claims.
var attributes = new List()
{
new SamlAttribute(ClaimTypes.Email, User?.FindFirst(ClaimTypes.Email)?.Value),
new SamlAttribute(ClaimTypes.GivenName, User?.FindFirst(ClaimTypes.GivenName)?.Value),
new SamlAttribute(ClaimTypes.Surname, User?.FindFirst(ClaimTypes.Surname)?.Value),
};

var partnerName = _configuration["PartnerName"];
var relayState = _configuration["RelayState"];

// Initiate single sign-on to the service provider (IdP-initiated SSO)
// by sending a SAML response containing a SAML assertion to the SP.
// The optional relay state normally specifies the target URL once SSO completes.
await _samlIdentityProvider.InitiateSsoAsync(partnerName, userName, attributes, relayState);

return new EmptyResult();
}



I recommend taking a look at this example project.

Also, take a look at the Examples Guide in the documentation folder. This walks you through this and the other projects.

The signing of the SAML response is specified through the SAML configuration and handled by the API.
[/quote]


Hi All ,
I have one question when passing attributes to _samlIdentityProvider.InitiateSsoAsync. I have one requirement where i have to send complete SAML response to IDP. i have creted the SMAL response but unable to pass to _samlIdentityProvider.InitiateSsoAsync.
is there a way if i can pass complete XMLResponse to this method.

No. InitiateSsoAsync constructs the SAML response XML. We don’t recommend constructing the SAML response within the application code. It’s better to let the SAML API handle this.

However, you can use the OnSamlResponseCreated event to access and change the SAML response prior to it being serialized as XML.


_samlIdentityProvider.Events.OnSamlResponseCreated += (httpContext, samlResponse) =>
{
// Update the SAML response object as required.
return samlResponse;
};

_samlIdentityProvider.InitiateSsoAsync(…);



Alternatively, you can use the OnSendMessage event to access and change the XML.


_samlIdentityProvider.Events.OnSendMessage += (httpContext, samlXmlElement) =>
{
// Update the SAML XML as required.
return samlXmlElement;
};

_samlIdentityProvider.InitiateSsoAsync(…);