ASP.NET MVC / Web API Custom Authentication - c#

I have ASP.NET MVC 4 / Web API hybrid application. The authentication is being handled by an existing application. In looking at securing these types of applications, most articles point to using Forms Authentication along with the [Authorize] attribute on the MVC and API controllers/actions you want to protect. I would like to use the [Authorize] attribute as it will handle both MVC routes and API routes but not sure how to do that without having an actual form and using the built-in membership provider.
Should I go with a simple approach like described here? Or should I create a Custom Membership provider that handles the logic?
For clarity, the workflow would be as follows:
The user logs in through the existing authentication portal.
If authenticated, they are redirected to my application along with some additional data like username and email (so no passwords need to be transferred)
My application sets an authentication cookie that allows the user to continue using the application.
Any help would be greatly appreciated.

Option 1
If your existing login portal is using Forms Authentication, then you can share the encryption keys between the applications so that they can read the authentication cookie created in the login portal:
How can I share .net (C#) based authenticated session between web forms and MVC2 applications?
Option 2
If you're not using forms authentication in the login portal, you could keep your existing process, but add on a step that manually creates the authentication cookie. There are lots of variations on this, but this question show a higher level way in the question, and a lower level method in the answers:
How can I manually create a authentication cookie instead of the default method?
This also requires the encryption configuration be the same between the applications.
Option 3
Otherwise you are either left with using an SSO protocol for the handoff. You can keep the existing authentication process for your login portal, but will need to add additional code to coordinate the SSO handoff to the other application. SSO came about because other than encrypted cookie methods used in #1 & 2 above, there is little other secure options for doing a browser redirect and communicating authentication.
Creating your own cookie based method is risky and may open up security holes that you don't foresee.
"Should I go with a simple approach like described here?"
The important thing about that example is it's not clear where username is coming from in SetAuthCookie(username, .... That question implies that the user will login to the additional application and that app will query the web service to determine if that login is valid. In this case it's not single sign on with a dedicated login portal, but instead each app collects login information and asks the web API if it is valid. In your case, you do not want to collect the login information in each portal, but instead detect that they've already logged in to the deicated login portal.
So the problem is how does the login protal tell you in a secure fashion what username is when you call SetAuthCookie(username, .... That's exactly what SSO is for. Using a SSO handoff, one site can tell the other in a secure fashion that "I'm sending Bob123 to you, and you can be sure it is really Bob123 and not someone else.
Options #1 and #2 get around this by having the login portal set the cookie instead, and by sharing keys across the apps, the other apps can securely read that cookie.
Note you can't do this with just any cookie. The forms authentication cookie is built in a certain way to prevent forgery of the cookie and other tampering.
SSO becomes your only option if you are going across domains because cookies written in one domain cannot be read in another(the browser only submits cookies for the current comain).
There are workarounds for forms authentication with multiple subdomains sharing a root domain:
Proper creation of a cross-domain forms authentication cookie
There are various hacks for redirecting to another site with encrypted information and letting that site write the forms authentication cookie, but most of them are just horrible hacks that are just as complicated as SSO.

You seem to try to reinvent a Single Sign-On protocol. Instead, there are existing SSO protocols like OAuth2 or WS-Federation you should definitely learn about.
In general, SSO protocols work similarily to what you expect your "workflow" to behave. The exact flow can differ but this is always the Identity Provider that authenticates/authrorizes users and the IdP somehow passes this information to the application that makes use of it (for example, the application issues a custom cookie to establish user authentication).
The Authorize attribute is not intended to be used with Forms Authentication only. Any authentication module that sets the principal for the request lifetime can replace Forms. For example, the Session Authentication Module is often used nowadays as it fixes some particular issues of the Forms module (e.g. the inability to persist long user data).
If you need a good free book on SSO, take a look here:
http://msdn.microsoft.com/en-us/library/ff423674.aspx

You can use Owin to handle this. Here's a code snippet I am using to authentication using Facebook, it also uses cookie:
using Microsoft.AspNet.Identity;
using Microsoft.Owin;
using Microsoft.Owin.Security.Cookies;
using Owin;
namespace ASPNetMVC53rdPartyAuth
{
public partial class Startup
{
public void ConfigureAuth(IAppBuilder app)
{
// Enable the application to use a cookie to store information for the signed
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login")
});
// Use a cookie to temporarily store information about a user logging in with a
// third party login provider
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
// third party login providers:
// You have to register this app at https://developers.facebook.com/ and get the
//appId and appSecret.
// Facebook requires SSL, so that need to be enanbled. Project url can be found
// under project properties and can be localhost.
app.UseFacebookAuthentication(
appId: "xxxxxxxxxxxxxxxx",
appSecret: "xxxxxxxxxxxxxxxx");
);
}
}
}

