Aspnetcore.docs: How to create authenticated request?

Created on 5 Jun 2018  ·  50Comments  ·  Source: dotnet/AspNetCore.Docs

Hi,
Can you possibly provide an example of how I would go about authenticating a request? Most of my controllers require a user to be authenticated prior to having access to a controller's actions. What's the best way to go about it using WebApplicationFactory? Prior I had used the TestServer and added a Test Middleware where I set the appropriate Header attributes. This however required me to overwrite the Startup class of my WebApplication. I'd prefer if I didn't have to do that anymore but I fail to see how I can achieve something similar using WebApplicationFactory.
Thanks.


Document Details

Do not edit this section. It is required for docs.microsoft.com ➟ GitHub issue linking.

Source - Docs.ms doc-enhancement

Most helpful comment

We are currently achieving this by adding two filters inside ConfigureTestServices.

factory.WithWebHostBuilder(builder => builder.ConfigureTestServices(
    services => services.AddMvc(
        options =>
        {
            options.Filters.Add(new AllowAnonymousFilter());
            options.Filters.Add(new FakeUserFilter());
        }))).CreateClient();

Then we have our FakeUserFilter defined as:

private class FakeUserFilter : IAsyncActionFilter
{
    public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
    {
        context.HttpContext.User = new ClaimsPrincipal(new ClaimsIdentity(new List<Claim>
        {
            new Claim(ClaimTypes.NameIdentifier, "12345678-1234-1234-1234-123456789012"),
            new Claim(ClaimTypes.Name, "TestUser"),
            new Claim(ClaimTypes.Email, "[email protected]"), // add as many claims as you need
        }));

        await next();
    }
}

All 50 comments

It's there (testing an unauthenticated user can't access a secure resource) ...

https://docs.microsoft.com/aspnet/core/test/integration-tests?view=aspnetcore-2.1#test-a-secure-endpoint

[EDIT] Are you suggesting that you would create an authenticated user and then make sure that the test user would be able to get a 200-OK for a secure resource?

Just noticed your edit. That is exactly what I'm trying to achieve. I would like to 'mock' a request as being authenticated and check whether the executed action of my secure resource returns what I expect.

Great idea. We're still working on 2.1 updates and have some other priority issues to address, but I'll get to this asap.

I'm new to AngleSharp. It was the engineering team's choice. It should make it easy for you to submit credentials to your sign-on page, and then test navigation to the secure endpoint once the user has been authenticated. Since I'm new to AngleSharp, I can't advise you further on exactly how to pull it off. I suggest searching for some examples.

Ok, cool, thank you.

I have never used AngleSharp before but I'll have a look.

@search8 - I'd recommend checking out the samples they've done to test the new IdentityUI.

Your specific example of wanting to send the login details is here: https://github.com/aspnet/Identity/blob/ae9aa9ebec4de42d2c0ab8c68f1b2bccd7b1c5f8/test/Identity.FunctionalTests/Pages/Account/Login.cs#L53

But you can navigate around the Identity.FunctionalTests folder for some examples. It was a little hard for me to follow just reading through it on GitHub, since a lot of complexity is stashed away in methods. So it helped me to clone the repo and step through it.

Hope that helps.

I would also like to make an authenticated request, thought I use AzureAd only and not Identity. I don't think anglesharp will help with that. So i need something to change the testserver to ignore authentication.

Ok i figured it out. When you have a test that uses your CustomWebApplication factory, just add a filter attribute to allow anonymous requests:
services.AddMvc(opts => { opts.Filters.Add(new AllowAnonymousFilter()); });

I actually achieved automated cookie authentication for my requests by adding a test authentication handler that logs the user in (by providing a cookie) whenever authentication is needed ... Not easy, but it seems to work.

I achieved this by using 2 WebHosts in my xUnit test. One for hosting web which we are testing and another for running IdentityServer (or any other Security Token Service you like) on different port. IdentityServer has a nice in memory configuration for Clients, Resources, Users,... . Sample solution on Github can be found here https://github.com/KollarM/WebAPIWithIdentityServerIntegrationTest

Would love to see this implemented in a way we can use via the WebApplicationFactory.

Logging in a test user to test endpoints and policies should be as easy as adding auth via services.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme).

It seems like right now only complex middleware is able to allow this. I will see what I can do with AngleSharp but I do not like the idea of being tied to my IdentityServer/SSO Portal for running tests.

