Skip to content

Instantly share code, notes, and snippets.

@jelster
Last active August 2, 2021 06:50
Show Gist options
  • Save jelster/6b850d567acef343e59fad70cb20455a to your computer and use it in GitHub Desktop.
Save jelster/6b850d567acef343e59fad70cb20455a to your computer and use it in GitHub Desktop.
ASP.NET Core 2.1 - Setting up functional integration tests with authentication middleware
using Microsoft.AspNetCore.Mvc.Testing;
using System.Net;
using System.Threading.Tasks;
using Xunit;
using System.Net.Http;
using System.Collections.Generic;
using Newtonsoft.Json;
namespace Api.FunctionalTests
{
public class BasicTests : IClassFixture<TestWebApplicationFactory<MyWebApi.Startup>>
{
private readonly HttpClient _client;
private readonly HttpClient _unauthorizedClient;
private readonly TestWebApplicationFactory<MyWebApi.Startup> _factory;
public BasicTests(TestWebApplicationFactory<MyWebApi.Startup> factory)
{
_factory = factory;
_client = _factory.CreateClient(new WebApplicationFactoryClientOptions
{
AllowAutoRedirect = false
});
// Requests made using this HTTP client will go through the test scheme handler...
_client.DefaultRequestHeaders.Add("X-Test-Auth", "false");
// and be considered authenticated
_client.DefaultRequestHeaders.Add("Authorization", "Bearer asdf1234qwerty");
// this client makes requests that end up being handled by the "real" auth middleware
_unauthorizedClient = _factory.CreateClient();
}
}
}
using Microsoft.AspNetCore.Mvc.Testing;
using System.Net;
using System.Threading.Tasks;
using Xunit;
using System.Net.Http;
using System.Collections.Generic;
using Newtonsoft.Json;
namespace Api.FunctionalTests
{
public class ClaimsTests : IClassFixture<TestWebApplicationFactory<MyWebApi.Startup>>
{
private readonly HttpClient _client;
private readonly HttpClient _unauthorizedClient;
private readonly TestWebApplicationFactory<MyWebApi.Startup> _factory;
public ClaimsTests(TestWebApplicationFactory<MyWebApi.Startup> factory)
{
_factory = factory;
_factory = _factory.WithWebHostBuilder(builder =>
{
builder.ConfigureServices(services =>
{
services.AddAuthentication(o =>
{
o.DefaultScheme = "TestAuthenticationScheme";
})
.AddTestAuthentication("TestAuthenticationScheme", "Test Auth", o =>
{
o.Identity = new ClaimsIdentity( /* pass in a Claim[] */);
});
});
});
_client = _factory.CreateClient(new WebApplicationFactoryClientOptions
{
AllowAutoRedirect = false
});
}
}
}
using Microsoft.AspNetCore.Authentication;
using System.Security.Claims;
using System.Threading.Tasks;
using System.Text.Encodings.Web;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.Logging;
using System;
using System.Linq;
namespace Api.FunctionalTests
{
public class TestAuthHandler : AuthenticationHandler<TestAuthenticationOptions>
{
public TestAuthHandler(IOptionsMonitor<TestAuthenticationOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock)
: base(options, logger, encoder, clock)
{
}
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
{
// if the header is provided and its value is 'true', then step aside and let the regular auth handle it
// a value of <anything other than 'true'> will bypass the rest of the auth pipeline
if (!Context.Request.Headers.TryGetValue("X-Test-Auth", out var header) || header.Contains("true"))
{
return Task.FromResult(AuthenticateResult.NoResult());
}
// value is false (or anything other than 'true'). Make sure they've made the effort to provide the auth header
// but don't bother to check the value
if (header.Any() && !Context.Request.Headers.TryGetValue("Authorization", out var authHeader))
{
return Task.FromResult(AuthenticateResult.NoResult());
}
// otherwise, here's your auth ticket!
return Task.FromResult(
AuthenticateResult.Success(
new AuthenticationTicket(
new ClaimsPrincipal(Options.Identity),
new AuthenticationProperties(),
this.Scheme.Name)));
}
}
public class TestAuthenticationOptions : AuthenticationSchemeOptions
{
public bool RequireBearerToken { get; set; }
// customize as needed
public virtual ClaimsIdentity Identity { get; set; } = new ClaimsIdentity(
new Claim[]
{
new Claim("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier", Guid.NewGuid().ToString()),
new Claim("http://schemas.microsoft.com/identity/claims/tenantid", "test"),
new Claim("http://schemas.microsoft.com/identity/claims/objectidentifier", Guid.NewGuid().ToString()),
new Claim("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname", "test"),
new Claim("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname", "test"),
new Claim("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn", "test"),
},
"test");
public TestAuthenticationOptions() {}
}
public static class TestAuthenticationExtensions
{
public static AuthenticationBuilder AddTestAuthentication(this AuthenticationBuilder builder, string authenticationScheme, string displayName, Action<TestAuthenticationOptions> configureOptions)
{
return builder.AddScheme<TestAuthenticationOptions, TestAuthHandler>(authenticationScheme, configureOptions);
}
}
}
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection;
using System.Linq;
using Microsoft.Extensions.Configuration;
namespace Api.FunctionalTests
{
public class TestWebApplicationFactory<TStartup> : WebApplicationFactory<MyWebApi.Startup>
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.UseEnvironment("Integration");
builder.ConfigureAppConfiguration((builderContext, config) =>
{
IHostingEnvironment env = builderContext.HostingEnvironment;
config.Sources.Clear();
config.AddJsonFile($"appsettings.{env.EnvironmentName}.json", false, reloadOnChange: true);
});
builder.ConfigureTestServices(services =>
{
services
.AddAuthentication(o =>
{
o.DefaultScheme = "TestAuthenticationScheme";
}).AddTestAuthentication("TestAuthenticationScheme", "Test Auth", o => { });
});
}
}
}
@nyghtrocker
Copy link

Can I use this code for testing .net core razor pages app? Do you have any example that show how to write integration test in razor pages app and mock identity. integration test for authenticated users in razor page app?

@jelster
Copy link
Author

jelster commented Feb 20, 2020

Since the bulk of the work is done in the standard service initialization fashion, I don't see why you couldn't use this in a razor pages app. I don't have any examples of the sort you're looking for, but I think there's some samples in the MSFT docs that do cover this topic explicitly. You could try starting with those and adapting this code into them - love to see what you come up with!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment