Post

Swagger security trimming of end points in ASP.NET Core application

TLTR;

If you need to hide endpoints in Swagger UI based on authorization token, here is the repository with demo for .NET Core app. The relevant parts are passing HTTP header and filter for rendering schema based on permissions.

Swagger

Swagger described as API framework, but for me the most important part of it is an ability to describe in source code the API - endpoint URIs and methods, models of request and response, supported response HTTP codes and so on. There are obvious pros of doing it in source code.

This makes super-easy and fast to integrate between different applications/parties - consumer just need to look at Swagger schema or UI to get an idea on what to expect. No, it is not SOAP, but definitely has some similarity.

Visibility trimming

While many things comes out of the box in Swagger UI, including OAuth2 flows support for indicating which endpoints require authorization and triggering the flow, there was still one thing I missed in a few projects in a row.

Let’s say there are different consumers of the API, each with different scopes or roles. Usually you will leverage standard Authorize attribute, which will handle the authorization part of the endpoint when the API invoked, however this doesn’t prevent from client to see and inspect the endpoint to which he is not authorized. Swagger ignores the attribute, and displays all endpoints, regardless the user is authorized or even authenticated.

At this point I must say that this behavior, probably, in most cases is desired. Even for me, in many projects regardless the permissions of the client, I don’t mind, or even willing, to show all available APIs of the application.

So what we want to do is hide APIs that the user is not allowed to access.

Passing authorization token

Let’s start from the fact that we need to pass authorization token from Swagger to our backend.

In my example I’ll be using JWT token with JwtBearer middleware, but it should work with any type of authorization, as long as you relies on Authorize attribute.

In Swagger UI there is a field called ‘api_key’. Depending on version it may looks different and you may find it in different places. Here it is in version I used:

The out-of-the-box behavior of this thing is to append the value from this input to the HTTP header called api_key to every request that done when you click on ‘Try it out!’ button. Instead, we’ll take the value, prefix it with Bearer and put it into standard HTTP header Authorization. To achieve it, we need to change JS function located in index.html called addApiKeyAuthorization.

The page itself (where the function located) rendered by SwaggerUi middleware, thus we’ll need to override the page with static file in the same path. The important part for this trick to work, is that StaticFiles middleware must be registered before SwaggerUi middleware.

Once the file in under wwwroot/swagger/ui/index.html and the function has the following line:

1
var apiKeyAuth = new SwaggerClient.ApiKeyAuthorization("Authorization", "Bearer " + key, "header");

The token will be passed in all requests, including the most important for us - when the schema file swagger/v1/swagger.json is retrieved by Swagger UI.

Dynamic schema file

In order to produce a custom swagger.json we’ll add a custom IDocumentFilter:

1
2
3
4
services.AddSwaggerGen(options =>
{
    options.DocumentFilter<SwaggerAuthorizationFilter>();
});

The filter evaluated when a schema file is rendered. The main part is in Apply method:

  • Loop through each endpoint method
  • Collect Authorize attributes from action method and controller class
  • If the attribute presented, check if user authenticated or, if policy specified, evaluate current user against the policy
  • If user doesn’t pass check, hide method or the whole endpoint

Summary

By using extensibility of Swashbuckle we were able to intercept schema rendering of Swagger UI and expose endpoints that the user is allowed to view/use based on the specified authorization token.

Update February 2017

In original demo I used package Swashbuckle 6.0.0-beta901 which uses a version of Swagger UI that didn’t had an ability to customize authentication beyound passing api_key in query string. This is the reason for adding a custom index.html to override method addApiKeyAuthorization.

This is no longer true with the version Swashbuckle.AspNetCore 1.0.0-rc1, where the latest Swagger UI version allows to specify where to pass authorization token. This simplifies the implementation a little and I added example in the same repository side by side with the original demo.

Update May 2020

I added another example for Swashbuckle 5+, which uses Swagger UI v. 3+ and latest OpenAPI. It does essentially the same as in previous versions.