Use JWT (Authorization: Bearer) in Swagger in ASP.NET Core

51,275

Solution 1

I struggled with the same problem and found a working solution in this blogpost: http://blog.sluijsveld.com/28/01/2016/CustomSwaggerUIField

It comes down to adding this in your configurationoptions

services.ConfigureSwaggerGen(options =>
{
   options.OperationFilter<AuthorizationHeaderParameterOperationFilter>();
});

and the code for the operationfilter

public class AuthorizationHeaderParameterOperationFilter : IOperationFilter
{
   public void Apply(Operation operation, OperationFilterContext context)
   {
      var filterPipeline = context.ApiDescription.ActionDescriptor.FilterDescriptors;
      var isAuthorized = filterPipeline.Select(filterInfo => filterInfo.Filter).Any(filter => filter is AuthorizeFilter);
      var allowAnonymous = filterPipeline.Select(filterInfo => filterInfo.Filter).Any(filter => filter is IAllowAnonymousFilter);

      if (isAuthorized && !allowAnonymous)
      {
          if (operation.Parameters == null)
             operation.Parameters = new List<IParameter>();

          operation.Parameters.Add(new NonBodyParameter
          {                    
             Name = "Authorization",
             In = "header",
             Description = "access token",
             Required = true,
             Type = "string"
         });
      }
   }
}

Then you will see an extra Authorization TextBox in your swagger where you can add your token in the format 'Bearer {jwttoken}' and you should be authorized in your swagger requests.

Solution 2

Currently Swagger has functionality for authentication with JWT-token and can automatically add token into header (I'm using Swashbuckle.AspNetCore 1.1.0).

enter image description here

The following code should help achieve this.

In the Startup.ConfigureServices():

services.AddSwaggerGen(c =>
{
    // Your custom configuration
    c.SwaggerDoc("v1", new Info { Title = "My API", Version = "v1" });
    c.DescribeAllEnumsAsStrings();
    // JWT-token authentication by password
    c.AddSecurityDefinition("oauth2", new OAuth2Scheme
    {
        Type = "oauth2",
        Flow = "password",
        TokenUrl = Path.Combine(HostingEnvironment.WebRootPath, "/token"),
        // Optional scopes
        //Scopes = new Dictionary<string, string>
        //{
        //    { "api-name", "my api" },
        //}
    });
});

Check and configure TokenUrl if your endpoint is different.

In the Startup.Configure():

app.UseSwagger();
app.UseSwaggerUI(c =>
{
    c.SwaggerEndpoint("/swagger/v1/swagger.json", "API V1");

    // Provide client ID, client secret, realm and application name (if need)

    // Swashbuckle.AspNetCore 4.0.1
    c.OAuthClientId("swagger-ui");
    c.OAuthClientSecret("swagger-ui-secret");
    c.OAuthRealm("swagger-ui-realm");
    c.OAuthAppName("Swagger UI");

    // Swashbuckle.AspNetCore 1.1.0
    // c.ConfigureOAuth2("swagger-ui", "swagger-ui-secret", "swagger-ui-realm", "Swagger UI");
});

If your endpoint for authentication by token follows the OAuth2 standard, all should work. But just in case, I have added sample of this endpoint:

public class AccountController : Controller
{
    [ProducesResponseType(typeof(AccessTokens), (int)HttpStatusCode.OK)]
    [ProducesResponseType((int)HttpStatusCode.BadRequest)]
    [ProducesResponseType((int)HttpStatusCode.Unauthorized)]
    [HttpPost("/token")]
    public async Task<IActionResult> Token([FromForm] LoginModel loginModel)
    {
        switch (loginModel.grant_type)
        {
            case "password":
                var accessTokens = // Authentication logic
                if (accessTokens == null)
                    return BadRequest("Invalid user name or password.");
                return new ObjectResult(accessTokens);

            case "refresh_token":
                var accessTokens = // Refresh token logic
                if (accessTokens == null)
                    return Unauthorized();
                return new ObjectResult(accessTokens);

            default:
                return BadRequest("Unsupported grant type");
        }
    }
}

public class LoginModel
{
    [Required]
    public string grant_type { get; set; }

    public string username { get; set; }
    public string password { get; set; }
    public string refresh_token { get; set; }
    // Optional
    //public string scope { get; set; }
}

public class AccessTokens
{
    public string access_token { get; set; }
    public string refresh_token { get; set; }
    public string token_type { get; set; }
    public int expires_in { get; set; }
}

Solution 3

To expand on HansVG answer which worked for me (thanks) and since I don't have enough contribution points I can't answer emseetea question directly. Once you have the Authorization textbox you will need to call the endpoint that generate the token which will be outside your must [Authorize] area of endpoints.

Once you have called that endpoint to generate the token from the endpoint you can copy it out of the results for that endpoint. Then you have the token to use in your other areas that are must [Authorize]. Just paste it in the textbox. Make sure, as HansVG mentioned, to add it in the correct format, which needs to include "bearer ". Format = "bearer {token}".

Solution 4

Thanks to the Pavel K.'s answer, this is the way I finally resolved this issue in ASP.NET Core 2.2 with Swagger 4.0.1.

In the Startup.cs ConfigureServices():

public void ConfigureServices(IServiceCollection services)
{
    .
    .
    .
    services.AddSwaggerGen(c =>
    {
        c.SwaggerDoc("v1", new Info { Title = "...", Version = "v1" });
        .
        .
        .
        c.AddSecurityDefinition("Bearer", new OAuth2Scheme
        {
            Flow = "password",
            TokenUrl = "/token"
        });

       // It must be here so the Swagger UI works correctly (Swashbuckle.AspNetCore.SwaggerUI, Version=4.0.1.0)
       c.AddSecurityRequirement(new Dictionary<string, IEnumerable<string>>
       {
           {"Bearer", new string[] { }}
       });
    });
    .
    .
    .
}

In the Startup.cs Configure():

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    .
    .
    .
    app.UseSwagger();
    app.UseSwaggerUI(c =>
    {
        c.SwaggerEndpoint("/swagger/v1/swagger.json", "...");
        // Provide client ID, client secret, realm and application name (if need)
        c.OAuthClientId("...");
        c.OAuthClientSecret("...");
        c.OAuthRealm("...");
        c.OAuthAppName("...");
    });
    .
    .
    .
}