As I think about it more id expect to maybe see something like this:

[Fact]
public async Task Get_()
{
    await _factory.Server.AuthenticateAsync("Jim", claims);

    // Arrange
    var client = _factory.CreateClient();

     await client.GetAsync("/Home/Secure");

}

But maybe the biggest issue is we have no way to access the context of the request easily. If we did that, we could set the identity to whatever we wanted.

@InDieTasten do you want to voice why you reacted to my suggestion with a thumbs down and a confusion face? It might help to have an actual discussion on this issue.

We need a way to set and remove an authenticated user and their claims when hitting endpoints with the client.

@VictorioBerra
I totally agree, that there needs to be documentation on how to achieve this.

Your comment doesn't belong the the documentation repo. It's relatively off-topic, as you are not talking about guiding readers towards a common solution, but creating extra framework functionality that would cover this, which would then still need documentation.

The next part will be off-topic as well as kind of a feedback to what I believe you are trying to express in your proposed interface:

The interface you provided for accomplishing that is not covering all scenarios. The SUT still needs to be "aware" of integration tests happening, since the proposed interface would need to make serious changes to what's happening inside the authentication middleware to allow for your behavior.

For what I know, there are 3 approaches to make your request go to a protected resource:

  1. Make the protected resource unprotected eg. global "AllowAnonymousAttribute" in MVC apps (not feasible when the resource makes decisions based on the details of authenticated users)
  2. Registering a test authentication scheme, that can be controlled from inside the integration test code (not feasible when the application is not using the default authentication middleware or is relying on specific characteristics of a real authentication scheme (eg. looking for authentication cookies)). This to me sounds like a good fit and to my knowledge is already possible with the WebApplicationHostFactory. It's just not built-in, as it would be slightly depending on how the authentication schemes are set up in the application. It's also the closest to what you are suggesting. Maybe that's what you meant to say, so that's what the confused emote was for.
  3. Consider authentication as part of your SUT. Eg. earn your session cookie by making "legit" requests to your login page, redirecting to identity providers through oauth and what not. Having helpers and extensions for accomplishing this for the most common authentication schemes would be very helpful, as this can be very tedious code to write. (Based on how much automation is tolerated at external systems this might not work, or entire external systems need to be faked)

The last one is obviously comes closest to the real thing, but is also be most difficult to set up.

I suppose it's just frustrating for devs using the WebApplicationFactory gets you 90% there. As soon as you .AddAuthentication then you need to add a bunch of boilerplate like you mentioned whether it be a ton of AngleSharp code, or stripping the auth using the WebApplicationFactory stuff, or registering fake schemes that way, or building an entire IdentityServer in the WebApplicationFactory. Which I personally hate that the best solution is currently to do just that, especially since I think making any structural changes to the SUT via the WAF is not really discoverable and eventually you're not Integration testing anymore, you're testing some abomination of infrastructure that is not even your app anymore.

From where I am sitting as a developer writing tests for my app I want to be able to toggle my users/impersonate all as a part of my arrange section of my test. You can consider my code confusing psudocode if you like, but it doesn't change the fact that we need to be able to toggle our claims, roles, user subjects, etc and test all our controllers and actions as different identity roles. It seems like we can almost taste AuthenticateAsync because really that's all we need to call to make everything work. But that can only be called in a controller.

It would be really cool to set my user, create my client, test my endpoint without this headache.

Here is a solution for people using Cookie authentication. I am still trying to find something equally as simple for bearer token auth.

Essentially how it works is you load a new controller which can sign someone in, but only as a part of the test server.

CustomWebApplicationFactory

using IntegrationTests.Controllers;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection;
using System.Reflection;

namespace IntegrationTests
{
    // https://docs.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-2.1#customize-webapplicationfactory
    public class CustomWebApplicationFactory<TStartup> : WebApplicationFactory<SampleAPI.Startup>
    {
        protected override void ConfigureWebHost(IWebHostBuilder builder)
        {
            builder.ConfigureTestServices(services =>
            {
                services
                    .AddMvcCore()
                    .AddApplicationPart(typeof(ImpersonationController).GetTypeInfo().Assembly);
            });
        }
    }
}

ImpersonationController (requires IdentityServer4 NuGet)

