user628661
user628661

Reputation: 128

Swagger UI authorization not sending the token

I'm new to Swagger. I'm using OpenAPI 3.0.2.

When I run Swagger UI, Authorization buttons appear, both on top and for each API but they don't work. When I click them, I can enter any text in the apiKey box, it accepts it and says that I'm authorized. But no API works, they all return 401

Here is the relevant code in Startup.ConfigureServices

services.AddAuthentication(
        x =>
        {
            x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
            x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
        }
    )
    .AddJwtBearer(
        x =>
        {
            x.RequireHttpsMetadata = false;
            x.SaveToken = true;
            x.TokenValidationParameters = new TokenValidationParameters
            {
                ValidateIssuer = false,
                ValidateAudience = false,
                ValidateIssuerSigningKey = true,
                IssuerSigningKey = new SymmetricSecurityKey(key),
                ValidIssuer = Configuration["Tokens:Issuer"],
                ValidAudience = Configuration["Tokens:Audience"],
            };
        }
    );
services.AddSwaggerGen(
    setupAction =>
    {
        setupAction.SwaggerDoc(
            "LibraryOpenApiSpecification",
            new Microsoft.OpenApi.Models.OpenApiInfo()
            {
                Title = "Library API",
                Version = "2.0",
                Description = "Text",

                Contact = new Microsoft.OpenApi.Models.OpenApiContact()
                {
                    Email = "[email protected]",
                    Name = "user1",
                    Url = new Uri("http://www.google.com")
                }
            }
        );

        setupAction.AddSecurityDefinition(
            "Bearer",
            new OpenApiSecurityScheme
            {
                Name = "Authorization",
                Type = SecuritySchemeType.ApiKey,
                Scheme = "Bearer",
                BearerFormat = "JWT",
                In = ParameterLocation.Header,
                Description = "JWT Authorization header using the Bearer scheme."
            }
        );


        setupAction.AddSecurityRequirement(
            new OpenApiSecurityRequirement
            {
                {
                    new OpenApiSecurityScheme
                    {
                        Reference = new OpenApiReference
                        {
                            Type = ReferenceType.SecurityScheme,
                            Id = "Bearer"
                        }
                    },
                    new string[] { }
                }
            }
        );


        var xmlCommentsFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
        var xmlCommentFullPath = Path.Combine(AppContext.BaseDirectory, xmlCommentsFile);
        setupAction.IncludeXmlComments(xmlCommentFullPath);
    }
);

The code is not mine, was brought in by another developer. I will provide more code and info if needed

Upvotes: 7

Views: 19170

Answers (3)

Vaibhav
Vaibhav

Reputation: 1

If you want to use Type = SecuritySchemeType.ApiKey then you need to append keyword Bearer with token like 'Bearer ' and the name of token variable should be Authorization only or you can replace the Security Scheme Type with Http then you don't need to declare any variable name and no need to append bearer while passing the token.

Upvotes: 0

RadicalGratitude
RadicalGratitude

Reputation: 415

To get Swagger UI (.NET 6, OpenApi v3, Swashbuckle v6.5) authentication to work for me, I had to create a AuthenticationRequirementsOperationFilter and add it to the filters as mentioned in this github answer. It was also important that the keyword bearer was kept the same in AddSecurityDefinition and OpenApiSecurityScheme.

Here is that sample code:

Startup.cs

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddSwaggerGen(opt =>
        {
            opt.SwaggerDoc("v1", new OpenApiInfo { Title = "My Api", Version = "v1" });
            opt.AddSecurityDefinition("bearer", new OpenApiSecurityScheme
            {
                Type = SecuritySchemeType.Http,
                BearerFormat = "JWT",
                In = ParameterLocation.Header,
                Scheme = "bearer"
            });
            opt.OperationFilter<AuthenticationRequirementsOperationFilter>();
        });
    }
}

