Search code examples
openid-connectasp.net-core-2.0aspnet-contrib

AspNet.Security.OAuth.Extensions Error while introspection


I'am writing a test application with asp.net core 2.0, AspNet.Security.OpenIdConnect.Server and AspNet.Security.OAuth.Extensions. I ran into a problem with introspection of the access token. When I get my token from /connect/token and send it to my resource server I get the following errors in my server:

fail: AspNet.Security.OpenIdConnect.Server.OpenIdConnectServerHandler[0]
      The introspection request was rejected with the following error: invalid_request ; (null)
info: AspNet.Security.OpenIdConnect.Server.OpenIdConnectServerHandler[0]
      The introspection response was successfully returned: {
        "error": "invalid_request"
      }

And in my resource server:

fail: AspNet.Security.OAuth.Introspection.OAuthIntrospectionHandler[0]
      An error occurred while validating an access token: the remote server returned a BadRequest response with the following payload: Date: Sat, 21 Oct 2017 17:11:50 GMT
      Server: Kestrel
       {"error":"invalid_request"}.
info: AspNet.Security.OAuth.Introspection.OAuthIntrospectionHandler[7]
      Bearer was not authenticated. Failure message: Authentication failed because the authorization server rejected the access token.

This is ConfigureServices method from my Startup class of my resource server (WebApi):

public void ConfigureServices(IServiceCollection services)
{
    services.AddAuthentication().AddOAuthIntrospection(
        options =>
        {
            options.Authority = new Uri("http://localhost:64855/");
            options.Audiences.Add("resource_server");
            options.ClientId = "client_id";
            options.ClientSecret = "client_secret";
            options.RequireHttpsMetadata = false;
        });
    services.AddMvc();
}

This is my protected by AuthorizeAttribute controller in resource server:

[Route("api/[controller]")]
[Authorize(AuthenticationSchemes = OAuthIntrospectionDefaults.AuthenticationScheme)]
public class ValuesController : Controller
{
    [HttpGet]
    public IEnumerable<string> Get()
    {
        return new string[] {"value1", "value2"};
    }
}

This is Startup class in auth server:

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddAuthentication(
                options =>
                {
                    options.DefaultScheme = OAuthValidationDefaults.AuthenticationScheme;
                })
            .AddOAuthValidation(
                options =>
                {
                    options.Audiences.Add("resource_server");
                });

        services.AddAuthentication().AddOpenIdConnectServer(options =>
        {
            options.TokenEndpointPath = "/connect/token";
            options.IntrospectionEndpointPath = "/connect/introspect";
            options.AllowInsecureHttp = true;
            options.ApplicationCanDisplayErrors = true;
            options.Provider.OnValidateTokenRequest = context =>
            {
                if (!context.Request.IsPasswordGrantType() && !context.Request.IsRefreshTokenGrantType())
                {
                    context.Reject(
                        error: OpenIdConnectConstants.Errors.UnsupportedGrantType,
                        description: "Only grant_type=password and refresh_token " +
                                     "requests are accepted by this server.");

                    return Task.CompletedTask;
                }

                if (string.Equals(context.ClientId, "client_id", StringComparison.Ordinal) &&
                    string.Equals(context.ClientSecret, "client_secret", StringComparison.Ordinal))
                {
                    context.Validate();
                }

                return Task.CompletedTask;
            };

            options.Provider.OnHandleTokenRequest = context =>
            {
                if (context.Request.IsPasswordGrantType())
                {
                    if (!string.Equals(context.Request.Username, "Bob", StringComparison.Ordinal) ||
                        !string.Equals(context.Request.Password, "P@ssw0rd", StringComparison.Ordinal))
                    {
                        context.Reject(
                            error: OpenIdConnectConstants.Errors.InvalidGrant,
                            description: "Invalid user credentials.");

                        return Task.CompletedTask;
                    }

                    var identity = new ClaimsIdentity(context.Scheme.Name,
                        OpenIdConnectConstants.Claims.Name,
                        OpenIdConnectConstants.Claims.Role);

                    identity.AddClaim(OpenIdConnectConstants.Claims.Subject, "[unique id]");

                    identity.AddClaim("urn:customclaim", "value",
                        OpenIdConnectConstants.Destinations.AccessToken,
                        OpenIdConnectConstants.Destinations.IdentityToken);

                    var ticket = new AuthenticationTicket(
                        new ClaimsPrincipal(identity),
                        new AuthenticationProperties(),
                        context.Scheme.Name);

                    ticket.SetScopes(
                        OpenIdConnectConstants.Scopes.Profile,
                        OpenIdConnectConstants.Scopes.OfflineAccess);

                    context.Validate(ticket);
                }

                return Task.CompletedTask;
            };
        });

        services.AddMvc();
    }

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
            app.UseBrowserLink();
        }
        else
        {
            app.UseExceptionHandler("/Home/Error");
        }

        app.UseStaticFiles();
        app.UseAuthentication();
        app.UseMvc(routes =>
        {
            routes.MapRoute(
                name: "default",
                template: "{controller=Home}/{action=Index}/{id?}");
        });
    }
}

It seems to me that I somewhere missed something when configuring the server and the client, but I can not understand where. Or maybe I'm just doing something wrong. Maybe, I should implement introspect method by myself... don't know( I already tried to override methods in OpenIdServerConnectProvider, but nothing happened in the end.

Tell me please what can be the problem or what I did wrong. Thanks.

UPD: after all fixes, thanks to Pinpoint, this is my working solution: https://github.com/mstya/Introspection.Sample I hope it helps someone.


Solution

  • Or maybe I'm just doing something wrong. Maybe, I should implement introspect method by myself... don't know( I already tried to override methods in OpenIdServerConnectProvider, but nothing happened in the end.

    You forgot to implement the ValidateIntrospectionRequest event. Just like ValidateTokenRequest, you have to validate the client credentials and call context.Validate() if they are valid.