Bearer token doesnot pass on swagger - asp.net-core

I am trying to implement OAuth2 ClientCredentials flow on ASP.NET CORE 3.1. I follow guidelines on the official GitHub repo.
The problem is regarding getting bearer token on Swagger-UI. It doesn't pass automatically. I investigated the issue on Swashbuckle Github repo. There are some closed issues. There's no solution.
Below my implementation :
public static class SwaggerExtensions
{
public static IServiceCollection EnableSwagger(this IServiceCollection services)
{
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "Sample API", Version = "v1" });
c.AddSecurityDefinition("oauth2",
new OpenApiSecurityScheme
{
Type = SecuritySchemeType.OAuth2,
Flows = new OpenApiOAuthFlows
{
ClientCredentials = new OpenApiOAuthFlow
{
TokenUrl = new Uri("/api/auth/token", UriKind.Relative),
Scopes = new Dictionary<string, string>
{
{ "readAccess", "Access read operations" },
{ "writeAccess", "Access write operations" }
},
}
},
Name = "Authorization",
In = ParameterLocation.Header,
Scheme = "Bearer"
});
c.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "oauth2" }, Name = "oauth2"
},
new[] { "readAccess", "writeAccess" }
}
});
c.OperationFilter<OAuth2OperationFilter>();
});
return services;
}
public class AuthenticationAttribute : Attribute, IAsyncAuthorizationFilter
{
public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
{
bool isAnonymousAllowed = context.Filters.Any(f => f.GetType() == typeof(AllowAnonymousFilter));
if (isAnonymousAllowed)
{
return;
}
if (!context.HttpContext.Request.Headers.TryGetValue("Authorization", out var authHeaderValue))
{
throw new UnauthorizedAccessException("A valid key must be supplied");
}
string authHeader = authHeaderValue.ToString();
if (string.IsNullOrEmpty(authHeader))
....
}
}
After creating an access_token, I cannot get this token on another controller on Swagger-UI.
Swashbuckle.AspNetCore version -> 5.5.1
Swashbuckle.AspNetCore.Swagger -> 5.5.1

Related

Can't authorize swagger through my Authorization Server using OIDC

I'm using Swashbuckle configured as
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v2", new OpenApiInfo { Title = "API", Version = "v2" });
c.AddSecurityDefinition("OpenId", new OpenApiSecurityScheme
{
Type = SecuritySchemeType.OpenIdConnect,
Name = "Authorization",
In = ParameterLocation.Header,
Scheme = "Bearer",
Flows = new OpenApiOAuthFlows
{
AuthorizationCode = new OpenApiOAuthFlow
{
AuthorizationUrl = new Uri($"{authority}connect/authorize"),
TokenUrl = new Uri($"{authority}connect/token"),
Scopes = new Dictionary<string, string>
{
{
"openid", "openid"
},
{
"api", "api"
},
},
},
},
OpenIdConnectUrl = new Uri($"{authority}.well-known/openid-configuration"),
});
c.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "OpenId",
},
},
new List<string> { "api", "openid" }
},
});
});
And after that
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v2/swagger.json", "API v2");
c.OAuthUsePkce();
c.OAuthClientId(Configuration.GetRequiredSection("SwaggerOptions:ClientId").Value);
c.OAuthClientSecret(Configuration.GetRequiredSection("SwaggerOptions:ClientSecret").Value);
c.EnablePersistAuthorization();
c.OAuthScopes("api", "openid");
});
I see resulting swagger.json seems to be correct, as it declared at the docs
But something goes definitely wrong - I get CORS header 'Access-Control-Allow-Origin' missing reason for discovery request rejecting, simultaneously it returns a correct configuration with 200 ok
What have I missed?
Eventually, I was able to get this to work. I was misunderstanding which part does require CORS in this case. To fix that, I added my Swagger UI host to allowed hosts on auth server side and switch CORS on there. Now, all work fine!

ASP.NET Core API: Authentication popup is not showing up in Swagger UI

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?
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.ApiKey,
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:
Bonus: using basic auth
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.
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.
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;
}

Enabled AuthUsePkce for SwaggerUI but the server returns pkce_missing_challenge

I am using version v5.0.0 of Swashbuckle in my asp net core 3.0 app :
options.AddSecurityDefinition("OAuth2", new OpenApiSecurityScheme
{
Type = SecuritySchemeType.OAuth2,
Flows = new OpenApiOAuthFlows
{
AuthorizationCode = new OpenApiOAuthFlow
{
AuthorizationUrl = new Uri("https://my.okta.com/oauth2/v1/authorize"),
TokenUrl = new Uri("https://my.okta.com/oauth2/v1/token"),
Scopes = new Dictionary<string, string>
{
{ "openid", "desc" }
},
}
},
Description = "Balea Server OpenId Security Scheme"
});
app.UseSwaggerUI(options =>
{
options.RoutePrefix = "docs";
options.OAuthScopeSeparator(",");
options.OAuthUsePkce();
});
But the Auth server returns error :
pkce_missing_challenge
Here is the request log that Auth server receives :
/oauth2/v1/authorize?response_type=code&client_id=xxxxxxxxxxxxx&redirect_uri=https%3A%2F%2Flocalhost%3A8002%2Fdocs%2Foauth2-redirect.html&scope=openid&state=VHVlIE1hciAwMiAyMDIxIDExOjIyOjM3IEdNVCswMDAwIChXZXN0ZXJuIEV1cm9wZWFuIFN0YW5kYXJkIFRpbWUp
So Instead of "Pkce" I used the following configuration thanks to this:
options.AddSecurityDefinition("OAuth2", new OpenApiSecurityScheme
{
Type = SecuritySchemeType.OAuth2,
Flows = new OpenApiOAuthFlows
{
Implicit = new OpenApiOAuthFlow
{
AuthorizationUrl = new Uri($"mydomain.com/oauth2/default/v1/authorize"),
TokenUrl = new Uri($"mydomain.com/oauth2/default/v1/token"),
Scopes = new Dictionary<string, string>
{
{ "openid", "test" },
},
}
},
});

