MVC3 Custom MembershipProvider & Active Directory

1k views Asked by At

I have an MVC3 application that has a custom membership provider and user/roles that are stored in the database. Users are created manually in the application as required and appropriate roles assigned.

I'd like to now extend the application to provide an option of using Active Directory, though because the application has several custom fields + tables with FK lookups on the user, I am thinking that I will still have to have a custom version of the default active directory membership provider.

Has anybody on SF done something similar that they can share with me? Thanks

1

There are 1 answers

0
Ben Tidman On

I know this is an old question but...

Let see where to start

In my web app i set up federated claims based authentication directly with my ADFS server. I haven't been able to find a good tutorial on how to do this cause it's not trivial. But there are plenty of references on how to do this using azure ACS as a middle man. This one will at least get you started:

http://haishibai.blogspot.com/2011/05/tutorialaspnet-mvc-3-claim-based.html

Once you get this working you just need a couple of things.

Add a couple of properties on your database user table that you can link with AD. I store the AD GUID in mine, but I also use Email address as a secondary. This allows me to create users in my app, then have them authenticate with AD. I just pass back their email as a claim, match them with user in my app, then add AD GUID to user.

I also take advantage of inheritance to do my authentication. All my controllers inherit from BaseController so they get this standard behavior.

public class BaseController
{
    protected override void OnAuthorization(System.Web.Mvc.AuthorizationContext filterContext)
    {
        if (filterContext.HttpContext.User.Identity.IsAuthenticated)
        {
            //read in the claims that we got back from ADFS
            IClaimsPrincipal icp = Thread.CurrentPrincipal as IClaimsPrincipal;
            IClaimsIdentity ici = icp.Identity as IClaimsIdentity;

            var claims = ici.Claims;

            // This is a claim that I add manually to see if I've already synced
            // ADFS user with DB user
            var ppid = claims.FirstOrDefault(x => x.ClaimType == ClaimTypes.PPID);
            if (ppid == null)
            {
                //query/sync user.
                var guidString = claims.FirstOrDefault(x => x.ClaimType == ClaimTypes.Name).Value;

                // get AD GUID 
                var userGuid = new Guid(System.Convert.FromBase64String(guidString));

                //look up user
                var currentUser = UserRepository.FetchUserByGUID(userGuid);

                //if user not found try fetch by email.
                if (currentUser == null)
                {
                    var email = claims.FirstOrDefault(x => x.ClaimType == ClaimTypes.Email).Value;
                    currentUser = UserRepository.FetchByEmail(email);
                }

                //If user is still not found create User
                if (currentUser == null)
                {
                    currentUser = new Models.User();
                    BaseRepository.GetDataContext().Users.Add(currentUser);
                }

                //update users information using AD claim as master record
                currentUser.ADID = userGuid;
                currentUser.Name = claims.FirstOrDefault(x => x.ClaimType == ClaimTypes.GivenName).Value;
                currentUser.EmailAddress = claims.FirstOrDefault(x => x.ClaimType == ClaimTypes.Email).Value;
                currentUser.LastLoginDate = DateTime.UtcNow;
                currentUser.LoginCount = currentUser.LoginCount + 1;

                BaseRepository.GetDataContext().SaveChanges();

                // Now that you have your AD user linked to your user record 
                // in your database...
                // Create new claims in your ADFS token that include all the roles that
                // your user has.  That way you can just piggyback on claims based 
                // authentication
                foreach (var r in currentUser.Roles)
                {
                    claims.Add(new Claim(ClaimTypes.Role, r.Name));
                }

                // Add userid claim so that we know that this users claims have already
                // been sync with my database
                claims.Add(new Claim(ClaimTypes.PPID, currentUser.Id.ToString()));
            }
        }

        base.OnAuthorization(filterContext);
    }

Hope that helps!