ASP.NET Active Directory Membership Provider and SQL Profile Provider - asp.net

I am currently designing a Membership/Profile scheme for a new project I am working on and I was hoping to get some input from others.
The project is a ASP.NET web application and due to the short time frame, I am trying to use any and all built in .NET framework components I can. The site will probably entertain < 5000 users. Each user will have a profile where custom settings and objects will be persisted between visits.
I am required to use an existing Active Directory for authentication. Since the AD schema cannot be extended to hold new fields, I am required to hold user settings and objects in a different data store. I have also been told ADAM is probably not a possible solution.
I was hoping to use the Active Directory Membership Provider for my authentication scheme and the SQL Profile Provider as a user profile data store. I would prefer not to build a custom profile provider, but I do not see this posing much of a problem if need be.
I was wondering if this is even a possible solution, and if so, has anyone had any luck with this approach.
Any comments would be greatly appreciated.
Thanks.

First off - I've never done this myself.
There's a really excellent series (14 !! parts) on the whole topic of ASP.NET 2.0 membership, roles and profile provider systems by Scott Mitchell at 4 Guys from Rolla.
According to my understanding, you should be able to configure this behavior you are looking for by using basically these two sections in your web.config:
<!-- configure Active Directory membership provider -->
<membership defaultProvider="AspNetActiveDirectoryMembershipProvider">
<providers>
<add name="AspNetActiveDirectoryMembershipProvider"
type="System.Web.Security.ActiveDirectoryMembershipProvider,
System.Web, Version=2.0.3600, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a" />
</providers>
</membership>
<!-- configure SQL-based profile provider -->
<profile defaultProvider="SqlProvider">
<providers>
<add name="SqlProvider"
type="System.Web.Profile.SqlProfileProvider"
connectionStringName="SqlProfileProviderConnection"
applicationName="YourApplication" />
</providers>
<!-- specify any additional properties to store in the profile -->
<properties>
<add name="ZipCode" />
<add name="CityAndState" />
</properties>
</profile>
I would think this ought to work :-)

In addition to this as replied by Marc :
<add name="AspNetActiveDirectoryMembershipProvider"
type="System.Web.Security.ActiveDirectoryMembershipProvider,
System.Web, Version=2.0.3600, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a" />
you might also need to add
connectionStringName="ADService",
attributeMapUsername="sAMAccountName"
with corresponnding connection string
<connectionStrings>
<add name="ADService" connectionString="LDAP://ServerIP" />
</connectionStrings>
If you are using .net 4.0 then you will need to replace
Version=2.0.3600
with
Version=4.0.0.0
So finally ,
<membership defaultProvider="AspNetActiveDirectoryMembershipProvider">
<providers>
<add name="AspNetActiveDirectoryMembershipProvider"
connectionStringName="ADService"
type="System.Web.Security.ActiveDirectoryMembershipProvider,
System.Web, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a"
attributeMapUsername="sAMAccountName"/>
</providers>
</membership>
and since it is set as default, it can be referenced as :
MembershipProvider provider = Membership.Provider;

I am using Visual Studio 2012 and tried to do as sugested, but an error is shown:
To call this method, the "Membership.Provider" property must be an instance of "ExtendedMembershipProvider".
So I discovered that a few changes should be done to the default login form on the VS2012 with MVC 4 and entity framework as follows:
on file "AccountController.cs"
on the "public ActionResult Login(LoginModel model, string returnUrl)"
Change the
if (ModelState.IsValid && WebSecurity.Login(model.UserName, model.Password, persistCookie: model.RememberMe))
for
if (ModelState.IsValid && Membership.Provider.ValidateUser(model.UserName, model.Password))
on the "public ActionResult LogOff()"
Change the
WebSecurity.Logout();
for
FormsAuthentication.SignOut();
and add the following: FormsAuthentication.SetAuthCookie(model.UserName, false);
public ActionResult Login(LoginModel model, string returnUrl)
{
if (ModelState.IsValid && Membership.Provider.ValidateUser(model.UserName, model.Password))
{
FormsAuthentication.SetAuthCookie(model.UserName, false);
return RedirectToLocal(returnUrl);
}
// If we got this far, something failed, redisplay form
ModelState.AddModelError("", "The user name or password provided is incorrect.");
return View(model);
}

