2

I am trying to get my integration tests working against a protected api endpoint. My test call the IDS connect/token endpoint and gets a valid token. When I use that to call the protected api I always get a failure with invalid_token. (the api is local to the IDS too)

If I manually debug the IDS and use postman to get a token and then manually set it and call the protected api with postman it works fine.

I suspect that the internals of the IDS validation code can't hit the discovery endpoint because it is spinning up its own HttpClient. I know that the WebApplicationFactory Client is a special implementation of HttpClient.

Is there a way that I can inject the WebApplicationFactory client into the IDS during configuration/startup so that it will work?

Or is there a way that I can make a fake authorization endpoint that just validates any token sent in the Auth header?

I would just like my integration tests to be able to work against the api, it would be great if it actually validated the token but if it can't I can fake it.

Thanks.

2 Answers 2

4

In usual fashion, right after I ask about something I've been stumped on for hours I figure it out. Here is the simple solution based on this other question which has a somewhat more convoluted answer (How can I set my IdentityServer4 BackChannelHandler from within an xUnit integration test using WebApplicationFactory?).

In your Startup.cs add a static property:

/// <summary>
/// For integrationtesting set this to Factory.Server.CreateHandler()
/// </summary>
public static HttpMessageHandler JwtBackChannelHandler { get; set; }

Then add this to your .AddIdentityServerAuthentication() options:

if (JwtBackChannelHandler != null)
{
    options.JwtBackChannelHandler = JwtBackChannelHandler;
}

In your constructor for your test class that implements IClassFixture<WebApplicationFactory<Startup>> add this:

Startup.JwtBackChannelHandler = Factory.Server.CreateHandler();

This will allow you to call the token endpoint and receive a real token and use that as an authentication header in your integration tests. Hot diggity.

Sign up to request clarification or add additional context in comments.

2 Comments

Did you found a way to totally block the identity server call? I want to test my api endpoint which is protected with the authorize attribute. On calling the endpoint the service is still trying to reach out to the identity server. But i want to skipp that.
@liqSTAR See my other answer I added for you. This is how I am doing it currently.
2

This answer is for @liqSTAR on how to ignore IdentitiyServer 4 and faking the authorization in integration tests.

Create a class that looks like this:

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

    protected override Task<AuthenticateResult> HandleAuthenticateAsync()
    {
        var context = Context;
        List<Claim> claims;
        if (context.Request.Headers.Keys.Contains("my-name"))
        {
            var name = context.Request.Headers["my-name"].First();
            var id = context.Request.Headers.Keys.Contains("my-id") ? context.Request.Headers["my-id"].First() : "";
            claims = new List<Claim>
                {
                    new Claim(ClaimTypes.Name, name),
                    new Claim(ClaimTypes.NameIdentifier, id),
                };
        }
        else
        {
            claims = new List<Claim> { new Claim(ClaimTypes.Name, "Test user") };
        }
        var identity = new ClaimsIdentity(claims, "Test");
        var principal = new ClaimsPrincipal(identity);
        context.User = principal;

        var ticket = new AuthenticationTicket(principal, "Test");

        var result = AuthenticateResult.Success(ticket);

        return Task.FromResult(result);
    }
}

Then in your override void ConfigureWebHost(IWebHostBuilder builder) in your custom class that inherits from WebApplicationFactory<TStartup> add

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

To the builder.ConfigureServices(services => { section.

Then in your test class add

Client.DefaultRequestHeaders.Add("my-id", "1234");
Client.DefaultRequestHeaders.Add("my-name", "Test_User");

to either your ctor or test methods when you want to dummy up a user so that the Authorize attribute works when your controller tries to do something with User. (Client is convenience property I have for Factory.CreateClient();)

The nice thing about this is that if you don't want an authorized user to be passed don't set the my-id and my-name headers.

I hope this helps get you going in the right direction.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.