6

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?

newmanth
  • 408
  • 7
  • 18
  • Have you added the claims authorization module to your .config? – stephen.vakil Jul 24 '15 at 20:35
  • 1
    No, I'm not customizing authorization. It's my understanding the two are separate and distinct and one is not dependent on the other. Is this not correct? – newmanth Jul 24 '15 at 22:15

2 Answers2

6

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.

Colin Mackay
  • 18,736
  • 7
  • 61
  • 88
  • 1
    MSDN was not clear that ClaimsAuthenticationManager doesn't get called for Windows authentication and must be manually wired up. Thanks... exactly what I was looking for. Am I correct in assuming I could also perform the wireup in an Application_PostAuthenticateRequest method if I already have a global.asax file? – newmanth Jul 27 '15 at 15:28
  • Yes, I suppose you could. I'm personally not a fan of doing that, I prefer to put things in classes that implement IHttpModule to keep concerns separate. (i.e. This module does one thing) If your Global asax is small and going to remain small then I see no issue with putting the code there. Obviously if it gets bigger then it is because it is doing too many things. So, you'll really need to think about splitting out the different responsibilities. – Colin Mackay Jul 27 '15 at 15:54
  • Any reason why we need to take the detour over System.IdentityModel.ClaimsAuthenticationManager though? - I mean, why can't I just put the code of the custom `ClaimsTransformer` directly into the `MyClaimsAuthenticationModule` ? – Efrain Feb 09 '17 at 09:58
  • For me the overriding reason is the Single Responsibility Principle. The purpose of the Module is to provide a hook into the ASP.NET HTTP pipeline, the purpose of the claims transformer is to produce/transform the claims principal that will be added on to the pipeline. – Colin Mackay Feb 10 '17 at 11:06
0

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?

DavidJS
  • 417
  • 2
  • 5
  • 16
  • 1
    The base.Authentication method does nothing. Literally it is: `public virtual ClaimsPrincipal Authenticate(string resourceName, ClaimsPrincipal incomingPrincipal) { return incomingPrincipal; }` – Colin Mackay Jul 25 '15 at 11:29