50

I created an asp.net webapi application which is using Individual Account Security so that the Bearer token is enabled by default. It's working fine so that I am able to test them in Postman without problem.

Here comes the question when I'm trying to integrate the Swagger UI by Swashbuckle. I installed the Swashbuckle by:

Install-Package Swashbuckle

Then change the SwaggerConfig.cs:

GlobalConfiguration.Configuration
    .EnableSwagger(c =>
    {
        c.ApiKey("Token")
            .Description("Filling bearer token here")
            .Name("Authorization")
            .In("header");
    })
    .EnableSwaggerUi(c =>
    {
        c.EnableApiKeySupport("Authorization", "header");
    });

Start my application and fill in the Bearer token:

enter image description here

But it doesn't work when I run the api request which need authorization. Here is the screenshot:

enter image description here

The bearer token is added to Authorization in header. But I still got error 401. I'm wondering if it's because the token is encoded (the SPACE is replaced by %20)? Any idea? Thanks.

By the way, I'm wondering how to add the /token in my Swagger document too so that I can get the token in Swagger UI.

John
  • 29,788
  • 18
  • 89
  • 130
Bagusflyer
  • 12,675
  • 21
  • 96
  • 179
  • I confirmed the encoded SPACE (%20) is the reason. But how can I replace the %20 with a SPACE? – Bagusflyer Aug 18 '16 at 04:31
  • 1
    Curious: Where is the method `EnableApiKeySupport()` defined? I'm using the latest Swashbuckle (v5.4.0) and I don't see it there...? – urig Aug 22 '16 at 11:53
  • Found it. It's a method of `SwaggeruiConfig` and should be set in `c.EnableSwaggerUi()`. – urig Aug 22 '16 at 11:55

4 Answers4

21

Update

The issue detailed below is now resolved in Swashbuckle v5.5.0.

Issue

Just ran into the exact same issue. I think the root cause is this line in Swashbuckle's source code:

var key = encodeURIComponent($('#input_apiKey')[0].value);

This is where the value from the HTML input field goes through URL encoding turning the space into %20. I'm planning to open an issue in the Swashbuckle repo on GitHub.

Workaround

Until that issue is resolved, here is a workaround based on replacing the above line using a Javascript file injected into the Swagger UI:

  1. In the project where you have Swashbuckle installed, create a new folder and call it "Swagger".

  2. In the new folder create a new Javascript file called "SwaggerUiCustomization.js" and put this script in it:

    (function () {
        function addApiKeyAuthorization() {
            var key = $('#input_apiKey')[0].value;
            if (key && key.trim() != "") {
                var apiKeyAuth = new SwaggerClient.ApiKeyAuthorization(swashbuckleConfig.apiKeyName, key, swashbuckleConfig.apiKeyIn);
                window.swaggerUi.api.clientAuthorizations.add("api_key", apiKeyAuth);
                log("added key " + key);
            }
        }
        $('#input_apiKey').change(addApiKeyAuthorization);
    })();
    
  3. In the Solution Explorer, choose the file and hit Alt+Enter to edit its Properties. In the Properties window change the file's Build Action to Embedded Resource.

  4. In your SwaggerConfig.cs file add the following line inside the EnableSwaggerUi() code block: c.InjectJavaScript(thisAssembly, "<Project_Default_Namespace>.Swagger.SwaggerUiCustomization.js");
    Be sure, of course, to replace <Project_Default_Namespace> with your project's default namespace.

  5. Run your project and enter "Bearer " into the text box. When you invoke a controller action, you should get this exact same value - with a whitespace instead of %20% - on the server side.

Rob
  • 26,989
  • 16
  • 82
  • 98
