WebApi — Fake Authentication — Digitteck
WebApi — Fake Authentication
dotnet·1 July 2020·4 min read

WebApi — Fake Authentication

Integration tests often need to test endpoints that require an authenticated user. The trick is to replace the IAuthenticationSchemeProvider with a fake that always returns a custom handler — injecting whatever user the test needs, without touching JWTs or identity servers.

A typical startup with auth middleware:

csharp
public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllers();
        services.AddAuthentication();
        services.AddAuthorization();
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UseHttpsRedirection();
        app.UseRouting();
        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
        });
    }
}

Looking at what AddAuthentication registers internally reveals the providers we can replace:

Authentication services registered by AddAuthentication

The solution structure:

Test project solution structure

A simple user model and an injectable provider that the test sets before each call:

csharp
public class IntegrationUser
{
    public string Name { get; }
    public Guid UserId { get; }

    public IntegrationUser(string name, Guid userId)
    {
        Name = name;
        UserId = userId;
    }
}
csharp
public interface IIntegrationUserProvider
{
    IntegrationUser Current { get; }
    void SetUser(IntegrationUser user);
}

public class IntegrationUserProvider : IIntegrationUserProvider
{
    public IntegrationUser Current { get; private set; }

    public void SetUser(IntegrationUser user)
    {
        Current = user;
    }
}

The authentication handler reads the current user from the provider and builds a ClaimsPrincipal:

csharp
public class IntegrationAuthenticationHandler
    : AuthenticationHandler<IntegrationAuthenticationOptions>
{
    public const string FakeSchemeName = "IntegrationTestFakeScheme";

    public IIntegrationUserProvider IntegrationUserProvider { get; }

    public IntegrationAuthenticationHandler(
        IOptionsMonitor<IntegrationAuthenticationOptions> options,
        ILoggerFactory logger,
        UrlEncoder encoder,
        ISystemClock clock,
        IIntegrationUserProvider integrationUserProvider)
        : base(options, logger, encoder, clock)
    {
        IntegrationUserProvider = integrationUserProvider;
    }

    protected override Task HandleChallengeAsync(AuthenticationProperties properties)
    {
        Response.StatusCode = 200;
        return Task.CompletedTask;
    }

    protected override Task<AuthenticateResult> HandleAuthenticateAsync()
    {
        if (IntegrationUserProvider.Current is null)
            return Task.FromResult(AuthenticateResult.Fail("No user set"));

        IntegrationUser user = IntegrationUserProvider.Current;

        var claims = new List<Claim>
        {
            new Claim(ClaimTypes.Name, user.Name),
            new Claim(ClaimTypes.NameIdentifier, user.UserId.ToString())
        };

        var identity = new ClaimsIdentity(claims, "Custom");
        var ticket = new AuthenticationTicket(new ClaimsPrincipal(identity), FakeSchemeName);

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

public class IntegrationAuthenticationOptions : AuthenticationSchemeOptions { }

The scheme provider always returns the fake handler regardless of which scheme is requested:

csharp
public sealed class IntegrationSchemeProvider : IAuthenticationSchemeProvider
{
    private static readonly AuthenticationScheme FakeScheme = new AuthenticationScheme(
        IntegrationAuthenticationHandler.FakeSchemeName,
        IntegrationAuthenticationHandler.FakeSchemeName,
        typeof(IntegrationAuthenticationHandler));

    private static readonly IEnumerable<AuthenticationScheme> FakeSchemes
        = new List<AuthenticationScheme> { FakeScheme };

    public void AddScheme(AuthenticationScheme scheme) { }
    public void RemoveScheme(string name) { }

    public Task<IEnumerable<AuthenticationScheme>> GetAllSchemesAsync()
        => Task.FromResult(FakeSchemes);

    public Task<AuthenticationScheme> GetDefaultAuthenticateSchemeAsync()
        => Task.FromResult(FakeScheme);

    public Task<AuthenticationScheme> GetDefaultChallengeSchemeAsync()
        => Task.FromResult(FakeScheme);

    public Task<AuthenticationScheme> GetDefaultForbidSchemeAsync()
        => Task.FromResult(FakeScheme);

    public Task<AuthenticationScheme> GetDefaultSignInSchemeAsync()
        => Task.FromResult(FakeScheme);

    public Task<AuthenticationScheme> GetDefaultSignOutSchemeAsync()
        => Task.FromResult(FakeScheme);

    public Task<IEnumerable<AuthenticationScheme>> GetRequestHandlerSchemesAsync()
        => Task.FromResult(FakeSchemes);

    public Task<AuthenticationScheme> GetSchemeAsync(string name)
        => Task.FromResult(FakeScheme);
}

Wire it up in the WebApplicationFactory by replacing the scheme provider service:

WebApplicationFactory replacing IAuthenticationSchemeProvider

Don't forget to add the real authentication scheme to the API controller — the framework needs to know a scheme is in use:

Controller with Authorize attribute and scheme name

Tags

.NETASP.NET CoreIdentityTesting
digitteck

© 2026 Digitteck