I am trying to write unit tests to cover my ExternalAuthenticationService
class; I'm trying to test my RedirectToLogin
method and ensure that the RelayStateQuery
is populated like it should be:
public IActionResult RedirectToLogin(LoginInfo loginInfo)
{
var binding = new Saml2RedirectBinding();
var dict = new Dictionary<string, string>
{
{ "SomeKey", info.SomeStringIWantToRoundTrip }
};
binding.SetRelayStateQuery(dict);
return binding.Bind(new Saml2AuthnRequest(config)).ToActionResult();
}
So I've tried to setup my test like this:
public class ExternalAuthenticationServiceTests
{
private IExternalAuthenticationService service;
public ExternalAuthenticationServiceTests()
{
var testCertificate = GenerateCertificate("Test Certificate");
var config = new Saml2Configuration
{
CertificateValidationMode = System.ServiceModel.Security.X509CertificateValidationMode.None,
Issuer = "http://wonderwoman.com",
RevocationMode = X509RevocationMode.NoCheck,
SignatureAlgorithm = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256",
SigningCertificate = testCertificate
};
service = new ExternalAuthenticationService(config);
}
[Fact]
public void RedirectToLogin_sets_tenantName_in_RelayStateQuery()
{
LoginInfo loginInfo = new() {
SomeStringIWantToRoundTrip = "user location or whatever"
};
var result = service.RedirectToLogin(loginInfo);
}
private X509Certificate2 GenerateCertificate(string subjectName)
{
// Generate a new RSA key pair
using var rsa = RSA.Create(2048);
// Create a certificate request
CertificateRequest request = new($"CN={subjectName}", rsa, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
// Set certificate validity dates
DateTimeOffset now = DateTimeOffset.UtcNow;
request.CertificateExtensions.Add(new X509BasicConstraintsExtension(false, false, 0, false));
request.CertificateExtensions.Add(new X509KeyUsageExtension(X509KeyUsageFlags.DigitalSignature | X509KeyUsageFlags.KeyEncipherment, false));
request.CertificateExtensions.Add(new X509EnhancedKeyUsageExtension(new OidCollection { new Oid("1.3.6.1.5.5.7.3.1") }, false));
request.CertificateExtensions.Add(new X509SubjectKeyIdentifierExtension(request.PublicKey, false));
// Create a self-signed certificate
X509Certificate2 certificate = request.CreateSelfSigned(now, now.AddYears(1));
// Return the generated certificate
return certificate;
}
}
}
…but this doesn't work, even before assertions are added. The RedirectToLogin
method throws an exception:
Message: System.NullReferenceException : Object reference not set to an instance of an object.
Stack Trace:
Saml2RedirectBinding.BindInternal(Saml2Request saml2RequestResponse, String messageName) Saml2Binding`1.Bind(Saml2Request saml2Request) ExternalAuthenticationService.RedirectToLogin(LoginInfo loginInfo) line 39
I'm assuming the Saml2Configuration
class needs something I'm not providing in the properties, but I can't tell what. What am I missing?