I have a domain and port number (636) as well as a username and password.
I am trying to figure out a way to connect to this AD via Secure LDAP and get a users 'givenname', 'sn', 'mail', and probably a few custom attributes.
However I have no idea how to do this in C#.
I think that Microsoft may have a method for this available already but I am going to defer to you all.
The final user experience will be: See login screen, enter username and password, those credentials are sent over LDAP and the users info is returned to my web app, then I log them in if it all went well... though I don't know what a failed attempt would look like either so I can deny them. Any ideas?
Please include code samples so I can understand the implementation, thanks!
Did you even try google?
EDIT
Sorry for the hubub and the snarky response. I think the problem you were having is you didn't quite ask the question right -- either here or on google. Anyhow, you don't need a lick of C# code here. You just need to configure your web app to use AD as a membership provider. You'll need a connection string [getting this right was the hardest part]:
<connectionStrings>
<add name="MyAd"
connectionString="LDAP://adserver/OU=Users"
/>
</connectionStrings>
And a membership provider:
<membership defaultProvider="AdProvider">
<providers>
<add
name="AdProvider"
type="System.Web.Security.ActiveDirectoryMembershipProvider,
System.Web, Version=2.0.0.0,
Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
connectionStringName="MyAd"
applicationName="ItRemoteHelpdesk"
enablePasswordReset="false"
/>
</providers>
</membership>
Then users can login with their normal username#domain and password.
The System.DirectoryServices.AccountManagement is the .NET dll to use for the newer, non-LDAP AD authentication.
Try this website for a good starting point with code examples:
http://www.codeproject.com/KB/system/usingAccountManagement.aspx
You should definitely check out the .NET 3.5 System.DirectoryServices.AccountManagement namespace as suggested by Brad.
To get a good head start on how to use it, read this MSDN Magazine article: Managing Directory Security Principals in the .NET Framework 3.5
The article does talk several times about how to securely (using SSL) connect to your AD domain, and how to e.g. create users or retrieve user information. I think reading that article closely and trying out the code samples should give you a good idea on how to do what you're looking for.
Update: quite obviously, all those method in S.DS.AM require you to be authenticated against AD. The new classes also provide for pretty simple verification of user credentials (as shown in that article I linked to):
// establish context
PrincipalContext domain = new PrincipalContext(ContextType.Domain);
// determine whether a user can validate to the directory
bool validated = domain.ValidateCredentials("user1", "Password1");
Related
This is my first time working with ASP.NET Role membership in active directory.
So far i've got a website running, and im able to log in with a active directory user.
My problem is: I cant get "Roles.IsUserInRole" to trigger. It's like it dosent even look at the logged in user for group memberships.
I have been searching for a solution, but the only solution i can find is to write my own membership provider. Is this really neccesary?
I want to control what the users can access with their memberships.
Like if a user is in the "students" security group in the AD then they can only access pages in a student fold in my ASP.NET solution.
I am useing form authentication.
Here is a sample of my webconfig for my rolemanager:
<system.web>
<roleManager defaultProvider="WindowsProvider"
enabled="true"
cacheRolesInCookie="false">
<providers>
<add
name="WindowsProvider"
type="System.Web.Security.WindowsTokenRoleProvider" />
</providers>
</roleManager>
</system.web>
and here im trying the IsUserInRole
protected void Login2_LoggingIn(object sender, LoginCancelEventArgs e)
{
if (Roles.IsUserInRole("Students"))
{
Response.Redirect("../Students/StartPage.aspx");
}
}
Bonus question: I am only able to login with users from the "Users" container ind my AD. Why cant i login with a user from a OU some levels down?
From your description, you appear to be using Forms Authentication with ActiveDirectoryMembershipProvider for authentication.
This is not compatible with WindowsTokenRoleProvider. To use WindowsTokenRoleProvider, which exposes roles based on Windows group membership, you need to be using Windows authentication.
As to your first question, have you already tried to use the group name together with the domain name i.e. Roles.IsUserInRole(#"DOMAIN\groupName")?
As to your second question. I assume that you use ActiveDirectoryMembershipProvider. If so, I think that you have a connection string in your web.config (which is used by the provider)and this connection string specifies that the provider should use Users container. However, you don't have to specify the concrete conatainer (for details see this site). For example instead of:
LDAP://testdomain.test.com/CN=Users,DC=testdomain,DC=test,DC=com
You can use:
LDAP://testdomain.test.com
Make sure you Remove:
<authentication mode="Forms">....</authentication>
then you can alternatively use:
User.IsInRole("Students");
I'm using the following code in an MVC5 site:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Login(LoginModel loginModel) {
if (ModelState.IsValid) {
var authenticated = FormsAuthentication.Authenticate(loginModel.UserName, loginModel.Password);
if (authenticated) {
FormsAuthentication.SetAuthCookie(loginModel.UserName, true);
return RedirectToAction("AdminPanel");
}
ModelState.AddModelError("", "The username and password combination were incorrect");
}
return View(loginModel);
}
Which throws up the following warning:
System.Web.Security.FormsAuthentication.Authenticate(string, string)'
is obsolete: 'The recommended alternative is to use the Membership
APIs, such as Membership.ValidateUser. For more information, see
http://go.microsoft.com/fwlink/?LinkId=252463.'
First a disclaimer - I'm one of those developers that likes to keep up to date with things, and I prefer to avoid warnings in VS entirely. However, in this particular instance, I am using extremely primitive authentication, which comes directly from the web.config, as follows:
<authentication mode="Forms">
<forms loginUrl="~/account/login" timeout="2880" slidingExpiration="true" name=".ASPXFORMSAUTH">
<credentials passwordFormat="SHA1">
<user name="MyUserName" password="a-big-long-password-hash"/>
</credentials>
</forms>
</authentication>
There is absolutely no further login requirements in this project - using a database is overkill, and there's no need for distributed login; basically, storing the details in web.config is the most ideal solution, and there will probably only be one user per application. I will no doubt abstract the authentication code out from the controller (using DI / IoC), but I am still intending to use the FormsAuthentication object to authenticate against the details in web.config.
While I am fully aware of membership providers, DotNetOpenAuth and the new (but pretty horrible imo) OWIN based authentication model, the above code is more than adequate.
First question - Why has FormsAuthentication been made obsolete when this is a perfectly valid use case? I understand that FormsAuthentication is a sealed black box, is hard to test, and the code behind it is domain specific, but in this particular instance (where I want to read details directly from the section of the web.config), it seems madness to write a membership provider or a custom authentication service?
Second Question - Unless I'm missing something, the new OWIN model is for distributed authentication, and is not suitable for a role based, enterprise level security system. From the many blog posts, white papers and SO posts I've read, it also seems like it's still incomplete. Am I right in my assumptions? Is OWIN the future of all security systems, or should I continue to write bespoke security systems more appropriate for my clients needs?
To your first point, we obsoleted it because it is woefully inadequate by modern security standards. It uses a straight hash with no iteration, which means that anybody who accesses your Web.config can easily figure out what the original passwords were. But if you're willing to accept these risks, you can certainly continue using it going forward. Just suppress the warning and solider on with your original code.
To your second point, there is no relation whatsoever between any of this and OWIN.
Hope this clears it up!
OWIN is not just about security. It is a standard that defines the interfaces between an application framework (ASP.NET MVC) and a web server (IIS). It is a new layer of abstraction Microsoft defined to let .NET developers write applications that are host agnostic, i.e. not dependent on IIS.
OWIN architecture is a pipeline that consists of multiple middleware components. In MVC5, security has been rewritten from scratch as an OWIN middleware component. If this does not work for you and you want to roll your own password verification routine, you can implement your own security middleware. Here is a post describing how to do it.
If you don't want to go that far, this post shows how to rely on the built in authentication framework without the Membership
Everything Levi mentioned is still valid, though. You want to be careful with your own hashing/storage mechanism and pick a good hashing algorithm.
Here some more background info on OWIN security midddleware implemented by Microsoft.
I created a new MVC4/.NET4.5 project and enabled Google OpenID. This worked, shockingly easily.
My company has "gone google" and our domains/ employee identities are in the Google Apps webspace.
How can I allow only our Google Apps domains to authenticate to my new website? I'm hoping it's a simple thing like the authentication piece was.
Here is some additional information:
I literally created a default web application and enabled the Google Authentication piece. I could not believe how simple it was to validate against Google.
My company has literally hundreds of email domains, all rolled up under one email domain "umbrella". For example, my company's corporate email domain name is "foo.com", but under this we have "x.foo.com", "bar.com", and "yomommasougly.net". All of these are part of the "foo.com" Google Apps domain.
The ultimate goal is, a description of what needs to be done (and where) to take this default application and restrict it to all domains under the "foo.com" domain.
With hundreds of domains, and more being added all the time, it is not practical to specify every domain explicitly.
Assuming you're using DotNetOpenAuth check out the authentication code for the Stack Exchange Data Explorer.
Essentially, you just ask for the e-mail address with your request:
request.AddExtension(
new ClaimsRequest
{
Email = DemandLevel.Require,
}
);
Then check the returned address against your domain whitelist (I'm assuming you're already only accepting google OpenIDs)
var sreg = response.GetExtension<ClaimsResponse>();
If (!HasWhiteListedDomain(sreg.Email)) {
// Fail Here
}
Note that these bits of code need to be added to your Web.config to get the exact code for fetching the e-mail above working:
<configSections>
<section name="dotNetOpenAuth" type="DotNetOpenAuth.Configuration.DotNetOpenAuthSection" requirePermission="false" allowLocation="true" />
</configSections>
<dotNetOpenAuth>
<openid>
<relyingParty>
<behaviors>
<!-- The following OPTIONAL behavior allows RPs to use SREG only, but be compatible
with OPs that use Attribute Exchange (in various formats). -->
<add type="DotNetOpenAuth.OpenId.Behaviors.AXFetchAsSregTransform, DotNetOpenAuth" />
</behaviors>
</relyingParty>
</openid>
</dotNetOpenAuth>
Edit:
If using OAuthWebSecurity getting the e-mail will just look something like this:
var userDataFromProvider = result.ExtraData;
var email = userDataFromProvider["email"];
Source
I already have a User table in my primary application database with an email address (which will act as the user name) and a password. I would like to authenticate using my database instead of the default authentication database (ASPNETDB).
Questions:
Is this a bad idea? Is it a huge can of worms to use my own DB for authentication?
How much work am I adding by doing this? I already have code for hashing the password and a query that will check if the email and password match the DB. So, I wouldn't be starting from scratch.
What would I need to do to use my database instead of ASPNETDB? I'm hoping this can be described in a few simple steps, but if not, could you point me to good source?
Update
I'm still looking for a little more detail here on my third question. Do I need to write my own MembershipProvider? What changes do I need to make to my web.config file? Will the [Authorize] attribute still work if I write my own solution? Can I use the automatically-generated AccountController with some minor modifications or do I basically need to rewrite the account controller from scratch?
It's quite simple, you need to derrive MembershipProvider and implement the ValidateUser method. Take a look at this post. I'm using custom membership provider with Postgres and MVC just fine.
I'll answer your updated questions:
Do I need to write my own MembershipProvider?
If you (a) want to continue using Forms Authentication, and (b) have an authorization table structure that doesn't follow the same conventions as the ASPNETDB, then yes. If you don't need FormsAuth (see below), then you can do away with the MembershipProvider entirely, but I wouldn't recommend it. Or, if you're using the exact same security tables as ASPNETDB but just want to point it to a different database, you can continue using the default provider and simply change its configuration.
What changes do I need to make to my web.config file?
If you are using your own custom MembershipProvider, then you need to register it in the <providers> section of the <membership> element and change the defaultProvider property. If you are using the standard AspNetSqlProvider then you probably just need to change the connection string.
Will the [Authorize] attribute still work if I write my own solution?
Yes, if you stick to Forms Authentication (either use the AspNetSqlProvider or write and register your own membership provider). No, if you abandon Forms Authentication (again, not recommended).
Can I use the automatically-generated AccountController with some minor modifications or do I basically need to rewrite the account controller from scratch?
You should rewrite the AccountController anyway - don't leave demo code in a production app. But if you must - yes, the AccountController will work under the same conditions as above.
No. And I would suspect most people do not trust that cruddy mechanism
Not much at all, especially since you have the table already.
Take a look at this for example: http://forums.asp.net/t/1250726.aspx
Hi ,
Just follow these simple steps :
First, you can delete the .mdf file in App_Data folder. Since we don’t need any of these tables.Then, we need to update the default connection string in the web.config to point to our database.
<connectionStrings>
<add name=”DefaultConnection” connectionString=”Data Source=SERVER\INSTANCENAME;Initial Catalog=DBNAME;Integrated Security=True” providerName=”System.Data.SqlClient” />
</connectionStrings>
Third, Open Nuget Package Manager and write the following commands:
Enable-Migrations
Add-Migration Init
Update-Database
Check out your database, all ASP.NET membership tables with Prefix Asp have been create and then you can test it out by running your application and execute membership actions such as Signing up or Signing in to your application.
Created tables after running above commands:
AspNetRoles
AspNetUserClaims
AspNetUserLogins
AspNetUserRoles
AspNetUsers
__MigrationHistory
Source : https://blogs.msmvps.com/marafa/2014/06/13/how-to-create-asp-net-mvc-authentication-tables-in-an-existing-database/
We're doing exactly this in one of our applications, and find it quite simple. We have an authentication service (called from the controller) that handles the mechanics of hashing the entered password to see if it is a match, then simply returns a bool for a method we call "IsValidLogon".
In our case, the purpose was to keep the management of what should be a pretty trivial task as lightweight as possible.
We bascially ignored ASPNETDB entirely. If we get a valid response from our user/password check, we simply call the standard FormsAuthentication.RedirectFromLoginPage(username, createCookieBool);
Hope that helps.
just building the same, so answer to 1 must be NO :)
I'm using the standard asp.net forms authentication, where i use the FormsAuthentication.RedirectFromLoginPage(username, createCookieBool) method to log a user in.
I gave a user a unique guid (you can use any other user id) and i'm storing it in the UserName parameter along with the username (to display on the masterpage: Html.Encode(Page.User.Identity.Name.Split("|".ToCharArray())[1]))
In each controller/method in which i must know which user is logged on (via User.Identity.Name, split the string and get the userguid).
Also i decorate those routines with the [Authorize] attribute.
Lets say that you have websites www.xyz.com and www.abc.com.
Lets say that a user goes to www.abc.com and they get authenticated through the normal ASP .NET membership provider.
Then, from that site, they get sent to (redirection, linked, whatever works) site www.xyz.com, and the intent of site www.abc.com was to pass that user to the other site as the status of isAuthenticated, so that the site www.xyz.com does not ask for the credentials of said user again.
What would be needed for this to work? I have some constraints on this though, the user databases are completely separate, it is not internal to an organization, in all regards, it is like passing from stackoverflow.com to google as authenticated, it is that separate in nature. A link to a relevant article will suffice.
Try using FormAuthentication by setting the web.config authentication section like so:
<authentication mode="Forms">
<forms name=".ASPXAUTH" requireSSL="true"
protection="All"
enableCrossAppRedirects="true" />
</authentication>
Generate a machine key. Example: Easiest way to generate MachineKey – Tips and tricks: ASP.NET, IIS ...
When posting to the other application the authentication ticket is passed as a hidden field. While reading the post from the first app, the second app will read the encrypted ticket and authenticate the user. Here's an example of the page that passes that posts the field:
.aspx:
<form id="form1" runat="server">
<div>
<p><asp:Button ID="btnTransfer" runat="server" Text="Go" PostBackUrl="http://otherapp/" /></p>
<input id="hdnStreetCred" runat="server" type="hidden" />
</div>
</form>
code-behind:
protected void Page_Load(object sender, EventArgs e)
{
FormsIdentity cIdentity = Page.User.Identity as FormsIdentity;
if (cIdentity != null)
{
this.hdnStreetCred.ID = FormsAuthentication.FormsCookieName;
this.hdnStreetCred.Value = FormsAuthentication.Encrypt(((FormsIdentity)User.Identity).Ticket);
}
}
Also see the cross app form authentication section in Chapter 5 of this book from Wrox. It recommends answers like the ones above in addition to providing a homebrew SSO solution.
If you are using the built in membership system you can do cross sub-domain authentication with forms auth by using some like this in each web.config.
<authentication mode="Forms">
<forms name=".ASPXAUTH" loginUrl="~/Login.aspx" path="/"
protection="All"
domain="datasharp.co.uk"
enableCrossAppRedirects="true" />
</authentication>
Make sure that name, path, protection and domain are the same in all web.configs. If the sites are on different machines you will also need to ensure that the machineKey and validation and encryption keys are the same.
If you store user sessions in the database, you could simply check the existance of the Guid in the session table, if it exists, then the user already authenticated on the other domain. For this to work, you would have to included the session guid in the URL when you redirect the user over to the other website.
Not sure what you'd use for .NET but ordinarily I'd use memcached in a LAMP stack.
The resolution depends on the type of application and environment in which it is running. E.g. on intranet with NT Domain you can use NTLM to pass windows credentials directly to servers in intranet perimeter without any need to duplicate sessions.
The approach how to do this is generally named single sign-on (see Wikipedia).
There are multiple approaches to this problem, which is described as "Cross-domain Single Sign On". The wikipedia article pointed to by Matej is particularly helpful if you're looking for an open source solution - however - in a windows environment I belive you're best off with one of 2 approaches:
Buy a commercial SSO product (like SiteMinder or PingIdentity)
Use MicroSoft's cross-domain SSO solution, called ADFS - Active Direcctory Federation Services. (federation is the term for coordinating the behavior of multiple domains)
I have used SiteMinder and it works well, but it's expensive. If you're in an all MicroSoft environment I think ADFS is your best bet. Start with this ADFS whitepaper.
I would user something like CAS:
[1]: http://www.ja-sig.org/products/cas/ CAS
This is a solved problem and wouldn't recommend rolling your own.
Alternatively if you want to roll your own and the sites in question are not on the same servers or don't have access to a shared database (in which case see the above responses) then you could place a web beacon on each of the sites which would refer back to the other site.
Place a single pixel image (web beacon) on site A which would call site B passing through the users ID (encrypted & time stamped). This would then create a new user session on site B for the user which would be set as logged in. Then when the user visited site B they would already be logged in.
To minimise calls you could only place the web beacon on the home page and or log in confirmation pages. I've used this successfully in the past to pass information between partner sites.