using System.Threading.Tasks;
using IdentityServer4.AccessTokenValidation;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Http;

namespace IntegrationTests.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class ImpersonationController : ControllerBase
    {
        [HttpGet]
        public async Task<IActionResult> Get(string subjectId)
        {
            await HttpContext.SignInAsync(IdentityServerAuthenticationDefaults.AuthenticationScheme, subjectId);
            return Ok();
        }
    }
}

UnitTest1

using Microsoft.AspNetCore.Mvc.Testing;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Xunit;

namespace IntegrationTests
{
    public class UnitTest1 : IClassFixture<CustomWebApplicationFactory<SampleAPI.Startup>>
    {
        private readonly HttpClient _client;
        private readonly CustomWebApplicationFactory<SampleAPI.Startup> _factory;

        public UnitTest1(CustomWebApplicationFactory<SampleAPI.Startup> factory)
        {
            _factory = factory;
            _client = factory.CreateClient(new WebApplicationFactoryClientOptions
            {
                AllowAutoRedirect = false
            });
        }

        [Fact]
        public async Task Test1Async()
        {
            // Arrange
            var authResult = await _client.GetAsync("/api/Impersonation?subjectId=Bob");

            // Act
            var result = await _client.GetAsync("/api/Values");

            // Assert
            Assert.Equal(HttpStatusCode.OK, result.StatusCode);
        }
    }
}

If you do this solution, you may want to tweak the controller to accept claims on the fly. Or maybe hard code users.

@InDieTasten

@VictorioBerra That looks like a nice solution. I don't see anything wrong with this pattern, so might as well be documented like this IMO.

Edit: Apart from the IdentityServer dependency, which you should be able to easily get rid off?

We are currently achieving this by adding two filters inside ConfigureTestServices.

factory.WithWebHostBuilder(builder => builder.ConfigureTestServices(
    services => services.AddMvc(
        options =>
        {
            options.Filters.Add(new AllowAnonymousFilter());
            options.Filters.Add(new FakeUserFilter());
        }))).CreateClient();

Then we have our FakeUserFilter defined as:

private class FakeUserFilter : IAsyncActionFilter
{
    public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
    {
        context.HttpContext.User = new ClaimsPrincipal(new ClaimsIdentity(new List<Claim>
        {
            new Claim(ClaimTypes.NameIdentifier, "12345678-1234-1234-1234-123456789012"),
            new Claim(ClaimTypes.Name, "TestUser"),
            new Claim(ClaimTypes.Email, "[email protected]"), // add as many claims as you need
        }));

        await next();
    }
}

APIWT

I was generating a JWT token manually, but your solution it's more straightforward.
Thank you very much.

For those tracking on this issue, I just made some remarks regarding my thoughts on when the little batch of authn/authz issues for testing might be addressed. See my remarks at https://github.com/aspnet/Docs/issues/9957#issuecomment-446331425 ... and keep in mind that I'm guessing a bit on timing except for where I'm describing the 3.0 doc issues. Those will be of highest priority as 3.0 gets close.

btw --- thanks to all for discussing this here. This convo is attached to the topic at the bottom. Readers will probably find this convo very helpful until the docs can address these issues. 🖖 _We salute YOU!_ 🖖

I was using impersonate user model previously (adding "if environment is integration test then"... to the startup class) but with the new WebApplicationFactory things are more simple. No need to change flow in the pipeline...

Request login page and extract verification token from the content.
Then add it to the header of "login post request". Response will carry authentication cookie. Therefore all next request with http client will send authentication cookie as well.

The only thing needs to added is removing antiforgery/verification token from default header for every test run.

