Is having multiple authentication providers at the same time possible in ASP.NET MVC3 application? - asp.net-mvc-3

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

Related

Custom authentication asp.net core web api

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!

Login via Xamarin Forms Application

we have an mvc 5 application with individual user authentication we also have a xamarin forms application. we need to be able to use the same login details thats created on the web application when we log in via the xamarin application we are creating. we have successfully been able to create web api controllers using existing models in the web application and read/write the data in the xamarin application. but the problem is that we are not able to provide the same authentication we have(username and password with role assigned to the user) to the xamarin application. how can we make an api controller that reads from our existing database..please note our application is hosted on azure with a sql database.
basically we want to provide a login to our web application via the mobile app.
You need to take a look at Adrian Halls book - chapter 2 covers custom authentication which is what you need.
https://adrianhall.github.io/develop-mobile-apps-with-csharp-and-azure/chapter2/custom/
The key points are setting the mobile app to use authentication in the Azure portal but don't set any of the authentication providers (this makes it custom)
You then need to implement your own custom authentication controller to handle the authentication call back like this example taken from Adrian's book;
using System;
using System.IdentityModel.Tokens;
using System.Linq;
using System.Security.Claims;
using System.Web.Http;
using Microsoft.Azure.Mobile.Server.Login;
using Newtonsoft.Json;
namespace AWPBackend.Controllers
{
[Route(".auth/login/custom")]
public class CustomAuthController : ApiController
{
private MobileServiceContext db;
private string signingKey, audience, issuer;
public CustomAuthController()
{
db = new MobileServiceContext();
signingKey = Environment.GetEnvironmentVariable("WEBSITE_AUTH_SIGNING_KEY");
var website = Environment.GetEnvironmentVariable("WEBSITE_HOSTNAME");
audience = $"https://{website}/";
issuer = $"https://{website}/";
}
[HttpPost]
public IHttpActionResult Post([FromBody] User body)
{
if (body == null || body.Username == null || body.Password == null ||
body.Username.Length == 0 || body.Password.Length == 0)
{
return BadRequest(); ;
}
if (!IsValidUser(body))
{
return Unauthorized();
}
var claims = new Claim[]
{
new Claim(JwtRegisteredClaimNames.Sub, body.Username)
};
JwtSecurityToken token = AppServiceLoginHandler.CreateToken(
claims, signingKey, audience, issuer, TimeSpan.FromDays(30));
return Ok(new LoginResult()
{
AuthenticationToken = token.RawData,
User = new LoginResultUser { UserId = body.Username }
});
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
db.Dispose();
}
base.Dispose(disposing);
}
private bool IsValidUser(User user)
{
return db.Users.Count(u => u.Username.Equals(user.Username) && u.Password.Equals(user.Password)) > 0;
}
}
public class LoginResult
{
[JsonProperty(PropertyName = "authenticationToken")]
public string AuthenticationToken { get; set; }
[JsonProperty(PropertyName = "user")]
public LoginResultUser User { get; set; }
}
public class LoginResultUser
{
[JsonProperty(PropertyName = "userId")]
public string UserId { get; set; }
}
The actual custom authentication takes place in the IsValidUser function and should link to your existing internal authentication method (do not use the example here, this is for demonstration only)
Custom authentication has to use a client side flow which also meets your requirements.

Custom route constraint causes intermittent 404 errors

I have an Asp.Net Core 1 RC1 application that uses a custom route constraint to control access to the application. The application (hosted on a server running IIS 7.5) is getting intermittent 404 errors which I suspect is caused by this routing constraint. Here you can see a screenshot that shows the intermittent 404 errors:
I suspect that this issue is related to the code that defines the route constraint not being thread-safe. The custom route constraint needs a DbContext because it needs to check in the database if the application is enabled for the brand specified in the route, and I suspect that this DbContext instance could be causing the issue. Here is how the routing is defined in the application:
// Add MVC to the request pipeline.
var appDbContext = app.ApplicationServices.GetRequiredService<AppDbContext>();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "branding",
template: "branding/{brand}/{controller}/{action}/{id?}",
defaults: new { controller="Home", action="Index" },
constraints: new { brand = new BrandingRouteConstraint(appDbContext) });
});
And here is the custom route constraint:
// Custom route constraint
public class BrandingRouteConstraint : IRouteConstraint
{
AppDbContext _appDbContext;
public BrandingRouteConstraint(AppDbContext appDbContext) : base() {
_appDbContext = appDbContext;
}
public bool Match(HttpContext httpContext, IRouter route, string routeKey, IDictionary<string, object> values, RouteDirection routeDirection)
{
if (values.Keys.Contains(routeKey))
{
var whiteLabel = _appDbContext.WhiteLabels.Where(w => w.Url == values[routeKey].ToString()).FirstOrDefault();
if (whiteLabel != null && whiteLabel.EnableApplication != null && (bool)whiteLabel.EnableApplication)
{
return true;
}
}
return false;
}
}
Can anyone confirm that this issue is caused by the code not being thread-safe and recommend a way to change the implementation so that it is thread-safe?
I can't comment on RouteContraint's, haven't used them much, but have you tried Resource Based Authorization instead? Looks like it might be more suited to what you're trying to achieve?
From here and here:
Request authentication service inside your controller
public class DocumentController : Controller
{
IAuthorizationService authorizationService;
public DocumentController(IAuthorizationService authorizationService)
{
this.authorizationService = authorizationService;
}
}
Apply authorization checks in your Action:
public async Task<IActionResult> Edit(Guid documentId)
{
Document document = documentRepository.Find(documentId);
if (document == null)
{
return new HttpNotFoundResult();
}
if (await authorizationService.AuthorizeAsync(User, document, Operations.Edit))
{
return View(document);
}
else
{
return new HttpUnauthorizedResult();
}
}
I've used the OperationAuthorizationRequirement class in the sample, so define this class in your project:
public static class Operations
{
public static OperationAuthorizationRequirement Create =
new OperationAuthorizationRequirement { Name = "Create" };
public static OperationAuthorizationRequirement Read =
new OperationAuthorizationRequirement { Name = "Read" };
public static OperationAuthorizationRequirement Update =
new OperationAuthorizationRequirement { Name = "Update" };
public static OperationAuthorizationRequirement Delete =
new OperationAuthorizationRequirement { Name = "Delete" };
}
Implement the authorization handler (using built in OperationAuthorizationRequirement requirement):
public class DocumentAuthorizationHandler : AuthorizationHandler<OperationAuthorizationRequirement, Document>
{
protected override void Handle(AuthorizationContext context,
OperationAuthorizationRequirement requirement,
Document resource)
{
// Validate the requirement against the resource and identity.
// Sample just checks "Name"field, put your real logic here :)
if (resource.Name == "Doc1")
context.Succeed(requirement);
else
context.Fail();
}
}
And not forgetting ConfigureServices:
services.AddInstance<IAuthorizationHandler>(
new DocumentAuthorizationHandler());
It's a bit more work, but adds quite a lot of flexibility.

