Reputation: 128
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
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
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
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
Once you fill enter the token and send a request, it sends it inside Authorization
header.
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