Thanks for the information, its helped alot. Also rather than Setting the default Provider with MembershipProvider provider = Membership.Provider; you can set it with in the membership tag.
<membership defaultProvider="AspNetActiveDirectoryMembershipProvider">
I"ve also writen a small how to and a download to a Visual Studio Project and Source configured to use AspNetActiveDirectoryMembershipProvider.
ASP.NET Forms Based Authentication - using AspNetActiveDirectoryMembershipProvider

Related

"Trust relationship between ... and the primary domain failed" in MVC5 Authentication

I have a ASP .NET MVC5 application in which I am not using Windows Authentication.
Everything was working fine until I tried running the application outside of the Domain in which it was being developed and (for whatever reason) got a:
The trust relationship between this workstation and the primary domain failed.
when I'm trying to do User.IsInRole("Admin").
I am using custom Identity, Role, IdentityStore, RoleStore, etc. from .NET's Identity and I can see that the User and Role data is being retrieved from the (MongoDB) database correctly.
There are plenty of questions regarding this issue, but they're from people who want to use Windows Auth. and impersonation in their MVC applications:
With windows authentication, The trust relationship between the primary domain and the trusted domain failed, when calling IsInRole
How to configure Windows Authentication / Impersonation + IIS 7 + MVC
The trust relationship between the primary domain and the trusted domain failed
My.User.IsInRole("Role Name") throws a Trust Relationship error on Windows 7
So why exactly am I getting this SystemException if I'm not using Active Directory and (as far as I know) not doing anything that might depend on the PC's domain? Am I missing some configuration (either in my Web.config or IIS Express)?
EDIT:
Ok, so narrowing it down a bit...
My User.IsInRole("Admin") line is inside an if() statement in my _Layout.cshtml View (i.e., to know what to show in the nav. bar depending on the role).
I now know I only get the error above when no user is authenticated and I'm not in the domain I used for dev. If I place a breakpoint on that line, I can see that the User object is is a System.Security.Principal.WindowsIdentity and its underlying Identity is System.Security.Principal.WindowsIdentity.
On the other hand, if the user is authenticated, then the User object and ts Identity are System.Security.Claims.ClaimsPrincipal and System.Security.Claims.ClaimsIdentity.
Why is it using Windows Identity at all (when unauthenticated) and how can I disable it?
So, based on my EDIT, I've modified my _Layout.cshtml so that instead of having
#if(User.IsInRole("Admin")) {...}
I have
#if(User.Identity.IsAuthenticated && User.IsInRole("Admin")) {...}
which seems to solve the problem.
I believe the problem was that ASP .NET Identity uses an empty WindowsIdentity when no user is authenticated and when I try to check for the User.IsInRole, then it will try to check the roles of a WindowsIdentity against an Active Directory that I don't have. Obviously I should first check if the user is even logged in before attempting to check its roles, so mea culpa.
But, even though the change above seems to fix my code, I'd be very interested in knowing more about this behavior: why is it using an empty System.Security.Principal.WindowsIdentity when no user is authenticated. I'll accept any answer which explains that.
I've had this issue - It failed for me if I tested an active directory group that didn't exist.
Make sure you're using a group that exists!
I was having this issue with Asp.Net Core 3.1 with Windows Authentication, but this thread came up first when searching the internet. I ended up resolving the issue by decorating the controller class declaration with the following:
using Microsoft.AspNetCore.Authorization;
[Authorize]
public class SetupController : Controller
Hope this is helpful for someone that is using Windows Authentication and is having the same error.
We were having this same issue on a new production server. Using the Identity Framework and restricting access to a specific directory with a web.config file denying any unauthenticated users. When unauthenticated users tried to access a page in this directory that contained any User.IsInRole("RoleName") code, they were getting the "Trust relationship..." error.
None of the fixes mentioned in other SO answers worked for us.
Turns out we just had to enable Forms Authentication in IIS - problem solved.
The "trust relationship between the primary domain and the workstation has failed" error message usaully requires that the computer be removed from the domain and then rejoined. Now there are a few ways to do this. As included in the link above, are instructions on how to do so either on the computer displaying the error or remotely. You can also do so in Active Directory and in PowerShell.
<authorization>
<allow roles="pri\Domain Users" users="pri\domain_user" />
<deny users="?" />
</authorization>
make sure that you have the above line in your web.config file and complete the user field with the correct user name.
I've just resolved this in our systems, unfortunately, none of the other suggestions worked for me. The issue was caused by an orphaned SID in a network folder the code was attempting to access. Once removed it started working again.
I had exactly the same scenario with custom Authentication Module and the same error when doing IsInRole. The highest ranking solution (User.Identity.IsAuthenticated && ...) did NOT help. So, I played quite a bit with it. Finally I found that I had to remove a (preCondition="managedHandler") attribute from my module declaration in web.config file. So, instead of:
<system.webServer>
...
<modules>
...
<add name="CompanyAuthentication" type="Company.Authentication.AuthHttpHandler" preCondition="managedHandler" />
</modules>
I would have to have:
<system.webServer>
...
<modules>
...
<add name="CompanyAuthentication" type="Company.Authentication.AuthHttpHandler" />
</modules>
That did the trick for me!
For me, the whole membership provider configuration tags were missing. After i copy those from one our previous apps, it worked fine.
<system.web>
<authentication mode="Windows" />
<compilation debug="true" targetFramework="4.7.1" />
<httpRuntime targetFramework="4.7.1" />
<httpModules>
<add name="TelemetryCorrelationHttpModule" type="Microsoft.AspNet.TelemetryCorrelation.TelemetryCorrelationHttpModule, Microsoft.AspNet.TelemetryCorrelation" />
<add name="ApplicationInsightsWebTracking" type="Microsoft.ApplicationInsights.Web.ApplicationInsightsHttpModule, Microsoft.AI.Web" />
</httpModules>
<profile defaultProvider="DefaultProfileProvider">
<providers>
<add name="DefaultProfileProvider" type="System.Web.Providers.DefaultProfileProvider, System.Web.Providers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" connectionStringName="DefaultConnection" applicationName="/" />
</providers>
</profile>
<membership defaultProvider="DefaultMembershipProvider">
<providers>
<add name="DefaultMembershipProvider" type="System.Web.Providers.DefaultMembershipProvider, System.Web.Providers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" connectionStringName="DefaultConnection" enablePasswordRetrieval="false" enablePasswordReset="true" requiresQuestionAndAnswer="false" requiresUniqueEmail="false" maxInvalidPasswordAttempts="5" minRequiredPasswordLength="6" minRequiredNonalphanumericCharacters="0" passwordAttemptWindow="10" applicationName="/" />
</providers>
</membership>
<roleManager defaultProvider="CustomRoleProvider" enabled="true" cacheRolesInCookie="false">
<providers>
<add name="CustomRoleProvider" type="ABC.ABCModels.ABCRoleProvider" />
</providers>
</roleManager>
<sessionState mode="InProc" customProvider="DefaultSessionProvider">
<providers>
<add name="DefaultSessionProvider" type="System.Web.Providers.DefaultSessionStateProvider, System.Web.Providers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" connectionStringName="DefaultConnection" />
</providers>
</sessionState>
On my case, I am not using User.Identity but rather Thread.CurrentPrincipal.Identity.Name
So when I approach this line of code:
Thread.CurrentPrincipal.IsInRole("admin");
That's where I will encounter the same error message of:
The trust relationship between this workstation and the primary domain failed.
There are two cases why I encountered the same issue and of course the fixes I made:
I was disconnected with my VPN. This will look for the role that doesn't exist since I am not connected with my VPN and not connecting with my AD accounts.
If I am connected with my VPN and the role admin doesn't exist based on my code above, it will certainly trigger the same error message.
I think it is worth sharing how I've fixed on my situation as these answers helped me to figure it out.
We were allowing/restricting access to pages via web.config and this was happening before touching any code (so it didn't matter where the breakpoint was, the error was coming).
Since this was not amazing too, I've decided to implement this validation in the code manually instead of using the web.config one.
our web.config used to look like this:
<configuration>
<system.web>
<authorization>
<allow roles="10,5"/>
<deny users="*"/>
</authorization>
</system.web>
</configuration>
We've changed to:
Created a page (called restricted page) that inherits from Page
This page has a protected variable called RequiredAccessLevel
The OnLoad of the page we check if the user is in the role of the RequiredAccessLevel and if it is not, we redirect to a custom AccessDenied page
Made the pages that should have this restriction inherit from RestrictedPage instead of Page
On the constructor of the pages that are inheriting from RestrictedPage we set the access level necessary
Like this:
RestrictedPage.cs
public class RestrictedPage : Page
{
protected int[] RequiredAccessLevel { get; set; } = { };
protected override void OnLoad(EventArgs e)
{
if (RequiredAccessLevel.Length > 0)
{
var allowed = false;
foreach (var ral in RequiredAccessLevel)
{
if (Page.User.IsInRole(ral.ToString()))
{
allowed = true;
break;
}
}
if (!allowed)
{
Response.Redirect("~/AccessDenied.aspx");
}
}
base.OnLoad(e);
}
}
Example.cs
public partial class Example: RestrictedPage
{
public Example()
{
RequiredAccessLevel = new[] {10};
}
}

MVC4 Simple Membership Provider setup

I would like to use mvc4 Simple membership provider. So I set up a new MVC4 internet application. Click Debug and I see that db and tables were created for me.
Is this all I need to do?.
Some articles:
http://weblogs.asp.net/jgalloway/archive/2012/08/29/simplemembership-membership-providers-universal-providers-and-the-new-asp-net-4-5-web-forms-and-asp-net-mvc-4-templates.aspx
http://blog.longle.net/2012/09/25/seeding-users-and-roles-with-mvc4-simplemembershipprovider-simpleroleprovider-ef5-codefirst-and-custom-user-properties/
tell me to add
<membership defaultProvider >...
section. is this necessary?
Also:
I can't get ASP.Net configuration tool to work. It says "An error was encountered. Please return to the previous page and try again. "
??
If you created a new MVC 4 web application using the Internet template it should have wired up SimpleMembership for a basic log-on/log-off functionality. It should have already updated your web.config with the proper settings. It should have setup the membership and roles and they should looks something like this.
<roleManager enabled="true" defaultProvider="SimpleRoleProvider">
<providers>
<clear/>
<add name="SimpleRoleProvider" type="WebMatrix.WebData.SimpleRoleProvider, WebMatrix.WebData"/>
</providers>
</roleManager>
<membership defaultProvider="SimpleMembershipProvider">
<providers>
<clear/>
<add name="SimpleMembershipProvider" type="WebMatrix.WebData.SimpleMembershipProvider, WebMatrix.WebData" />
</providers>
</membership>
SimpleMembership does not use the ASP.NET Configuration Tool used with the traditional ASP.NET membership. You have to create your own web pages for managing roles and users.
The Internet template just creates the basic functionality for authentication and authorization in your application. But SimpleMembership is very extensible. For example it is fairly easy to customize what type of information you want to capture in the user profile or setup email confirmation of new users.

Profile provider for ASP.NET Web App - Auto wired properties

In an ASP.NET website it is possible with a few short additions to the Web.Config section to add auto-magically wired properties to the user profile.
So for example with some XML like this.
<profile>
<providers>
<clear/>
<add name="AspNetSqlProfileProvider" connectionStringName="LocalSqlServer" applicationName="/" type="System.Web.Profile.SqlProfileProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"/>
</providers>
<properties>
<add name="Name" allowAnonymous="true"/>
<add name="VisitedOn" type="System.DateTime" allowAnonymous="true"/>
</properties>
</profile>
You would end up with the ability to do this. Without having to declare any further code.
Profile.Name = "Some Test Value";
Profile.VisitedOn = DateTime.Now;
Profile.Save();
I have attempted to duplicate this functionality in an ASP.NET Web App and can't even seem to find the base Profile declaration let alone the custom properties.
I have however found that System.Web.Profile.DefaultProfile.Properties does infact contain the custom declared properties I define in the Web.Config.
So where might I be going wrong? What is the process for getting auto wired properties in web apps working?
The properties are created during the Compilation of ASP.NET Web Application when the first request arrives.
Ref: ASP.NET Application Life Cycle Overview for IIS 5.0 and 6.0
Profile properties defined in the Web.config file If profile
properties are defined in the application's Web.config file, an
assembly is generated that contains a profile object.
You can hook into this Compilation by writing a custom BuildProvider and registering the same. This build provider can be used for generating the auto wired properties.

ASP.NET Membership - Which RoleProvider to use so User.IsInRole() checks ActiveDirectory Groups?

Very simple question actually:
I currently have IIS anonymous access disabled, users are automatically logged on using their Windows login. However calling User.IsInRole("Role name") returns false. I double-checked User.Identity.Name() and the "Role name" and it should return true.
I currently have this in my Web.Config:
UPDATE
I was calling User.IsInRole("Role name") where I should call User.IsInRole("DOMAIN\Role name")
However I still like to know if the <membership> entry is needed at all?
What should I change? (and is the <membership> entry needed at all?)
<authentication mode="Windows">
<forms
name=".ADAuthCookie"
timeout="10" />
</authentication>
<membership defaultProvider="ADMembershipProvider">
<providers>
<clear/>
<add
name="ADMembershipProvider"
type="System.Web.Security.ActiveDirectoryMembershipProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
connectionStringName="ADConnectionString"
connectionUsername="XXX\specialAdUser"
connectionPassword="xx"
/>
</providers>
</membership>
<roleManager enabled="true" defaultProvider="WindowsProvider">
<providers>
<clear />
<add name="WindowsProvider" type="System.Web.Security.WindowsTokenRoleProvider" />
</providers>
</roleManager>
If you use Windows authentication IsInRole will work with no extra configuration, as long as you remember to prefix the role with the domain, i.e. DOMAIN\groupName.
In addition you can role (pun intended) your own and use Windows auth against, for example, a SQL Role Provider, where you don't want your AD littered with custom roles for your application.
So no, you don't need the provider configuration at all.
The membership provider here isn't going to help. The ActiveDirectoryMembershipProvider seems to best(only?) fit with Forms authentication.
BlogEngine.NET has an Active Directory role provider.
Pretty sure the only thing you need in there is the roleManager group (along with the base authentication mode='windows' setting)
Out of the box, there's no role provider to use Active Directory directly. You can use the role table in the ASP.NET membership- and role-system, or you can use Authorization Manager (AzMan).
There's an article on CodeProject which shows the implementation of a role provider which works against the Active Directory - with full source code. Maybe this helps?
Marc

ASP.NET Membership - Which user is authenticated and which user is impersonated?

i'm a little confused while trying to find out how ActiveDirectory and ASP.NET Membership work... I've created a new MVC project and removed the AccountController / Views. I've changed the Web.Config so that it uses ActiveDirectory and automatically authenticates users based on their current Windows login:
Web.Config
<authentication mode="Windows">
<forms
name=".ADAuthCookie"
timeout="10" />
</authentication>
<membership defaultProvider="MyADMembershipProvider">
<providers>
<clear/>
<add
name="MyADMembershipProvider"
type="System.Web.Security.ActiveDirectoryMembershipProvider, System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
connectionStringName="ADConnectionString"
connectionUsername="MYDOMAIN\myuser"
connectionPassword="xxx"
/>
</providers>
</membership>
This works nicely, as I can do the following to get the users username like this:
User.Idenity.Name() 'Gives MYDOMAIN\myuser
Looking at the following, actually makes me confused:
Threading.Thread.CurrentPrincipal.Identity.Name() 'Gives MYDOMAIN\myuser
1. Shouldn't the thread identity be IUSR_WORKSTATION or ASPNET_WP username?
2. What's the difference between Authentication and Impersonation?
myuser is the Authenticated user on that application, that's why your CurrentPrincipal is giving you MYDOMAIN/myuser. The application impersonates IUSR_WORKSTATION when it uses resources like the database, and is a completely different issue.
If you go to Project on your toolbar, and select ASP.NET Configuration, it will open a website that lets you access these settings and create users, roles etc.

Resources