Centralize Authentication Code Between Applications - c#

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).

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.

Simple Login with ASP.Net MVC5

I have been trying to get my head around OWIN and Identity to get it play well with my Existing app. The application was coded in Plain ASP.Net and I want to port it to MVC to organize it better. The app have established database including Users table so using Identity table is not option. The wall I hit with Identity is that it requires Email and my system is NOT supposed to demand user email. After trying almost everything (days reading almost every answer on the subject) I have come to conclusion that it will take more time to get it done, and that brings me to my question.
Being PHP guy (jumped to ASP.Net not long ago) I could easily make a working login in 10minutes and now I need a thing like it, something like
$user = User::findByUserName($username); //somehow here I find user from database
if($user!=null)
{
$_SESSION['isLoggedIn'] = true;
$_SESSION['fullname'] = $user->fullName;
}
else
{
//redirect to login with error
}
I cannot find a session class in MVC5. Is that even possible? If no, is there a simple route to solve my issue?
The most common way to cache is to either place it in the 'Global' server cache or place it in the session. There are other ways to save state and I would recommend looking into alternatives as the techniques below use the System.Web namespace. System.Web will not be included in MVC6.
Global Cache
//Save User
System.Web.HttpContext.Current.Cache.Insert(
"CurrentUser",
User,
null,
System.Web.Caching.Cache.NoAbsoluteExpiration,
new TimeSpan(0,20,0),
System.Web.Caching.CacheItemPriority.Normal,
null);
//Get User
User user=(User)System.Web.HttpContext.Current.Cache["CurrentUser"];
Session Cache
//Save User
HttpContext.Current.Session.Add("CurrentUser",User);
//Get User
User=(User)HttpContext.Current.Session["CurrentUser"];
you could basically tell owin manuelly to authenticate the user without using the identity-stuff (i dont like it either, at least for small projects). Then you could easily use the default authorize anotation
Heres a tutorial: OWIN AUTH

Web API token authentication with a custom user database

I am developing a Web API 2.1 service that needs to authenticate the connecting clients (HTML5/JS clients that I will create and control). Unfortunately, the user information (username, password hashes, roles and much, much more info) is stored in an existing (SQL Server) database to which I only have read access. The Users database table was created 5-6 years ago without any reference to security frameworks, so it's a completely custom format. I'm not allowed to make any changes to either the data or the database structure.
Inspired by this article, I rolled my own token-based method of authenticating users, but I'm lacking the completeness and (re)assurance of using an established security framework.
Is there a way to integrate an existing framework, e.g. OAuth2, within my current project given the constraints I mentioned above? I don't know if it makes any difference, but I'm self-hosting using OWIN.
This is a good answer to a similar question.
It basically says:
Make a custom user class which implements IUser
Define a custom user store which implements public class UserStoreService
: IUserStore<CustomUser>, IUserPasswordStore<CustomUser>
wire everything up
Since the answer is pretty extensive I just provided the basic steps...
details are here: How to customize authentication to my own set of tables in asp.net web api 2?
This is also a very valuable content which also applies to web api:
Customizing ASP.NET Authentication with Identity by JumpStart
https://channel9.msdn.com/Series/Customizing-ASPNET-Authentication-with-Identity
HTH
Someone else, having the competence, can explain the options. But if authentication as service is an option, then check out Auth0 # https://auth0.com
I have tested the service (as Azure plugin) using both HTML/JS- and native Windows Phone applications, against simple Sql Server table and AD. Works liek charm, near zero headache.
I stumbled upon a my solution while trying to implement json token authentication within web api. It is important to note that my solution handles authentication by sending a json token through the Authentication header of the Http request (not via cookies) and not using Microsoft.Identity framework.
Anyway, I basically implemented in a cookbook fashion the solution helpfully described here by Taiseer Joudeh: http://bitoftech.net/2014/10/27/json-web-token-asp-net-web-api-2-jwt-owin-authorization-server/
The key thing to notice is the following bit of code:
//Dummy check here, you need to do your DB checks against memebrship system http://bit.ly/SPAAuthCode
if (context.UserName != context.Password)
{
context.SetError("invalid_grant", "The user name or password is incorrect");
//return;
return Task.FromResult<object>(null);
}
Naturally you would replace this bit of code above with your own method for checking your (presumably pre-existing) user database(s). Once I implemented this I realized that you don't need to use new code first identity framework that Visual Studio installs for you.
To make this work I did the following:
1) Created an an empty project and selected Change Authentication/Individual User Accounts. This installs most of the required references and files you need out of the box to use token authentication by way of cookies as well as the code-first identity framework files.
2) Edited these files following Taiseer Joudeh's lead. This requires
some new objects such as CustomOAuthProvider.cs among others. And you need to implement your own user/password check by customizing this code block:
if (context.UserName != context.Password)
{
context.SetError("invalid_grant", "The user name or password is incorrect");
//return;
return Task.FromResult<object>(null);
}
Link to Taiseer Joudeh's instructions: http://bitoftech.net/2014/10/27/json-web-token-asp-net-web-api-2-jwt-owin-authorization-server/
3) Pruned my project of extraneous files (AccountBindingModels.cs, AccountViewModels.cs, IdentityModels.cs, ApplicationOAuthProvider.cs, identityConfig.cs, AccountBindingModels.cs, AccountViewModels.cs). Basically, No more microsoft identity references.
I am sure the microsoft.identity thing is excellent, but I was annoyed with the code-first implementation of databases when I was already using some legacy databases of a different structure etc. Hope this helps. I am quite satisfied with the result (after a few days of messing around to get it to work).
I did not want to use any existing classes and finally come out some thing very simple like
var userName = context.UserName;
var password = context.Password;
var userService = new UserService(); // our created one
var user = userService.ValidateUser(userName, password);
if (user != null){
.......
}
See the full details here OAuth Web API token base authentication with custom database
For Role base authentication with custom database
Hope it will help
This might be a completely insane and invalid approach for you but I faced a similar challenge: New web app (MVVM + WebAPI), legacy system used to issue and validate tokens. Inspired by http://tech.pro/tutorial/1216/implementing-custom-authentication-for-aspnet, and because my application would primarily be used by its accompanied GUI (the MVVM webapp), I decided to use a "cookie based" token produced by FormsAuthentication. The FormsAutnentication cookie/ticket is secured by .net internal magic security (which I assume id completely safe and unbreakable).
In my case the cookie simply holds the ticket issued by the legacy system, (but you could store more details there as well, eg by JSONSerializing a custom type). During authorization, my system validates the token against the legacy system. I guess you could use something similar together with a custom AuthorizationFilter.

