6

(This question is similar to Get UPN or email for logged in user in a .NET web application, but not quite.)

In a .NET (C#) web application using Windows authentication, I'd like to find the UPN of the signed-in user.

I know how to do this by querying Active Directory: take the 'DOMAINNAME\userName' from HttpContext.Current.User.Identity as WindowsIdentity; look up DOMAINNAME under the ldap://RootDSE and find its dnsRoot; query for (&(objectCategory=person)(objectClass=user)(sAMAccountName=...userName...)) under ldap://...dnsRoot...; get this entry's userPrincipalName property. (More detail is in answer https://stackoverflow.com/a/513668/223837).

My question: Can the UPN be found without a call to Active Directory? Given Microsoft's focus on using UPNs everywhere, isn't the UPN already stored somewhere in the token which is created when the user authenticates to the web server?

(Supporting observation: If I run whoami /upn on Windows 7, then Wireshark does not show any Active Directory connections.)

(If it makes any difference: note that our web application does not use impersonation, i.e., our web app does not run under the user identity.)

Community
  • 1
  • 1

2 Answers2

5

Try System.DirectoryServices.AccountManagement.UserPrincipal.Current, which has the property UserPrincipalName. Of course, that would require the application to be running under Windows authentication.

Edit Meh, it looks like this API still performs a directory lookup.

moribvndvs
  • 42,191
  • 11
  • 135
  • 149
  • Well, at least this is without any _explicit_ AD querying, which removes the AD-related complexity from our code. However, I'm wondering whether this will also work for our _non-impersonated_ .NET application: does `UserPrincipal.Current` then contain the web app's identity, or the authenticated user's identity? Unfortunately the documentation suggests the former: "Gets a user principal object that represents the current user under which the thread is running." – MarnixKlooster ReinstateMonica Jun 08 '12 at 11:11
  • Yeah it gets the windows Identity, which would be that of the app pool if you aren't using windows and integrated auth, or impersonation. Look at the docs for the constrictor using context: it lets you fill in the details for the desired user then queries and loads the details for you. As you said, saves you the Complexity of LDAP queries yourself. – moribvndvs Jun 08 '12 at 23:26
  • 1
    `new UserPrincipal(context, ...)` doesn't seem to help me, since I would need to know the user's password. However, it seems that temporary impersonation might work: `var wi = HttpContext.Current.User.Identity as WindowsIdentity; using (wi.Impersonate()) { upn = UserPrincipal.Current.UserPrincipalName; }` I still have to try this out. – MarnixKlooster ReinstateMonica Jun 12 '12 at 05:12
  • 2
    I will not be trying this out anymore, but using `WindowsIdentity.Impersonate()` plus `UserPrincipal.Current.UserPrincipalName` should work. I'm marking this as the answer. – MarnixKlooster ReinstateMonica Jul 24 '12 at 07:36
5

I was having issues with the Active Directory query when using System.DirectoryServices.AccountManagement.UserPrincipal.Current, so I resorted to using GetUserNameEx with the NameUserPrincipal format.

This function gets the information about the current user, so it does require that you impersonate if you are not already. In C# it can be done by importing the function:

public enum EXTENDED_NAME_FORMAT
{
    NameUnknown = 0,
    NameFullyQualifiedDN = 1,
    NameSamCompatible = 2,
    NameDisplay = 3,
    NameUniqueId = 6,
    NameCanonical = 7,
    NameUserPrincipal = 8,
    NameCanonicalEx = 9,
    NameServicePrincipal = 10,
    NameDnsDomain = 12
}

[DllImport("secur32.dll", CharSet = CharSet.Auto)]
public static extern int GetUserNameEx(int nameFormat, StringBuilder userName, ref int userNameSize);

Then calling it like this:

string upn = null;
StringBuilder userName = new StringBuilder(1024);
int userNameSize = userName.Capacity;
if (GetUserNameEx((int)EXTENDED_NAME_FORMAT.NameUserPrincipal, userName, ref userNameSize) != 0)
{
    upn = userName.ToString();
}
Jack A.
  • 4,245
  • 1
  • 20
  • 34