Enable bearer token in Swashbuckle (Swagger document)

43,114

Solution 1

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.

Solution 2

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

Solution 3

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

Solution 4

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[] { }}
    });
}
Share:
43,114
Bagusflyer
Author by

Bagusflyer

Updated on October 09, 2021

Comments

  • Bagusflyer
    Bagusflyer over 2 years

    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.

  • Bagusflyer
    Bagusflyer over 7 years
    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.
  • urig
    urig over 7 years
    I don't understand what you mean by "getting the token" and "token API". Can you explain?
  • Bagusflyer
    Bagusflyer over 7 years
    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.
  • richardb
    richardb over 7 years
    Struggling to get the JavaScript injected - get an error " //localhost:44301/Swagger/ui/ext/SwaggerUiCustomization-js?_‌​=1477558866989 404 (Not Found)"
  • richardb
    richardb over 7 years
    Ok thanks - great post and answer. Finally got it working. Saved my life.
  • urig
    urig over 7 years
    @richardb Swashbuckle v5.5.0 was just released 3 hours ago and has the fix built into it.
  • richardb
    richardb over 7 years
    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.
  • Breeno
    Breeno almost 6 years
    @Zhou Hao How did you manage to resolve your issue of the "/token" endpoint not appearing in Swagger UI front end?
  • Breeno
    Breeno almost 6 years
    @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 github.com/domaindrivendev/Swashbuckle/issues/707
  • Admin
    Admin over 4 years
    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
  • Admin
    Admin over 4 years
    I cannot run the command c.InjectJavascript in Net Core 2, how would I fix this?, SwaggerGenOptions does not contain definition for InjectJavascript
  • Admin
    Admin over 4 years
    is there a way to store tokens after a computer restart or browser refresh?
  • Admin
    Admin over 4 years
    is there a way to store tokens after a computer restart or browser refresh?
  • Jeremy Ray Brown
    Jeremy Ray Brown over 4 years
    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?
  • Admin
    Admin over 4 years
    ahh, we keep refreshing browser, and keep having to type it in
  • Jeremy Ray Brown
    Jeremy Ray Brown over 4 years
    You can use the browser's session storage: w3schools.com/html/html5_webstorage.asp
  • Admin
    Admin over 4 years
    whats the command line to store this? I was trying to figure out @jeremyraybrown
  • Jeremy Ray Brown
    Jeremy Ray Brown over 4 years
    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
    Jeremy Ray Brown over 4 years
    Google Swagger InjectJavaScript and you'll find additional help
  • Abdessamad Jadid
    Abdessamad Jadid over 3 years
    Thanks , you saved my two days of searching, the second way work for me , i appreciate it
  • Ehsan Sajjad
    Ehsan Sajjad over 3 years
    @AbdessamadJadid cheers, glad to know it worked for you!