I'm integrating our asp.net MVC application with SAML2 Authentication. And using Kentor.AuthServices as module as described at kentor.AuthServices Configuration
Everithing works fine. But next step is to add usage of second service provider (which configured to use another auth mechanisms on server side) only for specified range of pages.
First, how to configure it via web.config to add second SP (not the second IdP in scope of first SP) with different entityId.
And Second, how to switch programmatically to second SP? I assume that it should happend in global.asax file in method Application_BeginRequest, but how?
Using two different SP instances in the same application is a quite rare scenario. But if you are really sure you need it, it can be achieved.
You will have to use the Kentor.AuthServices.Owin package and do the configuration in code - web.config won't do. Register two instances of the middleware. Each one will have their own configuration, including their own SP EntityID. Also make sure to change the ModulePath of at least one of them so that they get different endpoint addresses.
To challenge an authentication from either one, set the right authentication scheme in the challenge (typically in a ChallengeResult returned from a controller)
Self-answering.
Here is a workaround for multiple SP for MVC or HttpModule package, switching is based on specified range of URLs. In my case different SP realize different amount of security factors.
First, implementing custom IOptions and CookieHandler, with ability to switch to correct instance. In the web.config file, two kentor.authServices sections must be defined. In my case only "entityId" attribute differs.
public class CustomOptions : IOptions
{
private IOptions options1Factor;
private IOptions options2Factor;
private Func<bool> _checkIsSecure;
public CustomOptions(Func<bool> checkIsSecure)
{
_checkIsSecure = checkIsSecure;
AddOption(out options2Factor, "kentor.authServices1");
AddOption(out options1Factor, "kentor.authServices");
}
private void AddOption(out IOptions options, string sectionName)
{
var sp = new SPOptions((KentorAuthServicesSection)ConfigurationManager.GetSection(sectionName));
options = new Options(sp);
KentorAuthServicesSection.Current.IdentityProviders.RegisterIdentityProviders(options);
KentorAuthServicesSection.Current.Federations.RegisterFederations(options);
}
public SPOptions SPOptions
{
get
{
if (_checkIsSecure())
return options2Factor.SPOptions;
return options1Factor.SPOptions;
}
}
public IdentityProviderDictionary IdentityProviders
{
get
{
if (_checkIsSecure())
return options2Factor.IdentityProviders;
return options1Factor.IdentityProviders;
}
}
public KentorAuthServicesNotifications Notifications
{
get
{
if (_checkIsSecure())
return options2Factor.Notifications;
return options1Factor.Notifications;
}
}
}
public class CustomCookieHandler : CookieHandler
{
private Func<bool> _checkIsSecure;
private CookieHandler _originalCookieHandler1Factor;
private CookieHandler _originalCookieHandler2Factor;
public CustomCookieHandler(Func<bool> checkIsSecure)
{
_checkIsSecure = checkIsSecure;
_originalCookieHandler1Factor = new ChunkedCookieHandler()
{
Name = "commonAuth",
RequireSsl = false
};
_originalCookieHandler2Factor = new ChunkedCookieHandler()
{
Name = "securedAuth",
RequireSsl = false
};
}
public override string MatchCookiePath(Uri baseUri, Uri targetUri)
{
if (_checkIsSecure())
return _originalCookieHandler2Factor.MatchCookiePath(baseUri, targetUri);
return _originalCookieHandler1Factor.MatchCookiePath(baseUri, targetUri);
}
protected override void DeleteCore(string name, string path, string domain, HttpContext context)
{
if (_checkIsSecure())
_originalCookieHandler2Factor.Delete();
else
_originalCookieHandler1Factor.Delete();
}
protected override byte[] ReadCore(string name, HttpContext context)
{
if (_checkIsSecure())
return _originalCookieHandler2Factor.Read();
return _originalCookieHandler1Factor.Read();
}
protected override void WriteCore(byte[] value, string name, string path, string domain, DateTime expirationTime, bool secure, bool httpOnly, HttpContext context)
{
if (_checkIsSecure())
_originalCookieHandler2Factor.Write(value, true, expirationTime);
else
_originalCookieHandler1Factor.Write(value, true, expirationTime);
}
}
In Global.asax file setting static properties to custom implementations. No more modifications needed.
protected void Application_Start()
{
FederatedAuthentication.FederationConfiguration.CookieHandler = new CustomCookieHandler(CheckIsSecure);
Kentor.AuthServices.Mvc.AuthServicesController.Options = new CustomOptions(CheckIsSecure);
}
private bool CheckIsSecure()
{
if (HttpContext.Current == null)
return false;
var mainHost = "http://host.local"; // host url
var sp = new [] { "/Home/Secure" }; // array of URLs which must be secured with other SP
var request = HttpContext.Current.Request;
var isSecured = sp.Any(x => x.Equals(request.Path, StringComparison.InvariantCultureIgnoreCase));
if (!isSecured && request.Path.Equals("/AuthServices/SignIn", StringComparison.InvariantCultureIgnoreCase))
{
var returnUrl = request.QueryString["ReturnUrl"];
isSecured = !string.IsNullOrEmpty(returnUrl) &&
sp.Any(x => x.Equals(returnUrl, StringComparison.InvariantCultureIgnoreCase));
}
if (!isSecured && request.Path.Equals("/AuthServices/Acs", StringComparison.InvariantCultureIgnoreCase))
{
var _r = new HttpRequestWrapper(request).ToHttpRequestData();
isSecured = _r != null && _r.StoredRequestState != null && _r.StoredRequestState.ReturnUrl != null
&& sp.Any(x => x.Equals(_r.StoredRequestState.ReturnUrl.ToString(),
StringComparison.InvariantCultureIgnoreCase));
}
if (!isSecured && !string.IsNullOrEmpty(request.Headers["Referer"]))
{
var referer = request.Headers["Referer"];
isSecured = sp
.Select(x => string.Format("{0}/{1}", mainHost.TrimEnd('/'), x.TrimStart('/')))
.Any(x => x.Equals(referer, StringComparison.InvariantCultureIgnoreCase));
}
return isSecured;
}
Related
I have multi tenant application where each tenant can use different IdP to authenticate. Below code correctly redirects to IdP but problem is to get back the response to ACS endpoint.
Key is the Configuration method which configures the paths and their authentication:
[assembly: OwinStartup(typeof(SSOSamlDemoASPNET.App_Start.Startup))]
namespace SSOSamlDemoASPNET.App_Start
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.Map("/client/okta", (appx) =>
{
ConfigureAuthentication(appx, "/client/okta/Saml2", ...);
});
app.Map("/client/azuread", (appx) =>
{
ConfigureAuthentication(appx, "/client/azuread/Saml2", ...);
});
}
private static void ConfigureAuthentication(IAppBuilder app, string modulePath, string audience, string issuer, string metadataUrl)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = CookieAuthenticationDefaults.AuthenticationType,
CookieName = "LoggedUser",
CookiePath = "/",
CookieManager = new SystemWebCookieManager(),
});
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
ConfigureSaml(app, modulePath, audience, issuer, metadataUrl);
}
private static void ConfigureSaml(IAppBuilder app, string modulePath, string audience, string issuer, string metadataUrl)
{
var saml2options = new Saml2AuthenticationOptions(false);
var spOptions = new SPOptions
{
EntityId = new EntityId(audience),
ModulePath = modulePath,
PublicOrigin = new Uri("https://localhost:44340/"),
};
spOptions.Logger = new ConsoleLoggerAdapter();
saml2options.SPOptions = spOptions;
saml2options.IdentityProviders.Add(new IdentityProvider(new EntityId(issuer), spOptions)
{
AllowUnsolicitedAuthnResponse = true,
MetadataLocation = metadataUrl,
LoadMetadata = true,
Binding = Saml2BindingType.HttpPost,
});
app.UseSaml2Authentication(saml2options);
}
}
}
Authenticating against individual IdP is done like this:
authProperties.Dictionary["idp"] = "https://sts.windows.net/xxx/";
authProperties.RedirectUri = "https://localhost:44340/client/azuread/ExternalLoginCallback";
HttpContext.Current.Request.GetOwinContext().Authentication.Challenge(authProperties, "Saml2");
When inspecting code of the Sustainsys.Saml2 library (especially Saml2AuthenticationHandler). I found the conditions do not take into account OwinRequest.PathBase and therefore the identity is not coming back to the application.
An example can be (Saml2AuthenticationHandler.Invoke method).
Options.SPOptions.ModulePath = /client/azuread/Saml2
Request.Path = /Saml2/Acs
==> therefore the code inside the condition is not executed.
public override async Task<bool> InvokeAsync()
{
var Saml2Path = new PathString(Options.SPOptions.ModulePath);
if (Request.Path.StartsWithSegments(Saml2Path, out PathString remainingPath))
{
if (remainingPath == new PathString("/" + CommandFactory.AcsCommandName))
{
var ticket = (MultipleIdentityAuthenticationTicket)await AuthenticateAsync();
if (ticket.Identities.Any())
{
Context.Authentication.SignIn(ticket.Properties, ticket.Identities.ToArray());
// No need to redirect here. Command result is applied in AuthenticateCoreAsync.
}
else
{
Response.Redirect(ticket.Properties.RedirectUri);
}
return true;
}
Is there any way to change this behavioral? e.g. saml2Options.Notifications to get this working?
That is obviously a bug/lack of feature, but nothing that will be fixed on the Owin module - it's on life support.
The solution for a multi tenancy owin app is to register one Saml2 middleware and add multiple IdentityProviders to that one. The middleware will handle all responses on the same endpoint and use the configuration from the right IdentityProvider based on where the response came from.
I have an ASP.Net Core API project. I want to be able to write a custom routing logic to be able to choose different controller actions based on HTTP Body parameters. To illustrate my problem, this is my Controller class:
[ApiController]
[Route("api/[controller]")]
public class TestController
{
// should be called when HTTP Body contains json: '{ method: "SetValue1" }'
public void SetValue1()
{
// some logic
}
// should be called when HTTP Body contains json: '{ method: "SetValue2" }'
public void SetValue2()
{
// some logic
}
}
As you can see from my comments I want to choose different action methods based on HTTP body.
Here is my Startup class:
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
// I assume instead of this built in routing middleware,
// I will have to implement custom middleware to choose the correct endpoints, from HTTP body,
// any ideas how I can go about this?
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
One of the option I could use is having one entry Action method that will call differnt methods based on the HTTP Body content, but I would like to avoid that and encapsulate this logic somewhere in a custom routing.
In the old APS.Net Web API there was a handy class ApiControllerActionSelector that I could extend, and define my custom logic of selecting Action methods, however this is not supported in new ASP.Net Core. I think I will have to implement my own version of app.UseRouting middleware. Any ideas on how I can do it?
In the old asp.net core (before 3.0), we can implement a custom IActionSelector and it's especially convenient when the ActionSelector is still made public. But with the new endpoint routing, it's changed to the so-called EndpointSelector. The implementation is fairly the same, the point is how we extract the ActionDescriptor which is put in the Endpoint as metadata. The following implementation requires a default EndpointSelector (which is DefaultEndpointSelector) but that's unfortunately made internal. So we need to use a trick to get an instance of that default implementation to use in our custom implementation.
public class RequestBodyEndpointSelector : EndpointSelector
{
readonly IEnumerable<Endpoint> _controllerEndPoints;
readonly EndpointSelector _defaultSelector;
public RequestBodyEndpointSelector(EndpointSelector defaultSelector, EndpointDataSource endpointDataSource)
{
_defaultSelector = defaultSelector;
_controllerEndPoints = endpointDataSource.Endpoints
.Where(e => e.Metadata.GetMetadata<ControllerActionDescriptor>() != null).ToList();
}
public override async Task SelectAsync(HttpContext httpContext, CandidateSet candidates)
{
var request = httpContext.Request;
request.EnableBuffering();
//don't use "using" here, otherwise the request.Body will be disposed and cannot be used later in the pipeline (an exception will be thrown).
var sr = new StreamReader(request.Body);
try
{
var body = sr.ReadToEnd();
if (!string.IsNullOrEmpty(body))
{
try
{
var actionInfo = Newtonsoft.Json.JsonConvert.DeserializeObject<ActionInfo>(body);
var controllerActions = new HashSet<(MethodInfo method, Endpoint endpoint, RouteValueDictionary routeValues, int score)>();
var constrainedControllerTypes = new HashSet<Type>();
var routeValues = new List<RouteValueDictionary>();
var validIndices = new HashSet<int>();
for (var i = 0; i < candidates.Count; i++)
{
var candidate = candidates[i];
var endpoint = candidates[i].Endpoint;
var actionDescriptor = endpoint.Metadata.GetMetadata<ControllerActionDescriptor>();
if (actionDescriptor == null) continue;
routeValues.Add(candidate.Values);
constrainedControllerTypes.Add(actionDescriptor.MethodInfo.DeclaringType);
if (!string.Equals(actionInfo.MethodName, actionDescriptor.MethodInfo.Name,
StringComparison.OrdinalIgnoreCase)) continue;
if (!controllerActions.Add((actionDescriptor.MethodInfo, endpoint, candidate.Values, candidate.Score))) continue;
validIndices.Add(i);
}
if (controllerActions.Count == 0)
{
var bestCandidates = _controllerEndPoints.Where(e => string.Equals(actionInfo.MethodName,
e.Metadata.GetMetadata<ControllerActionDescriptor>().MethodInfo.Name,
StringComparison.OrdinalIgnoreCase)).ToArray();
var routeValuesArray = request.RouteValues == null ? routeValues.ToArray() : new[] { request.RouteValues };
candidates = new CandidateSet(bestCandidates, routeValuesArray, new[] { 0 });
}
else
{
for(var i = 0; i < candidates.Count; i++)
{
candidates.SetValidity(i, validIndices.Contains(i));
}
}
//call the default selector after narrowing down the candidates
await _defaultSelector.SelectAsync(httpContext, candidates);
//if some endpoint found
var selectedEndpoint = httpContext.GetEndpoint();
if (selectedEndpoint != null)
{
//update the action in the RouteData to found endpoint
request.RouteValues["action"] = selectedEndpoint.Metadata.GetMetadata<ControllerActionDescriptor>().ActionName;
}
return;
}
catch { }
}
}
finally
{
request.Body.Position = 0;
}
await _defaultSelector.SelectAsync(httpContext, candidates);
}
}
The registration code is a bit tricky like this:
//define an extension method for registering conveniently
public static class EndpointSelectorServiceCollectionExtensions
{
public static IServiceCollection AddRequestBodyEndpointSelector(this IServiceCollection services)
{
//build a dummy service container to get an instance of
//the DefaultEndpointSelector
var sc = new ServiceCollection();
sc.AddMvc();
var defaultEndpointSelector = sc.BuildServiceProvider().GetRequiredService<EndpointSelector>();
return services.Replace(new ServiceDescriptor(typeof(EndpointSelector),
sp => new RequestBodyEndpointSelector(defaultEndpointSelector,
sp.GetRequiredService<EndpointDataSource>()),
ServiceLifetime.Singleton));
}
}
//inside the Startup.ConfigureServices
services.AddRequestBodyEndpointSelector();
The old solution for the old conventional routing used in asp.net core 2.2
Your requirement is a bit weird and you may have to accept some trade-off for that. First that requirement may require you to read the Request.Body twice (when the selected action method has some arguments to model-bind). Even when the framework supports the so-called EnableBuffering on the HttpRequest, it's still a bit trade-off to accept. Secondly in the method to select the best action (defined on IActionSelector), we cannot use async so reading the request body of course cannot be done with async.
For high performance web apps, that definitely should be avoided. But if you can accept that kinds of trade-off, we have a solution here by implementing a custom IActionSelector, at best let it inherit from the default ActionSelector. The method we can override is ActionSelector.SelectBestActions. However that method does not provide the RouteContext (we need access to that to update the RouteData), so we'll re-implement another method of IActionSelector named IActionSelector.SelectBestCandidate which provides a RouteContext.
Here's the detailed code:
//first we define a base model for parsing the request body
public class ActionInfo
{
[JsonProperty("method")]
public string MethodName { get; set; }
}
//here's our custom ActionSelector
public class RequestBodyActionSelector : ActionSelector, IActionSelector
{
readonly IEnumerable<ActionDescriptor> _actions;
public RequestBodyActionSelector(IActionDescriptorCollectionProvider actionDescriptorCollectionProvider,
ActionConstraintCache actionConstraintCache, ILoggerFactory loggerFactory)
: base(actionDescriptorCollectionProvider, actionConstraintCache, loggerFactory)
{
_actions = actionDescriptorCollectionProvider.ActionDescriptors.Items;
}
ActionDescriptor IActionSelector.SelectBestCandidate(RouteContext context, IReadOnlyList<ActionDescriptor> candidates)
{
var request = context.HttpContext.Request;
//supports reading the request body multiple times
request.EnableBuffering();
var sr = new StreamReader(request.Body);
try
{
var body = sr.ReadToEnd();
if (!string.IsNullOrEmpty(body))
{
try
{
//here I use the old Newtonsoft.Json
var actionInfo = JsonConvert.DeserializeObject<ActionInfo>(body);
//the best actions should be on these controller types.
var controllerTypes = new HashSet<TypeInfo>(candidates.OfType<ControllerActionDescriptor>().Select(e => e.ControllerTypeInfo));
//filter for the best by matching the controller types and
//the method name from the request body
var bestActions = _actions.Where(e => e is ControllerActionDescriptor ca &&
controllerTypes.Contains(ca.ControllerTypeInfo) &&
string.Equals(actionInfo.MethodName, ca.MethodInfo.Name, StringComparison.OrdinalIgnoreCase)).ToList();
//only override the default if any method name matched
if (bestActions.Count > 0)
{
//before reaching here,
//the RouteData has already been populated with
//what from the request's URL
//By overriding this way, that RouteData's action
//may be changed, so we need to update it here.
var newActionName = (bestActions[0] as ControllerActionDescriptor).ActionName;
context.RouteData.PushState(null, new RouteValueDictionary(new { action = newActionName }), null);
return SelectBestCandidate(context, bestActions);
}
}
catch { }
}
}
finally
{
request.Body.Position = 0;
}
return SelectBestCandidate(context, candidates);
}
}
To register the custom IActionSelector in the Startup.ConfigureServices:
services.AddSingleton<IActionSelector, RequestBodyActionSelector>();
Accepted answer note:
Although I have appreciated the help of creating my own OwinMiddleware to send images after doing some checks instead of IHttpModule, that doesn't solve the issue entirely.
The thing is I have added an Authorization header to the ajax requests, and inside that header I am sending my Bearer's Token so that I can get logged user information from Owin. So I have to add this header to the image requests either, to be able to get logged user information from image handler middleware.
Original Question:
I am following this blog post to create token based authentication for my web project. Because some resources of my Web API will be used by native mobile clients. And I have heard that token based authentication is the way to go for that. And in my own project I have a custom image request handler. And need the logged user information inside this handler. But when i try to extract user information from ticket I get null. And I am not sure about this but, I think I have 2 different IIdentity objects here, and I need the one stored inside Owin Context.
Here let me show you some codes;
My GrantResourceOwnerCredentials which is storing claims into ClaimsIdentity,
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
....
// checking user credentials and get user information into 'usr' variable
....
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
identity.AddClaim(new Claim(ClaimTypes.Name, context.UserName));
identity.AddClaim(new Claim(ClaimTypes.Role, "user"));
identity.AddClaim(new Claim("sub", context.UserName));
identity.AddClaim(new Claim(ClaimTypes.Sid, usr.UserId.ToString()));
var props = new AuthenticationProperties(new Dictionary<string, string>
{
{
"as:client_id", (context.ClientId == null) ? string.Empty : context.ClientId
},
{
"userId", usr.UserId.ToString()
}
});
var ticket = new AuthenticationTicket(identity, props);
context.Validated(ticket);
}
Helper function to extract user id from the given IIdentity object
public class utils {
public Guid? GetUserIdFromTicket(IIdentity identity)
{
var cId = (ClaimsIdentity)identity;
var uid = cId.FindFirst(ClaimTypes.Sid);
if (uid != null && Comb.IsComb(uid.Value))
return new Guid(uid.Value);
else
return null;
}
....
}
Now I can get the loggedUserId from my controller like,
var loggedUserId = utils.GetUserIdFromTicket(User.Identity);
but if I call it from my IHttpHandler I get null,
public class ImageHandler : IHttpHandler
{
public ImageHandler()
{
}
public ImageHandler(RequestContext requestContext)
{
RequestContext = requestContext;
}
protected RequestContext RequestContext { get; set; }
public utils utils = new utils(); // changed name for simplicity.
public void ProcessRequest(HttpContext context)
{
var strUserId = RequestContext.RouteData.Values["userid"].ToString();
var strContentId = RequestContext.RouteData.Values["contentid"].ToString();
var fileName = RequestContext.RouteData.Values["filename"].ToString();
var size = RequestContext.RouteData.Values["size"].ToString();
var loggedUserId = utils.GetUserIdFromTicket(context.User.Identity);
....
image processing
....
context.Response.End();
}
}
Hope I didn't messed this up for good...
Solution:
I have implemented my own middleware to serv images to my users after doing some checks. Here is my Invoke task implementation. Everything else is just like as recommended in accepted answer. But as stated above, for this to work I have to send images with the Authorization header, or the loggedUserId will be null again.
public async override Task Invoke(IOwinContext context)
{
// need to interrupt image requests having src format : http://www.mywebsite.com/myapp-img/{userid}/{contentId}/{fileName}/{size}/
if (context.Request.Path.HasValue && context.Request.Path.Value.IndexOf("myapp-img") > -1)
{
// get values from url.
var pathValues = context.Request.Path.Value.Split('/');
var strUserId = pathValues[2].ToString();
var strContentId = pathValues[3].ToString();
var fileName = pathValues[4].ToString();
var size = pathValues[5].ToString();
// check if code returned a notfound or unauthorized image as response.
var hasError = false;
// get userId from static utils class providing current owin identity object
var loggedUserId = ChildOnBlogUtils.GetUserIdFromTicket(context.Request.User.Identity);
// save root path of application to provide error images.
var rootPath = AppDomain.CurrentDomain.SetupInformation.ApplicationBase;
// assign content type of response to requested file type
context.Response.ContentType = ChildOnBlogUtils.GetContentType(context.Request.Path.Value.ToString());
// if user requested thumbnail send it without doing checks
if (size == "thumb")
{
imgPath = "images/" + strUserId.ToLower() + "/thumbnail/" + fileName;
}
else
{
var canSee = false;
// check if user can see the content and put the result into canSee variable
// I am using loggedUserId inside these checks
...
...
// end checks
if (canSee)
{
// removed some more checks here for simplicity
imgPath = "images/" + strUserId.ToLower() + "/" + fileName;
}
else
{
context.Response.ContentType = "Image/png";
var imgData = File.ReadAllBytes(rootPath + "/images/unauthorized.png");
await context.Response.Body.WriteAsync(imgData, 0, imgData.Length);
hasError = true;
}
}
if (!hasError) // if no errors have been risen until this point. try to provide the requested image to user.
{
try
{
var imgData = UserMediaContainer.GetFileContent(imgPath); // get file from storage account (azure)
if (imgData.Length == 0)
{
context.Response.ContentType = "Image/png";
imgData = File.ReadAllBytes(rootPath + "/images/notfound.png");
await context.Response.Body.WriteAsync(imgData, 0, imgData.Length);
}
else
{
await context.Response.Body.WriteAsync(imgData, 0, imgData.Length);
}
}
catch (Exception ex)
{
context.Response.ContentType = "Image/png";
var imgData = File.ReadAllBytes(rootPath + "/images/notfound.png");
await context.Response.Body.WriteAsync(imgData, 0, imgData.Length);
}
}
}
else if (context.Request.Path.HasValue && context.Request.Path.Value.IndexOf("profile-img") > -1)
{
// profile image provider. Same code as providing thumbnails.
}
else
{
// if it is not an image request to be handled. move to the next middleware.
await Next.Invoke(context);
}
}
I guess your ImageHandler is processed before everything else in the owin pipeline, which means it is processed before the authorization comes into place.
Since you're using owin I would advise you to drop the IHttpHandler and use some custom owin middleware.
Following this path will allow you to inject your module in the right place in the pipeline.
Creating the middleware is quite easy:
public class ImageProcessingMiddleware : OwinMiddleware
{
public ImageProcessingMiddleware(OwinMiddleware next): base(next)
{
}
public async override Task Invoke(IOwinContext context)
{
string username = context.Request.User.Identity.Name;
Console.WriteLine("Begin Request");
await Next.Invoke(context);
Console.WriteLine("End Request");
}
}
Once you have defined your middleware you can create an extension method for the instantiation:
public static class ImageProcessingExtensions
{
public static IAppBuilder UseImageProcessing(this IAppBuilder app)
{
return app.Use<ImageProcessingMiddleware>();
}
}
Now you can plug-in your middleware in the pipeline:
app.UseImageProcessing();
If you have followed Taiseer sample, you would do that after you have configured the authorization module:
// Token Generation
app.UseOAuthAuthorizationServer(OAuthServerOptions);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
Going back to the middleware, you might have noticed there's a method called Invoke:
public async override Task Invoke(IOwinContext context)
{
string username = context.Request.User.Identity.Name;
Console.WriteLine("Begin Request");
await Next.Invoke(context);
Console.WriteLine("End Request");
}
This is the entry-point of each middleware. As you can see I am reading the user's name authorized right after the authorization token has been verified and authorized.
There's an interesting article about owin middleware which is worth reading.
Hi, I am trying to configure wcf using code behind, below is the code:
public static void Configure(ServiceConfiguration config)
{
string configPath = ConfigurationManager.AppSettings["wcfconfigDBPath"];
// Enable “Add Service Reference” support
config.Description.Behaviors.Add(new ServiceMetadataBehavior { HttpGetEnabled = true });
// set up support for http, https, net.tcp, net.pipe
if (isEnabled(configPath, "enablehttp"))
config.EnableProtocol(new BasicHttpBinding());
if (isEnabled(configPath, "enablenettcp"))
config.EnableProtocol(new NetTcpBinding());
if (isEnabled(configPath, "enablepipe"))
config.EnableProtocol(new NetNamedPipeBinding());
}
private static bool isEnabled(string path, string elementName)
{
try
{
string elementValue = string.Empty;
bool returnVal = false;
using (XmlTextReader reader = new XmlTextReader(path))
{
reader.ReadToFollowing(elementName);
if (reader.Read())
elementValue = reader.Value;
}
if (!string.IsNullOrEmpty(elementValue))
{
bool.TryParse(elementValue, out returnVal);
}
return returnVal;
}
catch (Exception ex)
{
return false;
}
}
The above code is not working. I am not sure when the "static void Configure" gets fired.
My question is, is there any way to enable/disable the protocol based on DB/xml configuration without bringing down the service?
New feature in .NET 4.5 which probably can be used in your case:
Note: The configure method is called by WCF before the service host is opened.
The Configure method takes a ServiceConfiguration instance that enables the developer to add endpoints and behaviors. This method is called by WCF before the service host is opened. When defined, any service configuration settings specified in an app.config or web.config file will be ignored.
The following code snippet illustrates how to define the Configure method and add a service endpoint, an endpoint behavior and service behaviors:
public class Service1 : IService1
{
public static void Configure(ServiceConfiguration config)
{
ServiceEndpoint se = new ServiceEndpoint(new ContractDescription("IService1"), new BasicHttpBinding(), new EndpointAddress("basic"));
se.Behaviors.Add(new MyEndpointBehavior());
config.AddServiceEndpoint(se);
config.Description.Behaviors.Add(new ServiceMetadataBehavior { HttpGetEnabled = true });
config.Description.Behaviors.Add(new ServiceDebugBehavior { IncludeExceptionDetailInFaults = true });
}
public string GetData(int value)
{
return string.Format("You entered: {0}", value);
}
public CompositeType GetDataUsingDataContract(CompositeType composite)
{
if (composite == null)
{
throw new ArgumentNullException("composite");
}
if (composite.BoolValue)
{
composite.StringValue += "Suffix";
}
return composite;
}
}
Refer for complete example to:https://msdn.microsoft.com/en-us/library/hh205277(v=vs.110).aspx
I have an ASP.NET 4 WebForms-based application, and I want to use routing to allow multi-tenancy, such that http://www.example.com/site/foo/Default.aspx is for the client named "foo" and http://www.example.com/site/bar/Default.aspx is for the client named bar.
I got as far as:
// Global.asax in Application_Start
routes.Add("ClientSelector", new System.Web.Routing.Route
(
"site/{client}/{*path}",
new Lcmp.Web.Configuration.ClientRoute()
));
public class ClientRoute : System.Web.Routing.IRouteHandler
{
private string m_Path;
private string m_Client;
public ClientRoute() { }
public bool IsReusable
{
get { return true; }
}
public IHttpHandler GetHttpHandler(System.Web.Routing.RequestContext requestContext)
{
this.m_Path = (string)requestContext.RouteData.Values["path"];
this.m_Client = (string)requestContext.RouteData.Values["client"];
string virtualPath = "~/" + this.m_Path;
bool shouldValidate = false;
if (shouldValidate && !UrlAuthorizationModule.CheckUrlAccessForPrincipal(
virtualPath, requestContext.HttpContext.User,
requestContext.HttpContext.Request.HttpMethod))
{
requestContext.HttpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
requestContext.HttpContext.Response.End();
return null;
}
else
{
HttpContext.Current.RewritePath(virtualPath);
HttpContext.Current.Items.Add("Client", this.m_Client);
return (IHttpHandler)BuildManager.CreateInstanceFromVirtualPath(virtualPath, typeof(Page));
}
}
}
and it seems to work for the initial .aspx page. But the routing is picking up .js and other non-compilable resources and throwing exceptions. What is the best way to avoid routing those?
You can use the StopRoutingHandler() to ignore requests for certain files.
routes.Add(new Route("*{js}", new {js=#".*\.js(/.*)?", new StopRoutingHandler()));