Search code examples
itfoxtec-identity-saml2

How to add attributes for custom claims types for IdP Saml2 response


I converted the IdPInitiatedController example code for my WebForms project. Although I don't get compiler or runtime error, I still cannot seem to get the manually created claims added to the Saml2AuthnResponse object after setting the claims identity. Is there another way to add attributes to the SAML response? The app uses forms authentication; and the credentials are in a SQL database. That's why I don't have Windows Identity.

' SSO CODE BEHIND
...
 Dim binding = New Saml2PostBinding With {
                    .RelayState = String.Format("RPID={0}", Uri.EscapeDataString(serviceProviderRealm))
                }
               
 Dim config = New Saml2Configuration With {
     .Issuer = "company.stage.app",
     .SingleSignOnDestination = New Uri(ConfigurationManager.AppSettings.Get("appValue")),
     .SigningCertificate = CertificateUtil.Load(X509Certificates.StoreName.TrustedPeople, X509Certificates.StoreLocation.LocalMachine, X509Certificates.X509FindType.FindByThumbprint, ConfigurationManager.AppSettings.Get("ThatKey")),
     .SignatureAlgorithm = Saml2SecurityAlgorithms.RsaSha256Signature
    }

Dim samlResponse = New Saml2AuthnResponse(config) With {
       .Status = Saml2StatusCodes.Success
      }

_ssoService.AddAttributes(samlResponse, currentUser)

binding.Bind(samlResponse)  ' NO ATTRIBUTES WERE ADDED

Me.SAMLResponse.Value = binding.XmlDocument.InnerXml
Me.RelayState.Value = binding.RelayState

frmSSO.Action = config.SingleSignOnDestination.AbsoluteUri()
frmSSO.Method = "POST"
Public Sub AddAttributes(ByRef samlResponse As Saml2AuthnResponse, ByVal currentUser As CurrentUser)
            Dim acctContact As CoveredInsuredDto = _coveragesService.GetAccountPointOfContact(currentUser.getControlNo())

            If Not IsNothing(acctContact) Then
                ' Adding Attributes as claims
                Dim claimIdentity = New ClaimsIdentity(Me.BuildClaims(currentUser, acctContact))

                samlResponse.NameId = New IdentityModel.Tokens.Saml2NameIdentifier(acctContact.Name)
                samlResponse.ClaimsIdentity = claimIdentity
            End If
        End Sub

Private Function BuildClaims(ByVal currentUser As CurrentUser, ByVal acctContact As CoveredInsuredDto) As IEnumerable(Of Claim)
            Dim claims = New List(Of Claim)

            If Not IsNothing(acctContact) Then               
                claims.Add(New Claim("FIRST_NAME", acctContact.FirstName))
                claims.Add(New Claim("LAST_NAME", acctContact.LastName))
            End If

            Return claims.ToList()
        End Function

Solution

  • The problem is that you have forgot the var token = response.CreateSecurityToken(appliesToAddress); line which is actually the line creating the token.

    The ASP.NET Core IdP Initiated sample code:

    public IActionResult Initiate()
    {
        var serviceProviderRealm = "https://some-domain.com/some-service-provider";
    
        var binding = new Saml2PostBinding();
        binding.RelayState = $"RPID={Uri.EscapeDataString(serviceProviderRealm)}";
    
        var config = new Saml2Configuration();
    
        config.Issuer = "http://some-domain.com/this-application";
        config.SingleSignOnDestination = new Uri("https://test-adfs.itfoxtec.com/adfs/ls/");
        config.SigningCertificate = CertificateUtil.Load(Startup.AppEnvironment.MapToPhysicalFilePath("itfoxtec.identity.saml2.testwebappcore_Certificate.pfx"), "!QAZ2wsx");
        config.SignatureAlgorithm = Saml2SecurityAlgorithms.RsaSha256Signature;
    
        var appliesToAddress = "https://test-adfs.itfoxtec.com/adfs/services/trust";
    
        var response = new Saml2AuthnResponse(config);
        response.Status = Saml2StatusCodes.Success;    
    
        var claimsIdentity = new ClaimsIdentity(CreateClaims());
        response.NameId = new Saml2NameIdentifier(claimsIdentity.Claims.Where(c => c.Type == ClaimTypes.NameIdentifier).Select(c => c.Value).Single(), NameIdentifierFormats.Persistent);
        response.ClaimsIdentity = claimsIdentity;
        var token = response.CreateSecurityToken(appliesToAddress);
    
        return binding.Bind(response).ToActionResult();
    }
    
    private IEnumerable<Claim> CreateClaims()
    {
        yield return new Claim(ClaimTypes.NameIdentifier, "some-user-identity");
        yield return new Claim(ClaimTypes.Email, "[email protected]");
    }