ClaimsAuthenticationManager.Authenticate never gets called - c#

I want to add some additional claims to a Principal during authentication. I am trying to implement a custom ClaimsAuthenticationManager in my MVC 4.5 project which uses Windows Authentication:
namespace Project.Infrastructure
{
public class ClaimsTransformer : ClaimsAuthenticationManager
{
public override ClaimsPrincipal Authenticate(string resourceName, ClaimsPrincipal incomingPrincipal)
{
if (incomingPrincipal != null && incomingPrincipal.Identity.IsAuthenticated == true)
{
((ClaimsIdentity)incomingPrincipal.Identity).AddClaim(new Claim(ClaimTypes.Role, "Admin"));
}
return incomingPrincipal;
}
}
}
I have the web.config set up to use my custom class:
<configSections>
<section name="system.identityModel" type="System.IdentityModel.Configuration.SystemIdentityModelSection, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
</configSections>
and
<system.identityModel>
<identityConfiguration>
<claimsAuthenticationManager type="Project.Infrastructure.ClaimsTransformer, [AssemblyName]" />
</identityConfiguration>
</system.identityModel>
But the Authenticate method never gets called. Am I missing something?

The missing step is that you need to add an HTTP Module to kick all of this off.
So, you need a class that looks like this:
public class MyClaimsAuthenticationModule : IHttpModule
{
public void Init(HttpApplication context)
{
context.PostAuthenticateRequest += Context_PostAuthenticateRequest;
}
public void Dispose()
{
// Nothing to dispose, method required by IHttpModule
}
void Context_PostAuthenticateRequest(object sender, EventArgs e)
{
var transformer = FederatedAuthentication.FederationConfiguration.IdentityConfiguration.ClaimsAuthenticationManager;
if (transformer != null)
{
var context = ((HttpApplication)sender).Context;
var principal = context.User as ClaimsPrincipal;
var transformedPrincipal = transformer.Authenticate(context.Request.RawUrl, principal);
context.User = transformedPrincipal;
Thread.CurrentPrincipal = transformedPrincipal;
}
}
}
This will pick up the transformer you specified in the web.config and call authenticate on it, then attach the returned principal to the HttpContext and the current thread.
You will also need something like the following in your web.config:
<system.webServer>
<modules>
<add name="MyClaimsAuthenticationModule" type="MyApplication.MyHttpModels.MyClaimsAuthenticationModule, MyApplicationAssembly" />
</modules>
</system.webServer>
Update
You can, of course, put the code from the method Context_PostAuthenticationRequest in the PostAuthenticateRequest handler in your Global.asax.cs class file. However, I prefer to keep the responsibilities of the classes small so I go for an implementation of IHttpModule so that the module does its thing and it is obvious what that is, and it is separate from other things that may be happening at various stages of the pipeline.
If your Global.asax.cs file is small then there is no problem with putting the code there. It should still work. However, you are mixing responsibilities in the class and it could get unwieldy in the future.

Are you calling the Authenticate method with something like this, and the Authentication is not taking place?
ClaimsTransformer manager = new ClaimsTransformer();
manager.Authenticate("resource", incomingPrincipal )
You may want to replace the "return incomingPrincipal" from inside the ClaimsTransformer class with a call to:
return base.Authenticate(resourceName, incomingPrincipal);
Also why do you need Windows Authentication?

Related

The data protection operation was unsuccessful on azure with autofac

