Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to protect swagger endpoint in .NET Core API?

I have an api build in .net core 2.1. To restrict access to various endpoints, I use IdentityServer4 and [Authorize] attribute. However, my goal during development is to expose the api swagger documentation to our developers so that they may use it no matter where they work from. The challenge that I face is how do I protect the swagger index.html file so that only they can see the details of the api.

I have created a custom index.html file in the wwwroot/swagger/ui folder and that all works, however, that file uses data from /swagger/v1/swagger.json endpoint which is not protected. I would like to know how can I override the return value for that specific endpoint so that I may add my own authentication to it?

EDIT:

Currently, I have achieved the above with the following middleware:

public class SwaggerInterceptor
{
    private readonly RequestDelegate _next;

    public SwaggerInterceptor(RequestDelegate next)
    {
        _next = next;
    }

    public async Task Invoke(HttpContext context)
    {
        var uri = context.Request.Path.ToString();
        if (uri.StartsWith("/swagger/ui/index.html"))
        {
            var param = context.Request.QueryString.Value;

            if (!param.Equals("?key=123"))
            {
                context.Response.StatusCode = 404;
                context.Response.ContentType = "application/json";
                await context.Response.WriteAsync("{\"result:\" \"Not Found\"}", Encoding.UTF8);
                return;
            }
        }

        await _next.Invoke(context);
    }
}

public class Startup 
{
    //omitted code

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        app.UseMiddleware<SwaggerInterceptor>();
        //omitted code
    }
}

What I don't like about this approach as it will inspect every single request. Is there a better way to achieve this? The above only protects the index.html file, but I can adjust it to protect the json endpoint in the similar fashion.

like image 638
Bagzli Avatar asked Mar 16 '19 17:03

Bagzli


People also ask

How do I protect Swagger endpoint?

You can choose some options: basic authorization. OpenId Connect authorization using identity server.

How do I add Swagger to .NET core API?

Add and configure Swagger middlewareLaunch the app and navigate to https://localhost:<port>/swagger/v1/swagger.json . The generated document describing the endpoints appears as shown in OpenAPI specification (openapi. json). The Swagger UI can be found at https://localhost:<port>/swagger .

How do I hide Swagger in production?

To set up Swagger with Spring, we define it in a configuration bean. By default, this configuration bean is always injected into our Spring context. Thus, Swagger becomes available for all environments. To disable Swagger in production, let's toggle whether this configuration bean is injected.

How do I add authorization in Swagger UI in .NET core?

Create an ASP.NET Core Web API project in Visual Studio 2022 Click on “Create new project.” In the “Create new project” window, select “ASP.NET Core Web API” from the list of templates displayed. Click Next. In the “Configure your new project” window, specify the name and location for the new project.


2 Answers

Here it is using OpenIdConnect and Swashbuckle in Asp.Net Core 3.1. Now, if I type https://myurl.com/swagger I get routed to my normal login page and after logging in successfully, I can see the swagger.

public class Startup
{ 
    //<snip/>

    public void Configure(IApplicationBuilder app)
    { 
      //<snip/>

        app.UseAuthentication();

        app.UseAuthorization();

        app.UseSwagger();

        app.UseSwaggerUI(c => { c.SwaggerEndpoint("v1/swagger.json", "Some name"); });

        app.UseEndpoints(routes =>
        {
            var pipeline = routes.CreateApplicationBuilder().Build();
            
            routes.Map("/swagger", pipeline).RequireAuthorization(new AuthorizeAttribute {AuthenticationSchemes = OpenIdConnectDefaults.AuthenticationScheme});
            routes.Map("/swagger/index.html", pipeline).RequireAuthorization(new AuthorizeAttribute {AuthenticationSchemes = OpenIdConnectDefaults.AuthenticationScheme});
            routes.Map("/swagger/v1/swagger.json", pipeline).RequireAuthorization(new AuthorizeAttribute { AuthenticationSchemes = OpenIdConnectDefaults.AuthenticationScheme });
            routes.Map("/swagger/{documentName}/swagger.json", pipeline).RequireAuthorization(new AuthorizeAttribute { AuthenticationSchemes = OpenIdConnectDefaults.AuthenticationScheme });

            routes.MapDefaultControllerRoute();
        });
     }
}

EDIT: Somehow I thought the below was working, but when I retested it later it turned out that actually it was giving error: The request reached the end of the pipeline without executing the endpoint. So, I changed to include a fixed set of endpoints under /swagger that contain afaik the key data.

routes.Map("/swagger/{**any}", pipeline).RequireAuthorization(new AuthorizeAttribute {AuthenticationSchemes = OpenIdConnectDefaults.AuthenticationScheme});

Note: this {**any} part of the route template protects all files under /swagger as well, so for example /swagger/index.html, /swagger/v1/swagger.json etc etc.

like image 128
ubienewbie Avatar answered Sep 28 '22 05:09

ubienewbie


You can choose some options:

  • basic authorization
  • OpenId Connect authorization using identity server

Basic Authorization

In this case you just close your swagger endpoints.

// Startup.cs
public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        ...
        services.AddAuthentication()
            .AddScheme<BasicAuthenticationOptions, BasicAuthenticationHandler>("Basic", _ => {});
        ...  
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        ...

        app.UseEndpoints(endpoints =>
        {
            ...
            
            var pipeline = endpoints.CreateApplicationBuilder().Build();
            var basicAuthAttr = new AuthorizeAttribute { AuthenticationSchemes = "Basic" };
            endpoints
                .Map("/swagger/{documentName}/swagger.json", pipeline)
                .RequireAuthorization(basicAuthAttr);
            endpoints
                .Map("/swagger/index.html", pipeline)
                .RequireAuthorization(basicAuthAttr);
        });
    }
}

// BasicAuthenticationHandler.cs
public class BasicAuthenticationHandler : AuthenticationHandler<BasicAuthenticationOptions>
{
    ...
}

OIDC Authorization Using IdentityServer4

I have written the article for this case: https://medium.com/dev-genius/csharp-protecting-swagger-endpoints-82ae5cfc7eb1

like image 36
Renat Sungatullin Avatar answered Sep 28 '22 05:09

Renat Sungatullin