Swagger "Authorization has been denied for this request" message - c#

In my Swagger API I am getting this message constantly even when it picks up the Api-Key specified in the header,Why is this occuring? any help would be great
Request URL
https://localhost:44338/api/Accounts/CreateRole?api_key=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1bmlxdWVfbmFtZSI6ImRhdGFseXR5eCIsInJvbGUiOiJTeXN0ZW1BZG1pbmlzdHJhdG9yIiwiaXNzIjoiRnJhbmtIaXJ0aCIsImF1ZCI6IkNsaWVudEFjY2VzcyIsImV4cCI6MTUyODMyMDI4NX0.w6eYfa4YSyJEwqVovdBUhQJkuHDf1IvG-YZk1rf6SVU
Response Body
{
"message": "Authorization has been denied for this request."
}
Startup.cs
[assembly: OwinStartup(typeof(ProjectScavengerAPI.Web.Startup))]
namespace ProjectScavengerAPI.Web
{
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
this.ConfigureOAuthTokenConsumption(app);
HttpConfiguration config = new HttpConfiguration();
config.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
config.Formatters.JsonFormatter.UseDataContractJsonSerializer = false;
config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;
WebApiConfig.Register(config);
app.UseWebApi(config);
}
}
}
WebApiConfig
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
config.SuppressDefaultHostAuthentication();
config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
Startup_Auth
public partial class Startup
{
// For more information on configuring authentication, please visit https://go.microsoft.com/fwlink/?LinkId=301864
public static OAuthAuthorizationServerOptions OAuthOptions { get; private set; }
public static string PublicClientId { get; private set; }
private void ConfigureOAuthTokenConsumption(IAppBuilder app)
{
var issuer = ConfigurationManager.AppSettings["Issuer"];
var audienceId = ConfigurationManager.AppSettings["AudienceId"];
var clientAudienceId = ConfigurationManager.AppSettings["ClientAudienceId"];
var audienceSecret = ConfigurationManager.AppSettings["AudienceSecret"];
// Api controllers with an [Authorize] attribute will be validated with JWT
app.UseJwtBearerAuthentication(new JwtBearerAuthenticationOptions
{
AuthenticationMode = AuthenticationMode.Active,
AllowedAudiences = new[] { audienceId, clientAudienceId },
IssuerSecurityTokenProviders = new IIssuerSecurityTokenProvider[]
{
new SymmetricKeyIssuerSecurityTokenProvider(issuer, audienceSecret)
}
});
}
}
CreateRole Function (SystemAdministrator already exists)
[HttpPost]
[Authorize(Roles = "SystemAdministrator")]
[Route("CreateRole")]
public IHttpActionResult CreateRole(string roleName)
{
return TryAction(() => _CreateRole(roleName));
}
private object _CreateRole(string roleName)
{
try
{
if (!Roles.RoleExists(roleName))
{
Roles.CreateRole(roleName);
}
return $"{roleName} created";
}
catch
{
if (Roles.RoleExists(roleName))
{
return $"{roleName} exists";
}
}
return "";
}
Postman Response working

After spending hours trying to figure out why it wasn't working it turns out I had a spelling error while injecting the javascript file that appends "bearer" to the token so it was never being injected.
I also had to add ApiKeySupport in the SwaggerConfig.EnableSwaggerUI
.EnableSwaggerUi(c =>
{
c.InjectJavaScript(thisAssembly, "ProjectScavengerAPI.Web.Scripts.Swagger.jwt-auth.js");
c.EnableApiKeySupport("Authorization", "header");
});

Related

Certificate Authentication not working in ASP.NET Core 5.0

