Get Active Directory User Information With Windows Authentication in MVC 4
active-directory, asp.net-mvc-4, authentication, windows-authentication
Solution
I was just about to add my own question to StackOverflow with my solution to help others with this issue, when I saw your existing question. It seems like this would be a very common thing, but the information about how to do it only is spread out between multiple sources and hard to track down. There's not just one complete resource, so hopefully this will help you and others.
The best way to do this is use a UserPrincipal extension. Basically, you're subclassing `UserPrincipal` from `System.DirectoryServices.AccountManagement` and adding your own additional properties. This is enabled via the `ExtensionGet` and `ExtensionSet` (somewhat magical) methods.
[DirectoryRdnPrefix("CN")]
[DirectoryObjectClass("user")]
public class UserPrincipalExtended : UserPrincipal
{
public UserPrincipalExtended(PrincipalContext context) : base(context)
{
}
public UserPrincipalExtended(PrincipalContext context, string samAccountName, string password, bool enabled)
: base(context, samAccountName, password, enabled)
{
}
[DirectoryProperty("title")]
public string Title
{
get
{
if (ExtensionGet("title").Length != 1)
return null;
return (string)ExtensionGet("title")[0];
}
set
{
ExtensionSet( "title", value );
}
}
[DirectoryProperty("department")]
public string Department
{
get
{
if (ExtensionGet("department").Length != 1)
return null;
return (string)ExtensionGet("department")[0];
}
set
{
ExtensionSet("department", value);
}
}
public static new UserPrincipalExtended FindByIdentity(PrincipalContext context, string identityValue)
{
return (UserPrincipalExtended)FindByIdentityWithType(context, typeof(UserPrincipalExtended), identityValue);
}
public static new UserPrincipalExtended FindByIdentity(PrincipalContext context, IdentityType identityType, string identityValue)
{
return (UserPrincipalExtended)FindByIdentityWithType(context, typeof(UserPrincipalExtended), identityType, identityValue);
}
}
The two attributes on the class need to be customized to your instance of AD. The value for `DirectoryRdnPrefix` needs to be the RDN (relative distinguished name) in AD, while the value for `DirectoryObjectClass` needs to be the directory object type name in AD for a userObject class. For a typical AD Domain Services setup, they should both be as used in the code presented above, but for an LDS setup, they could be different. I've added two new properties that my organization uses, "title" and "department". From that, you can get an idea of how to add any other property you like: basically you just create a property using the template I've provided here. The property can be named anything you like, but the string value passed to `DirectoryProperty` and inside the code block should match up to a property name from AD. With that in place, you can use `PrincipalContext` with your subclass instead of `UserPrincipal` to get back a user object with the properties you need added.
UserPrincipalExtended user = UserPrincipalExtended.FindByIdentity(
new PrincipalContext(ContextType.Domain), User.Identity.Name);
And access your property like any other on the `UserPrincipal` instance:
// User's title
user.Title
If you're unfamiliar with `System.DirectoryServices.AccountManagement.UserPrincipal`, there's a few user properties baked in: `GivenName`, `Surname`, `DisplayName`, etc. In particular to your circumstance, since you mentioned phone and email specifically, there's `VoiceTelephoneNumber` and `EmailAddress`. You can see the full list in the MSDN docs. If all you need is the built-in information, you don't need to extend `UserPrincipal` as I showed above. You would just do:
UserPrincipal user = UserPrincipal.FindByIdentity(
new PrincipalContext(ContextType.Domain), User.Identity.Name);
But, 9 times out of 10, the built-ins won't be enough, so it's good to know how to get the rest easily.
Finally, I didn't want to have to add `@using` lines to any view that uses this, so I went ahead and added the namespaces to my `Views` folder's web.config. That part is important, it needs to be added to the `Views` folder's web.config, not the project's (and each Area's individual `Views` folder if you're utilizing Areas).
<system.web.webPages.razor>
...
<pages pageBaseType="System.Web.Mvc.WebViewPage">
<namespaces>
...
<add namespace="System.DirectoryServices.AccountManagement" />
<add namespace="Namespace.For.Your.Extension" />
</namespaces>
</pages>
</system.web.webPages.razor>
Problem
I am working on an MVC 4 intranet application and am using Windows authentication. I would like to add to the user object that the authentication method uses (@User) and get that data from active directory (such as email, phone number, etc). I know I can create a custom Authorize attribute and add it to the controller that all of my other controllers inherit from, but I don't know if this is the right method to do what I want. My end goal is simple, I want @User object to have additional properties that are populated via Active Directory. Thanks for any help you can offer.