urig
  • 16,016
  • 26
  • 115
  • 184
  • I solved the issue by replace the index.html (it's actually the same as you did in the javascript). Thanks. But what is your suggestion on how to get the token? Because the ```token``` API is not in the Swagger document. I can of course to get the token by Postman, but it's not convinent. – Bagusflyer Aug 23 '16 at 01:04
  • I don't understand what you mean by "getting the token" and "token API". Can you explain? – urig Aug 23 '16 at 09:47
  • There is a ```ConfigureAuth``` method in Startup.Auth.cs which defile the endpoint, for example, we can define it as "/token" so that we can call this method to get token. But this api is not in swagger document. Anyway, I solved this already. Thanks anyway. – Bagusflyer Aug 24 '16 at 01:42
  • Struggling to get the JavaScript injected - get an error " //localhost:44301/Swagger/ui/ext/SwaggerUiCustomization-js?_=1477558866989 404 (Not Found)" – richardb Oct 27 '16 at 09:02
  • 1
    Ok thanks - great post and answer. Finally got it working. Saved my life. – richardb Oct 27 '16 at 09:27
  • @richardb Swashbuckle v5.5.0 was just released 3 hours ago and has the fix built into it. – urig Oct 27 '16 at 09:35
  • 1
    Ah that explains things thanks - I was having no luck yesterday and just updated packages this morning. All great now and working like a dream. – richardb Oct 27 '16 at 09:43
  • @Zhou Hao How did you manage to resolve your issue of the "/token" endpoint not appearing in Swagger UI front end? – Breeno May 31 '18 at 15:41
  • @Zhou Hao Never mind, found the solution. For benefit of others the solution is to create a custom class which extends DocumentFilter, give it the details of the operation and the schema, and inject it into the SwaggerConfig. Example can be found here https://github.com/domaindrivendev/Swashbuckle/issues/707 – Breeno Jun 01 '18 at 11:03
  • quick question, whats the work effort generally to add this into a project? I need to give estimate to manager, 5 hours or less? 10 hours? thinking it will be 2-3 hours most –  Jan 13 '20 at 23:36
  • I cannot run the command c.InjectJavascript in Net Core 2, how would I fix this?, SwaggerGenOptions does not contain definition for InjectJavascript –  Jan 13 '20 at 23:52
  • is there a way to store tokens after a computer restart or browser refresh? –  Jan 14 '20 at 00:05
16

In asp.net framework Web API , I was able to add the Bearer token on the UI and make it work properly by two different ways.

Way 1:

Adding an operation fitler. Create the following class :

public class AuthorizationHeaderParameterOperationFilter : IOperationFilter
{
    public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription)
    {
        if (operation.parameters == null)
        {
            operation.parameters = new List<Parameter>();
            
        }
        operation.parameters.Add(new Parameter
        {
            name = "Authorization",
            @in = "header",
            description = "access token",
            required = false,
            type = "string",
            @default = "Bearer "
        });
    }
}

and now in the SwaggerConfig.cs add the following:

GlobalConfiguration.Configuration
            .EnableSwagger(c =>
                {
                     // other settings

                     c.OperationFilter<AuthorizationHeaderParameterOperationFilter>();
             })
            .EnableSwaggerUi(c =>
                {
                    // UI configurations
            });

Way 2:

We can use the DocumentFilter as well to iterate all the operation and add the header, in the following one we skip the operation which actually takes username and password and gives the token for the first time:

public class SwaggerPathDescriptionFilter : IDocumentFilter
{
    private string tokenUrlRoute = "Auth";
    // the above is the action which returns token against valid credentials
    private Dictionary<HeaderType, Parameter> headerDictionary;
    private enum HeaderType { TokenAuth };

    public void Apply(SwaggerDocument swaggerDoc, SchemaRegistry schemaRegistry, IApiExplorer apiExplorer)
    {
        CreateHeadersDict();

        var allOtherPaths = swaggerDoc.paths.Where(entry => !entry.Key.Contains(tokenUrlRoute)) //get the other paths which expose API resources and require token auth
            .Select(entry => entry.Value)
            .ToList();

        foreach (var path in allOtherPaths)
        {
            AddHeadersToPath(path, HeaderType.TokenAuth);
        }
    }

    /// <summary>
    /// Adds the desired header descriptions to the path's parameter list
    /// </summary>
    private void AddHeadersToPath(PathItem path, params HeaderType[] headerTypes)
    {
        if (path.parameters != null)
        {
            path.parameters.Clear();
        }
        else
        {
            path.parameters = new List<Parameter>();
        }

        foreach (var type in headerTypes)
        {
            path.parameters.Add(headerDictionary[type]);
        }

    }

    /// <summary>
    /// Creates a dictionary containin all header descriptions
    /// </summary>
    private void CreateHeadersDict()
    {
        headerDictionary = new Dictionary<HeaderType, Parameter>();


        headerDictionary.Add(HeaderType.TokenAuth, new Parameter() //token auth header
        {
            name = "Authorization",
            @in = "header",
            type = "string",
            description = "Token Auth.",
            required = true,
            @default = "Bearer "
        });
    }
}

