Question

I am currently using windows azure active directory as a single sign-on in my MVC.NET application and that portion works great. I can authenticate against WAAD and get my ClaimsPrinicipal loaded without any problems.

The next step was to transform the claims retrieved from WAAD by adding new claims from a different data source. To this extent I created a class inheriting the ClaimsAuthenticationManager (below). The claims get added to the Principal and get persisted to the session cookie in the CreateSession method.

My problem right now is that ClaimsPrincipal.Current does not carry any of the additional claims that I've added. When I set a breakpoint in the SessionAuthenticationModule_SessionSecurityTokenReceived event, I can see that there's a discrepancy between the ClaimsPrincipal.Current

ClaimsPrincipal.Current.FindAll(ClaimTypes.Email)
Count = 0

and e.SessionToken.ClaimsPrincipal.

e.SessionToken.ClaimsPrincipal.FindAll(ClaimTypes.Email)
Count = 1
[0]: {http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress: me@mydomain.com}

What am I missing here? In all the samples that dealt with transforming claims that I could find there's no mention of manually reloading the ClaimsPrinicipal from the cookie. Would the session security token event be the right place to reload the ClaimsPrincipal or am I breaking the security model?

Thanks.

public class MyAuthenticationManager : ClaimsAuthenticationManager
{
    public override ClaimsPrincipal Authenticate(string resourceName, ClaimsPrincipal incomingPrincipal)
    {
        if (!incomingPrincipal.Identity.IsAuthenticated)
        {
            return base.Authenticate(resourceName, incomingPrincipal);
        }

        var transformedPrincipal = this.CreateUserPrincipal(incomingPrincipal.Identity.Name);
        this.CreateSession(transformedPrincipal);

        return transformedPrincipal;
    }

    private ClaimsPrincipal CreateUserPrincipal(String userName)
    {
        List<Claim> claims = new List<Claim>();
        var user = SecurityController.GetUserIdentity(userName);

        claims.Add(new Claim(ClaimTypes.Name, userName));
        claims.Add(new Claim(ClaimTypes.Email, user.Email));
        claims.Add(new Claim(ClaimTypes.GivenName, user.FirstName));
        claims.Add(new Claim(ClaimTypes.Surname, user.LastName));

        return new ClaimsPrincipal(new ClaimsIdentity(claims, "MyCustom"));
    }

    private void CreateSession(ClaimsPrincipal transformedPrincipal)
    {
        var sessionSecurityToken = new SessionSecurityToken(transformedPrincipal, TimeSpan.FromHours(8));

        if (FederatedAuthentication.SessionAuthenticationModule != null &&
        FederatedAuthentication.SessionAuthenticationModule.ContainsSessionTokenCookie(HttpContext.Current.Request.Cookies))
        {
            return;
        }
        FederatedAuthentication.SessionAuthenticationModule.WriteSessionTokenToCookie(sessionSecurityToken);
        //Added line below as per suggestion in one of the posts
        //Doesn't seem to have any effect
        Thread.CurrentPrincipal = transformedPrincipal;
        FederatedAuthentication.SessionAuthenticationModule.SessionSecurityTokenReceived += SessionAuthenticationModule_SessionSecurityTokenReceived;
    }

    void SessionAuthenticationModule_SessionSecurityTokenReceived(object sender, SessionSecurityTokenReceivedEventArgs e)
    {
        System.Diagnostics.Debug.WriteLine("SessionAuthenticationModule_SessionSecurityTokenReceived");
    }
Was it helpful?

Solution

Looks like I had to access the claims through ClaimsIdentity instead of ClaimsPrincipal. Now I can successfully access the claims from any view or controller in my application.

((ClaimsIdentity)Thread.CurrentPrincipal.Identity).FindAll(ClaimTypes.Email)
Count = 1
    [0]: {http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress: me@mydomain.com}

Final codebase in AuthenticationManager looks like this (note that there is no explicit assignment operation to the ClaimsPrincipal on the current thread).

public class MyAuthenticationManager : ClaimsAuthenticationManager
{
    public override ClaimsPrincipal Authenticate(string resourceName, ClaimsPrincipal incomingPrincipal)
    {
        if (!incomingPrincipal.Identity.IsAuthenticated)
        {
            return base.Authenticate(resourceName, incomingPrincipal);
        }

        var transformedPrincipal = this.CreateUserPrincipal(incomingPrincipal.Identity.Name);
        this.CreateSession(transformedPrincipal);

        return transformedPrincipal;
    }

    private ClaimsPrincipal CreateUserPrincipal(String userName)
    {
        List<Claim> claims = new List<Claim>();
        var user = SecurityController.GetUserIdentity(userName);

        claims.Add(new Claim(ClaimTypes.Name, userName));
        claims.Add(new Claim("UserId", user.Id.ToString()));
        claims.Add(new Claim(ClaimTypes.Email, user.Email));
        claims.Add(new Claim(ClaimTypes.GivenName, user.FirstName));
        claims.Add(new Claim(ClaimTypes.Surname, user.LastName));
        //claims.Add(new Claim(ClaimTypes.NameIdentifier, userName));

        if (user.Account != null)
        {
            claims.Add(new Claim("AccountId", user.Account.Id.ToString()));
            claims.Add(new Claim("AccountName", user.Account.Name.ToString()));
        }
        if (user.Owner != null)
        {
            claims.Add(new Claim("OwnerId", user.Owner.Id.ToString()));
            claims.Add(new Claim("OwnerName", user.Owner.Name.ToString()));
        }

        return new ClaimsPrincipal(new ClaimsIdentity(claims, "MyCustom"));
    }

    private void CreateSession(ClaimsPrincipal transformedPrincipal)
    {
        if (FederatedAuthentication.SessionAuthenticationModule != null &&
        FederatedAuthentication.SessionAuthenticationModule.ContainsSessionTokenCookie(HttpContext.Current.Request.Cookies))
        {
            return;
        }
        var sessionSecurityToken = new SessionSecurityToken(transformedPrincipal, TimeSpan.FromHours(8));
        FederatedAuthentication.SessionAuthenticationModule.WriteSessionTokenToCookie(sessionSecurityToken);
    }
}

OTHER TIPS

I don't see you adding your ClaimsPrincipal (after the transformation) back to Thread.CurrentPrincipal. Please try

private void CreateSession(ClaimsPrincipal transformedPrincipal)
    {
        var sessionSecurityToken = new SessionSecurityToken(transformedPrincipal, TimeSpan.FromHours(8));

        if (FederatedAuthentication.SessionAuthenticationModule != null &&
        FederatedAuthentication.SessionAuthenticationModule.ContainsSessionTokenCookie(HttpContext.Current.Request.Cookies))
        {
            return;
        }
        FederatedAuthentication.SessionAuthenticationModule.WriteSessionTokenToCookie(sessionSecurityToken);
        //Following is the missing line of code.
        Thread.CurrentPrincipal = transformedPrincipal;
   }
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top