Log AD users in

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).

MVC3 + How to get the current logged on user's user name

I am new to MVC and actually new to web development all together. I have about 7 years of development experience but in services, database, object models, etc.. basically middle-tier and back-end development. I am trying to learn ASP.NET and decided to build a site using MVC3 for a personal site for myself. I will be hosting this from an account at dotnet-hosts.com. Here is my question... I don't have a domain and I will be using the built in membership provider. I noticed in the auto generated code that was created when I added the project template that in the AccountController in the method ChangePassword (ChangePasswordModel model) there is this line of code...
MembershipUser currentUser = Membership.GetUser(User.Identity.Name, true /* userIsOnline */);
My question is specifically around User.Identity.Name, this looks like it would be returning the Windows user name just like Environment.UserName would. The Visual Studio template I used is the (Mobile Ready HTML5 MVC.NET) as I want to be able to support clients from any device...Windows PC, Apple, Windows Phone, iPhone, etc... If the call to User.Identity.Name is correct then I would like to ask how does this work on devices that are not Windows like an iPhone? If my assumption is correct that this will only work for Windows computers with a domain then how can I achieve this? would I need to perhaps use some caching? If so could I maybe grab the user name and their IP address to be used as the cache key from the Authentication page?
My high level question is... How do I get the current logged in user's userName regardless of the device/platform? I know this question is probably not written well and may be hard to understand... I apologize for that. I am new to web development and trying to get my feet wet and would like to start to the latest technology.
The call is correct. The User.Identity.Name is filled out by whatever authentication provider is in use - Windows authentication, Forms authentication, some custom authentication provider, or whatever. It isn't bound to a specific user "type". The authentication provider has the responsibility of making sure the Identity object corresponds to the current user on every request. Usually that part is taken care of using a combination of cookies and database.
The MVC template (although I haven't had a look at the template since MVC 2) uses ASP.NET's Membership class, which in turn uses a membership provider - for example SqlMembershipProvider or ActiveDirectoryMembershipProvider - the former stores your users' credentials (username and password etc.) in an SQL Server database, the latter uses Active Directory (i.e. primarily Windows logons). SqlMembershipProvider is the default, and MVC is set up to use a local SQLExpress database file as its user store.
The authentication provider that's implemented in the template project uses FormsAuthentication, which does the login procedure through a simple HTML form (the one in the LogOn view) and keeps the user signed in by way of an encrypted cookie. Works on any platform.
The setup for both FormsAuthentication and SqlMembershipProvider can be found in web.config (the one in the root of the site). There you can find the connection strings for the SQLExpress database (and e.g. change them to use a "real" SQL Server if needed), the timeout for logins etc.
(Note that you can do a lot of that configuration easily in a GUI through the "ASP.NET Configuration" button in the toolbar of Solution Explorer in Visual Studio - it also provides an easy way to set up the first users).
In short, it's all ready to go - and doesn't lock out non-Windows users.
Like you said User.Identity.Name is indeed correct. for returning the logged in users name. But the membership section like you said, provides only windows accounts. You can use similar without the user of windows accounts, to work in every scenario, and can still verify against windows if present. If you call it without membership, and follow the default MVC3 template it should work fine.
String Username = User.Identity.Name;
When you log on, using the template MVC3, it creates an authcookie. See account controller code. Here, two parameters are passed into it. The username, and to persist (when browser is closed - login is still cached).
The username is a string field, which is what is called by User.Identity.Name and infact, anything can be put into it, and is not in anyway linked to Windows login.
You could test the login via method you desire, and if yes, set a cookie using the authcookie method. (its encripted). And set the username to what ever you want. And if your verification of the user fails, dont create one, and redrect back to page.
See the example code. This is all from memory, as I dont have code infront of me for reference. But its all in the account controller, Login Action.
When the cookie is set, The users login state is cached for the session. You will need to ensure the user is logged in when visiting a webpage. Otherwise loggin in will be pointless. This is a simple attribute on the controller/action.
Note: dont do this to the Account/logon controller, as you wont be able to visit the logon page, as you are not logged in.
[Authorize]
public ActionResult DoSomething()
{
// ...
}
Hope I have helped.

Categories

Resources