title: Building an integration test for an AspNetCore API that uses IdentityServer 4 for Auth
body: <p>I've built a simple <code>AspNetCore 2.2</code> API that uses <code>IdentityServer 4</code> to handle OAuth. It's working fine but I'd now like to add integration tests and recently discovered <a href="https://docs.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-2.2" rel="nofollow noreferrer">this</a>. I used it to build some tests which all worked fine - as long as I didn't have the <code>[Authorize]</code> attribute on my controllers - but obviously that attribute needs to be there.</p>
  
  <p>I came across <a href="https://stackoverflow.com/questions/39390339/integration-testing-with-in-memory-identityserver#55573671">this stackoverflow question</a> and from the answers given there I tried to put a test together but I'm still getting an <code>Unauthorized</code> response when I try to run tests.</p>
  
  <p><strong>Please note</strong>: I really don't know what details I should be using when I'm creating the client. </p>
  
  <ul>
  <li>What should the allowed scopes be?  (Should they match the real
  scopes)</li>
  </ul>
  
  <p>Also when building the <code>IdentityServerWebHostBuilder</code> </p>
  
  <ul>
  <li>What should I pass to <code>.AddApiResources</code>?  (Maybe a dumb question but
  does it matter)</li>
  </ul>
  
  <p>If anyone can guide me it would be greatly appreciated.    </p>
  
  <p>Here is my test:</p>
  
  <pre><code>[Fact]
  public async Task Attempt_To_Test_InMemory_IdentityServer()
  {
      // Create a client
          var clientConfiguration = new ClientConfiguration("MyClient", "MySecret");
  
          var client = new Client
          {
              ClientId = clientConfiguration.Id,
              ClientSecrets = new List&lt;Secret&gt;
              {
                  new Secret(clientConfiguration.Secret.Sha256())
              },
              AllowedScopes = new[] { "api1" },
              AllowedGrantTypes = new[] { GrantType.ClientCredentials },
              AccessTokenType = AccessTokenType.Jwt,
              AllowOfflineAccess = true
          };
  
          var webHostBuilder = new IdentityServerWebHostBuilder()
              .AddClients(client)
              .AddApiResources(new ApiResource("api1", "api1name"))
              .CreateWebHostBuilder();
  
          var identityServerProxy = new IdentityServerProxy(webHostBuilder);
          var tokenResponse = await identityServerProxy.GetClientAccessTokenAsync(clientConfiguration, "api1");
  
          // *****
          // Note: creating an IdentityServerProxy above in order to get an access token
          // causes the next line to throw an exception stating: WebHostBuilder allows creation only of a single instance of WebHost
          // *****
  
          // Create an auth server from the IdentityServerWebHostBuilder 
          HttpMessageHandler handler;
          try
          {
              var fakeAuthServer = new TestServer(webHostBuilder);
              handler = fakeAuthServer.CreateHandler();
          }
          catch (Exception e)
          {
              throw;
          }
  
          // Create an auth server from the IdentityServerWebHostBuilder 
          HttpMessageHandler handler;
          try
          {
              var fakeAuthServer = new TestServer(webHostBuilder);
              handler = fakeAuthServer.CreateHandler();
          }
          catch (Exception e)
          {
              Console.WriteLine(e);
              throw;
          }
  
          // Set the BackChannelHandler of the 'production' IdentityServer to use the 
          // handler form the fakeAuthServer
          Startup.BackChannelHandler = handler;
          // Create the apiServer
          var apiServer = new TestServer(new WebHostBuilder().UseStartup&lt;Startup&gt;());
          var apiClient = apiServer.CreateClient();
  
  
          apiClient.SetBearerToken(tokenResponse.AccessToken);
  
          var user = new User
          {
              Username = "simonlomax@ekm.com",
              Password = "Password-123"
          };
  
          var req = new HttpRequestMessage(new HttpMethod("GET"), "/api/users/login")
          {
              Content = new StringContent(JsonConvert.SerializeObject(user), Encoding.UTF8, "application/json"),
          };
  
          // Act
          var response = await apiClient.SendAsync(req);
  
          // Assert
          Assert.Equal(HttpStatusCode.OK, response.StatusCode);
  
  }
  </code></pre>
  
  <p>My Startup class:</p>
  
  <pre><code>public class Startup
  {
  
      public IConfiguration Configuration { get; }
      public static HttpMessageHandler BackChannelHandler { get; set; }
  
      public Startup(IConfiguration configuration)
      {
          Configuration = configuration;
      }
  
      // This method gets called by the runtime. Use this method to add services to the container.
      public void ConfigureServices(IServiceCollection services)
      {
          services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
          ConfigureAuth(services);    
          services.AddTransient&lt;IPassportService, PassportService&gt;();
          services.Configure&lt;ApiBehaviorOptions&gt;(options =&gt;
          {
              options.SuppressModelStateInvalidFilter = true;
          });
  
      }
  
      protected virtual void ConfigureAuth(IServiceCollection services)
      {
          services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
              .AddJwtBearer(options =&gt;
              {
                  options.Authority = Configuration.GetValue&lt;string&gt;("IdentityServerAuthority");
                  options.Audience = Configuration.GetValue&lt;string&gt;("IdentityServerAudience");
                  options.BackchannelHttpHandler = BackChannelHandler;
              });
      }
  
  
      // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
      public void Configure(IApplicationBuilder app, IHostingEnvironment env)
      {
          if (env.IsDevelopment())
          {
              app.UseDeveloperExceptionPage();
          }
          else
          {
              app.UseHsts();
          }
  
          app.UseAuthentication();
          app.UseHttpsRedirection();
          app.UseMvc();
          app.UseExceptionMiddleware();
      }
  }
  </code></pre>