How to grab AD credentials from client machine in a web application? - asp.net

Is it possible to grab activedirectory credentials for the user on a client machine from within a web application?
To clarify, I am designing a web application which will be hosted on a client's intranet.
There is a requirement that the a user of the application not be prompted for credentials when accessing the application, and that instead the credentials of the user logged onto the client machine should be grabbed automatically, without user interaction.

Absolutely. This is especially useful for intranet applications.
Since you did not specify your environment, I'll assume it is .NET, but that isn't the only way possible of course.
Active Directory can be queried easily using LDAP. If you're using .NET, you can do something like in this code example or my example below. You can also do it within SQL environments as well.
If you just need Windows to handle authentication, you can set, for example, a .NET Web app up for Windows Authentication. Be sure to turn off Anonymous Logins within IIS for your application. Once done, you'll be able to access the user's Windows logon name and use it to make further security checks (for example, their group/role membership in AD).
You can also simplify the whole mess using something like Enterprise Library's Security Application Block.
Here is a short C# example: (convert to VB.NET here)
using System.DirectoryServices;
/// <summary>
/// Gets the email address, if defined, of a user from Active Directory.
/// </summary>
/// <param name="userid">The userid of the user in question. Make
/// sure the domain has been stripped first!</param>
/// <returns>A string containing the user's email address, or null
/// if one was not defined or found.</returns>
public static string GetEmail(string userid)
{
DirectorySearcher searcher;
SearchResult result;
string email;
// Check first if there is a slash in the userid
// If there is, domain has not been stripped
if (!userid.Contains("\\"))
{
searcher = new DirectorySearcher();
searcher.Filter = String.Format("(SAMAccountName={0})", userid);
searcher.PropertiesToLoad.Add("mail");
result = searcher.FindOne();
if (result != null)
{
email = result.Properties["mail"][0].ToString();
}
}
return email;
}
You do not have to specify a domain controller. Performing the empty/default constructor for DirectorySearcher will cause it to attempt to look one up automatically — in fact, this is the preferred method.

Maybe .NET has a more direct way to do it, but with PHP I just access our Active Directory server as an LDAP server.
I'm not sure what adjustments to the server are required to do this. I didn't setup the server, I just query it.
I'm not suggesting you use PHP either. I just find it easier to deal with LDAP then trying to tie directly into Active Directory.

Windows Integrated Authentication, user has to use IE, AND the site has to be in the user's trusted sites. If these things are true, then IE will pass your windows security token to the web site and it will authenticate with it. We do this with SharePoint on our intranet otherwise it's a pain to access anything restricted -- you'd get prompted every time you click on a document.

No, of course not. Can you imagine the havoc that would result in random web apps being able to get your AD username and password?
Now, if you just want the username - that's in REMOTE_USER if you're using integated windows auth. And, windows auth will auto login the user to your site - assuming you share a domain (or trust).
Edit: IWA works in an intranet scenario, since IE - by default - includes intranet sites in the Intranet security zone. Also, a sysadmin can use GPO to set other trusted sites. Firefox also supports NTLM, as does Opera and Chrome. All in all, it's not a bad way to setup an intranet.
Note, though - that you don't get credentials. You negotiate a token with the client, which is what keeps IWA secure (and my above point relevant).

Related

If/When use "Azure Session State Provider (redis)" No need to use User.Identity.Name/IsAuthenticated?