SignalR in SharePoint

My project has a need for realtime user interaction and I think SignalR will solve my need. I'm technically on a SharePoint 2007 project, although I'm exclusively in application pages and thus barely use SharePoint at all. Regardless, I'm stuck in a 2.0 framework app pool in IIS.
My first approach was to try to create a 4.0 application as a sub-site. Unfortunately, that failed miserably. That approach works in a non-SharePoint world, but it appears that SharePoint has hijacked too much of the request pipeline for this approach to work for me.
So now I'm going down the path of creating a separate IIS Site that's 4.0 and using IIS rewrite rules to fake my app into thinking a particular subdirectory (/realtime/) is local and not a separate site so that I don't have to deal with cross domain request issues. The problem is I can't get IIS rewrite rules to rewrite to another http host (e.g. http://www.mySharepoint.com/_layouts/MySite/realtime/Hello.aspx to http://realtime.mySharePoint.com/Hello.aspx).
Any help with approach #1 or approach #2 or any alternative ideas would be greatly appreciated.
Here is what I did... Web App with signalR .net4.0, then your SharePoint Web App .net 2.
Add this to the global.asax in your Signalr project
RouteTable.Routes.MapHttpHandlerRoute("spproxy","spproxy/{*operation}", new SharePointRProxyHandler());
If you want to raise an event from SharePoint you can do a http POST to this new route URL for example
http://localhost:38262/spproxy
It will pass any posted data onto the httphandler below, that will then broadcast it to your clients.
Here is the code for MapHttpHandlerRoute
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace System.Web.Routing
{
public class HttpHandlerRoute : IRouteHandler
{
private String _virtualPath = null;
private IHttpHandler _handler = null;
public HttpHandlerRoute(String virtualPath)
{
_virtualPath = virtualPath;
}
public HttpHandlerRoute(IHttpHandler handler)
{
_handler = handler;
}
public IHttpHandler GetHttpHandler(RequestContext requestContext)
{
IHttpHandler result;
if (_handler == null)
{
result = (IHttpHandler)System.Web.Compilation.BuildManager.CreateInstanceFromVirtualPath(_virtualPath, typeof(IHttpHandler));
}
else
{
result = _handler;
}
return result;
}
}
public static class RoutingExtensions
{
public static void MapHttpHandlerRoute(this RouteCollection routes, string routeName, string routeUrl, string physicalFile, RouteValueDictionary defaults = null, RouteValueDictionary constraints = null)
{
var route = new Route(routeUrl, defaults, constraints, new HttpHandlerRoute(physicalFile));
RouteTable.Routes.Add(routeName, route);
}
public static void MapHttpHandlerRoute(this RouteCollection routes, string routeName, string routeUrl, IHttpHandler handler, RouteValueDictionary defaults = null, RouteValueDictionary constraints = null)
{
var route = new Route(routeUrl, defaults, constraints, new HttpHandlerRoute(handler));
RouteTable.Routes.Add(routeName, route);
}
}
}
Or you could just post directly to a httphandler and get the handler to do a connection.Broadcast
namespace SharePointRProxy
{
/// <summary>
/// Summary description for SharePointRProxyHandler
/// </summary>
public class SharePointRProxyHandler : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
context.Response.ContentType = "text/plain";
IConnectionManager connectonManager = AspNetHost.DependencyResolver.Resolve<IConnectionManager>();
IConnection connection = connectonManager.GetConnection<MyConnection>();
object payload = null; //Add payload here 'context.Request.Params["data"] ?'
JavaScriptSerializer jss = new JavaScriptSerializer();
var payloadJSON = jss.Serialize(payload);
connection.Broadcast(payloadJSON);
}
public bool IsReusable
{
get
{
return false;
}
}
}
}
You could also use either an event handler calling a .net 4.0 web service or an http handler to grab requests from SharePoint and pass them over to a .net 4.0 application running your signalr code.
You can see an example of using an http handler here: http://spmatt.wordpress.com/2012/04/12/harnessing-signalr-in-sharepoint/

MVC3: How can I route non-SSL requests automatically to the SSL protocol?

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.

Resources