Im implementing Aspnet OpenIdConnect Server (ASOS) in a asp.net core 1.1 project and im currently trying to implement some integration testing (xunit
& moq
) using Microsoft.AspNetCore.TestHost.TestServer.
The issue i have is manually generating a fake accesstoken with which to populate a AuthenticationHeaderValue
for the HttpClient
requests. Searched for a working solution to this but sofar ive been unsuccessful.
So my question: Anyone has a tip as how to manually generating accesstokens for TestServer without having to call the token endpoint of ASOS for testing?
While ASOS deliberately prevents you from creating tokens from arbitrary places (they can be only generated during OpenID Connect requests), you can directly use the underlying ASP.NET Core APIs to generate fake tokens that will be accepted by the OAuth2 validation middleware:
var provider = container.GetRequiredService<IDataProtectionProvider>();
var protector = provider.CreateProtector(
nameof(OpenIdConnectServerMiddleware),
OpenIdConnectServerDefaults.AuthenticationScheme, "Access_Token", "v1");
var format = new TicketDataFormat(protector);
var identity = new ClaimsIdentity();
identity.AddClaim(new Claim(ClaimTypes.Name, "Bob le Bricoleur"));
var ticket = new AuthenticationTicket(
new ClaimsPrincipal(identity),
new AuthenticationProperties(),
OpenIdConnectServerDefaults.AuthenticationScheme);
var token = format.Protect(ticket);
That said, it's rarely the most efficient method if you want to test your own token-protected APIs. Instead, I'd recommend setting HttpContext.User
or using the OAuth2 validation middleware events to use fake identities without involving crypto operations.
You can also mock the AccessTokenFormat
:
[Fact]
public async Task ValidTokenAllowsSuccessfulAuthentication()
{
// Arrange
var server = CreateResourceServer();
var client = server.CreateClient();
var request = new HttpRequestMessage(HttpMethod.Get, "/");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "valid-token");
// Act
var response = await client.SendAsync(request);
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal("Fabrikam", await response.Content.ReadAsStringAsync());
}
private static TestServer CreateResourceServer(Action<OAuthValidationOptions> configuration = null)
{
var builder = new WebHostBuilder();
var format = new Mock<ISecureDataFormat<AuthenticationTicket>>(MockBehavior.Strict);
format.Setup(mock => mock.Unprotect(It.Is<string>(token => token == "invalid-token")))
.Returns(value: null);
format.Setup(mock => mock.Unprotect(It.Is<string>(token => token == "valid-token")))
.Returns(delegate
{
var identity = new ClaimsIdentity(OAuthValidationDefaults.AuthenticationScheme);
identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, "Fabrikam"));
var properties = new AuthenticationProperties();
return new AuthenticationTicket(new ClaimsPrincipal(identity),
properties, OAuthValidationDefaults.AuthenticationScheme);
});
builder.ConfigureServices(services =>
{
services.AddAuthentication();
});
builder.Configure(app =>
{
app.UseOAuthValidation(options =>
{
options.AccessTokenFormat = format.Object;
// Run the configuration delegate
// registered by the unit tests.
configuration?.Invoke(options);
});
// Add the middleware you want to test here.
app.Run(context =>
{
if (!context.User.Identities.Any(identity => identity.IsAuthenticated))
{
return context.Authentication.ChallengeAsync();
}
var identifier = context.User.FindFirst(ClaimTypes.NameIdentifier).Value;
return context.Response.WriteAsync(identifier);
});
});
return new TestServer(builder);
}