I have been getting this error when trying to create or update a user.
The full error is:
The data protection operation was unsuccessful. This may have been caused by not having the user profile loaded for the current thread's user context, which may be the case when the thread is impersonating.
We use autofac in our application, so I after reading this article I created my own IdentityFactoryOptions like this:
public class IdentityFactoryOptions: IdentityFactoryOptions<UserProvider>
{
public IdentityFactoryOptions()
{
DataProtectionProvider = new DpapiDataProtectionProvider("ASP.NET Identity");
}
}
and then I created my own DataProtectionTokenProvider like this:
public class DataProtectionTokenProvider : DataProtectorTokenProvider<User>
{
public DataProtectionTokenProvider(IdentityFactoryOptions options) : base(options.DataProtectionProvider.Create("ASP.NET Identity"))
{
TokenLifespan = TimeSpan.FromHours(6);
}
}
I registered both these as SingleInstances like this:
builder.RegisterType<IdentityFactoryOptions>().AsSelf().SingleInstance();
builder.RegisterType<DataProtectionTokenProvider>().AsSelf().SingleInstance();
and I injected the DataProtectionTokenProvider into my UserManager and assigned it in the managers constructor like this:
UserTokenProvider = dataProtectionTokenProvider;
But after doing all this, I still get the error.
I also read this article and saw you have to update your web.config too, so I added this:
<system.identityModel>
<identityConfiguration>
<securityTokenHandlers>
<add type="System.IdentityModel.Services.Tokens.MachineKeySessionSecurityTokenHandler, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<remove type="System.IdentityModel.Tokens.SessionSecurityTokenHandler, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
</securityTokenHandlers>
</identityConfiguration>
</system.identityModel>
But the error persists.
Does anyone have any solution for this? It is driving me mad....
Ok, I managed to fix this.
Using the first post I linked, I injected the IAppBuilder into my autofac module and removed my IdentityFactoryOptions class. So the registration now looks like this:
builder.Register(m => new DataProtectorTokenProvider(_app.GetDataProtectionProvider())).AsSelf().SingleInstance();
And the DataProtectorTokenProvider looks like this:
public class DataProtectorTokenProvider : DataProtectorTokenProvider<User>
{
public DataProtectorTokenProvider(IDataProtectionProvider dataProtectionProvider) : base(dataProtectionProvider.Create("ASP.NET Identity"))
{
TokenLifespan = TimeSpan.FromHours(6);
}
}
Everything else I kept the same. This solved the issue.

How to create a rule in a web.config file for a C# application, to block based on parameters in the URL

I want to create filter rule in the web.config file for a C# application, to block http://website.com/folder/Default.aspx,
but allow http://website.com/folder/Default.aspx?db=Database. Basically they don't have "Default.aspx?db=Database" in the URL, then it will either redirect them to a website another website (or Deny access).
You could implement an HttpModule that performs your desired logic. You can register it in the web.config without any code changes to the project. Just compile it into its own dll and drop that into the bin folder of your website.
The module
class RequestInterceptor : IHttpModule
{
public void Dispose()
{
}
// In the Init function, register for HttpApplication
// events by adding your handlers.
public void Init(HttpApplication application)
{
application.BeginRequest += (new EventHandler(this.Application_BeginRequest));
}
private void Application_BeginRequest(Object source,
EventArgs e)
{
HttpApplication application = (HttpApplication)source;
HttpContext context = application.Context;
//Inspect the URL and decide if this is a request you are interested in
//context.Request.Url
//context.Request.QueryString
//Redirect, or whatever...
//context.Response.Redirect(...)
}
}
The web.config:
<system.webServer>
<modules>
<add name="MyInterceptor" type="YourNamespace.RequestInterceptor, RequestInterceptor"/>
</modules>
...

The name 'Roles' does not exist in the current context