Related

How to use one user credential across different related Owin-based MVC apps and web APIs?

I have an Owin-based MVC application which uses my web api to provide many functionalities. At the moment the user should login to both of them separately (using ajax calls, at the login page I do the login for web api and receive the token as well), but both use the same table, so there is only one place to store the user information.
Unfortunately other MVC applications are using separate username and passwords and are not using the mentioned api. As now I should create a new MVC app which is again the same domain I am looking for a way to use a single username and password (managed by one main MVC app) for whatever reason it's needed, i.e., all the MVC apps and the web APIs use the same username and password, and therefore for example I can use the [Authorize] attribute or roles, ... in all of them.
Is there any known solution for this? Does Creating an OAuth authorization server suit this problem?
Implementing Single-Sign on using OpenID Connect on top of OAuth2 fits your requirement. checkout identityServer3 or identityServer4 if you are using .net core.
basically, you will need to setup a shared STS authority, and all your applications will use this authority to validate a requests using a OWIN middleware to check for token validity.
On first login, The STS authority will issue a token for the user, and you will need to manage through your front-end/back-end code, re-using the token when navigating across multiple applications/domains.
C# ASP.NET Single Sign-On Implementation

Can a website authenticate against multiple ADFS servers?

We have an ASP.NET/MVC website that's using FormsAuthentication. As is usual, when the user tries to access a page, and doesn't have a valid FormsAuthentication cookie, IIS redirects him to the login view. When the user does a HttpPost to the login controller, our controller action makes a call to our WebApi webservice, which validates username, password, and customerid against a Sql Server database. If the authentication passes, the controller action sets a FormsAuthentication cookie, and redirects to the page the user had asked for.
Now sales is making noises about "Single Sign-On", though I'm not clear exactly what they mean by that. From what I've read, in the Microsoft World this usually means accessing MS's Active Directory Federation Services.
At this point I have almost no idea how this would work, but before I dig into this too deeply, would it be possible to put the authentication code within the WebApi webservice, where we could choose to validate against the Sql Server database, or against whichever ADFS server was appropriate for the specified customer?
Our problem is that we have I don't know how many thousands of users, working for some hundreds of customers. Many customers will not have ADFS running, and those who do will each have their own ADFS server.
Most of what I see with respect to Single Sign-On seems to involve doing browser redirection to the ADFS server, then redirection back, and looks to be avoiding login at all, if you're already logged in. I don't think we can do that, in our case. We can't know which ADFS server to redirect to, until we hit the database.
So, the question - is it possible to do ADFS authentication entirely from C# code in our WebAPI web service?
(One possible complication - the website itself has zero access to any database. The sole configuration setting in its web.config is the base URL of the webservice. Whatever authentication happens has to happen in the webservice, not in the website.)
First of all, "Single Sign-On" (SSO) is not limited to ADFS. It simply means that you type your credentials only once, and then all systems you access automatically "recognize" you; all subsequent authorizations request are transparent. For instance, if you have several web sites using Windows Authentication in your company Intranet (same AD domain), you have SSO: you authenticate once when you log in to your computer, and then your web browser authenticates automatically to these web sites using NTLM or Kerberos. No ADFS in this case.
What ADFS (and "Federation" more generally) allows, is SSO accross security boundaries. In Windows world, a security zone is typically created by an Active Directory forest; everything within this forest is accessible using SSO provided by Windows authentication. But as soon as you leave this zone (SaaS application, web site in another company network), you need another authentication protocol to perform SSO, and these protocols are implemented in ADFS.
Then about your particular problem:
What you could do is instead of using FormsAuth, you use AdfsAuth. When a unknown user accesses a page, he would be redirected to ADFS for authentication (using browser redirects as you correctly mention). To know which ADFS server should authenticate your user, you need a way to differentiate them indeed: a list of IP range per customer? a different URL per customer? If you don't have something like this, then the only way is to show them a list of choices such as: "I work for CompanyA", "I work for CompanyB", "I work for CompanyC", "I don't work for any of these companies and want to authenticate using FormsAuth."
In this case, what your WebApi web service has to do is: if I know which ADFS server to use, redirect the user there. Otherwise authenticate the user as usual using the database.
When you use AdfsAuth for a customer, your database is useless. You can delete all credentials related to this customer.
do ADFS authentication entirely from C# code in our WebAPI
Well it's possible to "re-implement" ADFS in your service, but you won't get SSO if you do that. When you use federation, your redirect the user to the ADFS server of his company. This ADFS server is in the same domain as his computer, so the user gets SSO here. Once again, your users can't get SSO if you authenticate them yourself, because your users are not in the same security zone as your site.
When authenticating to multiple identity providers, it is typical redirect to your own STS. So, in this case, you would have www.yourapp.com redirecting to sts.yourapp.com, which redirects to sts.somecustomer.com.
The specific tools to enable such a dataflow is the home realm parameter (whr), and the AD FS Powershell API (to allow IDP maintenance).
Your RP-STS acts as the trust-point for the app, and manages selection of the appropriate IDP. One RP-STS, many IP-STS's. Each of your Customer's IP-STS gets set up as a Claims Provider Trust in AD FS.
As always, Vittorio has already covered the subject better than I could.

