Log AD users in - c#

I have a login form written in C# and I want only AD users to be able to login.
How should I do this?
string UserName = "";
string Pass = "";

Although it is not an ASP.Net app the active directory membership provider will work just fine.
Here is info on how to use this library:
http://msdn.microsoft.com/en-us/library/system.web.security.activedirectorymembershipprovider.aspx
and here is some more information:
http://msdn.microsoft.com/en-us/library/ff650308.aspx

I am sure that this is not a best practice, but, depending on your security needs, you could allow all domain users and exclude local users by checking just the UserDomainName in the Form_Load. This simple approach piggybacks on their computer login, and does not have the complexity of any LDAP/AD calls.
if (SystemInformation.UserDomainName.ToString() == "myDomain")
{
// your normal form load code here
}
else
{
form1.Close(); //this is a simple but effective to pull the rug out from
//under them if they do not have the permissions
//TODO email the application administrator the `SystemInformation.UserName` of the user who was not given permissions
}
In my environment, since our in-house apps are deployed via ClickOnce (installed per user per computer), a similar approach (we compare usernames too) has always been sufficient for us.

If you want to know how to verify credentials to Active Directory in order to allow AD users in you application, you should check this.
You'll find how to verify the content of your textboxes and verify if username and passowrd matches (directly with the AD).

Related

Matching current user with ActiveDirectory DirectoryEntry

My scenario:
A client app (Net Core WPF) should somehow find out the current user's identity (for example using System.Security.Principal.WindowsIdentity.GetCurrent()) and authenticate with a REST server application (Net Core) which has access to AD (it knows the address, name and password of root AD DirectoryEntry). The authentication should be successful if and only if the user from the client app is found among users in AD. This is an intranet setup btw.
Solutions to similar questions here on SO (for example How to get the current user's Active Directory details in C#) generally propose using DirectorySearcher and filtering on user name "(sAMAccountName=theUserIWantToMatch)".
But IMHO this is not sufficient:
1) It is not secure enough, you can easily impersonate anybody just by creating a user with a similar name. Not to mention man-in-the-middle attacks.
2) It needn't even be malicious, plenty of people have similar names. I might have connected to the intranet network via VPN using a computer with a similar user name (similar to somebody else already on that network).
Can you think of a better way to match the users (using some GUID or token for example) or completely different authentication method? Just to reiterate: I can't use usual ASP.NET windows auth because my client is a WPF app that communicates with the server using HttpClient instance.
Thank you.
A fail-proof way of getting the exact user that's logged in is by using the SID, which is available from WindowsIdentity.GetCurrent().User.
From there, you can bind directly to the AD object using the LDAP SID binding syntax of LDAP://<SID=XXXXX>.
That will look something like this:
var sid = WindowsIdentity.GetCurrent().User;
var currentUser = new DirectoryEntry($"LDAP://<SID={sid}>");
If the computer you're running this from is not joined to the same domain as the user (or trusted domain), then you will need to include the domain name in the LDAP path:
var currentUser = new DirectoryEntry($"LDAP://example.com/<SID={sid}>");
This method is also faster than any other method, since you're not performing a search and then binding to the object. It's all done in one network request.

Need to add Offline and Touch ID to Microsoft Authentication Layer

