I've built a simple AspNetCore 2.2
API that uses IdentityServer 4
to handle OAuth. It's working fine but I'd now like to add integration tests and recently discovered this. I used it to build some tests which all worked fine - as long as I didn't have the [Authorize]
attribute on my controllers - but obviously that attribute needs to be there.
I came across this stackoverflow question and from the answers given there I tried to put a test together but I'm still getting an Unauthorized
response when I try to run tests.
Please note: I really don't know what details I should be using when I'm creating the client.
- What should the allowed scopes be? (Should they match the real scopes)
Also when building the IdentityServerWebHostBuilder
- What should I pass to
.AddApiResources
? (Maybe a dumb question but does it matter)
If anyone can guide me it would be greatly appreciated.
Here is my test:
[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<Secret>
{
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<Startup>());
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);
}
My Startup class:
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<IPassportService, PassportService>();
services.Configure<ApiBehaviorOptions>(options =>
{
options.SuppressModelStateInvalidFilter = true;
});
}
protected virtual void ConfigureAuth(IServiceCollection services)
{
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.Authority = Configuration.GetValue<string>("IdentityServerAuthority");
options.Audience = Configuration.GetValue<string>("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();
}
}