Tim Doke
Tim Doke

Reputation: 31

netcoreapp2.0 swagger custom authentication failure gives no result

I'm using a netcoreapp2.0 web application and have installed Swashbuckle.AspNetCore version 1.1 to handle swagger. I am only using authentication. I am not using authorization. My intention is to have authentication applied to every end point.

As far as my swagger code in the Startup.cs, I have the following under ConfigureServices.

services.AddSwaggerGen(Config.Configure);

public class Config
{
    public static void Configure(SwaggerGenOptions options)
    {
        options.SwaggerDoc("v1", new Info { Title = "My service name", Version = "v1", });
        options.DescribeAllEnumsAsStrings();
        options.OperationFilter<HttpRequestHeadersFilter>();

    }
}

public class HttpRequestHeadersFilter : IOperationFilter
{
    public void Apply(Operation operation, OperationFilterContext context)
    {


        var anon = context.ApiDescription.ControllerAttributes().OfType<AllowAnonymousAttribute>().FirstOrDefault();

        if (anon == null)
        {
            if (operation.Parameters == null)
            {
                operation.Parameters = new List<IParameter>();
            }

            operation.Parameters.Add(
                new NonBodyParameter()
                {

                    Description = "Authorization token.",
                    In = "header",
                    Name = "Authorization",
                    Required = true,

                });
        }
    }
}

Under Configure, I have this code.

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {

        app.UseSwagger();



        //MUST ADD THIS LINE FOR IT TO AUTHENTICATE
        app.UseAuthentication();



        app.UseSwaggerUI(c =>
        {
            c.SwaggerEndpoint("/swagger/v1/swagger.json", "My service");

            c.ShowRequestHeaders();


        });


        app.UseMvc();


    }

I'm using an internal token generator. The token generator works fine. When I put in valid security information, swagger works correctly. When I put in invalid security information, I get this response from swagger.

Response Body: no content

Response Code: 0

Response Headers: { "error": "no response from server" }

I have this code for OnAuthenticationFailed:

OnAuthenticationFailed = c =>
                    {

                        c.Response.StatusCode = 403;
                        c.Response.ContentType = "application/json";

                        return c.Response.WriteAsync("Invalid token");
                    }

Interestingly enough, if I comment this code and just call the HandleAuthenticateAsync code and return a failed AuthenticateResult (and I have a Context.ForbidAsync as in my GitHub post above), I get the results from the api end point AND it gives a 403 status code, which is really weird.. I'm thinking the problem might be around what I return in that OnAuthenticationFailed callback, but I don't know what that would be. It really should not continue to process after failing authentication in my humble opinion.. That seems not ideal..

I can hit the end point directly with invalid credentials, and it will fail as expected. I can throw a random exception in the code with valid credentials, and get a 500 Response Code with my error in swagger. I posted a question on GitHub around the security setup here with the name timdoke -- https://github.com/aspnet/Security/issues/1338.

I can add more stuff around how I authenticate if needed; but, suffice to say the only problem I'm having around that, seems to be related to the GitHub link above (double tapping the authenticate code).

My question is, how do I get swagger to honor the http status codes I send it when it is from an authentication failure? I tried changing the Response Content Type, but that did nothing.

On a side note.. Interestingly enough, if I change the error content type to text/plain in my OnAuthenticationFailed callback, even calling the end point directly with invalid credentials causes me to get no response at all.

I've tried adding ErrorFilters to help here and Exception handling middleware; these are unfortunately bypassed during authentication. I've also tried messing around with app.UseStatusCodePages and app.UseDeveloperExceptionPage but they also don't seem to come into affect here.

Can anyone help me out? Thanks!

Upvotes: 1

Views: 1052

Answers (1)

Tim Doke
Tim Doke

Reputation: 31

I posted a variant of this question on GitHub. I was lucky enough that they created a ticket for this with a whole lot more information: https://github.com/aspnet/Security/issues/1613

Basically, the answer is that an AuthenticationHandler does not affect the "control flow" of the application. In other words, you can fail authentication and the request is intended to continue. The bug above was partly intended to update the samples.. They purposely require an Authorization attribute to actually reject the request.

In order to make this work without a Authorization header, they advised me that I could force global authentication by creating an authorization middleware. That code looks like this, and it works.

            app.UseAuthentication();

        app.Use(next => context =>
        {
            if (context.User.Identity.IsAuthenticated)
            {
                return next(context);
            }

            var ps = new PathString("/swagger");
            if (context.Request.Path.StartsWithSegments(ps))
            {
                return next(context);
            }

            return context.ChallengeAsync("Bearer");
        });

Upvotes: 2

Related Questions