I have ported Migrations from a web app to a class library project.
Everything works fine, except that I can't call the static class Roles.
I have included the namespace using System.Web.Security; which is where the Roles are located.
Here is the Configuration.cs file contents:
namespace _DataContext.Migrations
{
using System;
using System.Data.Entity;
using System.Data.Entity.Migrations;
using System.Linq;
using WebMatrix.WebData;
using System.Web.Security;
internal sealed class Configuration : DbMigrationsConfiguration<_DataContext.DataContext>
{
public Configuration()
{
AutomaticMigrationsEnabled = true;
}
protected override void Seed(_DataContext.DataContext context)
{
// This method will be called after migrating to the latest version.
// You can use the DbSet<T>.AddOrUpdate() helper extension method
// to avoid creating duplicate seed data. E.g.
//
// context.People.AddOrUpdate(
// p => p.FullName,
// new Person { FullName = "Andrew Peters" },
// new Person { FullName = "Brice Lambson" },
// new Person { FullName = "Rowan Miller" }
// );
//
SeedMembership();
}
private void SeedMembership()
{
WebSecurity.InitializeDatabaseConnection("DefaultConnection", "UserProfile", "UserId", "UserName", autoCreateTables: true);
// doesn't work either:
//var roles = (SimpleRoleProvider)Roles.Provider;
//var membership = (SimpleMembershipProvider)Membership.Provider;
if (Roles.RoleExists("Administrator"))
Roles.CreateRole("Administrator");
}
}
}
Error message is:
The name 'Roles' does not exist in the current context
What am I missing here?
[Edit]
I have been doing some more research and it appears that i have to create an object from SimpleRoleProvider in order to access the RoleExists method.
But why do i have to do it this way? Why can't i just use:
if (Roles.RoleExists("Administrator"))
Roles.CreateRole("Administrator");
Roles comes from a static class?
Have you added the roleManager element to the system.web section of your Web.config file? From the MSDN page on Roles:
To enable role management for your ASP.NET application, use the roleManager element of the system.web section in the Web.config file for your application, as shown in the following example.
The section looks like this:
<roleManager defaultProvider="SqlProvider"
enabled="true"
cacheRolesInCookie="true"
cookieName=".ASPROLES"
cookieTimeout="30"
cookiePath="/"
cookieRequireSSL="false"
cookieSlidingExpiration="true"
cookieProtection="All" >
<providers>
<add
name="SqlProvider"
type="System.Web.Security.SqlRoleProvider"
connectionStringName="SqlServices"
applicationName="SampleApplication" />
</providers>
</roleManager>
You should be able to access Roles directly, but I would not recommend it when using the SimpleMembership providers. That being said, do you have the assembly System.Web referenced in your project?
The preferred method for getting the role provider is to do something like this:
var roles = (WebMatrix.WebData.SimpleRoleProvider)Roles.Provider;
if (!roles.RoleExists("Admin"))
{
roles.CreateRole("Admin");
}
If you compare the definition of Roles versus SimpleRoleProvider you will see there is quite a bit of difference. It looks like the SimpleRoleProvider does not implement the complete interface for Roles, which is not required when implementing a custom provider. You may get a "not implemented" exception on some the methods if you call them directly from Roles. SimpleRoleProvider also provides additional methods/properties that can be useful when using SimpleMembership.
You are using seeding method in Class Library project.
You have to add two references
1. System.Web
2. System.Web.ApplicationServices
then resolve Roles, Membership from these references.

entlib CustomTraceListener unresolved

im new to Enterprise Library (5, with the optional update), and am trying to use my own CustomTraceListener with the Logging Block.
Logging works with a supplied flat file listener, but i dont seem to be able to extend that to my custom class.
Logs to FlatFileListener, but Not to my CustomListener:
static UnityContainer _container;
static LogWriter _writer;
internal static void Inf(this string message)
{
if (_container == null)
{
_container = new UnityContainer();
_container.AddNewExtension<EnterpriseLibraryCoreExtension>();
}
if (_writer == null && _container != null)
{
_writer = _container.Resolve<LogWriter>();
LogEntry log = new LogEntry();
log.Message = message;
log.Categories.Add("Information");
log.Severity = TraceEventType.Information;
_writer.Write(log);
}
}
My CustomListener based on bychkov:
namespace Test.Catch
{
[ConfigurationElementType(typeof(CustomTraceListenerData))]
public class LoggerCustom : CustomTraceListener
{
public override void TraceData(TraceEventCache eventCache, string source, TraceEventType eventType, int id, object data)
{
if (data is LogEntry && this.Formatter != null)
{
this.WriteLine(this.Formatter.Format(data as LogEntry));
} else
{
this.WriteLine(data.ToString());
}
}
public override void Write(string message)
{
Trace.Write(message);
}
public override void WriteLine(string message)
{
Trace.WriteLine(message);
}
}
}
First i tried to add the customtracelistener with the Configuration Editor, but the configuration editor did not list/detect any customtracelisteners, which i suspect is part, or whole of the problem?
So instead, i added the following, again, based on bychkov:
app.config listener manual addition
<add listenerDataType="Microsoft.Practices.EnterpriseLibrary.Logging.Configuration.CustomTraceListenerData, Microsoft.Practices.EnterpriseLibrary.Logging, Version=5.0.505.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
type="Test.Catch.LoggerCustom, Test.Catch" traceOutputOptions="None"
name="Custom Trace Listener" initializeData="" formatter="Text Formatter Plain" />
Now when I call Inf("logme"), with the above app.config addition included, then an exception is thrown at _container.AddNewExtension():
The type 'Test.Catch.LoggerCustom, Test.Catch' cannot be resolved.
Given I dont understand the underlying mechanisms, how could i get to logging with my custom listener?
Do i need to create my own TraceListenerData, like the Tavares solution ?
Or do i need to understand and mess with UnityDefaultBehaviorExtension, as detailed by MacLeod ?
Is the existing code wrong?
Or the solution lies elsewhere?
Thank you for your expertise.
Go grab the Entlib 5 extensibility labs. There's an entire chapter on writing custom trace listeners. You'll learn the underlying technologies, why things work the way they do, how it fits together, and then you'll be able to answer your own questions.
As for why the above doesn't work: when using CustomXXXData configuration classes, your provider needs a constructor that takes a NameValueCollection. You don't have one, so your code is in fact wrong. Work through the labs, you'll learn that and more.

