Manual accesstoken generation using OpenIdConnect Server (ASOS)

853 views Asked by At

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?

1

There are 1 answers

4
Kévin Chalet On BEST ANSWER

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);
}