WebApiConfig.Configure() does not exist (Auth0/OWIN) - c#

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();
}
}

Related

Swagger "Authorization has been denied for this request" message

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");
});

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 :-)

FluentValidation Doesn't Work When Using WebApi [Route] Attribute

I successfully implemented FluentValidation in my WebApi project controller that only had one HttpGet method. When I added another HttpGet method, I added route attribute to both methods. i.e. [Route("Method1")] and [Route("Method2")].
Now the ModelState comes back as true regardless of whether I enter any data or not.
Here is my code.
WebApiConfig
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Filters.Add(new ValidateModelStateFilter());
//FluentValidation
FluentValidationModelValidatorProvider.Configure(config);
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "{action}/{id}",
defaults: new { controller = "Menu", id = RouteParameter.Optional}
);
}
}
ValidateModelStateFilter
public class ValidateModelStateFilter : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext actionContext)
{
if (!actionContext.ModelState.IsValid)
{
actionContext.Response = actionContext.Request.CreateErrorResponse(HttpStatusCode.BadRequest, actionContext.ModelState);
}
}
}
Controller
[HttpGet]
[Route("Method1")]
public IHttpActionResult ReadAllMenusByApplication([FromUri] ReadAllMenusByApplicationInput input)
{
var result = new List<ApplicationMenu>();
...
}
Input Object
using FluentValidation;
using FluentValidation.Attributes;
namespace MenuService.Models
{
[Validator(typeof(ReadAllMenusByApplicationInputValidator))]
public class ReadAllMenusByApplicationInput
{
public ReadAllMenusByApplicationInput() {
this.ApplicationName = string.Empty;
}
/// <summary>
/// The MenuSystem name of the application
/// </summary>
public string ApplicationName { get; set; }
}
public class ReadAllMenusByApplicationInputValidator : AbstractValidator<ReadAllMenusByApplicationInput>
{
public ReadAllMenusByApplicationInputValidator()
{
RuleFor(x => x.ApplicationName).NotEmpty();
}
}
}
Using this article for reference
Custom Validation in ASP.NET Web API with FluentValidation
You seem to have most of what is done in the referenced article.
Check your configuration order.
public static class WebApiConfig {
public static void Register(HttpConfiguration config) {
// Web API configuration and services
config.Filters.Add(new ValidateModelStateFilter());
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "{action}/{id}",
defaults: new { controller = "Menu", id = RouteParameter.Optional}
);
//FluentValidation
FluentValidationModelValidatorProvider.Configure(config);
}
}
FluentValidation automatically inserts its errors into the ModelState. You should include an error message.
public class ReadAllMenusByApplicationInputValidator : AbstractValidator<ReadAllMenusByApplicationInput> {
public ReadAllMenusByApplicationInputValidator() {
RuleFor(x => x.ApplicationName).NotEmpty()
.WithMessage("The Application Name cannot be blank.");
}
}
The article has some content that is outside of the scope of your question. mainly wrapping the responses but everything else should work for you.

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