1

I need to be able to retrieve the current logged-on Azure AD user's email (technically a UPN) from Windows 10 via C#. I can get the following information:

WindowsIdentity currentIdentity = WindowsIdentity.GetCurrent();

string userSid =
    currentIdentity.Claims.FirstOrDefault(
        u => u.Type == System.Security.Claims.ClaimTypes.PrimarySid).Value;

string username = currentIdentity.Name; // This returns "AzureAD\\TestUser"

But what I really need is the email/UPN as below (test.user@testtenant.onmicrosoft.com):

Windows 10 User Info

I've looked through all properties of the currentIdentity object and can't see it anywhere, is this possible?

ataraxia
  • 995
  • 13
  • 31
  • Please check this issue: https://stackoverflow.com/a/4497032/13308381 – unknown Mar 26 '21 at 01:20
  • @PamelaPeng I noticed that question shortly after posting, however it throws an `InvalidCastException` with the error message `Unable to cast object of type 'System.DirectoryServices.AccountManagement.GroupPrincipal' to type 'System.DirectoryServices.AccountManagement.UserPrincipal'.` – ataraxia Mar 26 '21 at 11:12

3 Answers3

2

Because the computer in question is Azure registered, it is not seen as on a domain (i.e. local network domain), therefore any operations involving connecting to a PrincipalContext will fail. The user's UPN is also not stored in the WindowsIdentity object, presumably for security?

It can be retrieved from the GetUserNameEx method in secur32.dll using something like the below:

public enum ExtendedFormat
{
    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.Unicode)]
public static extern int GetUserNameEx(int nameFormat, StringBuilder userName, ref int userNameSize);

public string GetCurrentUPN()
{
    StringBuilder userUPN = new StringBuilder(1024);
    int userUPNSize = userUPN.Capacity;

    if (GetUserNameEx((int)ExtendedFormat.NameUserPrincipal, userUPN, ref userUPNSize) != 0)
    {
        return userUPN.ToString();
    }

    return null;
}

This is obviously for the current user only, presumably to retrieve another user's UPN you'd need to contact the Azure AD tenant directly using the Graph API.

ataraxia
  • 995
  • 13
  • 31
0

You have to use System.Security.Claims.ClaimTypes.Upn

That will give you test.user@testtenant.onmicrosoft.com

SmartCoder
  • 856
  • 6
  • 11
  • I checked all claims on the returned object and UPN isn't in there, just the SID and some other identifiers which appear to be groups that the user belongs to. – ataraxia Mar 28 '21 at 08:43
  • Are you using the right namespace? I have used it in my code and can see also referred in MS docs https://learn.microsoft.com/en-us/dotnet/api/system.security.claims.claimtypes.upn?view=netframework-4.8 – SmartCoder Mar 29 '21 at 02:33
  • I checked through every claim that the returned object contained, along with the claim values, the UPN wasn't there at all. The `ClaimType` you are referring to is just a library of string constants to assist with cleaning up code, the actual value in the object's claims dictionary is `http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn`. So no, this claim isn't in the `WindowsIdentity` object. – ataraxia Mar 29 '21 at 13:30
0

If you're on .NET 3.5 and up, you can use UserPrincipal.Current Property:

// set up domain context
PrincipalContext ctx = new PrincipalContext(ContextType.Domain);

// find current user
UserPrincipal user = UserPrincipal.Current;

if(user != null)
{
   string loginName = user.SamAccountName; // or whatever you mean by "login name"
}    

There is a similar issue about InvalidCastException: https://stackoverflow.com/a/31819930/13308381

unknown
  • 6,778
  • 1
  • 5
  • 14
  • 1
    `new PrincipalContext(ContextType.Domain)` throws a `PrincipalServerDownException` with an inner `LdapException` with the error message `The LDAP server is unavailable.` and `UserPrincipal.Current` throws an `InvalidCastException` with the error message `Unable to cast object of type 'System.DirectoryServices.AccountManagement.GroupPrincipal' to type 'System.DirectoryServices.AccountManagement.UserPrincipal'.` – ataraxia Mar 28 '21 at 17:04