And here is how I made an endpoint to give out a JWT token:

[ApiController, Route("[controller]")]
public class TokenController : ControllerBase
{
    [HttpPost, AllowAnonymous]
    public async Task<ActionResult<AccessTokensResponse>> RequestToken([FromForm]LoginRequest request)
    {
        var claims = await ValidateCredentialAndGenerateClaims(request);

        var now = DateTime.UtcNow;
        var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_setting.SecurityKey));
        var signingCredentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);

        var token = new JwtSecurityToken(
            issuer: _setting.Issuer,
            audience: _setting.Audience,
            claims: claims,
            notBefore: now,
            expires: now.AddMinutes(_setting.ValidDurationInMinute),
            signingCredentials: signingCredentials);

        return Ok(new AccessTokensResponse(token));
    }
}

All your rules and logic on validating user name and password (and/or client_id and clinet_secret) will be in ValidateCredentialAndGenerateClaims().

If you just wonder, these are my request and response models:

/// <summary>
/// Encapsulates fields for login request.
/// </summary>
/// <remarks>
/// See: https://www.oauth.com/oauth2-servers/access-tokens/
/// </remarks>
public class LoginRequest
{
    [Required]
    public string grant_type { get; set; }
    public string username { get; set; }
    public string password { get; set; }
    public string refresh_token { get; set; }
    public string scope { get; set; }

    public string client_id { get; set; }
    public string client_secret { get; set; }
}

/// <summary>
/// JWT successful response.
/// </summary>
/// <remarks>
/// See: https://www.oauth.com/oauth2-servers/access-tokens/access-token-response/
/// </remarks>
public class AccessTokensResponse
{
    /// <summary>
    /// Initializes a new instance of <seealso cref="AccessTokensResponse"/>.
    /// </summary>
    /// <param name="securityToken"></param>
    public AccessTokensResponse(JwtSecurityToken securityToken)
    {
        access_token = new JwtSecurityTokenHandler().WriteToken(securityToken);
        token_type = "Bearer";
        expires_in = Math.Truncate((securityToken.ValidTo - DateTime.UtcNow).TotalSeconds);
    }

    public string access_token { get; set; }
    public string refresh_token { get; set; }
    public string token_type { get; set; }
    public double expires_in { get; set; }
}

Solution 5

You may add any additional header with API call by using this swagger configuration