I have some Xamarin C# code that authenticates users against the company directory, it's basically the code found in the Microsoft tutorial, and right now we're iOS-only:
App.xaml.cs
PublicClientApplicationOptions options = new PublicClientApplicationOptions()
{
ClientId = MyAppClientId,
TenantId = MyAppTenantId
};
var builder = PublicClientApplicationBuilder.CreateWithApplicationOptions(options);
if (!string.IsNullOrEmpty(iOSKeychainSecurityGroup))
{
builder = builder.WithIosKeychainSecurityGroup(iOSKeychainSecurityGroup);
}
PCA = builder.Build();
ViewModel.cs
string Scopes = "User.Read";
var scopes = Scopes.Split(' '); // Yeah, overkill
// First, attempt silent sign in
// If the user's information is already in the app's cache,
// they won't have to sign in again.
string accessToken = string.Empty;
try
{
var accounts = await App.PCA.GetAccountsAsync();
// PCA.GetAccountsAsync() returned [List<IAccount> #=0]
if (accounts.Count() > 0)
{
var silentAuthResult = await App.PCA
.AcquireTokenSilent(scopes, accounts.FirstOrDefault())
.ExecuteAsync();
accessToken = silentAuthResult.AccessToken;
}
}
catch (MsalUiRequiredException)
{
// This exception is thrown when an interactive sign-in is required.
// Don't need to do anything, we will notice the empty access token later
}
if (string.IsNullOrEmpty(accessToken))
{
// Prompt the user to sign-in
var interactiveRequest = App.PCA.AcquireTokenInteractive(scopes);
// PCA.AcquireTokenInteractive(scopes) returned Microsoft.Identity.Client.AcquireTokenInteractiveParameterBuilder
if (authUiParent != null)
{
interactiveRequest = interactiveRequest
.WithParentActivityOrWindow(authUiParent);
}
try
{
var authResult = await interactiveRequest.ExecuteAsync();
}
catch (MsalClientException clientException)
{
// When I entered the wrong password, and then hit cancel:
// Or, when I got the "you need admin permissions" error and then hit cancel:
/*
interactiveRequest.ExecuteAsync() threw MsalClientException [error code "authentication_canceled"]
Exception MsalClientException: User canceled authentication.
*/
}
}
That's working great, now I need it to be slightly different (of course).
1) First of all, we need an "offline" mode. If the user wants to access the app in a place that has no Internet, we want them to enter their username and password which will be compared against the last known good value for that user. Right now we are using an internal encrypted database to store last known good values for comparison. Yes, there is a security hole here, if we've disabled a user's account on the server they can continue to log in as long as they disable the Internet on their mobile device - we have other ways to minimize this problem that I won't go into here.
2) Secondly, we want to allow touch ID. But even if the fingerprint verifies the identify of the user, we still want to check if that user has been disabled at the server - so we need to send the "last known good values" for that user to the server for verification. Of course, if there is no Internet, the fingerprint will be enough. I'm assuming this means we need a screen before calling AcquireTokenInteractive() where we give the user a chance to use Touch ID, with a button that says "nope, I want to type in my username and password please"
3) Finally, even when we have Internet and the user has chosen to not use Touch ID, we want them to enter their password every time. We want to remember the username of the most-recently-logged-in user and fill that in for them to speed things up, but for security reasons we need a password every time.
It seems security of your application is your biggest concern. For this reason I'll go in to some detail on security and identity to help your decision making.
Issue 1: Offline Mode
I'm afraid the approach you've taken for offline access isn't advisable. In fact, in MSAL we actively mitigate it by using the System Browser.
Security Issues with Embedded Browsers and Forms Based Authentication in Mobile Applications
When a user signs in to a mobile application using either an embedded webview or a form based authentication, that application can access the plain text username and password entered in to the application. If a user downloads an application that is acting like a legitimate application from your company, those credentials can be stolen without the user being aware. This is a security vulnerability, and one that is severe enough that Google has taken the step of blocking all apps that use embedded webview or forms based authentication.
System Browsers and Authentication
To prevent this storage of credentials by applications Google, Microsoft, and others have switched to the System Browser to collect credentials. We use the new ability of the operating system on mobile devices to display a web sign-in experience on top of your application. This appears native to the user but is actually the browser of the operating system. Because neither the application nor Microsoft has access to the browser of the operating system, the credentials entered by your users are safe. You'll see most modern applications using this pattern.
This will also prevent you from storing username and password of your users and is by design. Please don't store your user's credentials anywhere.
How To Allow Offline Access for Applications
To achieve your scenario correctly there are two options we've seen apps use:
The most popular pattern we've seen is for applications to ask the user to set a PIN to access the application when they first sign-in or as an option in Settings. Often times the user is asked to set a PIN as well as Touch ID/Face ID if Touch ID/Face ID fails or is reset. The user then uses the PIN to access the app on each launch, and this also works when internet isn't available. This PIN is stored securely and encrypted on the device. As soon as the internet is available the application should call acquireTokenSilently() to ensure the user still has access to the resource. If they don't, you should prompt the user to sign in again. Many banks and other highly regulated industries use this pattern as the best compromise between user experience and security.
The next option is to use the resiliency we've built in to the library for companies and application developers that want the user to maintain access to resources in case of an outage. We allow companies and application developers to configure a token lifetime that is longer than our default token lifetime for access tokens and refresh tokens. When a user isn't able to access the internet to get a new access token, the extended lifetime can allow the user to continue to use the application.
This has the added benefit of working if your APIs are local to your environment but the identity provider is cloud based or if Azure AD is suffering an outage but the rest of the internet is working. Your internal APIs will accept the tokens so your users can continue to work even if the internet is inaccessible. Both the API and your app will need to use MSAL in order for your API to honor the extended lifetime specified in the token. Keep in mind we have maximum value for the access token of one day. If your internet outage is longer than that, I recommend using option 1 above.
Issue 2: Check User With Identity Service When Using Touch ID
This answer is fairly easy. Just use acquireTokenSilently() after the Touch ID is used. This will acquire a new token for the resource you requested the token for. For default token values, the access token will be refreshed every 1 hour. I do not recommend forcing authenticating every Touch ID as that will introduce significant slowdown and cellular usage. If you just want to check the user is present, I recommend you use a locally stored PIN as discussed above.
Issue 3: Force Authentication if Touch ID is not enabled
You can force authentication at any time by using acquireToken()with the appropriate values. See the code sample here for the method acquireTokenInteractively().
However, a small amount of warning here on usability:
The Problem With Using Password Frequently To Test User Access
The identity service is used to check if a user has access to the resources, not to check if the user has access to the device or if the user is still the user from a previous session. For that you should use the PIN method I discuss above, along with Touch ID/Face ID if available. Why?
For true security, a password should be combined with 2FA. Research indicates that 99% of illegal access can be mitigated by 2FA. However, when a user has to use 2FA for a sign-in it increases the friction they have to do endure to sign-in. In fact, relying on passwords at all is problematic.
Your use case seems to indicate you want the maximum amount of security for your application. That necessitates using 2FA if possible. However, moving to the best security posture for your application would also make using a password at every app launch very difficult for your users.
The recommended pattern would again be PIN or Touch ID/Face ID along with a password prompt after either a change to the user's access to a resource or after a long period of time.