```C#
[Theory(DisplayName = "D_Login"), TestPriority(14)]
[InlineData("/Member/Account/Login")]
public async Task C_Login(string url)
{
var verificationToken = _testHostFixture.GetVerificationToken(url);
LoginModel.InputModel inputModel = new LoginModel.InputModel
{
Email = UserTestData.UserName,
Password = UserTestData.Password,
RememberMe = false
};
var contentToSend = new FormUrlEncodedContent(inputModel.ToKeyValuePair());
_client.DefaultRequestHeaders.Add(AppEnvironment.AntiForgeryHeaderName, verificationToken);

        HttpResponseMessage response = await _client.PostAsync(url, contentToSend);

        // Assert
        Assert.Equal(HttpStatusCode.Found, response.StatusCode);
        Assert.Equal("/Member", response.Headers.Location.OriginalString);
    }
```C#
 public string GetVerificationToken(string url)
        {
            HttpResponseMessage response = Client.GetAsync(url).Result;
            var verificationToken = response.Content.ReadAsStringAsync().Result;
            if (verificationToken != null && verificationToken.Length > 0)
            {
                verificationToken = verificationToken.Substring(verificationToken.IndexOf("__RequestVerificationToken"));
                verificationToken = verificationToken.Substring(verificationToken.IndexOf("value=\"") + 7);
                verificationToken = verificationToken.Substring(0, verificationToken.IndexOf("\""));
            }
            return verificationToken;
        }

C# public void Dispose() { //Remove AntiForgeryHeader token for every test. So that next test can create fresh one. _client.DefaultRequestHeaders.Remove(AppEnvironment.AntiForgeryHeaderName); }

For those interested I built a package that makes this simple without having to host a separate identity server: https://github.com/jabbera/aspnetcore-testing-role-handler

Feedback welcomed.

I took a slightly different approach by implementing a test authentication scheme and middleware, then using the .ConfigureTestServices() method of a custom WebApplicationFactory<T> class to specify my test authentication scheme as the default scheme. Test clients that wish to simulate requests with or without authentication include or omit the HTTP header X-Test-Auth along with an Authorization Bearer token header. Here's a full gist demonstrating it

The most direct route to using this is to configure your web application factory with the test authentication scheme is like this:

        _factory = (TestWebApplicationFactory<MyWebApi.Startup>)factory.WithWebHostBuilder(
          builder => {
            builder.ConfigureServices(services =>
            {
                services.AddAuthentication(o =>
                {
                    o.DefaultScheme = "TestAuthenticationScheme";
                })
                .AddTestAuthentication("TestAuthenticationScheme", "Test Auth", o => { });
            });
      });

You're not overwriting your existing Startup here, instead you're just supplementing it with your own test setup calls, taking advantage of how subsequent calls to builder.ConfigureServices are additive in nature to override the default authentication scheme with our own.

Thanks a lot everyone for throwing ideas and solutions into the ring. This thread will help a lot of people out there searching for a solution.

Maybe Microsoft will prioritize this a little higher since they are really putting effort into testing infrastructure and there is clear interest in out of the box solutions for testing apps that require auth.

That’s easy too. I needed a way to test aribitrary claims (roles) per test as my API behaves differently depending on the claims you have. Hence sending them over in the auth header.

@jabbera Same. As a user of PolicyServer identity role claims are mapped to policies automatically. So claims are the most important part to me as well.

It looks like if you use his AddTestAuthentication you are able to set the claims in his TestAuthenticationOptions so maybe that would still work for you. He also shows how you can modify the factory WithWebHostBuilder inside of one of your test classes so you can tweak this on a per test basis I think.

The 2.2 release took priority ... and then the ❄️holidays ⛄️ hit. Now that we're beyond all of that, we'll get to this fairly soon. I'm busy this sprint with higher priority issues, but perhaps in a few weeks on the next sprint I can get on this.

This issue and all of the wonderful comments and examples here are is linked at the bottom of the topic; so thanks to all of you, readers aren't left out in the cold until we can get to this issue.

@guardrex This is off topic but do you have a second keyboard purely dedicated to emojiis? 😛

Note to Microsoft management: Add _Speech-to-emoji_ technology in Win10. "grin" ... :grin:

It looks like if you use his AddTestAuthentication you are able to set the claims in his TestAuthenticationOptions

That's correct. The gist shows a static set of claims being generated, but there's no reason you couldn't have an individual test create its' own set of claims, modifying the logic in the TestAuthenticationHandler to be more dynamic in the claims it generates (e.g., read from custom header, config file, etc...).

Now that I think of it, it is pretty easy to implement that on a per-test basis -- I've updated the Gist to show how to do this (ClaimsTests.cs)

Thanks for the update!

Latest guidance from engineering is that they will triage this issue for the Integration Testing topic along with the rest on the Integration Testing Project here. If they decide that this one makes the cut, we'll get it on a sprint for work.

