Skip to content

Commit

Permalink
Port IgnoreAuthenticationContextInResponse option to 2.x
Browse files Browse the repository at this point in the history
  • Loading branch information
explunit committed Feb 12, 2019
1 parent d4482f1 commit fa45eba
Show file tree
Hide file tree
Showing 6 changed files with 79 additions and 8 deletions.
8 changes: 8 additions & 0 deletions Sustainsys.Saml2/Configuration/Compatibility.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,14 @@ public Compatibility(CompatibilityElement configElement)
/// </summary>
public bool StrictOwinAuthenticationMode { get; set; }

/// <summary>
/// Do not read the AuthnContext element in Saml2Response.
/// If you do not need these values to be present as claims in the generated
/// identity, using this option can prevent XML format errors (IDX13102)
/// e.g. when value cannot parse as absolute URI
/// </summary>
public bool IgnoreAuthenticationContextInResponse { get; set; }

/// <summary>
/// Ignore the check for the missing InResponseTo attribute in the Saml response.
/// This is different to setting the allowUnsolicitedAuthnResponse as it will only
Expand Down
2 changes: 1 addition & 1 deletion Sustainsys.Saml2/Configuration/SPOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ public Saml2PSecurityTokenHandler Saml2PSecurityTokenHandler
if(value == null)
{
// Set the saved value, but don't trust it - still use a local var for the return.
saml2PSecurityTokenHandler = value = new Saml2PSecurityTokenHandler();
saml2PSecurityTokenHandler = value = new Saml2PSecurityTokenHandler(this);
}