SignInManager,what it is and how,when to use?

I am exploring SignInManager class. But the information given on MSDN is very useless. It only tells what are the methods and properties provided.
What I am looking for is,
What is SignInManager?
How to use it?
And I have my own database that contains credentials related info(username and passwords)
How can I use SignInmanager and how to use it so my custom database is used for authenticating users?
I am using asp.net MVC 5 and Visual Studio 2015. In my sample project I have accounts controller that contains action methods like
public async Task<ActionResult> ExternalLoginCallback(string returnUrl)
But I have no idea how to use it, MSDN is completely useless to provide info on this. Any helpful links that explains it in details because I have no idea what SignInManager is and what it's for.
Thanks
Disclaimer: I am confused by model used in ASP.NET identity myself and what I say is my understanding of things, that may be inaccurate (I might also be stating obvious things, so I apologize). Also, I was playing with Asp.Net Core's identity recently which is slightly different compared to what was available for Asp.Net 4, so I may mix things up.
Cookies
ASP.NET identity operates with two kinds of cookies: Application cookie and External cookie. Application cookie contains your application's identity and is issued by sign in manager. External cookie contains external authentication provider identity and is issued by authentication middleware (such as FacebookAuthenticationMiddleware, for example). You use sign in manager to consume the external cookie and issue application cookie instead. If you don't use external authentication you don't deal with external cookies.
Sign in manager
Class declared like this:
public class SignInManager<TUser, TKey> : IDisposable
where TUser : class, IUser<TKey>
where TKey : IEquatable<TKey>
So you may use any class as your user as long as it implements IUser<TKey> interface. Or use IdentityUser as your base if you start from scratch, which implements IUser<string>. In the past I attempted to create an implementation that uses int as TKey, but abandoned attempt after spending quite some time trying to make it work and not seeing any progress.
Password sign in
SignInManager.SignInAsync method issues application cookie for the specified user right away without any checks, so if you implement any custom authentication logic, you might want to use it (default asp.net MVC template uses it after registering user so they don't have to authenticate right after registration).
SignInManager.PasswordSignInAsync given the user name and password checks their validity and issues application cookie if they are correct.
External sign in
Instead of having the user to create login and password for your site specifically you might want them to use some external web site to authenticate and pass the authentication information to you with OAuth.
Asp.Net Identity has notion of User and Login, where User is... well, user (a person), and Login is the credential with which User authenticates. User might have multiple Logins.
OAuth flow as seen from Asp.Net web site looks like this (based on the default log in flow generated by VS template):
You set up external authentication providers (authentication middleware) which you are willing to accept (that likely involves registering on external web site. For example, in order to use Facebook authentication you need to create Facebook app, set up return URL there to point to your web site and configure FacebookAuthenticationMiddleware with app ID and app secret Facebook provides you with).
You present unauthenticated user with a choice of external providers you support.
User picks a provider, the choice is sent to your Asp.Net web application
Web application issues a ChallengeResult containing the name of the provider to be used (this usually happens in AccountController.ExternalLogin), return URL is set to call AccountController.ExternalLoginCallback and actual return URL user should end up in is saved for later.
Appropriate middleware catches the ChallengeResult object and converts it into HTTP redirect response that causes user's browser to go to third party web site that asks user for credentials.
Third part web site upon successful authentication redirects user back to you web site to the specific URL crafted by the authentication middleware (for Facebook it's /signin-facebook IIRC).
Authentication middleware intercepts this call, validates the data passed by third party web site and if everything OK issues the external cookie, and redirects you to whatever was set as return URL at step 4 (which should be AccountController.ExternalLoginCallback).
In AccountController.ExternalLoginCallback you are expected to consume the external cookie and issue an application cookie instead. That's what SignInManager.ExternalSignInAsync does: given the log in information it tries to find user with that Login. If it finds, it issues Application cookie; if it does not, it informs you and you should do what you think is right when you receive unknown Login (generally, you create new user at this point. Default implementation from VS template asks for additional info at this point and creates user in AccountController.ExternalLoginConfirmation). After that user is redirected to actual return URL "saved for later" in step 4.
Custom storage
I've been unsuccessful so far with creating custom storage for Asp.Net Identity. It generally involves implementing your own user manager class descending the UserManager<TUser, TKey> and storage class implementing bunch of interfaces like IUserStore<TUser, TKey>, IUserRoleStore<TUser, TKey>, etc.

Custom Identity 2.x Hooking Into OWIN Pipeline - Multi-Tenant SaaS Application

Question: Can we simply hook into the OWIN pipeline to set and retrieve the security authentication ticket (cookie)?
I'm searching for the best approach to roll our own security/membership for an MVC 5 application. I have no issue with registration, sign-in, password change, two factor, password recovery etc. My concern is the pipeline.
Project: The project is a multi-tenant SaaS application that requires many changes to the the Identity framework. We are exploring writing our own, which we have done for many projects in the past.
We do not make use of claims or outside providers such as Google or Twitter for authentication, all accounts are local db role based accounts. The project does not use EF.
We explored creating our own store for Identity, however, by the time it's fully implemented, there were too many areas of concern and it felt as though though the User Manager was simply in the way. Code was starting to bloat beyond a level of what is needed to role our own.
Passwords are created, managed, and stored securely and are not of concern for this question.
We are using our custom authentication mechanism wired in the OWIN pipeline. We are using our own custom middleware into the owin pipeline and thus will enable the authentication to happen
The only process that needs to be done is your middleware should validate the identity and the cookie authentication middle ware in the pipeline will set the cookie based on the established identity.
The way this will be working is the ordering of the middlewares and the use of the AuthenticationManager in the Owin authentication pipeline.
We too are using social logins like Google, Facebook etc that co-exist with our own custom authentication provider middleware, we also use JWT middleware for implicit flow authentication.
Long answer short is : Yes
More details : based on your further post on the exact details that need to be attended to.
If you are willing to consider a hosted solution, I would recommend taking a look at http://aka.ms/aadb2c - the samples referenced by that page will show how to use OWIN middleware to connect to a hosted identity store.
We are running multi-tenanted SaaS app with configurable per-role permissions. We make use of Claims system to store user permissions in the cookie when users login. And that works wonderfully and is really performant.
You don't have to use actual Claims to put your permissions, but you can store bits of information as a Claim on a user cookie that will be used to determine user permissions.
Since your question is a bit vague about the actual implementation I will not bring any code samples - given your hand-rolled security system, most likely my examples will not be applicable. Unless you clarify your question with a specific example of what you are doing.

Does .Net Forms Authentication Require Session?

We are having a discussion about how forms authentication really works.
Is all information that identifies the user as being logged in stored in the cookie, or is some information stored in the session?
Information about the user being authenticated is stored in the FormsAuthenticationTicket in a cookie, by default named .ASPXAUTH.
Information about a user's session is separate from information about authentication. The identifier for session can be stored in a cookie (a different cookie from the authentication cookie) or, as Henk has pointed out, in a cookieless session i.e. as part of the URL.
The problem with storing some information about authentication in a user's session is that session is not available until some time after the authentication event (5 events later IIRC) in the processing pipeline, in PostAcquireRequestState. This means you wouldn't have access to the authentication data in session until after authentication!
It's possible to store data in session and to overwrite the IIdentity and IPrincipal with that data, but this does means that the user identity will have some data for the events before session is available and different data for the events after session is available, which may or may not be a problem. Furthermore, you'll probably want to cryptographically secure that data in session in some way.
To answer your title question, forms authentication does not require session; they are distinct entities required for different purposes.
For how Forms Authentication works, you can check out the below links:
Explained: Forms Authentication in ASP.NET 2.0
Understanding the Forms Authentication Ticket and Cookie
Forms Authentication works in web farm scenarios where the server handling a request from a Forms authenticated user may be different than the server that actually authenticated the user and issued the Forms authentication ticket and the cookie unless cookieless forms authentication is configured. To make this work, according to Web Farm Scenario section of the first link:
To address this issue, the validationKey and decryptionKey values must be identical on all computers in the Web farm. For more information about configuring the machineKey element, see How To: Configure MachineKey in ASP.NET 2.0.
which suggests that the Forms Authentication does not store anything in ASP.NET session. Otherwise, you would need to set up some form of an out-of-process session management in-place as well.
I also had a sample Forms Authentication application on hand and wanted to prove this quickly. After getting authenticated via Forms Authentication and landing on the home page, I restarted the application pool that the sample application was running in which should kill the user session. I then clicked on one of the links requiring authentication on the home page and was able to go to that link without getting redirected to the login page.

Categories

Resources