Ninject OnePerRequestBehaviour doesn't seem to work correctly?

I've been using Ninject as my IOC in my web app. It's great and I think it works really well, however I have been trying to register some interfaces / classes as OnePerRequestBehaviour but it doesn't seem to actually use the behaviour. The code runs correctly but in one of my classes it lazy loads page information from the database then once it has been loaded it doesn't need to hit the database.
My problem is that the lazily loaded property will load in my first request, when I then request the next page the same instance of the class is used. The reason I know this is because the class is not instantiated again and the lazily loaded property is already set.
This code is within my module class :
public class NinjectModule : StandardModule
{
public override void Load()
{
Bind<IUnitOfWorkDataStore>().To<HttpContextDataStore>().Using<OnePerRequestBehavior>();
Bind<CmsService>().ToSelf().Using<OnePerRequestBehavior>();
Bind<CmsRepository>().ToSelf().Using<OnePerRequestBehavior>();
}
}
Then inside my Global.asax which inherits from NinjectHttpApplication I have the following:
protected override IKernel CreateKernel()
{
OnePerRequestModule module = new OnePerRequestModule();
module.Init(this);
KernelOptions options = new KernelOptions();
options.InjectNonPublicMembers = true;
IKernel kernel = new StandardKernel(options, new NinjectModule());
return kernel;
}
The first call made to CmsService is within the global.asax as well on authenticate_request:
protected void Application_AuthenticateRequest(object sender, EventArgs e)
{
if (HttpContext.Current.Request.Url.AbsoluteUri.Contains(".aspx") &&
!HttpContext.Current.Request.Url.AbsoluteUri.Contains(".aspx/"))
{
CmsService facCMS = HttpKernelFactory.Get<CmsService>();
ContentPage page = facCMS.GetCurrentPage();
// DO Logic based on the page being brought back
}
}
The above GetCurrentPage() code:
public ContentPage GetCurrentPage()
{
if (_currentPage != null)
return _currentPage;
return GetCurrentPage(_isAdmin);
}
So as you can see the _currentPage variable is only loaded if it hasn't been set before, which should be once per request, however Ninject doesn't seem to be creating the CmsService per request it seems to create it for an abritrary amount of time.
Deos anyone have any idea of why this isn't working for me or any example code of where it definately does work?
Thanks
The OnePerRequestModule is an HttpModule, and needs to be loaded into your ASP.NET pipeline in order to work. If you add it to your web.config, it should work:
IIS7:
<system.webServer>
<modules>
<add name="OnePerRequestModule" type="Ninject.Core.Behavior.OnePerRequestModule, Ninject.Core"/>
</modules>
</system.webServer>
IIS6:
<system.web>
<httpModules>
<add name="OnePerRequestModule" type="Ninject.Core.Behavior.OnePerRequestModule, Ninject.Core"/>
</httpModules>
</system.web>
The OnePerRequest behavior is greatly improved in Ninject2 (which is yet to be released).

Categories