// Register the Swagger generator, defining 1 or more Swagger documents
services.AddSwaggerGen(c =>
{
    c.SwaggerDoc("v1", new Info
    {
        Version = "v1",
        Title = "Core API",
        Description = "ASP.NET Core API",
        TermsOfService = "None",
        Contact = new Contact
        {
            Name = "Raj Kumar",
            Email = ""
        },
        License = new License
        {
            Name = "Demo"
        }
    });
    c.AddSecurityDefinition("Bearer", new ApiKeyScheme()
    {
        Description = "JWT Authorization header using the Bearer scheme. Example: \"Authorization: Bearer {token}\"",
        Name = "Authorization",
        In = "header",
        Type = "apiKey"
    });
    c.AddSecurityRequirement(new Dictionary<string, IEnumerable<string>>
    {
    {"Bearer",new string[]{}}
    });
});

enter image description here

Share:
51,275
MonkeyDreamzzz
Author by

MonkeyDreamzzz

Updated on August 17, 2021

Comments

  • MonkeyDreamzzz
    MonkeyDreamzzz over 2 years

    I'm creating a REST api in ASP.NET Core 1.0. I was using Swagger to test but now I added JWT authorization for some routes. (with UseJwtBearerAuthentication)

    Is it possible to modify the header of the Swagger requests so the routes with the [Authorize] attribute can be tested?

  • emseetea
    emseetea over 6 years
    where do you obtain the bearer token to put into the jwttoken field when using the try out functionality in swagger ui?
  • Whoever
    Whoever over 6 years
    This works, except when UserId/Password/Client/Secret failed, it just failed quietly in the background and still show logged in. Any thought?
  • Pavel K.
    Pavel K. over 6 years
    Please check that you return HTTP status code 400, if authorization failed. It's requirement from RFC 6749 and Swagger also process it. I have updated the answer.
  • Whoever
    Whoever over 6 years
    Yes, I use IdentityServer 4 and it returns 400. But swagger UI shows Logout button as if user has successfully logged in. I'm not sure how to config that swagger popup screen to show authentication failed.
  • Pavel K.
    Pavel K. over 6 years
    I'm afraid, but I haven't any new ideas. As you can see I have decided to create my own implementation of authentication method (as I will more free with DB wrappers and schema). The Swagger works correctly with my token-endpoint with exception a part of refresh the token, which seems does not implemented yet.
  • statler
    statler about 6 years
    Just a quick helper; using Microsoft.AspNetCore.Mvc.Authorization; using Swashbuckle.AspNetCore.Swagger; using Swashbuckle.AspNetCore.SwaggerGen; using System.Collections.Generic; using System.Linq;
  • hubert17
    hubert17 about 6 years
    How can we automatically assign the Bearer <token> in the authorization field?
  • Papa Stahl
    Papa Stahl almost 6 years
    yes, this works but is cumbersome. Like @hubert17 I would like to add the token to the request automatically. This is working in a 4.5.2 api, but not under core 2.0.
  • Tohid
    Tohid over 5 years
    I use Swashbuckle.AspNetCore 4.0.1 package in my ASP.NET Core application. It doesn't recognize ConfigureOAuth2() method. Do I miss something? Compile error: 'SwaggerUIOptions' does not contain a definition for 'ConfigureOAuth2' and no accessible extension method 'ConfigureOAuth2' accepting a first argument of type 'SwaggerUIOptions' could be found (are you missing a using directive or an assembly reference?)
  • Pavel K.
    Pavel K. over 5 years
    @Tohid please check updated answer, in the Swashbuckle.AspNetCore 4.0.1 was a bit change in the API.
  • SledgeHammer
    SledgeHammer about 5 years
    @PavelK. I'm also using Swashbuckle.AspNetCore 4.0.1. In the authorize dialog, is there a way to hide clientId & clientSecret? I have flow = "password", but the dialog shows Username / Password AND clientId / clientSecret. If I set to "application" then it only shows clientId / clientSecret.
  • Pavel K.
    Pavel K. about 5 years
    @SledgeHammer, I'm afraid, but I don't know how to hide these fields. As I understand, they are part of "password" flow. About screenshot - this is from Swashbuckle.AspNetCore 1.1.0 (currently I'm also see these fields).
  • Mateja Petrovic
    Mateja Petrovic over 3 years
    If you're stuggling, NonBodyParameter and IParameter may be replaced with OpenApiParameter