@guardrex I like the multiple samples in this discussion. I know for sure one of these samples work. I'm facing slightly a different problem. I have an authorized Controller that is calling another authorized controller (different project) or external api (like Microsoft Graph). Both apis are authenticated against the Azure AD. In all the controller actions we need the authenticated user.
We can get in the first api by getting a token based on the username and password (grant_type=password). When the call continues to the second api, it breaks because of an interactive login prompt. Normally, the user authenticates with open id connect, we then have the authentication code and get the accesstoken + refresh token. With the refresh token we can get an access token for the second api.

See StackOverflow issue for more information.

They haven't pulled the trigger on further updates to the topic yet. Thanks for that info. I (or someone on the team who works on the doc further) will appreciate all of the community tips and code provided here when the time comes to work further on the topic.

I had a customPolicyProvider to resolve my policy and even though I enabled AllowAnonymous filter, the authFilter needed to resolve the bypassed policy by name. Here's my fix :

        protected override void ConfigureWebHost(IWebHostBuilder builder)
        {
            builder.ConfigureTestServices(
                services =>
                {
                    services.AddMvc(options => options.Filters.Add(new AllowAnonymousFilter()));

                    var unusedPolicyStub = new AuthorizationPolicyBuilder(Array.Empty<string>())
                        .AddRequirements(Mock.Of<IAuthorizationRequirement>())
                        .Build();

                    var policyProviderMock = new Mock<IAuthorizationPolicyProvider>();
                    policyProviderMock.Setup(x => x.GetPolicyAsync(ClientCertificatesPolicyProvider.PolicyName)).ReturnsAsync(unusedPolicyStub);

                    services.AddSingleton(policyProviderMock.Object);
                });
        }

@blalonde Odd that you use AllowAnonymousFilter and you still needed to do this. I use PolicyServer.io and didn't have any issues.

// this code work for me - avoid windows authentication

using System.Net.Http;
using Microsoft.AspNetCore.Mvc.Authorization;
using Microsoft.AspNetCore.Mvc.Testing;
using Microsoft.AspNetCore.TestHost;
using Microsoft.Extensions.DependencyInjection;
using WebService;
using Xunit;

namespace Test.WebServiceTests.IntegrationTest
{
    public class WebServiceIntegrationTest : IClassFixture<WebApplicationFactory<Startup>>
    {
        private readonly WebApplicationFactory<Startup> _factory;

        public WebServiceIntegrationTest(WebApplicationFactory<Startup> factory)
        {
            _factory = factory;
        }

        [Fact]
        public void GetProject()
        {
            using (HttpClient client = BuildAnonymousClient())
            {
                var response = client.GetAsync("/api/admin/project/101").Result;
                Assert.True(response.IsSuccessStatusCode);
            }
        }

        private HttpClient BuildAnonymousClient()
        {
            return _factory.WithWebHostBuilder(c => 
                c.ConfigureTestServices(s => s.AddMvcCore(o => o.Filters.Add(new AllowAnonymousFilter())))).CreateClient();
        }
    }
}


@uvn85 I am not a big fan of modifying the host. Especially turning off MVC features like auth just for testing.

Ideally, we would have a way provided by Microsoft to tweak the client and provide a set of claims.

How do I use Windows Authentication with the WebApplicationFactory? Allowing Anonymous access is not an option as my Domain Service keeps an Audit Log of every Write request made to the system and will (by intention) throw an Exception if the Identity is not valid (this is in addition to the standard HTTP Authentication checks that are handled by IIS\ASP.Net Core. For the purposes of Integration Testing I would expect that the Windows Identity of the User running the Tests be sent to the Web Service with the Http Requests. Please help, this is a very critical and urgent blocking issue for us. Thank you in advance!

We are using v3.0.0

I tried a few solutions mentioned in this and other threads but was unable to get anything to work. Perhaps some things have changed and no longer work with ASP.NET Core 3.0. I solved it by creating a fake AuthenticationHandler.

public class UnitTest1 : IClassFixture<WebApplicationFactory<Startup>>
{
    private readonly WebApplicationFactory<Startup> _factory;

    public UnitTest1(WebApplicationFactory<Startup> factory)
    {
        _factory = factory;
    }

    [Fact]
    public async Task GetWeatherForecast()
    {
        using (var client = BuildAuthenticatedClient())
        {
            var response = await client.GetAsync("/WeatherForecast");
            Assert.True(response.IsSuccessStatusCode);
        }
    }

    private HttpClient BuildAuthenticatedClient()
    {
        return _factory.WithWebHostBuilder(builder =>
        {
            builder.ConfigureTestServices(services =>
            {
                services.AddAuthentication("TestAuthentication")
                    .AddScheme<AuthenticationSchemeOptions, TestAuthenticationHandler>("TestAuthentication", null);
            });
        }).CreateClient();
    }
}

This is my fake AuthenticationHandler.

public class TestAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
    public TestAuthenticationHandler(IOptionsMonitor<AuthenticationSchemeOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock)
    {
    }

    protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
    {
        var claims = new[] {
            new Claim(ClaimTypes.NameIdentifier, "test"),
            new Claim(ClaimTypes.Name, "test")
        };
        var identity = new ClaimsIdentity(claims, Scheme.Name);
        var principal = new ClaimsPrincipal(identity);
        var ticket = new AuthenticationTicket(principal, Scheme.Name);

        return await Task.FromResult(AuthenticateResult.Success(ticket));
    }
}

