Aspnetcore.docs: [Testing] Add section to mock authentication

Created on 25 Sep 2019  路  5Comments  路  Source: dotnet/AspNetCore.Docs

This is a common question we get on the repo in one way or another. For integration tests, unless you are specifically testing your auth mechanisms, you should mock the auth handler the same way you do the database.

Can we cover this in the docs? It involves creating a TestAuthenticationHandler and registering it through ConfigureServices in your WebApplicationFactory.

P1 area-mvc doc-enhancement

Most helpful comment

Update ... I should be working this into the topic+sample (and probably one or two additional integration testing topic updates) this weekend (10/26-10/27).

All 5 comments

I think this is a dup of one or more existing issues on the Testing Project, but I'll sort that out later. I'm 馃弮馃槄 right now with a backlog of work.

I hope this helps anyone looking for it, be sure to have UseAuthentication() before UseAuthorization() in your pipeline. If you don't have UseAuthentication() the handler won't fire and you won't be signed in through the test handler.

This creates a scheme named "Test" which will always sign you in, and adds the Authorization header to the client so you won't be challenged with a 401.

    public static class TestWebApplicationFactory
    {
        public static HttpClient Create()
        {
            var factory = new WebApplicationFactory<Startup>()
                .WithWebHostBuilder(host =>
                {
                    host.ConfigureTestServices(services =>
                    {
                        services
                            .AddAuthentication("Test")
                            .AddScheme<AuthenticationSchemeOptions, AuthenticationTestHandler>("Test", null);
                    });

                });

            var client = factory.CreateClient();
            client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Test", "true");

            return client;
        }
    }

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

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

            var result = AuthenticateResult.Success(ticket);

            return Task.FromResult(result);
        }
    }

@guardrex You sound swamped with work, I hope this is useful to you as well and saves you some time.

Thank you! :beers: I'm 100% sure that you're right about that! 馃弮馃弮馃弮馃弮馃弮馃弮馃槄馃弮馃弮

Update ... I should be working this into the topic+sample (and probably one or two additional integration testing topic updates) this weekend (10/26-10/27).

I hope this helps anyone looking for it, be sure to have UseAuthentication() before UseAuthorization() in your pipeline. If you don't have UseAuthentication() the handler won't fire and you won't be signed in through the test handler.

This creates a scheme named "Test" which will always sign you in, and adds the Authorization header to the client so you won't be challenged with a 401.

    public static class TestWebApplicationFactory
    {
        public static HttpClient Create()
        {
            var factory = new WebApplicationFactory<Startup>()
                .WithWebHostBuilder(host =>
                {
                    host.ConfigureTestServices(services =>
                    {
                        services
                            .AddAuthentication("Test")
                            .AddScheme<AuthenticationSchemeOptions, AuthenticationTestHandler>("Test", null);
                    });

                });

            var client = factory.CreateClient();
            client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Test", "true");

            return client;
        }
    }

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

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

            var result = AuthenticateResult.Success(ticket);

            return Task.FromResult(result);
        }
    }

@guardrex You sound swamped with work, I hope this is useful to you as well and saves you some time.

I had to add app.UseAuthentication(); to my app's Startup.Configure() method in order to get this working.

Thank you @jvandertil and @guardrex for your help here. I look forward to seeing your solution in the docs @guardrex!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Rick-Anderson picture Rick-Anderson  路  3Comments

madelson picture madelson  路  3Comments

royshouvik picture royshouvik  路  3Comments

danroth27 picture danroth27  路  3Comments

serpent5 picture serpent5  路  3Comments