How to validate a SAML token from ADFS 3.0 in an ASP.NET Core Web API

I am trying to validate a SAML token that is sent as a header to a ASP.NET Core 6 Web API. Frankly, I do not have a lot of experience working with tokens, so I probably lack some foundational knowledge that would be helpful here. Any guidance is appreciated.

Tokens are acquired from a .NET Framework service like which obtains them from our ADFS 3.0 instance on Windows Server 2012R2 using code like the following:

WSTrustChannelFactory trustChannelFactory =
    new WSTrustChannelFactory(new WindowsWSTrustBinding(SecurityMode.TransportWithMessageCredential),
        new EndpointAddress(new Uri(url)));

trustChannelFactory.TrustVersion = TrustVersion.WSTrust13;
trustChannelFactory.Credentials.Windows.ClientCredential.Domain = domain;
trustChannelFactory.Credentials.Windows.ClientCredential.UserName = username;
trustChannelFactory.Credentials.Windows.ClientCredential.Password = password;

RequestSecurityToken rst = new RequestSecurityToken(WSTrust13Constants.RequestTypes.Issue, WSTrust13Constants.KeyTypes.Bearer);
rst.AppliesTo = new EndpointAddress(relyingParting);
rst.TokenType = Microsoft.IdentityModel.Tokens.SecurityTokenTypes.Saml2TokenProfile11;
WSTrustChannel channel = (WSTrustChannel)trustChannelFactory.CreateChannel();
GenericXmlSecurityToken token = channel.Issue(rst) as GenericXmlSecurityToken;
tokenString = token.TokenXml.OuterXml;

return tokenString;

The token comes out looking something like this:

<Assertion ID="_1d020948-dd8c-4349-9b4e-94832d2381b3" IssueInstant="2024-04-29T19:18:26.948Z" Version="2.0" xmlns="urn:oasis:names:tc:SAML:2.0:assertion">
    <Issuer>http://my-adfs-instance.net/adfs/services/trust</Issuer>
    <ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
        <ds:SignedInfo><ds:CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
        <ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256" />
        <ds:Reference URI="#_1d020948-dd8c-4349-9b4e-94832d2381b3">
            <ds:Transforms>
                <ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
                <ds:Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
            </ds:Transforms>
            <ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256" />
            <ds:DigestValue>NoClueIfSensitive</ds:DigestValue>
        </ds:Reference>
        </ds:SignedInfo>
        <ds:SignatureValue>Redacted</ds:SignatureValue>
        <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
            <ds:X509Data>
                <ds:X509Certificate>MII...</ds:X509Certificate>
            </ds:X509Data>
        </KeyInfo>
    </ds:Signature>
    <Subject>
        <SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
            <SubjectConfirmationData NotOnOrAfter="2024-04-29T19:23:26.948Z" />
        </SubjectConfirmation>
    </Subject>
    <Conditions NotBefore="2024-04-29T19:18:26.653Z" NotOnOrAfter="2024-04-29T20:18:26.653Z">
        <AudienceRestriction>
            <Audience>https://my-app.net</Audience>
        </AudienceRestriction>
    </Conditions>
    <AttributeStatement>
        ...
    </AttributeStatement>
    <AuthnStatement AuthnInstant="2024-04-29T19:18:26.590Z">
        ...
    </AuthnStatement>
</Assertion>

I’ve abbreviated some fields for brevity since its already pretty long – just let me know if those things are important for an answer.

That token is sent over as a header to the ASP.NET Core Web API I mentioned.

I have tried a couple different ways of validating this token.

Ideally, I’d like to use a Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityTokenHandler so that I can leverage the ValidateToken method that comes with it like so:

var headers = Request.Headers;
var token = headers["token"];

var tokenHandler = new Saml2SecurityTokenHandler();
var tokenValidationParameters = new TokenValidationParameters()
{
    ValidIssuer = _config.GetSection("TokenValidation")["ValidIssuer"],
    ValidAudience = _config.GetSection("TokenValidation")["ValidAudience"]
};

tokenHandler.ValidateToken(token, tokenValidationParameters, out SecurityToken validatedToken);

This fails, which I believe is to be expected, with a SecurityTokenSignatureKeyNotFoundException exception that states

IDX10500: Signature validation failed. No security keys were provided to validate the signature

After rummaging around documentation, I then tried the IssuerSigningKey property out by pulling down the code-signing cert from my ADFS instance and placing it on my local machine. Not a perfect solution, but just trying to get some kind of successful test at this point:

var tokenHandler = new Saml2SecurityTokenHandler();
var issuerSigningCert = new X509Certificate2("C:\signingCert.cer");
var securityKey = new X509SecurityKey(issuerSigningCert);
var tokenValidationParameters = new TokenValidationParameters()
{
    ValidIssuer = _config.GetSection("TokenValidation")["ValidIssuer"],
    ValidAudience = _config.GetSection("TokenValidation")["ValidAudience"],
    IssuerSigningKey = securityKey
};

tokenHandler.ValidateToken(token, tokenValidationParameters, out SecurityToken validatedToken);

This results in a SecurityTokenInvalidSignatureException with message

IDX10514: Signature validation failed

I also tried plugging in the PFX instead of just the cer by changing the file path to the pfx and supplying the password in the X509Certificate2 constructor, but I received the same exception and message.

I did notice something interesting when trying to use the ReadSaml2Token method:

var tokenHandler = new Saml2SecurityTokenHandler();
using (XmlReader reader = XmlReader.Create(new StringReader(token)))
{
    var tokenReadIn = tokenHandler.ReadSaml2Token(reader);
}

null-properties

The SecurityKey and SigningKey are always null. This makes me wonder if there is some issue with my tokens in particular that they aren’t being parsed correctly, and this is where I am stuck with this route.

I have a separate .NET Framework 4.5 API that is able to receive and validate tokens using the following code

Microsoft.IdentityModel.Configuration.ServiceConfiguration serviceConfig
       = new Microsoft.IdentityModel.Configuration.ServiceConfiguration();

SecurityToken theToken = null;
ClaimsIdentityCollection identity = null;
using (XmlReader reader = XmlReader.Create(new StringReader(authSamlString)))
{
    theToken = serviceConfig.SecurityTokenHandlers.ReadToken(reader);
    identity = serviceConfig.SecurityTokenHandlers.ValidateToken(theToken);
}

The packages used here are System.IdentityModel.Tokens and Microsoft.IdentityModel.Claims and they don’t appear to be intended for .NET Core/.NET so I wasn’t able to replicate that exactly, but I don’t see any specific customization or configuration other than just the ValidateToken call in this application.

The alternate route I took involved using SignedXml to load in the Signature, specify the cert public key, and attempt to check with CheckSignature, very similar to the answer posted here Asp.Net Core SAML Response Signature Validation.

This was quite a mess, and I’d greatly prefer to avoid that route unless it’s my only option. No matter what I tried with that route, I also could not get anything other then a false result from CheckSignature. I can post the actual code I used if that seems necessary, but I’d like to see if I can get the Microsoft.IdentityModel solution working instead.

Any and all help is greatly appreciated. Upgrading ADFS is unfortunately not feasible, but if there are any configuration changes or alternate approaches in general, those are fine.

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật