We are planning on using ASP.Net MVC3 to create a JSON API. For handling security we will have something like an API key, or possibly the Username/Password and maybe a timestamp.
I haven't done any MVC before but I'm wondering if there isn't some simple way to add code to Global.asax that ensures that ALL requests have these variables in them somehow. That way, no request could even get through unless it included the API key.
That way we don't have to add API key handling to each section of the site.
Create a global authorization filter -
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = true)]
public class MyAuthorizationFilterAttribute : FilterAttribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationContext filterContext)
{
// do Authorization
}
}
then register it in Global.asax -
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new MyAuthorizationFilterAttribute());
}
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
}
Although you could create a bespoke Authentication module -
public class CustomAuthentication : IHttpModule
{
public void Init(HttpApplication application)
{
application.AuthenticateRequest += new EventHandler(this.Authenticate);
}
public void Authenticate(object source, EventArgs eventArgs)
{
HttpApplication _application = (HttpApplication)source;
HttpContext _context = _application.Context;
// do authentication
// if authenticated set pricipal
// _context.User = new GenericPrincipal(new GenericIdentity("user"), new string[]);
}
public void Dispose() { }
}
Then you just need to register the module in web.config
<modules runAllManagedModulesForAllRequests="true">
<add name="CustomAuthentication" type="AuthenticationNamespace.CustomAuthentication"/>
</modules>
and set asp.net authentication to none -
<authentication mode="None">
</authentication>
Then you can check to see it the user is authenticated in your AuthorizationFilter.
if(HttpContext.Current.Request.User.Identity.IsAuthenticated)
Related
I want to use a secret key (api key) authorization asp.net core web api. The key will be passed in Authorization header like given below,
ex. Authorization keytype;h43484344343bbhfdjfdfhj34343
I want to write a middleware to read this key from request headers and call an internal api to validate the key.
In web api we can write a message handler to do this, but I am new to asp.net core. I'm seeing a lot of samples but they are using inbuilt JWT token authentication. But I wanted to use my own key and I decrypt this key and validate against a database entry.
Can anyone suggest some code samples on how to do this?
I have used this approach in a solution using asp core 1.1. First define a custom scheme:
public static class Authentication
{
public const string Scheme = "Custom";
}
You then have to inherit AuthenticationHandler<TOptions>. Here is where the logic for validating the header value will go:
public class MyAuthenticationHandler : AuthenticationHandler<MyOptions>
{
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
{
var authorizationHeader = Context.Request.Headers["Authorization"];
if (!authorizationHeader.Any())
return Task.FromResult(AuthenticateResult.Skip());
var value = authorizationHeader.ToString();
if (string.IsNullOrWhiteSpace(value))
return Task.FromResult(AuthenticateResult.Skip());
// place logic here to validate the header value (decrypt, call db etc)
var claims = new[]
{
new Claim(System.Security.Claims.ClaimTypes.Name, "Bob")
};
// create a new claims identity and return an AuthenticationTicket
// with the correct scheme
var claimsIdentity = new ClaimsIdentity(claims, Authentication.Scheme);
var ticket = new AuthenticationTicket(new ClaimsPrincipal(claimsIdentity), new AuthenticationProperties(), Authentication.Scheme);
return Task.FromResult(AuthenticateResult.Success(ticket));
}
}
In order to inherit AuthenticationHandler you must create an options class where you set the AuthenticationScheme-property to the scheme you are using:
public class MyOptions : AuthenticationOptions
{
AuthenticationScheme = Authentication.Scheme;
}
After this you have to inherit AuthenticationMiddleware<TOptions>. This will create the handler you implemented in the previous step:
public class MyAuthenticationMiddleware : AuthenticationMiddleware<MyOptions>
{
public MyAuthenticationMiddleware(RequestDelegate next, IOptions<MyOptions> options, ILoggerFactory loggerFactory, UrlEncoder encoder) : base(next, options, loggerFactory, encoder)
{
}
protected override AuthenticationHandler<MyOptions> CreateHandler()
{
return new MyAuthenticationHandler();
}
}
In order to easily plug in your middleware you can define these extension methods:
public static IApplicationBuilder UseMyAuthentication(this IApplicationBuilder app, IConfigurationSection config)
{
return app.UseMyAuthentication(options => {});
}
private static IApplicationBuilder UseMyAuthentication(this IApplicationBuilder app, Action<MyOptions> configure)
{
var options = new MyOptions();
configure?.Invoke(options);
return app.UseMiddleware<MyAuthenticationMiddleware>(new OptionsWrapper<MyOptions>(options));
}
Then in your Startup class you can finally add your middleware:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseMyAuthentication(Configuration.GetSection("MyAuthenticationOptions"));
// other stuff
app.UseMvc();
}
Then add the AuthorizeAttribute on your actions specifying the scheme you just created:
[Authorize(ActiveAuthenticationSchemes = Authentication.Scheme)]
public IActionResult Get()
{
// stuff ...
}
There are a lot of steps but hopefully this will get you going!
Currently I have a simple custom policy handler that looks like so:
protected override void Handle(AuthorizationContext context, UserPolicyRequirement requirement)
{
// authorize user against policy requirements
if (_authorizationTask.AuthorizeUserAgainstPolicy(context.User, requirement))
{
// User passed policy req's
context.Succeed(requirement);
}
}
Problem is, this authorization step takes a long time to execute, but this is required in many different areas of the website. Is there any readily available mechanisms to save/cache the results of this policy authorization so that I only need to do this once per session?
I am currently using Windows Authentication, if that helps.
If per session way does not cause any problem, you can use Session to store user data. Simple implementation is something like below:
First you need a service to get user data from any store
public interface IGetUserDataService
{
<type> GetUserData();
}
I assume that there is Session configuration(see) and IGetUserDataService implementation.
Then you need to create a middleware to handle Session
public class SessionMiddleware
{
private readonly RequestDelegate _next;
private readonly IGetUserDataService _getUserDataService;
public SessionMiddleware(RequestDelegate next, IGetUserDataService getUserDataService)
{
_next = next;
_getUserDataService = getUserDataService;
}
public async Task Invoke(HttpContext context)
{
//user data is obtained only once then is stored in Session
if (context.Session.Get("UserData") == null)
{
context.Session.Set("UserData", getUserDataService.GetData());
}
await _next.Invoke(context);
}
}
//In Startup.cs
app.UseMiddleware<SessionMiddleware>();
Finally get and use session data in handler
public class YourHandler : AuthorizationHandler<YourRequirement>
{
private readonly IHttpContextAccessor _accessor;
public YourHandler(IHttpContextAccessor accessor)
{
_accessor = accessor;
}
protected override void Handle(AuthorizationContext context, PermissionRequirement requirement)
{
var userData =(<type>)_accessor.HttpContext.Session.Get("UserData");
// check
}
}
I want to access RouteTemplate inside custom action filter in my Web Api Project.
I have registered a custom action filter to be executed before any action, as below.
public class AuthorizationFilter : ActionFilterAttribute
{
public override void OnActionExecuting(HttpActionContext filterContext)
{
}
}
If you can see in below image I can access route template from Quick Watch inside filter. But if I write same code, it throws an error 'System.Web.Http.WebHost.Routing.HttpWebRoute' is inaccessible due to its protection level
Is there any other way to access route template
Property Using : (((System.Web.Http.WebHost.Routing.HttpWebRoute)(HttpContext.Current.Request.RequestContext.RouteData.Route)).HttpRoute).RouteTemplate
This is how it will work.
public override void OnActionExecuting(HttpActionContext filterContext)
{
if (Settings.GetKeyValue<string>("EnableAuthorization") == "Enabled")
{
var routeTemplate = filterContext.Request.GetRouteData().Route.RouteTemplate;
}
}
A client application is uploading an audio file in "chunks" to an MVC3 site. A client uses HttpWebRequest POST to do it.
On the server, I have the following controller action:
[Authorize]
[HttpPost]
public JsonResult RecieveChunk(string id, [ModelBinder(typeof(AudioChunkModelBinder))] byte[] audio)
{
//Process chunk
var chunk = new AudioChunk
{
ThoughtId = Guid.Parse(id),
Data = audio
};
//Process chunk by BL
return new JsonResult {Data = "Success"};
}
Currently, a built-in AspNetMemebershipProvider is handling the authorization, so the client app has to first authenticate at the logon page, obtain cookie into a CookieContainer and then make a call to a server to upload a chunk of data.
I want to allow clients to also be able to anonymously upload audio files to the server, without a need to previously register. They the client app code will provide the same guid each time the file is uploaded from the same device.
I want both categories of users to share the same RecieveChunk action to do it. But they must be authrized either anonymously (with just guid), or with the logon/pass combination.
Can I have two different controllers linked to two different authentication providers? The third controller, that has [Authorize] marked action, will allow action if either one provider has given a user a cookie (or some other auth method).
Is it possible in general in ASP.NET MVC3?
As discussed in comments you can create a custom implementation of the FilterAttribute class and implement the IAuthorizationFilter interface. For example here is the ChildActionOnlyAttribute implementation:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public sealed class ChildActionOnlyAttribute : FilterAttribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationContext filterContext)
{
if (filterContext == null)
throw new ArgumentNullException("filterContext");
if (!filterContext.IsChildAction)
throw Error.ChildActionOnlyAttribute_MustBeInChildRequest(filterContext.ActionDescriptor);
}
}
And here is the RequireHttpsAttribute implementation:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class RequireHttpsAttribute : FilterAttribute, IAuthorizationFilter
{
public virtual void OnAuthorization(AuthorizationContext filterContext)
{
if (filterContext == null)
throw new ArgumentNullException("filterContext");
if (filterContext.HttpContext.Request.IsSecureConnection)
return;
this.HandleNonHttpsRequest(filterContext);
}
protected virtual void HandleNonHttpsRequest(AuthorizationContext filterContext)
{
if (!string.Equals(filterContext.HttpContext.Request.HttpMethod, "GET", StringComparison.OrdinalIgnoreCase))
throw new InvalidOperationException(MvcResources.RequireHttpsAttribute_MustUseSsl);
string url = "https://" + filterContext.HttpContext.Request.Url.Host + filterContext.HttpContext.Request.RawUrl;
filterContext.Result = (ActionResult) new RedirectResult(url);
}
}
So you could do something like:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public sealed class CustomAuthorizeAttribute : FilterAttribute, IAuthorizationFilter
{
public void OnAuthorization(AuthorizationContext filterContext)
{
if (filterContext == null)
throw new ArgumentNullException("filterContext");
var guidPresent = CheckForGuid();
if (!filterContext.HttpContext.User.Identity.IsAuthenticated && !guidPresent)
throw new InvalidOperationException("Must authenticate properly")
}
}
I have a site that needs to be secured with SSL. How can I set up a route or IIS configuration that will automatically send any request received on the non-SSL protocol over to the SSL protocol? Is this something that can be handled as a routing rule, or would it be best to use the RequireHttps attribute in the primary controller only on the methods rather than on the controller itself and detect the protocol in the Index() method?
As a note: I read this question that makes use of UrlRewriting and IIS7's Application Request Routing, but I am stuck with IIS6 and UrlRewriting is not an option for me.
Something like this will help:
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new HandleErrorAttribute());
filters.Add(new RequireHttpsAttribute());
}
This is what we use. Would love to hear if it can be improved.
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = false)]
public class EnforceHttpsAttribute : RequireHttpsAttribute
{
private static bool AuthorizeCore(HttpContextBase httpContext)
{
return httpContext.Request.IsSecureConnection;
}
public override void OnAuthorization(AuthorizationContext filterContext)
{
if (!AuthorizeCore(filterContext.HttpContext))
{
HandleNonHttpsRequest(filterContext);
}
else
{
var cache = filterContext.HttpContext.Response.Cache;
cache.SetProxyMaxAge(new TimeSpan(0L));
cache.AddValidationCallback(CacheValidateHandler, null);
}
}
// ReSharper disable RedundantAssignment
private static void CacheValidateHandler(HttpContext context, object data, ref HttpValidationStatus validationStatus)
// ReSharper restore RedundantAssignment
{
validationStatus = OnCacheAuthorization(new HttpContextWrapper(context));
}
private static HttpValidationStatus OnCacheAuthorization(HttpContextBase httpContext)
{
return !httpContext.Request.IsSecureConnection
? HttpValidationStatus.IgnoreThisRequest
: HttpValidationStatus.Valid;
}
}
Response to comment 1
Good question, I'm not sure. HandleNonHttpsRequest comes from the base RequireHttpsAttribute. I just did an inspection in fiddler2, and there was only 1 request sent over http. However the response came back over https.
I just realized we use the above to make RequireHttps work with the output cache. You might be better off just using the RequireHttps attribute.