How to setup a swagger API login with authorize button in asp.net web api?

I have an asp.net web api project integrated with swagger ui. I am new in swagger.I installed all required OWIN packages for authentication.It works fine with postman but when i give authorize attribute in any controller it's not working in swagger.
How can i make a login panel for authentication in swagger and how to add the bearer+access_token automatically with every request in swagger after successful login?
Thanx in advance
Here is what I did in a .net core project (2.2). This might help you in setting up authentication with swagger. I'm using swashbackle and Microsoft.OpenApi:
1-Add authentication Support:
services.AddAuthentication("ApiKey").AddScheme<AuthenticationSchemeOptions, ApiKeyAuth>("ApiKey", null);
2-Add this in the AddSwaggerGen in the ConfigureServices method:
setup.AddSecurityDefinition("ApiKey", new OpenApiSecurityScheme
{
Type = SecuritySchemeType.ApiKey,
Name = "ApiKey",
In = ParameterLocation.Header,
Description = "Please enter the API Key provided to you"
});
setup.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference {
Type = ReferenceType.SecurityScheme,
Id = "ApiKey" }
}, new List<string>() }
});
3-Create your authentication handler:
public class ApiKeyAuth : AuthenticationHandler<AuthenticationSchemeOptions>
{
public ApiKeyAuth(
IOptionsMonitor<AuthenticationSchemeOptions> options,
ILoggerFactory logger,
UrlEncoder encoder,
ISystemClock clock)
: base(options, logger, encoder, clock)
{
}
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
{
if (!Request.Headers.ContainsKey("ApiKey"))
{
return Task.FromResult(AuthenticateResult.Fail("Missing ApiToken"));
}
try
{
var identity = new ClaimsIdentity( new List<Claim>() { new Claim(ClaimTypes.Sid, "1") },Scheme.Name);
var principal = new ClaimsPrincipal(identity);
var ticket = new AuthenticationTicket(principal, Scheme.Name);
return Task.FromResult(AuthenticateResult.Success(ticket));
}
catch (Exception)
{
return Task.FromResult(AuthenticateResult.Fail("Invalid Authentication Failed"));
}
}
}
you need to implement this properly since this a testing code.
As a result, you should see a new Authorize button appearing on the UI:
The authorize dialog box should look like this:
Hope this helps!
The ApiKeyAuth class is not called.
I have added [Authorize] to the controller.
Added this code to StartUp.
services.AddAuthentication("ApiKey").AddScheme("ApiKey", null);
Please add below code in Program file to authorize button on swagger page
builder.Services.AddSwaggerGen(option =>{
option.SwaggerDoc("v1", new OpenApiInfo { Title = "Portal API", Version = "v1" });
option.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
In = ParameterLocation.Header,
Description = "Please enter a valid token",
Name = "Authorization",
Type = SecuritySchemeType.Http,
BearerFormat = "JWT",
Scheme = "Bearer"
});
option.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type=ReferenceType.SecurityScheme,
Id="Bearer"
}
},
new string[]{}
}
});
});

Incorrect link in Swagger

I am using the ASP.NET Core 2.0 with Swashbuckle.AspNetCore (2.4.0)
in Startup ConfigureServices
services.AddSwaggerGen(c =>
{
c.OperationFilter<AuthorizationHeaderParameterOperationFilter>();
c.SwaggerDoc("v1", new Info
{
Version = "v1",
Title = "Library API",
Description = "ASP.NET Core Web API",
TermsOfService = "None",
Contact = new Contact
{
Name = "my name",
Email = "myname#mywebsite"
}
});
});
});
in Startup configure
app.UseSwagger(c =>
{
c.RouteTemplate =
"api-docs/{documentName}/swagger.json";
});
app.UseSwaggerUI(c =>
{
c.RoutePrefix = "api-docs";
c.SwaggerEndpoint("v1/swagger.json", "Api v1");
});
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"
});
}
}
}
which looks like this
however the links go to the url
about:blank
How do I set the v1/swagger.json link and the Terms of service link to go to the right place?
Use c.SwaggerEndpoint("./v1/swagger.json", "Api v1");
It's looks like a bug that relative path (without '/') renders empty link but swagger.json is available and all works.
I use relative path to work properly with reverse proxies but this is other story...