and then we need to regiter it in the SwaggerConfig.cs :

GlobalConfiguration.Configuration
            .EnableSwagger(c =>
                {
                     // other settings

                     c.DocumentFilter<SwaggerPathDescriptionFilter>();
             })
            .EnableSwaggerUi(c =>
                {
                    // UI configurations
            });

Now we will see the Token input for the headers in the swagger UI like:

enter image description here

Ehsan Sajjad
  • 61,834
  • 16
  • 105
  • 160
12

Note: this example uses Json Web Tokens.

Your code can be set up so "Bearer" isn't required in the authorization string.

Code in WebApi project to retrieve token (see token = ... in the code segment below):

private static bool TryRetrieveToken(HttpRequestMessage request, out string token)
    {
        token = null;
        IEnumerable<string> authzHeaders;
        if (!request.Headers.TryGetValues("Authorization", out authzHeaders) || authzHeaders.Count() > 1)
        {
            return false;
        }
        var bearerToken = authzHeaders.ElementAt(0);
        token = bearerToken.StartsWith("Bearer ") ? bearerToken.Substring(7) : bearerToken;
        return true;
    }

Swagger ApiKey:

c.ApiKey("Authorization")
                        .Description("Filling bearer token here")
                        .Name("Bearer")
                        .In("header");

Swagger Enable ApiKey Support:

c.EnableApiKeySupport("Authorization", "header");

Pasting token into Api_Key form element in Swagger UI: enter image description here

How it looks in the request header in Swagger: enter image description here

Jeremy Ray Brown
  • 1,499
  • 19
  • 23
  • is there a way to store tokens after a computer restart or browser refresh? –  Jan 14 '20 at 00:05
  • Usually the tokens are short lived and only requested to authorize a few requests. Can you provide more details for this need? A scenario your worried about? – Jeremy Ray Brown Jan 14 '20 at 02:39
  • ahh, we keep refreshing browser, and keep having to type it in –  Jan 14 '20 at 04:32
  • You can use the browser's session storage: https://www.w3schools.com/html/html5_webstorage.asp – Jeremy Ray Brown Jan 14 '20 at 15:57
  • whats the command line to store this? I was trying to figure out @jeremyraybrown –  Jan 14 '20 at 18:52
  • In your SwaggerConfig.cs file, you call the EnableSwaggerUI method. You pass in a SwaggerUIConfig object. That object has a method InjectJavaScript. That is where you provide JavaScript to Swagger. Example: `c.InjectJavaScript(typeof(MySwaggerConfigClass)).Assembly, "Namespace.To.my-javascriptfile.js");`. You're JavaScript should wait for the DOM to be ready and then load a stored token into it. Put an event listener on the input that will save the token whenever its value changes. – Jeremy Ray Brown Jan 15 '20 at 13:32
  • Google Swagger InjectJavaScript and you'll find additional help – Jeremy Ray Brown Jan 15 '20 at 13:52
8

Update for OpenAPI 3, Swashbuckle.AspNetCore (6.7.1) full article address: https://codeburst.io/api-security-in-swagger-f2afff82fb8e The code to add JWT Bearer authorization to swagger. Add this in your Startup.cs in ConfigureServices method:

services.AddSwaggerGen(c =>
{
    // configure SwaggerDoc and others

    // add JWT Authentication
    var securityScheme = new OpenApiSecurityScheme
    {
        Name = "JWT Authentication",
        Description = "Enter JWT Bearer token **_only_**",
        In = ParameterLocation.Header,
        Type = SecuritySchemeType.Http,
        Scheme = "bearer", // must be lower case
        BearerFormat = "JWT",
        Reference = new OpenApiReference
        {
            Id = JwtBearerDefaults.AuthenticationScheme,
            Type = ReferenceType.SecurityScheme
        }
    };
    c.AddSecurityDefinition(securityScheme.Reference.Id, securityScheme);
    c.AddSecurityRequirement(new OpenApiSecurityRequirement
    {
        {securityScheme, new string[] { }}
    });
}
Alexey Sukhanov
  • 309
  • 4
  • 3