After we have moved to Azure farm, I have implied Azure Session State Provider (redis) to my asp.net mvc application, But some Authorized pages redirect me to login page !!
Is that because I use User.Identity.Name or User.Identity.IsAuthenticated in some actions!!
Do I have to replace the User.Identity.Name with :
// instead of below line
//Boolean usern = User.Identity.IsAuthenticated;
// is below lines :
Boolean usern = "";
object objValue = Session["usersession"];
if (objValue != null)
{ usern = true;}
else {usern = false;}
Is that right, if not why users redirect to login again sometimes !!!
This is probably not a session issue but rather an authentication cookie/ticket issue. Azure most likely has their servers load balanced, even if you are only using a single instance/role(this gives them reliability %). Meaning that your application actually exists on more than one server at a time.
The MachineKey in a .NET app is what is responsible for encrypting and decrypting your authentication cookie. In your web.config, if you are not properly defining the <machineKey> attribute, then IIS makes up a machine key for you. Each server running the application will make their own machine key if it is not defined by you. As a result, one server is able to decrypt and read your authentication ticket, while the next request goes to another server which cannot decrypt the authentication ticket because it was encrypted with a different key and this server thinks that you are not logged in.
To address this issue, open your web.config file and define your <machineKey> attribute and redeploy. Once you login with the newly deployed application, you should see this issue disappear.
Forms authentication and Machine Key information on MSDN
Machine Key Generator
I don't think the session management would do this, regardless of Azure Redis or otherwise. If you are using asp.net authentication cookies you need to make changes to support multiple roles.
I know this is an old article but if you look through Moving Applications to Microsoft Azure Cloud Services you will see this:
There is one further change to the application that potentially
affects the authentication process. If you were to run the aExpense
application on more than one web role instance in Azure, the default
cookie encryption mechanism (which uses DPAPI) is not appropriate
because each instance has a different key. This would mean that a
cookie created by one web role instance would not be readable by
another web role instance. To solve this problem you should use a
cookie encryption mechanism that uses a key shared by all the web role
instances. The following code from the Global.asax file shows how to
replace the default SessionSecurityHandler object and configure it to
use the RsaEncryptionCookieTransform class.
essentially if this is happening the redirects to the login page are because you are bouncing to a different instance and it could not read the asp.net auth cookie and just assumed the user was not authenticated as a result.

ASP.NET Forms Authentication and Calling Web Service

I have an ASP.NET application that uses Forms Authentication.
I need to call the Sharepoint search.asmx web service to retrieve a list of files from the network satisfying the search criteria (there's a good reason for me doing this outside of Sharepoint)
I'm not sure of the security information I need to pass the search.asmx. I've tried:
queryService.ClientCredentials.Windows.ClientCredential = System.Net.CredentialCache.DefaultNetworkCredentials
queryService.ClientCredentials.Windows.AllowedImpersonationLevel = Security.Principal.TokenImpersonationLevel.Impersonation
-which works in my development environment as my user has access to the File Shares Sharepoint is accessing. What I can't understand, and can't infer from debugging or event viewers, etc. is what credentials are passed in the above code once I deploy this code on a server.
Will it pass the windows credentials of the user who opened the IE window prior to using forms authentication. Will it pass the credentials of the account that is running the asp.net components i.e. the account of the AppPool I'm running in, or will it pass something else?
I can't seem to get Sharepoint to return any files and I guess it's because the credentials being passed don't have access to the File Share.
Thanks
Andy
To get this working quickly you can access the SharePoint webservices with your username & password. This isn't the best solution long term obviously.
Set the Credentials property on the SharePoint webservice proxy to your username & password:
spProxy.Credentials = new NetworkCredential("username", "password", "domain");
You'll need to make sure the credentials property is set before you call the webservice.
I'm not a SharePoint expert (I've only used it as a developer), but I believe it only uses Windows authentication to secure the webservices. So forms authentication isn't going to help you out here if you want to access the webservices as the logged in user (unless you're validating the username & password manually with LDAP). ASP.NET Impersonation & Delegation only makes sense if you are using Windows authentication.
As stated by pseudocoder you might want to setup a special account to access SharePoint from the web server.

Obtaining an ICredentials object for the authenticated user of an ASP website

I'm attempting to add a feature to an existing ASP.net web site that needs to pull the users calendar data from the Exchange Web Service. I know that this is possible, and have local tests running as a proof of concept, but I'm having some issues in implementing this in the context of an ASP website.
My intention is to override the default credentials by constructing an instance of the WebCredentials class using the current user of the ASP sites credentials and then using this to create an Exchange Service Binding.
Example
ExchangeService service = new ExchangeService(ExchnageVersion.2007);
service.AutoDetect(emailAddress);
service.Credentials = new WebCredentials(credentials);
Unfortunately, the WebCredentials object can only be constructed from either a set of strings containing username, password and domain, or by passing in an ICredentials object.
So, the question is, within a Forms authenticated web site, is there any way to obtain an ICredentials object for the currently authenticated user?
No forms authentication uses an bespoke encrypted cookie which is sent by the browser with every request. It let's ASP.NET know that the user is authenticated and who they are. It's not designed to work with external systems, and in fact it requires the machinekey on the server to be able to decrypt the details. Passing forms credentials to another server would be pointless.
This system is different to Windows auth which can use various authentication schemes such as basic, digest, NTLM, and Kerberos authentication to negotiate credentials as part of the HTTP protocol, and which can be understood by external systems on the same Exchange.
So to use ICredentials you either need to be using Windows authentication in your ASP.Net application, or have access to username/password so that you can use NetworkCrententials explicitly.

