46

How I can get my first name last name with c# in my system (logging in windows with Active Directory username and pass)?

Is it possible to do that without going to the AD?

TylerH
  • 20,799
  • 66
  • 75
  • 101
Data-Base
  • 8,418
  • 36
  • 74
  • 98

6 Answers6

62

If you're using .Net 3.0 or higher, there's a lovely library that makes this practically write itself. System.DirectoryServices.AccountManagement has a UserPrincipal object that gets exactly what you are looking for and you don't have to mess with LDAP or drop to system calls to do it. Here's all it'd take:

Thread.GetDomain().SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal);
WindowsPrincipal principal = (WindowsPrincipal)Thread.CurrentPrincipal;
// or, if you're in Asp.Net with windows authentication you can use:
// WindowsPrincipal principal = (WindowsPrincipal)User;
using (PrincipalContext pc = new PrincipalContext(ContextType.Domain))
{
    UserPrincipal up = UserPrincipal.FindByIdentity(pc, principal.Identity.Name);
    return up.DisplayName;
    // or return up.GivenName + " " + up.Surname;
}

Note: you don't actually need the principal if you already have the username, but if you're running under the users context, it's just as easy to pull it from there.

Juan
  • 4,910
  • 3
  • 37
  • 46
Jacob Proffitt
  • 12,664
  • 3
  • 41
  • 47
  • Upgrade? Yeah, I know, unhelpful. Sorry. I never was satisfied with the hoops you had to go through to get info out of AD in earlier versions of the framework. – Jacob Proffitt Aug 13 '10 at 16:03
  • 5
    [`PrincipalContext`](http://msdn.microsoft.com/en-us/library/system.directoryservices.accountmanagement.principalcontext.aspx) seems to have been introduced in .NET 3.5 – Albin Sunnanbo Jun 13 '11 at 14:59
  • 3
    I found that this didn't work, until I called this first: `Thread.GetDomain().SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal);` – Polynomial Oct 01 '12 at 15:44
  • Interesting. That wasn't necessary in my basic testing. I'm glad you got it to work, though. – Jacob Proffitt Oct 01 '12 at 21:13
  • Great exactly what I was looking for except... I had to change the first line slightly due to casting in .NET 3.5 and add the line Polynominal mentioned beforehand... `Thread.GetDomain().SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal); var principal = (WindowsPrincipal)Thread.CurrentPrincipal;` – Aidan H Oct 04 '12 at 13:32
  • Works great. How risky / inefficient would it be though to put a call to this on a page that was hit frequently? How expensive is FindByIdentity? – Craig May 31 '13 at 15:51
  • Good question. That namespace is wrapping the LDAP calls, and I suspect that the call to the directory itself is the bulk of the work. So I'd suspect that this isn't much worse than lower-level LDAP calls, if at all. – Jacob Proffitt Jun 02 '13 at 05:15
  • Can I do this without threading? I don't work in more threads yet :| – Ms. Nobody Jun 25 '13 at 11:55
  • @Ms.Nobody It isn't actually doing anything with the thread than simply identifying the principle from it (i.e. asking the system what user permissions are associated with its current execution context). It's still all single-threaded and won't introduce the complications of threading. – Jacob Proffitt Jun 25 '13 at 15:00
  • 2
    Just as a note, this answer and the one by Shivakant below will not work unless you are using AD domain admin credentials to connect to AD. – TylerH Feb 01 '18 at 21:59
57

There is an easier way to do this:

using System.DirectoryServices.AccountManagement;

UserPrincipal userPrincipal = UserPrincipal.Current;
String name = userPrincipal.DisplayName;

enter image description here

Bucket
  • 7,415
  • 9
  • 35
  • 45
11

This solution didn't work for me but this function worked great:

public static string GetUserFullName(string domain, string userName)
        {
            DirectoryEntry userEntry = new DirectoryEntry("WinNT://" + domain + "/" + userName + ",User");
            return (string)userEntry.Properties["fullname"].Value;
        }

You should call it that way:

GetUserFullName(Environment.UserDomainName, Environment.UserName);

(Found it here).

ParPar
  • 7,355
  • 7
  • 43
  • 56
  • 6
    Wait, what do you mean by "this solution". If you mean your own answer, then why did you post it? If you meant another solution, please clarify which one. – TylerH Feb 01 '18 at 20:33
  • 1
    UserPrincipal.Current completely failed to work for me. It kept throwing a COM exception. This works perfectly. Thank you! – ketar Feb 17 '23 at 03:17
  • It works in 2023, thank you! Do not forget to add the Reference System.DirectoryServices to the solution. – Julian Rios Apr 05 '23 at 20:26
3

The problem with the approved answer is that if you have a policy of Lastname, Firstname in place, then DisplayName gives Smith, John, not John Smith. There are two ways to get the correct form, the userPrincipal.Name property contains "John Smith (jsmith1)" so you could use this, and just string.Split on "(". Or use the following:

private string ConvertUserNameToDisplayName(string currentSentencedByUsername)
    {
        string name = "";
        using (var context = new PrincipalContext(ContextType.Domain))
        {
            var usr = UserPrincipal.FindByIdentity(context, currentSentencedByUsername);
            if (usr != null)
                name = usr.GivenName+" "+usr.Surname;
        }
        if (name == "")
            throw new Exception("The UserId is not present in Active Directory");
        return name;
    }

This would give the required form "John Smith" as required by the original poster.

3

The fastest way is to bind directly to their AD object using their SID, which you already have. For example:

//ASP.NET
var identity = (WindowsIdentity) HttpContext.Current.User.Identity;

//Desktop
var identity = WindowsIdentity.GetCurrent();

var user = new DirectoryEntry($"LDAP://<SID={identity.User.Value}>");

//Ask for only the attributes you want to read.
//If you omit this, it will end up getting every attribute with a value,
//which is unnecessary.
user.RefreshCache(new [] { "givenName", "sn" });

var firstName = user.Properties["givenName"].Value;
var lastName = user.Properties["sn"].Value;
Gabriel Luci
  • 38,328
  • 4
  • 55
  • 84
-1

Had the same issue. After some research and browsing the Microsoft Docs, I found the solution.

First install the System.DirectoryServices.AccountManagement package using Nuget Package Manager.

Then while calling the GetUserNameWithoutDomain method pass the following as the parameters:

GetUserNameWithoutDomain(UserPrincipal.Current.GivenName, UserPrincipal.Current.Surname); This should definitely work!

  • Please post a comment to explain your down votes. I would be happy to resolve your queries. Simply voting down a solution may hinder the correct solution from being projected to the developers facing the same issue. – Darkknight Jan 17 '19 at 19:00
  • Downvotes (not from me) are because you've posted an answer which is basically saying the same as from [the accepted answer eight years ago](https://stackoverflow.com/a/3471954/1364007). If it's different, [edit](https://stackoverflow.com/posts/54242119/edit) your answer and explain why - otherwise it looks like you've (a) copied the accepted answer, or (b) not looked at the other answers to see if you're adding anything new. – Wai Ha Lee Jan 17 '19 at 19:03