I am trying to implement Certificate authentication in ASP.NET Core 5.0. I created an API method that will be authenticated wtih Root CA and Child Certificate. I am following the official docs for it - https://learn.microsoft.com/en-us/aspnet/core/security/authentication/certauth?view=aspnetcore-5.0
There are 2 projects one is a web api project (that requires certificated) other is client project which has to call the web api project to fetch data from api.
The Web API Project code is as follows:
HomeController.cs
[ApiController]
[Route("api/[controller]")]
public class HomeController : ControllerBase
{
[Authorize]
[HttpGet]
public IEnumerable<Reservation> Get()
{
return new List<Reservation> {
new Reservation {Id=1, Name = "Ankit", StartLocation = "New York", EndLocation="Beijing" },
new Reservation {Id=2, Name = "Bobby", StartLocation = "New Jersey", EndLocation="Boston" },
new Reservation { Id = 3, Name = "Jacky", StartLocation = "London", EndLocation = "Paris" }
};
}
}
Startup.cs
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddAuthentication(
CertificateAuthenticationDefaults.AuthenticationScheme)
.AddCertificate(options =>
{
options.AllowedCertificateTypes = CertificateTypes.All;
options.Events = new CertificateAuthenticationEvents
{
OnCertificateValidated = context =>
{
var validationService = context.HttpContext.RequestServices.GetService<MyCertificateValidationService>();
if (validationService.ValidateCertificate(context.ClientCertificate))
{
context.Success();
}
else
{
context.Fail("invalid cert");
}
return Task.CompletedTask;
},
OnAuthenticationFailed = context =>
{
context.Fail("invalid cert");
return Task.CompletedTask;
}
};
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCertificateForwarding();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
}}
In the above code I have configured certificate authentication inside the ConfigureServices() method.
MyCertificateValidationService.cs
This class check the client certificate thumbprint.
public class MyCertificateValidationService
{
public bool ValidateCertificate(X509Certificate2 clientCertificate)
{
return CheckIfThumbprintIsValid(clientCertificate);
}
private bool CheckIfThumbprintIsValid(X509Certificate2 clientCertificate)
{
var listOfValidThumbprints = new List<string>
{
"D8BF2DB9766110BF3768C18E39ECE19EA746342A"
};
if (listOfValidThumbprints.Contains(clientCertificate.Thumbprint))
{
return true;
}
return false;
}}
Program.cs
Here I have configured Kestrel
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
webBuilder.ConfigureKestrel(o =>
{
o.ConfigureHttpsDefaults(o =>
o.ClientCertificateMode =
ClientCertificateMode.RequireCertificate);
});
});
}
Next, on the client project the code which calls the API is given as follows:
private async Task<JsonDocument> GetApiDataUsingHttpClientHandler()
{
var handler = new HttpClientHandler();
var client = new HttpClient(handler);
var request = new HttpRequestMessage()
{
RequestUri = new Uri("https://localhost:44392/api/Home"),
Method = HttpMethod.Get,
};
var response = await client.SendAsync(request);
if (response.IsSuccessStatusCode)
{
var responseContent = await response.Content.ReadAsStringAsync();
var data = JsonDocument.Parse(responseContent);
return data;
}
throw new ApplicationException($"Status code: {response.StatusCode}, Error: {response.ReasonPhrase}");
}
I always get "ApplicationException: Status code: Forbidden, Error: Forbidden" error on the client project. It seems some problem which I am not able to find out. The error screenshot is given below:
If I remove the [Authorize] attribute from the web api controller then it works fine. This proves certificate authentication is not working at all. What can be the problem?
Note that I created 2 certificates in powershell. One is root ca which I imported to the Trusted Root Certification Authorities.
other is client certificate which is imported to Personal folder through Cert manager.

WebApiConfig.Configure() does not exist (Auth0/OWIN)

I am currently following this tutorial for implementing Auth0 in my WebApi, https://community.auth0.com/t/asp-net-web-api-tutorial/7335.
In the Startup class they say to implement WebApiConfig.Configure(app).
But the method does not exist. What is this method suppose to contain.
Startup.cs
public class Startup {
public void Configuration(IAppBuilder app) {
var domain = $"https://{ConfigurationManager.AppSettings["Auth0Domain"]}/";
var apiIdentifier = ConfigurationManager.AppSettings["Auth0ApiIdentifier"];
var keyResolver = new OpenIdConnectSigningKeyResolver(domain);
app.UseJwtBearerAuthentication(
new JwtBearerAuthenticationOptions {
AuthenticationMode = AuthenticationMode.Active,
TokenValidationParameters = new TokenValidationParameters() {
ValidAudience = apiIdentifier,
ValidIssuer = domain,
IssuerSigningKeyResolver = (token, securityToken, kid, parameters) => keyResolver.GetSigningKey(kid)
}
});
// Configure Web API
WebApiConfig.Configure(app);
}
}
WebApiConfig.cs
public static class WebApiConfig {
public static void Register(HttpConfiguration config) {
// Web API configuration and services
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
internal static void Configure(IAppBuilder app) {
throw new NotImplementedException();
}
}

Cannot see my controllers actions in the Swagger UI

I am trying to setup my web api project to use Swagger.
I installed Swashbuckle and while the Swagger UI works when I go to http://localhost:55010/swagger, I see none of my controllers actions.
I am using this kind of path for my actions: http://localhost:55010/api/v1/Role
I currently have only one version of my api, but I am planning to have more than one so I am using v1 in my URL paths (it is set up by using subfolders in my Controllers folder).
Here is what I see when I go to http://localhost:55010/swagger/docs/v1:
{"swagger":"2.0","info":{"version":"v1","title":"Swashbuckle Dummy API V1"},"host":"localhost:55010","schemes":["http"],"paths":{},"definitions":{}}
This is the configuration that I am using:
public class SwaggerConfig
{
public static void Register()
{
var thisAssembly = typeof(SwaggerConfig).Assembly;
GlobalConfiguration.Configuration
.EnableSwagger(c =>
{
c.MultipleApiVersions(
(apiDesc, targetApiVersion) => ResolveVersionSupportByRouteConstraint(apiDesc, targetApiVersion),
(vc) =>
{
//vc.Version("v2", "Swashbuckle Dummy API V2");
vc.Version("v1", "Swashbuckle Dummy API V1");
});
})
.EnableSwaggerUi(c =>
{
});
}
private static bool ResolveVersionSupportByRouteConstraint(ApiDescription apiDesc, string targetApiVersion)
{
// I don't know how I am supposed to use this
return true;
}
}
My Route config :
config.Routes.MapHttpRoute(
name: "WithActionApi",
routeTemplate: "api/{folder}/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional },
constraints: new { action = #"[A-Za-z]+" }
);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{folder}/{controller}/{id}",
defaults: new { action = "DefaultAction", id = RouteParameter.Optional }
);
and one example of a controller :
public class GridTemplateController : BaseController
{
GridTemplateLogic logic;
public GridTemplateController(IPermissionValidator permissionValidator, IRolePermissionLogic logicRolePermission)
: base(permissionValidator, logicRolePermission)
{
logic = new GridTemplateLogic(new GridTemplateRepository(ConnectionString, CurrentUser), permissionValidator);
}
// GET: api/v1/GridTemplate/ForGrid/5
[HttpGet]
public IHttpActionResult ForGrid(int id)
{
try
{
var entityList = logic.GetAllByGridId(id);
return Ok(new ApiResponse(entityList));
}
catch (UnauthorizedAccessException)
{
return Unauthorized();
}
}
...........
Change swagger configuration to this:
public class SwaggerConfig
{
public static void Register(HttpConfiguration config)
...
}
And configure it after all the other configurations:
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
...
SwaggerConfig.Register(config);
}
I removed the [AcceptVerbs("xxx")] on my methods and they appeared in my Swagger :-)