WPF application with Windows authentication

I have a simple wpf client (few text boxes) that uploads some data to a web service. And I want to use windows authentication to go with my application.
I am checking in OnStartup of App.xaml, whether or not the user is authenticated. My question is around what is the meaning of Thread.CurrentPrincipal.Identity.IsAuthenticated.
I don't want my application to be used from outside my network as it is connecting to a web service and uploads data. But my assumption is as long as you run this application from inside any windows network the above mentioned property will always return true?
So how do I find out if the application is being run from inside my network. I don't think checking domain name or role name is any different, because I can always setup a domain and name it whatever I want. I don't want to prompt user for username or password of any sort.
How do you check Identity of user against a particular AD (AD might not be publically available). Basically the application should only works from my local network or through VPN.
var context = new PrincipalContext(ContextType.Domain, "DOMAINNAME");
var result = UserPrincipal.FindByIdentity(domainContext, IdentityType.SamAccountName, userName);
If the result is null, then the user does not exists in the AD domain.
You can also user DirectorySearcher class to query AD based on a filter criteria. This is more useful only if you would like to retrieve additional details about the user like contact, email address etc.

How restrict multiple logins same user C# and Active Directory membership

How to prevent multiple logins of same user using AD membership and C#?
I try to use MembershipUser.IsOnline but this is not suported by AD membership provider.
This is a code:
MembershipUser muser = Membership.GetUser(model.UserName);
if(muser.IsOnline)
{
//TODO
}
But it catch this exeption: "LastLoginDate" is not supported by the Active Directory Membership Provider
Not with help of AD membership classes but still:
Add user's SessionId to Application every time session starts.
Add user's SessionId to his cookies
Every request you have to check if SessionId's match.
that seems more an issue of configuration in your active directory, you may set your cache credentials in your network policy to 0, so you won't use the kerberos ticket cache.
Then you have to solve the next problem, that is how many logins do you allow in your network, this article describes one way to approach it: http://technet.microsoft.com/en-us/magazine/2005.05.utilityspotlight.aspx
I think the situation I had was a little different, but I feel that the same principles can apply. My situation was such that I had to make sure that user ID's were logged in from just one device at a time (Forms Authentication, by the way, not AD). When a user ID tried to log in to another device while still logged in to an existing device, it killed the session on their existing device while allowing them to log-in to the new device. I wrote up a solution on my original post on Stack Overflow:
When the same user ID is trying to log in on multiple devices, how do I kill the session on the other device?