When I was trying to make this work I found various variations of replacing middleware parts, along the lines of @vanillajonathan solution. To me this is just not what I'm after, as I'm specifically trying to do integration testing of the authentication process. If I visit a secured page anonymously I should receive a redirect to the login page, for example. Or testing visiting a secured page with two authenticated users, one authorized for that page and one not. Part of what I want to test is that I've correctly secured the page, so mocking the success or deny is completely defeating that.

In the end I was able to get something working with the real stack, but only against a physical DB and not in-memory, which makes the tests quite expensive to run and exacerbates test concurrency problems.

I am really surprised they didn't improve the extensibility of all this in Core 3. This is obviously a massive shortcoming of the TestServer/Host.

@boaterdan, the AuthenticationHandler only gets inserted on the unit tests that call the private BuildAuthenticatedClient method. So you have your unit tests consist of a mix of methods are authenticated and non-authenticated in order to test redirects and such.

Yes @vanillajonathan I can test that I get a redirect when I'm supposed to - meaning when access is declined. But my point is that doesn't test that access is declined when it's supposed to. A couple of unit tests can confirm the mechanism for the first part works, but every page must be tested "for real" for the second one, so the authentication/authorization stack used live has to be intact. Of course, those are not even unit tests at that point - it's those full integration tests I finally got working only against a physical database. (I'm using "physical" here to mean in contrast to the "in-memory" option that I can use for other tests.)

I had this issue when I migrated to .NET Core 3.0 (from 2.2)
Finally I found the solution for my integration tests.

I Use a base class for all the tests for that category

  • In Memory DB does not work with ConfigureTestServices extension
  • Skipping Authentication does not work anymore with ConfigureServices

Skypping authorization example (not working anymore):
c# services.AddControllers( setupAction => { setupAction.Filters.Add( new AllowAnonymousFilter() ); setupAction.Filters.Add( new Factory.MVC.FiltersFactory.FakeUserFilter() ); } );

This is the solution (Hybrid configuration, using both).

  • Memory DB from my specific test have to be disposable. But is not always needed: it depends from the test requirements.

The base class:
```c#
public class SkipAuthMemoryDbIntegrationTest : IDisposable {
protected readonly HttpClient TestClient;
private readonly IServiceProvider _serviceProvider;

    protected SkipAuthMemoryDbIntegrationTest() {

        var appFactoryNoAuthMemoryDB = new WebApplicationFactory<BPER_API.Startup>()
            .WithWebHostBuilder( builder => {
                builder.ConfigureServices( services => {
                    services.RemoveAll( typeof( DataContext ) );
                    services.AddDbContext<DataContext>( options => { options.UseInMemoryDatabase( "TestDb" ); } );
                } )
                .ConfigureTestServices( services => {
                    services
                      .AddAuthentication( "TestAuthentication" )
                      .AddScheme<AuthenticationSchemeOptions, TestAuthenticationHandler>( "TestAuthentication", null );
                } );
            } );

        this._serviceProvider = appFactoryNoAuthMemoryDB.Services;
        this.TestClient = appFactoryNoAuthMemoryDB.CreateClient();
    }

    public void Dispose() {
        using var serviceScope = this._serviceProvider.CreateScope();
        var context = serviceScope.ServiceProvider.GetService<DataContext>();
        context.Database.EnsureDeleted();
    }
}
The Handler

```c#
 public class TestAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions> {
        public TestAuthenticationHandler( IOptionsMonitor<AuthenticationSchemeOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock ) : base( options, logger, encoder, clock ) { }

        protected override async Task<AuthenticateResult> HandleAuthenticateAsync() {
            var claims = Factory.Security.ClaimsFactory.GetMockClaimsFromUser(Factory.Domain.UsersFactory.GetMockAuthenticatedUser());
            var identity = new ClaimsIdentity( claims, this.Scheme.Name );
            var principal = new ClaimsPrincipal( identity );
            var ticket = new AuthenticationTicket( principal, this.Scheme.Name );

            return await Task.FromResult( AuthenticateResult.Success( ticket ) );
        }
    }

The Mocks:
```c#
internal static User GetMockAuthenticatedUser() {
return new User {
Username = "TestUser",
Email = "test.[email protected]",
IdSts = 2020
};
}

```c#
internal static List<Claim> GetMockClaimsFromUser(User user) {
            return new List<Claim>
                {
                    new Claim(ClaimTypes.Name, user.Username),
                    new Claim(ClaimTypes.Email, user.Email),
                    new Claim("sub", user.IdSts.ToString())
                };
        }

And an example integration test extending the base test
```c#
public class TransactionsControllerTest : SkipAuthMemoryDbIntegrationTest {

    [Fact]
    public async Task GetAll_WithoutAnyTransactions_ReturnsEmptyDataResponse() {
        //  Arrange

        //  Act
        var response = await this.TestClient.GetAsync( ApiRoutes.Transactions.GetAll );

        //  Assert
        response.StatusCode.Should().Be( HttpStatusCode.OK );
        (await response.Content.ReadAsAsync<PagedResponse<TransactionResponse>>()).Data.Should().BeEmpty();
    }

}
```
Annotazione 2019-10-11 161421

Hello everyone. There are already many helpful solutions here, which almost all seem to work for the most of us. But today I have something to share with you.

Whilst all our current solutions here are trying to mock the authentication middleware in some way or another, I have just completed creating a way of actually signing into ASP.NET Core Identity with actual seeded credentials. This is of course much more work to setup, but it will allow you to include the entire authentication setup to be tested as part of your tests. As it should be, in an _integration_ test.

The sample code of a minimal solution can be found here: https://github.com/InDieTasten/Authenticated-Integration-Testing

It's heavily inspired by the current docs, but going the extra step to GET the login page, POST the credentials to the login page, and then GETting an endpoint secured by [Authorize].

The code is obviously a bit dirty, and things can be rearranged for easier usage, but for a prototype and understanding the the underlying workings, it should suffice.

The linked sample is on ASP.NET Core 3.0.

I'm hoping, that somebody will have the courage to extend the current documentation with this functionality, because this seems to be the first big issue most developers face, once they decided to give integration testing a shot. If there are further questions about the code, feel free to ask by creating an issue on the linked repo.

Good luck

Although there are many variations discussed here for auth+testing, we're going to start with https://github.com/aspnet/AspNetCore.Docs/pull/15340. That will go in today and go live within 24 hours. We'll continue to take feedback on authn/authz as we go ... but let's start with that, which covers the basics of what engineering would like to show _initially_. We'll keep an :eye: out and an :ear: open for more feedback.

I had to do following to get it work:

services.AddAuthentication(x =>
{
  x.DefaultAuthenticateScheme = "Test";
  x.DefaultChallengeScheme = "Test";
})
.AddScheme<AuthenticationSchemeOptions, TestAuthHandler>(
   "Test", options => { });

Hi @guardrex --
Thanks for all the effort you've put into this! 🙇
I like the approach you've taken, and not just because it's similar to the pattern I laid out in my comment and the linked gist 😄

Something I think that was in my gist that I didn't see in your implementation was my update to support per-fixture testing of arbitrary claims - was that an oversight or on purpose because you didn't want to complicate things further by including claims?

Thanks, but that wasn't my pattern ... it was from engineering. I just got the commas in there. :smile:

Open a new issue using the This Page feedback button/form at the bottom of the topic. We can't work closed issues/PRs. Include the cross-link to your comment and gist.

Was this page helpful?
0 / 5 - 0 ratings