public class AuthenticationRequirementsOperationFilter : IOperationFilter
{
    public void Apply(OpenApiOperation operation, OperationFilterContext context)
    {
        if (operation.Security == null)
            operation.Security = new List<OpenApiSecurityRequirement>();

        var scheme = new OpenApiSecurityScheme { Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "bearer" } };
        operation.Security.Add(new OpenApiSecurityRequirement
        {
            [scheme] = new List<string>()
        });
    }
}

Upvotes: 2

abdusco
abdusco

Reputation: 11061

Here's a working example:

services.AddSwaggerGen(
    c =>
    {
        c.SwaggerDoc("v1", new OpenApiInfo { Title = "ApiPlayground", Version = "v1" });
        c.AddSecurityDefinition(
            "token",
            new OpenApiSecurityScheme
            {
                Type = SecuritySchemeType.Http,
                BearerFormat = "JWT",
                Scheme = "Bearer",
                In = ParameterLocation.Header,
                Name = HeaderNames.Authorization
            }
        );
        c.AddSecurityRequirement(
            new OpenApiSecurityRequirement
            {
                {
                    new OpenApiSecurityScheme
                    {
                        Reference = new OpenApiReference
                        {
                            Type = ReferenceType.SecurityScheme,
                            Id = "token"
                        },
                    },
                    Array.Empty<string>()
                }
            }
        );
    }
);

Once you configure it, Swagger UI gives you a button to authenticate

auth popup

Once you fill enter the token and send a request, it sends it inside Authorization header.

request

Now, the app must parse & validate this token for each request, so you must enable the auth middlewares inside Startup.Configure method.

app.UseAuthentication();
app.UseAuthorization();

Then if the token passes the validation, you should be able to access the user with HttpContext.User property.

If you're still getting 401 errors, that implies a problem with the token validation, check if you've configured the authorization services in Startup.Configure method:

services.AddAuthorization(
    options => options.FallbackPolicy = new AuthorizationPolicyBuilder()
        .RequireAuthenticatedUser()
        .AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme).Build()
);

this will require all request to authenticate unless another authorization policy is set with [Authorize] (or [AllowAnonymous]) attribute. This will ensure the token is parsed & validated.

Here's an end-to-end API that signs & validates the token.

internal class HardCodedConfiguration
{
    public static SymmetricSecurityKey SigningKey = new SymmetricSecurityKey(
        Encoding.UTF8.GetBytes(
            "alongrandomstringhere.11b48736983e87c5cff022c462849ca5b7c5e99b76d81e9707fa35e76024cba8"
        )
    );

    public static SigningCredentials SigningCredentials => new SigningCredentials(
        SigningKey,
        SecurityAlgorithms.HmacSha256
    );
}

[ApiController]
[Route("")]
public class HelloController : ControllerBase
{
    [AllowAnonymous]
    [HttpGet("token")]
    public ActionResult SignToken()
    {
        var token = new JwtSecurityTokenHandler().WriteToken(
            new JwtSecurityToken(
                claims: new List<Claim>()
                {
                    new Claim("name", "abdusco")
                },
                expires:DateTime.Now.AddHours(1),
                signingCredentials: HardCodedConfiguration.SigningCredentials
            )
        );
        return Ok(token);
    }

    [HttpGet]
    public IActionResult AuthorizedEndpoint()
    {
        return Ok(User.Claims.Select(c => new { c.Type, c.Value }).ToList());
    }
}

// inside Startup class
public void ConfigureServices(IServiceCollection services)
{
    // swagger configuration
    // ...
    services.AddControllers();
    services.AddAuthorization(
        options => options.FallbackPolicy = new AuthorizationPolicyBuilder()
            .RequireAuthenticatedUser()
            .AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme).Build()
    );
    services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
        .AddJwtBearer(
            o =>
            {
                o.TokenValidationParameters = new TokenValidationParameters
                {
                    ValidateIssuer = false,
                    ValidateAudience = false,
                    IssuerSigningKey = HardCodedConfiguration.SigningKey,
                };
            }
        );
}

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

    app.UseAuthentication();
    app.UseAuthorization();

    app.UseEndpoints(endpoints => { endpoints.MapControllers(); });
}

Upvotes: 7

Related Questions