Centralize Authentication Code Between Applications

For a given application I have a .aspx login form backed by a C# code behind file.
In the code behind I am using the following "home grown" method:
private bool AuthenticateUser(String username, String password)
{
bool validated = false;
try
{
PrincipalContext pc = new PrincipalContext(ContextType.Domain, "domnet.domad.com", "dc=domnet,dc=domad,dc=com");
IdentityType ADElement;
UserPrincipal up;
//Try first with no #DOM.COM - this should work for SamAccountName values:
username = username.ToUpper().Replace("#DOM.COM", "");
ADElement = IdentityType.SamAccountName;
up = UserPrincipal.FindByIdentity(pc, ADElement, username);
validated = pc.ValidateCredentials(username, password, ContextOptions.Negotiate);
//If SamAccountName fails try UserPrincipalName with #DOM.COM
if (!validated)
{
username = username + "#DOM.COM";
ADElement = IdentityType.UserPrincipalName;
up = UserPrincipal.FindByIdentity(pc, ADElement, username);
validated = pc.ValidateCredentials(username, password, ContextOptions.Negotiate);
}
//Put username into session
if (validated)
{
Session["Username"] = username.Replace("#DOM.COM", "");
}
}
catch (Exception) //login failure...
{
validated = false;
}
return validated;
}
This works fine for the application but I have other applications that need authentication too.
I really don't want to copy / paste the login files into ever application.
So my most basic question is what are my options to centralize the authentication code between applications?
In the future I will also be looking to:
Verify not only username/password but also AD group membership.
Once user is authenticated no more log in screens between apps. (SSO)
It seems to me I am not the first person to run into this problem.
I would prefer to use an out of the box solution vs. developing my own if possible.
You could:
Enable Windows authentication in IIS and set your Web.Config to use Window authentication http://weblogs.asp.net/scottgu/archive/2006/07/12/Recipe_3A00_-Enabling-Windows-Authentication-within-an-Intranet-ASP.NET-Web-application.aspx
Setup an ADFS claims authentication server and have all your applications use claims based authentication http://blogs.msdn.com/b/alextch/archive/2011/06/27/building-a-test-claims-aware-asp-net-application-and-integrating-it-with-adfs-2-0-security-token-service-sts.aspx
Have one application that all others redirect to for authentication and set an encrypted cookie that the other applications can read to verify authentication Encrypt cookies in ASP.NET
One approach would be to create a Core project (.dll/library) that contains the common parts that you wish to share between your applications, and then to reference that project in your applications.
Ie: Say that you have 2 applications: A and B you would create three projects A, B and Core. In project A and B simply add a project reference to the Core library. Now you can access any method in core from both A and B.
This approach works well with SVN and similar version control systems and you will find it is a very flexible way of working. The hard part is to identify what is really common code and and to make as general as possible.
#Baxter Not sure if my answer comes a bit late as this question was posted a few days ago, but am looking into the same problem of implementing centralized session and authentication management in my MVC 3 application, and I believe the following link would be of great interest to you:
http://www.codeproject.com/Articles/246631/ASP-NET-MVC3-Form-Authentication
The author of the article corresponding to the link above, factors out the authentication functionality into a separate DLL and uses dependency injection to use application context to utilize the external 'security' DLL. I am planning to use this approach to centralize the security mechanism and reuse it in 3 different MVC 3 web applications, so it is still research in progress, and will update this answer accordingly with what i find :)
You can refactor this method out into a separate project (meaning a different dll) and reference that project from any web application where you want to use this code.
An alternative if you are using Windows Authentication is to grab their SID, query AD for a piece of information that is shared between AD and the application's user table (we use the email address) and check to see if the user table has an entry with that email address.
This way, by logging onto their workstation, they are essentially pre-logged into any application using this authentication method. You just have to make sure that when you create a new user account (at the application level) you capture the info that you want to check for authentication (this is why we use the email address - everyone knows their company email).
This works really well with the Core library method suggested by Avada Kedavra. This method also allows you to have each application maintain its own user base (although it will also work well with a central user database).

Categories

Resources