How to handle log out and redirect - c#

Created a SPA application with .NET Framework 4.5 that will use AngularJS. I implemented the System.IdentityModel per instructions to point to a third party authentication.
Web.config edits:
<configSections>
<section name="system.identityModel" type="System.IdentityModel.Configuration.SystemIdentityModelSection, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" />
<section name="system.identityModel.services" type="System.IdentityModel.Services.Configuration.SystemIdentityModelServicesSection, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" />
</configSections>
<location path="FederationMetadata">
<system.web>
<authorization>
<allow users="*" />
</authorization>
</system.web>
</location>
...
<system.web>
<authorization>
<deny users="?" />
</authorization>
<authentication mode="None" />
<compilation debug="true" targetFramework="4.5" />
<httpRuntime requestValidationType="ourcustomvalidator, ourcustomnamespace" />
<pages controlRenderingCompatibilityVersion="4.0" validateRequest="false" />
</system.web>
....
<modules runAllManagedModulesForAllRequests="true">
<remove name="FormsAuthentication" />
<add name="WSFederationAuthenticationModule" type="System.IdentityModel.Services.WSFederationAuthenticationModule, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" preCondition="managedHandler" />
<add name="SessionAuthenticationModule" type="System.IdentityModel.Services.SessionAuthenticationModule, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" preCondition="managedHandler" />
</modules>
...
So on initial launch of the Single Page Application, the site redirects to the authorization site. From there you login, authorize and you're redirected back to the application.
Now I have a WebAPI Restful section being a part of the solution as well to log client errors and also to handle logging out within our application. This has lead me to a few problems that I am not grasping:
1) If I make a /api/logoff call to my WebApi, I can call FederatedAuthentication.SessionAuthenticationModule.SignOut(); and I am signed out behind the scenes. Using angular, how should I go about redirecting the user? I noticed after I issue this command, if I hit F5, my site is refreshed but I am automatically logged back in. I would prefer this go back to the login screen I get on initial page load.
2) If I make a /api/custom call to my WebApi and I was logged out behind the scenes, how do I capture that and redirect the user? Right now I am getting an error message along the lines of:
XMLHttpRequest cannot load https://mycustomloginurl.com/?wa=wsignin1.0&wtrealm=http%3a%2f%2flocalhost%3a561…%3dpassive%26ru%3d%252fwebapi%252fims%252ftesting&wct=2014-02-21T21%3a47%3a09Z.
No 'Access-Control-Allow-Origin' header is present on the requested resource.
Origin 'http://localhost:56181' is therefore not allowed access.
Sorry if this confusing, I am trying to wrap my head around all of this.