Asp.net web api oauth cors results in 400 bad request

Following is the code. When I make an separate request using postman to "/token" it works. But when I call from a client side code, it fails with 400 bad request. When I debug I can see that the "GrantResourceOwnerCredentials" method is not getting hit. Any idea?
Client Code
return this.$http({
url: this.config.remoteUri.account.login,
method: "POST",
data: { UserName: user.name, Password: user.password, grant_type: "password" },
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
}).success(function (data, status, headers, config) {
// $scope.persons = data; // assign $scope.persons here as promise is resolved here
}).error(function (data, status, headers, config) {
// $scope.status = status;
});
Startup.cs
public class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureOAuth(app);
HttpConfiguration config = new HttpConfiguration();
WebApiConfig.Register(config);
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
app.UseWebApi(config);
}
public void ConfigureOAuth(IAppBuilder app)
{
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(20),
Provider = new ActiveDirectoryAuthorizationProvider()
};
// Token Generation
app.UseOAuthAuthorizationServer(OAuthServerOptions);
}
}
Webapi Config
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
var jsonFormatter = config.Formatters.OfType<JsonMediaTypeFormatter>().First();
jsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
}
}
Authorization Filter
public class ActiveDirectoryAuthorizationProvider : OAuthAuthorizationServerProvider
{
public override Task MatchEndpoint(OAuthMatchEndpointContext context)
{
if (context.IsTokenEndpoint && context.Request.Method == "OPTIONS")
{
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Headers", new[] { "authorization" });
context.RequestCompleted();
return Task.FromResult(0);
}
return base.MatchEndpoint(context);
}
public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
context.Validated();
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
First things first: Origin header has to be set on your outgoing request (OPTIONS+POST)
I've seen this implementation of the filter in other threads but here is mine which is in fact working for me - local and production server:
public override Task MatchEndpoint(OAuthMatchEndpointContext context)
{
if (context.IsTokenEndpoint && context.Request.Method == "OPTIONS")
{
if (!context.OwinContext.Response.Headers.Keys.Contains("Access-Control-Allow-Origin"))
context.OwinContext.Response.Headers.AppendCommaSeparatedValues("Access-Control-Allow-Origin", new[] { ConfigurationManager.AppSettings["allowedOrigin"] });
if (!context.OwinContext.Response.Headers.Keys.Contains("Access-Control-Allow-Headers"))
context.OwinContext.Response.Headers.AppendCommaSeparatedValues("Access-Control-Allow-Headers", new[] { "Accept", "Content-Type", "Authorization", "Cache-Control", "Pragma", "Origin" });
if (!context.OwinContext.Response.Headers.Keys.Contains("Access-Control-Allow-Methods"))
context.OwinContext.Response.Headers.AppendCommaSeparatedValues("Access-Control-Allow-Methods", new[] { "GET", "POST", "PUT", "DELETE", "OPTIONS" });
context.MatchesTokenEndpoint();
context.RequestCompleted();
return Task.FromResult<object>(null);
}
return base.MatchEndpoint(context);
}
If you still have issues please respond with more details. Please note that AppendCommaSeparatedValues is required for this to work under IE and Edge

ASP.NET Identity is null even the token is sent

For my thesis Project i have to implement a token-based (Bearer) Authentication in my ASP.NET solution. I implemented it like Taiseer Jouseh (http://bitoftech.net/2014/06/01/token-based-authentication-asp-net-web-api-2-owin-asp-net-identity).
The basic part is working correctly. I have a Mobile Client on which i can register a new User. Then i can Login and receive the token. When i then make a request, the token is sent in the Request Header. It all works fine.
My problem is, that I get an 401 Unauthorized error if I call a [Authorize] Method, even if i send the token. So i removed the [Authorize] Annotation to test some things:
var z = User.Identity;
var t = Thread.CurrentPrincipal.Identity;
var y = HttpContext.Current.User.Identity;
var x = Request.GetOwinContext().Authentication.User.Identity;
Here i got alwas the same Identity: AuthenticationType=null; IsAuthenticated=false; Name=null; Claims:empty
var token = Request.Headers.Authorization;
Here i get the right token. So the token is sent by the request.
I hope you can help me. I have the token but no identity.
Here are parts of my code:
OAuthServiceProvider:
public class SimpleAuthorizationServerProvider : OAuthAuthorizationServerProvider
{
public async override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
context.Validated();
}
// POST /token
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
var userManager = DependencyResolver.Current.GetService<UserManager<IdentityUser, int>>();
IdentityUser user = await userManager.FindAsync(context.UserName, context.Password);
if (user == null)
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
return;
}
var identity = await userManager.CreateIdentityAsync(user, context.Options.AuthenticationType);
identity.AddClaim(new Claim("sub", context.UserName));
identity.AddClaim(new Claim("role", "user"));
context.Validated(identity);
}
}
The Controller Method:
#region GET /user/:id
[HttpGet]
[Route("{id:int:min(1)}")]
[ResponseType(typeof(UserEditDto))]
public async Task<IHttpActionResult> GetUser(int id)
{
try
{
// tests
var z = User.Identity;
var t = Thread.CurrentPrincipal.Identity;
var y = HttpContext.Current.User.Identity;
var x = Request.GetOwinContext().Authentication.User.Identity;
var token = Request.Headers.Authorization;
User user = await _userManager.FindByIdAsync(id);
if (user == null)
{
return NotFound();
}
Mapper.CreateMap<User, UserEditDto>();
return Ok(Mapper.Map<UserEditDto>(user));
}
catch (Exception exception)
{
throw;
}
}
#endregion
The WebApiConfig:
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.SuppressDefaultHostAuthentication();
config.Filters.Add(new HostAuthenticationFilter("Bearer"));
config.MapHttpAttributeRoutes();
var corsAttr = new EnableCorsAttribute("*", "*", "*");
config.EnableCors(corsAttr);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
Startup:
[assembly: OwinStartup(typeof(Startup))]
public class Startup
{
public void Configuration(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
var container = new UnityContainer();
UnityConfig.RegisterComponents(container);
config.DependencyResolver = new UnityDependencyResolver(container);
//config.DependencyResolver = new UnityHierarchicalDependencyResolver(container);
WebApiConfig.Register(config);
app.UseWebApi(config);
ConfigureOAuth(app);
}
public void ConfigureOAuth(IAppBuilder app)
{
OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
Provider = new SimpleAuthorizationServerProvider()
};
// Token Generation
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
app.UseOAuthAuthorizationServer(OAuthServerOptions);
}
}
Finally I found the problem. It is so simple, that I can't believe I spent more than a week to solve this problem.
The problem was in the startup. I simply had to call ConfigureOAuth(app); before app.UseWebApi(config);
So the correct Startup looks like
[assembly: OwinStartup(typeof(Startup))]
public class Startup
{
public void Configuration(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
var container = new UnityContainer();
UnityConfig.RegisterComponents(container);
config.DependencyResolver = new UnityDependencyResolver(container);
//config.DependencyResolver = new UnityHierarchicalDependencyResolver(container);
WebApiConfig.Register(config);
ConfigureOAuth(app);
app.UseWebApi(config);
}
public void ConfigureOAuth(IAppBuilder app)
{
OAuthAuthorizationServerOptions OAuthServerOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
Provider = new SimpleAuthorizationServerProvider()
};
// Token Generation
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
app.UseOAuthAuthorizationServer(OAuthServerOptions);
}
}
My guess is that the token is not being sent correctly from the client. Are you sending the Authorization token with word "Bearer" prefixed in front ?
the Authorization Header should be something like below -
Authorization:Bearer reNjoIUZBJHCMigQJHzCgMMVUyu4vg
Look in the chrome developer tools network tab to see what is being sent.

Categories