Reputation: 2159
I'm using the TestWebAppCore project to test SAML integration for an ASP.NET Core web app and i thought i got it working but the claims that are associated with the user session aren't the claims being returned by the IdP in the SAML Response and i'm not sure what extra configuration is required to map the returned claims.
After clicking Login i'm redirected to my IdP, after logging in my IdP responds with the following SAML response (parts removed to keep question short):
...
<saml:Subject>
<saml:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"
NameQualifier="samlsso"
SPNameQualifier="https://my.identity.provider"
>edde16f1-9fee-4e44-9c4d-3810a3a6f73a</saml:NameID>
<saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
<saml:SubjectConfirmationData InResponseTo="_01b18bfb2348b2d1dcc1df73bcdb88dc"
NotOnOrAfter="2020-11-27T13:20:41Z"
Recipient="https://my.identity.provider/samlsso"
/>
</saml:SubjectConfirmation>
</saml:Subject>
...
<saml:AttributeStatement>
<saml:Attribute Name="MiddleName">
<saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:type="xs:string"
>Ben</saml:AttributeValue>
</saml:Attribute>
<saml:Attribute Name="email">
<saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:type="xs:string"
>[email protected]</saml:AttributeValue>
</saml:Attribute>
<saml:Attribute Name="GivenName">
<saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:type="xs:string"
>Peter</saml:AttributeValue>
</saml:Attribute>
<saml:Attribute Name="FamilyName">
<saml:AttributeValue xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:type="xs:string"
>Parker</saml:AttributeValue>
</saml:Attribute>
</saml:AttributeStatement>
...
After logging i'm redirected to the homepage and i see "Hi, edde16f1-9fee-4e44-9c4d-3810a3a6f73a" so i click on "SAML Claims" and the page shows:
The users Claims (Iteration on User.Claims)
http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier
Value: edde16f1-9fee-4e44-9c4d-3810a3a6f73a
http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod
Value: urn:oasis:names:tc:SAML:2.0:ac:classes:Password
http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationinstant
Value: 2020-11-27T13:10:37.504Z
http://schemas.itfoxtec.com/ws/2014/02/identity/claims/saml2nameid
Value: edde16f1-9fee-4e44-9c4d-3810a3a6f73a
http://schemas.itfoxtec.com/ws/2014/02/identity/claims/saml2nameidformat
Value: urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress
This list doesn't include the claims i want to use from the SAML response that i get from the IdP, so i tried to add the claims in the ClaimsTransform
class by modifying the code slightly:
private static ClaimsPrincipal CreateClaimsPrincipal(ClaimsPrincipal incomingPrincipal)
{
var claims = new List<Claim>();
// All claims
////claims.AddRange(incomingPrincipal.Claims);
var givenName = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname";
claims.Add(new Claim(givenName, GetClaimValue(incomingPrincipal, givenName)));
// Or custom claims
//claims.AddRange(GetSaml2LogoutClaims(incomingPrincipal));
//claims.Add(new Claim(ClaimTypes.NameIdentifier, GetClaimValue(incomingPrincipal, ClaimTypes.NameIdentifier)));
return new ClaimsPrincipal(new ClaimsIdentity(claims, incomingPrincipal.Identity.AuthenticationType, ClaimTypes.NameIdentifier, ClaimTypes.Role)
{
BootstrapContext = ((ClaimsIdentity)incomingPrincipal.Identity).BootstrapContext
});
}
private static Claim GetClaim(ClaimsPrincipal principal, string claimType)
{
return ((ClaimsIdentity)principal.Identity).Claims.FirstOrDefault(c => c.Type == claimType);
}
private static string GetClaimValue(ClaimsPrincipal principal, string claimType)
{
var claim = GetClaim(principal, claimType);
return claim?.Value;
}
But this change to the code results in an error from the Claim
class:
Value cannot be null.
Value seems to be null wether i add the claim with GivenName
or the claim address. Is there additional configuration i'm missing that wil allow me to consume the claims in the "AttributeStatement" section?
Update
Further reading of the code confuses me, in the AssertionConsumerService
route the test code is creating a brand new SAMLResponse
? The new response doesn't contain any of the attributes from the IdP response which would explain why there are no claims.
If this is how the code is supposed to work, is it possible to include the claims from the IdP response in the new response generated by ITfoxtec.identity.saml2
?
<saml2p:Response xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol"
Destination="https://my.test.website/Auth/AssertionConsumerService"
ID="_9099f6ccf0b9ac7703d6b320df6357a0"
InResponseTo="_08e3a2b0-4ac8-4673-80bc-31460812738f"
IssueInstant="2020-11-28T01:13:15.726Z"
Version="2.0"
>
<saml2:Issuer xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion"
Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity"
>https://my.test.provider</saml2:Issuer>
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
<CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
<SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256" />
<Reference URI="#_9099f6ccf0b9ac7703d6b320df6357a0">
<Transforms>
<Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
<Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
<DigestValue>IRYj+9sUoEsO5rEgEj+laMogGk0=</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>...removed...</SignatureValue>
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:X509Data>
<ds:X509Certificate>...removed...</ds:X509Certificate>
</ds:X509Data>
</ds:KeyInfo>
</Signature>
<saml2p:Status>
<saml2p:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success" />
</saml2p:Status>
<saml2:Assertion xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion"
ID="_fbe41ccdaa799fe0c3038d5d07edc18e"
IssueInstant="2020-11-28T01:13:15.726Z"
Version="2.0"
>
<saml2:Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity">https://my.test.provider</saml2:Issuer>
<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
<SignedInfo>
<CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
<SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256" />
<Reference URI="#_fbe41ccdaa799fe0c3038d5d07edc18e">
<Transforms>
<Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
<Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
</Transforms>
<DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1" />
<DigestValue>FbSefxSL8LDE1pJdhScHaNijdEY=</DigestValue>
</Reference>
</SignedInfo>
<SignatureValue>...removed...</SignatureValue>
<ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:X509Data>
<ds:X509Certificate>...removed...</ds:X509Certificate>
</ds:X509Data>
</ds:KeyInfo>
</Signature>
<saml2:Subject>
<saml2:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress">edde16f1-9fee-4e44-9c4d-3810a3a6f73a</saml2:NameID>
<saml2:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
<saml2:SubjectConfirmationData InResponseTo="_08e3a2b0-4ac8-4673-80bc-31460812738f"
NotOnOrAfter="2020-11-28T01:18:15.726Z"
Recipient="https://my.test.website/Auth/AssertionConsumerService"
/>
</saml2:SubjectConfirmation>
</saml2:Subject>
<saml2:Conditions NotBefore="2020-11-28T01:13:15.726Z"
NotOnOrAfter="2020-11-28T01:18:15.726Z"
>
<saml2:AudienceRestriction>
<saml2:Audience>https://my.test.website</saml2:Audience>
</saml2:AudienceRestriction>
</saml2:Conditions>
<saml2:AuthnStatement AuthnInstant="2020-11-28T01:13:15.647Z">
<saml2:AuthnContext>
<saml2:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:Password</saml2:AuthnContextClassRef>
</saml2:AuthnContext>
</saml2:AuthnStatement>
</saml2:Assertion>
</saml2p:Response>
Code for the AssertionConsumerService
:
[Route("AssertionConsumerService")]
public async Task<IActionResult> AssertionConsumerService()
{
var binding = new Saml2PostBinding();
var saml2AuthnResponse = new Saml2AuthnResponse(config);
binding.ReadSamlResponse(Request.ToGenericHttpRequest(), saml2AuthnResponse);
if (saml2AuthnResponse.Status != Saml2StatusCodes.Success)
{
throw new AuthenticationException($"SAML Response status: {saml2AuthnResponse.Status}");
}
binding.Unbind(Request.ToGenericHttpRequest(), saml2AuthnResponse);
await saml2AuthnResponse.CreateSession(HttpContext, claimsTransform: (claimsPrincipal) => ClaimsTransform.Transform(claimsPrincipal));
var relayStateQuery = binding.GetRelayStateQuery();
var returnUrl = relayStateQuery.ContainsKey(relayStateReturnUrl) ? relayStateQuery[relayStateReturnUrl] : Url.Content("~/");
return Redirect(returnUrl);
}
Upvotes: 2
Views: 2018
Reputation: 4334
I have not tried to read attributes that looks like you describe. But I think the library should be able to read the attributes.
Normally attributes look like this:
<Attribute Name="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname">
<AttributeValue>Peter</AttributeValue>
</Attribute>
With a full namespace. However, it should also be possible to read a claim with a name like givenname
.
The ITfoxtec Identity SAML package only support SAML 2.0. In SAML 2.0 the NameID has a SAML 2.0 namespace:
<NameID Format="urn:oasis:names:tc:SAML:2.0:nameid-format:persistent">edde16f1-9fee-4e44-9c4d-3810a3a6f73a</NameID>
Maybe there is other problems in the XML not making SAML 2.0 compliant.
Upvotes: 0