Reputation: 741
I have an ASP.NET Core Web API with Swagger integrated using Swashbuckle. I have successfully integrated authorization on Swagger UI using an operation filter, because I do not want to show padlock for anonymous APIs.
.OperationFilter<AuthorizeFilter>()
Inside the filter, I have registered basic auth security requirement for Swagger UI.
My problem is, even though authentication is happening in APIs on Swagger UI, I no longer see that nice authentication popup which is giving when click on the padlock icon.
Could someone answer, why I am not seeing the auth popup now?
Upvotes: 10
Views: 17188
Reputation: 11061
Update 2023-04-17: Updated security definition type to Http
as per @mimmoz81's suggestion
Assuming you have some endpoints that protected with [Authorize]
attribute (can also be put on the controller).
[Route("")]
public class HelloController : ControllerBase
{
[Authorize]
[HttpGet("secure")]
public IActionResult GetSomethingPrivate()
{
return Ok("secret");
}
[HttpGet("public")]
public IActionResult GetSomethingPublic()
{
return Ok("hey");
}
}
You need to define a security scheme suitable for your needs. But do not require it globally, instead add it inside an operation filter. Here I've added a simple token auth:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "ApiPlayground", Version = "v1" });
c.AddSecurityDefinition("token", new OpenApiSecurityScheme
{
Type = SecuritySchemeType.Http,
In = ParameterLocation.Header,
Name = HeaderNames.Authorization,
Scheme = "Bearer"
});
// dont add global security requirement
// c.AddSecurityRequirement(/*...*/);
c.OperationFilter<SecureEndpointAuthRequirementFilter>();
});
}
And here's the operation filter which references the token
auth scheme we've just created. It checks if the endpoint needs authentication, then adds the requirement.
internal class SecureEndpointAuthRequirementFilter : IOperationFilter
{
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
if (!context.ApiDescription
.ActionDescriptor
.EndpointMetadata
.OfType<AuthorizeAttribute>()
.Any())
{
return;
}
operation.Security = new List<OpenApiSecurityRequirement>
{
new OpenApiSecurityRequirement
{
[new OpenApiSecurityScheme
{
Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "token" }
}] = new List<string>()
}
};
}
}
When you run the app, it works as you expect:
So does the auth popup:
Define a new security scheme with following values:
public void ConfigureServices(IServiceCollection services)
{
// ...
services.AddSwaggerGen(c =>
{
// ...
// basic auth scheme (username + password)
c.AddSecurityDefinition("basic", new OpenApiSecurityScheme
{
Type = SecuritySchemeType.Http,
Scheme = "basic"
});
// dont add global security requirement
// c.AddSecurityRequirement(/*...*/);
c.OperationFilter<SecureEndpointAuthRequirementFilter>();
});
}
Then update the operation filter to reference basic
auth scheme:
internal class SecureEndpointAuthRequirementFilter : IOperationFilter
{
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
if (!context.ApiDescription
.ActionDescriptor
.EndpointMetadata
.OfType<AuthorizeAttribute>()
.Any())
{
return;
}
operation.Security = new List<OpenApiSecurityRequirement>
{
new OpenApiSecurityRequirement
{
[new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "basic" // <-- changed "token" -> "basic"
}
}] = new List<string>()
}
};
}
}
here's how the auth popup looks:
After logging in, requests include the correct Authorization
header.
Upvotes: 16
Reputation: 51
@abdusco answer was really great, but I had to slightly modify to SecuritySchemeType.Http
, instead of SecuritySchemeType.ApiKey
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "ApiPlayground", Version = "v1" });
c.AddSecurityDefinition("token", new OpenApiSecurityScheme
{
Type = SecuritySchemeType.Http, // ----> HAD TO CHANGE THIS TO Http instead of ApiKey
In = ParameterLocation.Header,
Name = HeaderNames.Authorization,
Scheme = "Bearer"
});
// dont add global security requirement
// c.AddSecurityRequirement(/*...*/);
c.OperationFilter<SecureEndpointAuthRequirementFilter>();
});
}
Doing this, I actually see the Bearer token auth popup style.
As of swagger docs https://swagger.io/docs/specification/authentication/
Upvotes: 1
Reputation: 681
the Abdusco's answer is true but modify the Apply method like this for AllowAnonymousAttribute
Methods in Authorized Controllers
if (!context.ApiDescription
.ActionDescriptor
.EndpointMetadata
.OfType<AuthorizeAttribute>()
.Any() || context.ApiDescription
.ActionDescriptor
.EndpointMetadata
.OfType<AllowAnonymousAttribute>()
.Any())
{
return;
}
Upvotes: 0
Reputation: 552
In my case, I am using JWT Token Authentication with .NET Core API. I Configure the swagger with the authorization token using the below code. This code will add global security requirements.
In Startup Class ConfigureServices Method.
//Swagger Configuration
services.AddSwaggerGen(options =>
{
options.SwaggerDoc("v1", new Microsoft.OpenApi.Models.OpenApiInfo
{
Title = "API",
Version = "v2",
Description = "Your Api Description"
});
options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
Description = "JWT Authorization header using the Bearer scheme (Example: 'Bearer 12345abcdef')",
Name = "Authorization",
In = ParameterLocation.Header,
Type = SecuritySchemeType.ApiKey,
Scheme = "Bearer"
});
options.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "Bearer"
}
},
Array.Empty<string>()
}
});
});
And In Configure Method
app.UseSwagger();
app.UseSwaggerUI(options =>
{
options.SwaggerEndpoint("/swagger/v1/swagger.json", "API");
});
After running the API project Authorize button will appear on the right side. On Click the authorize button Authorization popup open and then pass the token in the text box with 'Bearer token'.
Authorization working fine for me.
Upvotes: 6