How to Anonymously Authenticate between a VB.Net Desktop App and ASP.Net Web App

I'm looking for a way to pass some sort of credentials or Authorization token from a VB.Net Client to an ASP.Net web application that allows the Client to auto-login to our Forms-Authenticated website. If a user is logged into a local application, I want them to be able to view some web pages without having to login to the website as well. The credentials are not the same between the apps, but I would just like to pass some sort of encrypted token or key to the web page so I know they are coming from the desktop application. Is this possible without requiring a username and password login?
I also need to make sure this URL that is used cannot be simply copied and used from another location, so I'll need to include some sort of information in the encrypted value to know where it's coming from.
I know how to login the user with Forms Authentication and all that, just need to figure out the best way to secure this. Thanks!
OAuth is commonly used to allow desktop applications to access a user's private data on a web site. Since you're using .NET, I suggest you check out DotNetOpenAuth which includes sample OAuth web sites and client applications. It allows for this secure token to be passed that can tell your web site that the desktop app is the one making the requests and (usually) whose data is being accessed.
The best part about the OAuth solution is your desktop app never has to ask for the user's credentials. No credentials are in the URL. And if the desktop application is ever compromised (perhaps by the computer being stolen), the web site can kill the secure token the desktop app was using to cut off access without requiring the user to change their password.
You might want to look into issuing client-side certificates for these applications. Basically, you generate a certificate that you install with the client application and then on the server side, you check the ClientCertificate property of the HttpRequest instance exposed by the Request property on the current context.
Note that what you are doing is really a very bad idea, in that applications should never be assigned identity, only users. To that end, you should be authenticating each and every user that is using your app, not considering the application to be the identity. It's commonly considered a bad practice to do such a thing.
You can share credentials between the applications using ASP.NET Client Application Services.
Here are some resources:
Client Application Services
Client Application Services with Visual Studio 2008
Is your desktop app running on machines that are in the same domain as your web server (i.e. all in the same company)? If so, Integrated Windows Authentication is your easiest solution.
I think its best idea to use a web browser control inside the desktop application .
Then use the WebBrowser1.Document most probably
WebBrowser1.Document.Cookie
get if the user is singed in.
I also need to make sure this URL that
is used cannot be simply copied and
used from another location, so I'll
need to include some sort of
information in the encrypted value to
know where it's coming from.
If you store the encrypted value in a cookie or as a field in a form (POST request), then the credential is no longer in the URL and so it can't be easily copied (note that I said "easily").

Custom .NET authentication, membership, profile provider for portlets?