Thanks to some more research, this post: Prevent XmlHttpRequest redirect response in .Net MVC WS-Federation Site helped me get to the right answer.
Basically I followed the same code but added the following:
resp.Clear(); // cleared the response
var fa = FederatedAuthentication.WSFederationAuthenticationModule;
var signInRequestMessage = new SignInRequestMessage(new Uri(fa.Issuer), fa.Realm);
var signInURI = signInRequestMessage.WriteQueryString();
resp.Write(signInURI);
So I cleared the response and made the body of the response contain the Sign-In URL for the authentication. In angular, I have a HTTP interceptor that checks for status code 401 and then uses the above data to redirect to the login.
app.config([
'$httpProvider', function($httpProvider) {
var interceptor = ['$rootScope', '$q','$window', function (scope, $q, $window) {
function success(response) {
return response;
}
function error(response) {
var status = response.status;
if (status == 401) {
$window.location.href = response.data.replace(/"/g, "");
}
// otherwise
return $q.reject(response);
}
return function (promise) {
return promise.then(success, error);
}
}];
$httpProvider.responseInterceptors.push(interceptor);
}
]);

Related

WCF & Castle Windsor - Looks like you forgot

We have recently started migrating to Castle Windsor and i'm having some issues getting our WCF service running. It is a regular windows service NOT HOSTED IN IIS where we serve up SSL material and use a custom X509CertificateValidator to verify the client's presented certificate.
Below is the code i'm using to create the WCF service. It is in a separate project to the WCF service which references it.
public IWindsorContainer RegisterService<T,K>(
IServiceBehavior customBehavior,
Action<ServiceHost> onCreate = null,
Action<ServiceHost> onOpen = null,
Action<ServiceHost> onClose = null,
Action<ServiceHost> onFault = null) where T : class where K : T
{
var facility = this.AddFacility<WcfFacility>(f => f.CloseTimeout = TimeSpan.Zero);
var serviceModel = new DefaultServiceModel()
.OnCreated(onCreate)
.OnOpened(onOpen)
.OnClosed(onClose)
.OnFaulted(onFault);
var service = Component.For<T>()
.ImplementedBy<K>()
.AsWcfService<T>(serviceModel)
.LifestylePerWcfOperation<T>();
if (customBehavior != null)
facility.Register(Component.For<IServiceBehavior>().Instance(customBehavior));
facility.Register(service);
return facility;
}
The service starts as expected (i can navigate to the service using chrome with no issues) and the service is presenting and validating the SSL material (i.e. hits the custom validator) but after that, the client gets this in a FaultException:
Looks like you forgot to register the http module Castle.MicroKernel.Lifestyle.PerWebRequestLifestyleModule
To fix this add
<add name="PerRequestLifestyle" type="Castle.MicroKernel.Lifestyle.PerWebRequestLifestyleModule, Castle.Windsor" />
to the <httpModules> section on your web.config.
If you plan running on IIS in Integrated Pipeline mode, you also need to add the module to the <modules> section under <system.webServer>.
Alternatively make sure you have Microsoft.Web.Infrastructure, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35 assembly in your GAC (it is installed by ASP.NET MVC3 or WebMatrix) and Windsor will be able to register the module automatically without having to add anything to the config file.
Below is a chunk of my App.Config, i have tried to place the module in all areas that were suggested through googles and through some guesswork:
...
<system.web>
<httpModules>
<add name="PerRequestLifestyle" type="Castle.MicroKernel.Lifestyle.PerWebRequestLifestyleModule, Castle.Windsor" />
</httpModules>
<membership defaultProvider="ClientAuthenticationMembershipProvider">
<providers>
<add name="ClientAuthenticationMembershipProvider" type="System.Web.ClientServices.Providers.ClientFormsAuthenticationMembershipProvider, System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" serviceUri="" />
</providers>
</membership>
<roleManager defaultProvider="ClientRoleProvider" enabled="true">
<providers>
<add name="ClientRoleProvider" type="System.Web.ClientServices.Providers.ClientRoleProvider, System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" serviceUri="" cacheTimeout="86400" />
</providers>
</roleManager>
</system.web>
<system.webServer>
<modules>
<add name="PerRequestLifestyle" type="Castle.MicroKernel.Lifestyle.PerWebRequestLifestyleModule, Castle.Windsor" />
</modules>
<handlers>
<add name="PerRequestLifestyle" verb="*" path="*.castle" preCondition="managedHandler" type="Castle.MicroKernel.Lifestyle.PerWebRequestLifestyleModule, Castle.Microkernel"/>
</handlers>
</system.webServer>
<system.serviceModel>
<extensions>
<endpointExtensions>
<add name="PerRequestLifestyle" type="Castle.MicroKernel.Lifestyle.PerWebRequestLifestyleModule, Castle.Windsor" />
</endpointExtensions>
<bindingExtensions>
<add name="PerRequestLifestyle" type="Castle.MicroKernel.Lifestyle.PerWebRequestLifestyleModule, Castle.Windsor" />
</bindingExtensions>
<behaviorExtensions>
<add name="PerRequestLifestyle" type="Castle.MicroKernel.Lifestyle.PerWebRequestLifestyleModule, Castle.Windsor" />
</behaviorExtensions>
<bindingElementExtensions>
<add name="PerRequestLifestyle" type="Castle.MicroKernel.Lifestyle.PerWebRequestLifestyleModule, Castle.Windsor" />
</bindingElementExtensions>
</extensions>
...
Im pretty much out of ideas. Does anyone know what could be the cause? If not, could anyone explain abit more about the error i'm getting?
Any help would be much appreciated!
Once again i'm answering my own question (the next day after posting, oh dear) :P
This error message is being thrown here (thank god for open source!):
https://github.com/castleproject/Windsor/blob/016730de012f15985410fb33e2eb907690fe5a28/src/Castle.Windsor/MicroKernel/Lifestyle/PerWebRequestLifestyleModule.cs
tldr - see below:
public class PerWebRequestLifestyleModule : IHttpModule
{
...
private static void EnsureInitialized()
{
if (initialized)
{
return;
}
var message = new StringBuilder();
message.AppendLine("Looks like you forgot to register the http module " + typeof(PerWebRequestLifestyleModule).FullName);
message.AppendLine("To fix this add");
message.AppendLine("<add name=\"PerRequestLifestyle\" type=\"Castle.MicroKernel.Lifestyle.PerWebRequestLifestyleModule, Castle.Windsor\" />");
message.AppendLine("to the <httpModules> section on your web.config.");
if (HttpRuntime.UsingIntegratedPipeline)
{
message.AppendLine(
"Windsor also detected you're running IIS in Integrated Pipeline mode. This means that you also need to add the module to the <modules> section under <system.webServer>.");
}
else
{
message.AppendLine(
"If you plan running on IIS in Integrated Pipeline mode, you also need to add the module to the <modules> section under <system.webServer>.");
}
#if !DOTNET35
message.AppendLine("Alternatively make sure you have " + PerWebRequestLifestyleModuleRegistration.MicrosoftWebInfrastructureDll +
" assembly in your GAC (it is installed by ASP.NET MVC3 or WebMatrix) and Windsor will be able to register the module automatically without having to add anything to the config file.");
#endif
throw new ComponentResolutionException(message.ToString());
}
...
}
From this I quickly gathered that the issue was that the PerWebRequestLifestyleModule was not being initialized, which was ok for me as i did not need it for this service!
Looking further into my own code, some of my repositories that were being loaded for this service were set to use LifestylePerWebRequest from when they were being used in our web console, bingo!
After adjusting them to something else (in this case 'LifestylePerWcfOperation`) all was working fine.

Glimpse works locally, not on remote server

After following all the guides, SO pages and troubleshooting pages I can find I'm finally out of ideas.
I've got Glimpse working fine on my local dev server, but when I deploy my ASP.net (MVC5) app to my remote server it doesn't work - at all. /glimpse.axd gives a 404 with both LocalPolicy and ControlCookiePolicy set to ignore, and with a custom security policy that returns On in all cases. My understanding is that with ControlCookiePolicy disabled, I shouldn't need to go to /glimpse.axd to enable it - but I'm not seeing the glimpse icon on the remote server either.
Even if I go to the remote server and browse localhost to /glimpse.axd I still get a 404.
My web.config looks like this:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<section name="glimpse" type="Glimpse.Core.Configuration.Section, Glimpse.Core" />
</configSections>
<system.web>
<compilation debug="false" />
<httpRuntime targetFramework="4.5.1" relaxedUrlToFileSystemMapping="true" />
</system.web>
<system.webServer>
<modules runAllManagedModulesForAllRequests="true">
<remove name="FormsAuthentication" />
</modules>
<urlCompression doDynamicCompression="true" dynamicCompressionBeforeCache="false" />
</system.webServer>
<glimpse defaultRuntimePolicy="On" endpointBaseUri="~/Glimpse.axd">
<logging level="Trace" />
<runtimePolicies>
<ignoredTypes>
<add type="Glimpse.AspNet.Policy.LocalPolicy, Glimpse.AspNet" />
<add type="Glimpse.Core.Policy.ControlCookiePolicy, Glimpse.Core" />
</ignoredTypes>
</runtimePolicies>
</glimpse>
</configuration>
This is the version off the remote server (after transform). I've trimmed it a little to remove sections like appSettings.
My GlimpseSecurityPolicy.cs looks like this:
// Uncomment this class to provide custom runtime policy for Glimpse
using Glimpse.AspNet.Extensions;
using Glimpse.Core.Extensibility;
namespace RationalVote
{
public class GlimpseSecurityPolicy:IRuntimePolicy
{
public RuntimePolicy Execute(IRuntimePolicyContext policyContext)
{
return RuntimePolicy.On;
}
public RuntimeEvent ExecuteOn
{
// The RuntimeEvent.ExecuteResource is only needed in case you create a security policy
// Have a look at http://blog.getglimpse.com/2013/12/09/protect-glimpse-axd-with-your-custom-runtime-policy/ for more details
get { return RuntimeEvent.EndRequest | RuntimeEvent.ExecuteResource; }
}
}
}
The real one does an actual check, but I get the same issue with the policy above.
I cannot seem to find any trace output anywhere on the remote server, it is logging fine on my local machine.
I am deploying using the Visual Studio publish to web feature, and I've verified that the Glimpse.Core.dll is in the bin folder.
I can't see anything in the event log that is relevant.
I've also added <add namespace="Glimpse.Mvc.Html" /> to the namespaces block of the web.config in the views folder.
I tried putting #Html.GlimpseClient() in the _Layout.cshtml file just above </body> but this renders nothing.
Anybody got any ideas?
If the glimpse.axd is returning a 404 then this means the Glimpse resource handler is not registered.
If the web.config content you show above is not trimmed to much, then it is normal that Glimpse won't do much as the Glimpse HttpModule and the Glimpse HttpHandler are not registered in the system.web and/or the system.webserver sections like this
<system.web>
<httpModules>
<add name="Glimpse" type="Glimpse.AspNet.HttpModule, Glimpse.AspNet"/>
</httpModules>
<httpHandlers>
<add path="glimpse.axd" verb="GET" type="Glimpse.AspNet.HttpHandler, Glimpse.AspNet"/>
</httpHandlers>
</system.web>
<system.webServer>
<validation validateIntegratedModeConfiguration="false"/>
<modules>
<add name="Glimpse" type="Glimpse.AspNet.HttpModule, Glimpse.AspNet" preCondition="integratedMode"/>
</modules>
<handlers>
<add name="Glimpse" path="glimpse.axd" verb="GET" type="Glimpse.AspNet.HttpHandler, Glimpse.AspNet" preCondition="integratedMode" />
</handlers>
</system.webServer>
Maybe your transform removed to much from the local web.config?

Asp.net Routing wrong MIME type for .css files served with UrlRoutingModule and PageBuildProvider

I need to route request like:
http://www.site.com/channel1/somestring
http://www.site.com/channel1/otherstring
http://www.site.com/channel2/anotherstring
to my default page: http://www.site.com/default.aspx page
for a web project that runs on .net 4.5, IIS Express with integrated pipeline.
Routes:
protected virtual void RegisterRoutes()
{
RouteTable.Routes.MapPageRoute("AliasCss","{channel}/css/{*url}","~/css/{url}",
false,null, new RouteValueDictionary { { "channel", "(channel1|channel2)"}});
RouteTable.Routes.Ignore("{resource}.js");
RouteTable.Routes.Ignore("{resource}.css");
RouteTable.Routes.MapPageRoute("Alias", "{*url}", "~/Default.aspx", true,
new RouteValueDictionary { { "url", "Default.aspx" } },
new RouteValueDictionary { { "url", "(channel1|channel2)" } });
}
It works great except for the css files
Example: http://www.site.com/channel1/css/custom.css
that gives 404 error insted of serving the css files in http://www.site.com/css/custom.css
To solve this I needed to add a PageBuildProvider for making Asp.net serve static .css files and set runAllManagedModulesForAllRequests="true"
web.config relevant parts:
<configuration>
...
<system.web>
<compilation debug="true" targetFramework="4.5" >
<buildProviders>
<add extension=".css" type="System.Web.Compilation.PageBuildProvider" />
</buildProviders>
</compilation>
<httpRuntime targetFramework="4.5" enableVersionHeader="false" />
...
</system.web>
<system.webServer>
<validation validateIntegratedModeConfiguration="true" />
<modules runAllManagedModulesForAllRequests="true">
<add name="UrlRoutingModule" type="System.Web.Routing.UrlRoutingModule,System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
</modules>
...
</system.webServer>
...
</configuration>
Now .css files are correctly routed but are served with wrong MIME type:
Resource interpreted as Stylesheet but transferred with MIME type
text/html
I tried to set the mimetype in web.config but with no luck:
<staticContent>
<remove fileExtension=".css" />
<mimeMap fileExtension=".css" mimeType="text/css; charset=UTF-8" />
</staticContent>

Doesn`t run http handler in asp.net application

I want to run my httphandler, when some user get access to image file with extentions like png, jpeg, gif and other. But i get eror 404 when i try to path. I think what server try find file on a disk, but i want use alias for access to file in my handler and newer access for real phisical file path.
Example config file:
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.0" />
<httpHandlers>
<add verb="GET,HEAD" path="*.jpg" type="Startup.Shop.ImageHandler, Startup.Shop" validate="false"/>
</httpHandlers>
<authorization>
<allow users="*" />
</authorization>
</system.web>
<system.webServer>
<handlers>
<add name="ImageHandlerfor JPG" path="*.jpg" verb="GET" type="Startup.Shop.ImageHandler, Startup.Shop" resourceType="Unspecified" />
<add name="ImageHandler for GIF" path="*.gif" verb="GET" type="Startup.Shop.ImageHandler, Startup.Shop" resourceType="Unspecified" />
<add name="ImageHandler for BMP" path="*.bmp" verb="GET" type="Startup.Shop.ImageHandler, Startup.Shop" resourceType="Unspecified" />
<add name="ImageHandler for PNG" path="*.png" verb="GET" type="Startup.Shop.ImageHandler, Startup.Shop" resourceType="Unspecified" />
</handlers>
</system.webServer>
/configuration>
Example handler class:
public class ImageHandler : IHttpHandler, IRouteHandler
{
public void ProcessRequest(HttpContext context)
{
context.Response.ContentType = "text/plain";
context.Response.Write("Hello World");
}
public bool IsReusable
{
get
{
return false;
}
}
public IHttpHandler GetHttpHandler(RequestContext requestContext)
{
return this;
}
}
And more - iis server configurated as classic mode
If you're in classic mode, then asp.net won't get called by IIS unless IIS knows the file type that should get mapped. In your IIS control panel, you should be able to configure the file types handled by ASP.NET. Otherwise, it's going to assume that a file type is for a physical file, which it can't find on the file system.

Problem with ASP.NET Authentication

I'm having problem with our login procedure.
Some customers complain that they can't login. I can see in our logs that their login is successful and that they are redirected from the login page to the member area. But there somehow the login isn't detected and they are bounced back to the login page.
I've asked customers to check if cookies are supported (http://www.html-kit.com/tools/cookietester/) but problem remains even if this test returns true.
This is how I've implemented the login procedure (simplyfied):
protected void Login(string email, string password)
{
FormsAuthentication.SignOut();
Guid clientId = /* Validate login by checking email and password, if fails display error otherwise get client id */
FormsAuthentication.SetAuthCookie(clientId.ToString(), true);
HttpContext.Current.Response.Redirect("~/Members.aspx");
}
On the member page I check for authentication by in Page_Load function:
public static void IsAuthenticated()
{
if (!HttpContext.Current.User.Identity.IsAuthenticated)
{
HttpContext.Current.Response.Redirect("~/Login.aspx", true);
}
}
Maybe I'm using FormsAuthentication completely wrong?
I've asked this before but still haven't been able to figure this out, I'd appreciate any help.
From my Web.Config:
<system.web>
<compilation debug="false">
<assemblies>
...
</assemblies>
</compilation>
<authentication mode="Forms"/>
<sessionState mode="InProc" cookieless="false" timeout="180"/>
<customErrors mode="On"/>
<httpHandlers>
...
</httpHandlers>
<httpModules>
...
</httpModules> </system.web>
public static void IsAuthenticated()
{
if (!HttpContext.Current.User.Identity.IsAuthenticated)
{
HttpContext.Current.Response.Redirect("~/Login.aspx", true);
}
}
is not necessary when you use forms authentication.
When you specify the forms authentication in the web.config (in which you also specify the login page)
<authentication mode="Forms">
<forms loginUrl="/Authorization/Login" timeout="60" />
</authentication>
and you deny all non-athenticated users access
<authorization>
<deny users="?" />
</authorization>
you don't have to check the authentication of a user yourself, the framework takes care of that.
I would place the FormsAuthentication.SignOut(); code behind a 'logout' link
Seperate the call of SignOut() and SetAuthCookie() in different methods. You may call FormsAuthentication.SignOut(); when the Login page loads first time - simply just do away from calling SignOut() on Login page. And Call
FormsAuthentication.SetAuthCookie(clientId.ToString(), true); after authentication is successful.
Normally you would use FormsAuthentication.Authenticate together with some membership provider, but this should work, and it actually does in my machine.
Are you removing the FormsAuthentication from your registered HTTP modules? Normally, this is in the machine wide web.config:
<configuration>
<system.web>
<httpModules>
<add name="FormsAuthentication" type="System.Web.Security.FormsAuthenticationModule" />
</httpModules>
</system.web>
</configuration>
If you put a <clear /> inside that same section of your own web.config, you're effectively removing that module.
My tested Web.config is pretty clean, it only has <authentication mode="Forms"/> configured.

Categories

Resources