return value;
Expand Down
6 changes: 3 additions & 3 deletions Sustainsys.Saml2/SAML2P/Saml2PSecurityTokenHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ namespace Sustainsys.Saml2.Saml2P
/// could be handled at transport level.
/// </summary>
public class Saml2PSecurityTokenHandler : Saml2SecurityTokenHandler
{
public Saml2PSecurityTokenHandler()
{
public Saml2PSecurityTokenHandler( SPOptions spOptions)
{
Serializer = new Saml2PSerializer();
Serializer = new Saml2PSerializer(spOptions);
}

// Overridden to fix the fact that the base class version uses NotBefore as the token replay expiry time
Expand Down
22 changes: 21 additions & 1 deletion Sustainsys.Saml2/SAML2P/Saml2PSerializer.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using Microsoft.IdentityModel.Tokens.Saml2;
using Microsoft.IdentityModel.Xml;
using Sustainsys.Saml2.Configuration;
using Sustainsys.Saml2.Internal;
using System;
using System.Collections.Generic;
Expand Down Expand Up @@ -45,6 +46,13 @@ public Saml2EncryptedAssertion(Saml2NameIdentifier issuer) :
// - ignore authentication context if configured to do so
class Saml2PSerializer : Saml2Serializer
{
private SPOptions spOptions;

public Saml2PSerializer(SPOptions spOptions)
{
this.spOptions = spOptions;
}

public ICollection<X509Certificate2> DecryptionCertificates { get; set; }

/// <summary>
Expand Down Expand Up @@ -284,7 +292,19 @@ public override void WriteAssertion(XmlWriter writer, Saml2Assertion assertion)

writer.WriteEndElement();
}


protected override Saml2AuthenticationContext ReadAuthenticationContext(XmlDictionaryReader reader)
{
if (spOptions.Compatibility.IgnoreAuthenticationContextInResponse)
{
reader.Skip();
//hack to get around the lack of a sane constructor
return (Saml2AuthenticationContext)System.Runtime.Serialization.FormatterServices.GetUninitializedObject(typeof(Saml2AuthenticationContext));
}

return base.ReadAuthenticationContext(reader);
}

internal static Exception LogReadException(string message)
{
return LogExceptionMessage(new Saml2SecurityTokenReadException(message));
Expand Down
2 changes: 1 addition & 1 deletion Tests/Tests.Shared/Configuration/SPOptionsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -624,7 +624,7 @@ public void SPOptions_Saml2PSecurityTokenHandler_Setter()
{
var subject = StubFactory.CreateSPOptions();

var handler = new Saml2PSecurityTokenHandler();
var handler = new Saml2PSecurityTokenHandler(subject);

subject.Saml2PSecurityTokenHandler = handler;

Expand Down
47 changes: 45 additions & 2 deletions Tests/Tests.Shared/Saml2P/Saml2ResponseTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -456,6 +456,49 @@ public void Saml2Response_GetClaims_CorrectSignedResponseMessage_WithAuthnContex
authMethodClaim.Value.Should().Be("urn:somespecialvalue");
}

[TestMethod]
public void Saml2Response_GetClaims_BadAuthnContext_IgnoredWhenConfigured()
{
var response =
@"<?xml version=""1.0"" encoding=""UTF-8""?>
<saml2p:Response xmlns:saml2p=""urn:oasis:names:tc:SAML:2.0:protocol""
xmlns:saml2=""urn:oasis:names:tc:SAML:2.0:assertion""
ID = """ + MethodBase.GetCurrentMethod().Name + @""" Version=""2.0"" IssueInstant=""2013-01-01T00:00:00Z"">
<saml2:Issuer>https://idp.example.com</saml2:Issuer>
<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""
Version=""2.0"" ID=""" + MethodBase.GetCurrentMethod().Name + @"_Assertion1""
IssueInstant=""2013-09-25T00:00:00Z"">
<saml2:Issuer>https://idp.example.com</saml2:Issuer>
<saml2:Subject>
<saml2:NameID>AuthenticatedSomeone</saml2:NameID>
<saml2:SubjectConfirmation Method=""urn:oasis:names:tc:SAML:2.0:cm:bearer"" />
</saml2:Subject>
<saml2:Conditions NotOnOrAfter=""2100-01-01T00:00:00Z"" />
<saml2:AuthnStatement AuthnInstant=""2013-09-25T00:00:00Z"" SessionIndex=""17"" >
<saml2:AuthnContext>
<saml2:AuthnContextClassRef>badvalue</saml2:AuthnContextClassRef>
</saml2:AuthnContext>
</saml2:AuthnStatement>
</saml2:Assertion>
</saml2p:Response>";

var signedResponse = SignedXmlHelper.SignXml(response);

var options = StubFactory.CreateOptions();
options.SPOptions.Compatibility.IgnoreAuthenticationContextInResponse = true;
var result = Saml2Response.Read(signedResponse).GetClaims(options);

var authMethodClaim = result.Single().Claims.SingleOrDefault(c => c.Type == ClaimTypes.AuthenticationMethod);
authMethodClaim.Should().BeNull("the authentication method claim should not be generated");

var nameidClaim = result.Single().Claims.SingleOrDefault(c => c.Type == ClaimTypes.NameIdentifier);
nameidClaim.Should().NotBeNull("the subject nameid claim should be generated");
nameidClaim.Value.Should().Be("AuthenticatedSomeone");
}

[TestMethod]
public void Saml2Response_GetClaims_SessionIndexButNoNameId()
{
Expand Down Expand Up @@ -1008,7 +1051,7 @@ public void Saml2Response_GetClaims_CorrectEncryptedSingleAssertion_OAEP()
}

[TestMethod]
public void Saml2Response_GetClaims_CorrectEncryptedSingleAssertion_UsingWIF()
public void Saml2Response_GetClaims_CorrectEncryptedSingleAssertion_UsingMSIdentityModel()
{
var response =
@"<saml2p:Response xmlns:saml2p=""urn:oasis:names:tc:SAML:2.0:protocol""
Expand All @@ -1027,7 +1070,7 @@ public void Saml2Response_GetClaims_CorrectEncryptedSingleAssertion_UsingWIF()
assertion.Conditions = new Saml2Conditions { NotOnOrAfter = new DateTime(2100, 1, 1) };

var token = new Saml2SecurityToken(assertion);
var handler = new Saml2PSecurityTokenHandler();
var handler = new Saml2SecurityTokenHandler();

var signingKey = new X509SecurityKey(SignedXmlHelper.TestCert);
var signingCreds = new SigningCredentials(signingKey,
Expand Down

0 comments on commit fa45eba

Please sign in to comment.