I am wondering if it is possible to leverage the Authentication, Membership, and/or Profile provider features in .NET to help integrate .NET web apps into my company's enterprise portal. In a nutshell, the portal sends custom header values to any application that is 'behind' the portal for fields like the username, user profile data, and some access rights. One issue that we have with the portal is that we aren't able to leverage many of the .NET apps available on the web because they weren't designed to be "portal aware", primarily to trust that the user has already authenticated.
Would it be possible to somehow write a custom authentication provider (or maybe leverage forms auth somehow) to just look at the header (plus the IP) and automatically "authenticate" as that user? My thinking is that by writing a profile provider, possibly a membership provider, and somehow adding authentication I would be able to download cool components like the Oxite blog (.net mvc demo that i found), switch providers to my custom one, and leverage it behind my company's portal with minimal code changes.
Does this make any sense? I feel that I might not be understanding how these components fit into the puzzle.
I don't think you can do this with zero changes to the .Net app, but I think you might be able to do it with minimal changes. Please note that I'm assuming that the portal Gateways everything that is going to the .Net web application. That is, the portal gets every HTTP request from the browser, adds its own headers to it, submits the request to the .Net app, gets a reply from the .Net app, rewrites things some more, and then returns info to the browser.
I'm guessing that what happens now (the reason you wrote this question) is you embed this .Net app into a portlet on your portal, but when someone tries to browse to it, even though they are logged into your portal, they see the external .Net login screen inside the portlet box. Not very nice.
There are two steps that need to be taken here:
Re-do the login page for the .Net app to auto-login portlet users
Create a custom membership provider that works along with #1
1. Re-do the login page for the .Net app to auto-login portlet users
Find the login page for the .Net app. It'll probably be something like login.aspx. Copy it (and any associated codebehind files) to portallogin.aspx and portallogin.cs.
Open up the portallogin.aspx and portallogin.cs files. Get rid of all of the controls and code in there. Replace it with something like what you see below. Please note that everywhere you see PORTAL_SomeFunctionName, you'll need to replace that with code from your Portal's SDK that makes the appropriate function call.
const string specialpassword = "ThisPasswordTellsTheBackendSystemThisUserIsOK";
Page_Load()
{
if (PORTAL_IsLoggedInToPortal())
{
string username = PORTAL_GetCurrentUserName();
// Authenticate the user behind the scenes
System.Web.Security.FormsAuthentication.SetAuthCookie(username, false);
System.Web.Security.FormsAuthentication.Authenticate(username, specialpassword);
}
else
{
throw new Exception ("User isn't coming from the Portal");
}
}
Next, edit web.config for the .Net application and tell it that the login page is portallogin.aspx instead of login.aspx.
That should take care of automatically attempting to log the user in.
2. Create a custom membership provider that works along with #1
This is where you will need to create a custom membership provider. In order for this to work, the .Net application you are working with has to make use of membership providers and allow for a custom membership provider to be used.
Create a new Membership Provider. You need to create a class and inherit from System.Web.Security.MembershipProvider. At a minimum, I think you'll need to implement the GetUser and ValidateUser functions and ApplicationName property. Below are some ideas for how they could look. The are many more functions that need to be overridden, but the stubs (with the accompanying NotImplementedException(s)) can probably be left alone.
public override string ApplicationName
{
get
{
return "Portal";
}
set
{
;
}
}
private const string specialpassword =
"ThisPasswordTellsTheBackendSystemThisUserIsOK";
public override bool ValidateUser(string username, string password)
{
// If the password being passed in is the right secret key (same
// for all users), then we will say that the password matches the
// username, thus allowing the user to login
return (password == specialpassword);
}
public override MembershipUser GetUser(string username, bool userIsOnline)
{
string email = PORTAL_getemailfromusername(username);
System.Web.Security.MembershipUser u = new MembershipUser(
this.name, username, username, email, "", "", true, false,
DateTime.Now(), DateTime.Now(), DateTime.Now(),
DateTime.Now(), DateTime.Now(), DateTime.Now()
);
return u;
}
You can also do similar implementations for the .Net RoleProvider and ProfileProvider if that functionality would be helpful in integrating with this .Net app. (the Role provider will provide group membership information, and the ProfileProvider will provide extra bits of profile info such as email address, zipcode, or whatever other properties you want it to provide for each user. This information would have to be lookuped up from a database or from the portal HTTP header information.
Other Considerations
Since you're using a third party authentication provider for this external .Net application, you need to figure out how you can tell this .Net application which users/groups are administrators. I can't tell you that - you'll have to find that out from the third party .Net application. This is needed if there are any permissions that are needed to do anything in this .Net application beyond having an account.
Since you're using this in a portal, there are a couple of ways it can be used. You can just have one big portlet that shows the entire .Net web application. You could also have lots of little portlets that show bits and pieces of the .Net web application. Either way, you'll have to consider that the portal might or might not render things properly when it puts a full .Net application inside a little portlet box on a portal page. If you get HTML that looks or works weird, it'll be annoying to fix it. You can either try to fix the original .Net web app to spit out different HTML, or you can add a module to IIS to rewrite the HTML on the fly (I'm not completely sure it's a module... you'll have to dig some on IIS to figure out how you might do this).
Whew!
I know this doesn't cover everything, but I've worked with setting up the Plumtree Portal (now BEA's Aqualogic User Interaction) as the authentication source for Microsoft's SQL Server Reporting Services, and I've implemented a Custom Authentication Provider for IIS based on user membership stored in a Dynamics NAV table. Hopefully my experience with these projects will be helpful to you in your integration of this external .Net application with your portal.
Tim
I think that's a great idea. You definately want the Membership provider, and ensure authentication is set to 'forms' for all the .Net web apps you download.
Also check out IIS7 because its newly constructed pipeline means you can leverage .Net based authentication types in any handler (CGI, etc). So for example, you can use Windows Authentication for your PHP applications.
One minor point that the other answers are missing, is that forms authentication in asp.net supports cross authentication.
The forms authentication tag has domain and cookie name properties.
matching these & using the same machine key, will allow cross authentication between apps.
Your membership provider for each site will need to point to the same datastore though.

Resources