Last active
August 2, 2021 06:50
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
}); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 => { }); | |
}); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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!