Take the 2-minute tour ×
Stack Overflow is a question and answer site for professional and enthusiast programmers. It's 100% free, no registration required.

I have an ASP.NET MVC 4 application, in which I'm trying to implement claims-based authentication and authorization. We have an SSO proxy that inserts certain info about the user (such as username) in the header. I have implemented my own ClaimsAuthenticationManager, and what I want to do is take the username from the header, create a ClaimsPrinsipal containing the UserName claim, pass it into my ClaimsAuthenticationManager, which will hit the database to figure out what roles the user should have, and populate the principal with role claims. Then I want to use the transformed claim for the rest of the user's session.

What I have so far:

Web.config:

<configuration>
  ...
  <system.web>
    ...
    <authentication mode="Forms">
      <forms loginUrl="~/Account/Authorize" timeout="20" />
    </authentication>
  </system.web>
</configuration>

MyClaimsTransformer.cs

public class MyClaimsTransformer : ClaimsAuthenticationManager
{
    private readonly IAuthenticationUow _authenticationUow;
    private readonly IWebServiceUrls _webServiceUrls;

    public MyClaimsTransformer(IAuthenticationUow authenticationUow, IWebServiceUrls webServiceUrls)
    {
        _authenticationUow = authenticationUow;
        _webServiceUrls = webServiceUrls;
    }

    public override ClaimsPrincipal Authenticate(string resourceName, ClaimsPrincipal incomingPrincipal)
    {
        if (incomingPrincipal == null || incomingPrincipal.Identity == null || !incomingPrincipal.Identity.IsAuthenticated)
            throw new SecurityException("No claims principal or not authenticated");

        var userName = incomingPrincipal.Identity.Name;

        if (string.IsNullOrWhiteSpace(userName))
            throw new SecurityException("No user name found");

        // Add role claims

        return incomingPrincipal;
    }
}

Global.asax.cs

public class MvcApplication : System.Web.HttpApplication
{
    protected void Application_Start()
    {
        AreaRegistration.RegisterAllAreas();

        WebApiConfig.Register(GlobalConfiguration.Configuration);
        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
        RouteConfig.RegisterRoutes(RouteTable.Routes);
        BundleConfig.RegisterBundles(BundleTable.Bundles);

        FederatedAuthentication.FederationConfigurationCreated += FederatedAuthentication_FederationConfigurationCreated;
    }

    private void FederatedAuthentication_FederationConfigurationCreated(object sender, System.IdentityModel.Services.Configuration.FederationConfigurationCreatedEventArgs e)
    {
        WebServiceUrls serviceUrls = new WebServiceUrls();
        AppProperties appProperties = new AppProperties();
        serviceUrls.UserNameWebServiceUrl = appProperties.UserNameWebServiceUrl;
        serviceUrls.PointOfContactWebServiceUrl = appProperties.PointOfContactWebServiceUrl;
        e.FederationConfiguration.IdentityConfiguration.ClaimsAuthenticationManager =
            ObjectFactory.With<IWebServiceUrls>(serviceUrls).GetInstance<MyClaimsTransformer>();
    }
}

AccountController.cs

[Authorize]
public class AccountController : BaseController
{
    [AllowAnonymous]
    public ActionResult Authorize(string returnUrl)
    {
        string userId = Request.Headers["userid"];

        if (Url.IsLocalUrl(returnUrl))
        {
            var claims = new List<Claim>
            {
                new Claim(ClaimTypes.Name, userId)
            };

            var id = new ClaimsIdentity(claims, "Custom", ClaimTypes.Name, ClaimTypes.Role);
            var principal = new ClaimsPrincipal(id);

            // call out to registered ClaimsAuthenticationManager
            var claimsAuthManager = FederatedAuthentication.FederationConfiguration.IdentityConfiguration.ClaimsAuthenticationManager;

            // set the transformed principal
            var url = Request.Url;
            if (url != null)
            {
                var newPrincipal = claimsAuthManager.Authenticate(url.AbsolutePath, principal);
                SetSessionCookie(newPrincipal);
                Thread.CurrentPrincipal = newPrincipal;
            }

            return Redirect(returnUrl);
        }
        throw new InvalidDataException("BAD URL");
    }

    private void SetSessionCookie(ClaimsPrincipal incomingPrincipal)
    {
        SessionSecurityToken token = new SessionSecurityToken(incomingPrincipal);
        FederatedAuthentication.SessionAuthenticationModule.WriteSessionTokenToCookie(token);
    }
}

This code runs fine, and when the Thread.CurrentPrincipal = newPrincipal; line gets executed, all the proper claims are in the principal. But when I use my custom ClaimsAuthorizationManager to check the claims, none of them are there (also, the Principal is no longer Authenticated). It just re-directs back to the AccountController to authenticate again.

What am I missing?

share|improve this question
    
Is the Authorize action in the AccountController the only place you set the modified principal on the thread and in the cookie? –  ulty4life Oct 17 '13 at 21:58

1 Answer 1

In your Authenticate override, you should probably route the incomingPrincipal through the base ClaimsAuthenticationManager:

return base.Authenticate(resourceName, incomingPrincipal);
share|improve this answer
    
True, but the base Authenticate method doesn't perform any logic; it just returns the incomingPrincipal. –  ulty4life Oct 17 '13 at 21:41

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.