Formsauthentication and owin facebook authentication side by side (web forms) - asp.net

I need to add Facebook (and later google etc.) authentication to an existing asp.net webforms application using forms authentication.
Im almost there but there seems to be a conflict between formsauthentication and the owin authentication. My solution is based on the standard VS 2015 template.
The relevant code to redirect to the the external authentication provider (facebook) is as follows:
string redirectUrl =
ResolveUrl(String.Format(CultureInfo.InvariantCulture,
"~/Account/RegisterExternalLogin?{0}={1}&returnUrl={2}", IdentityHelper.ProviderNameKey,
provider, ReturnUrl));
var properties = new AuthenticationProperties { RedirectUri = redirectUrl };
Context.GetOwinContext().Authentication.Challenge(properties, provider);
Response.StatusCode = 401;
Response.End();
This works if i turn off forms authentication. If I have forms authentication, the user gets redirected to the forms authentication url defined in the web.config:
<authentication mode="Forms">
<forms name=".ASPXAUTH_Test" loginUrl="~/start/login.aspx" timeout="60" >
</forms>
</authentication>
From my understandint the Http Status code (401) triggers the redirect to the Url in the web.config. I tried to set other status codes, but they don't work at all.
When I turn off forms authentication in the web.config, the actual login process still works (surprisingly) but if I access a protected page while not having logged in a get an ugly IIS error page, instead of being redirected.
It seems, that I can't get work forms authentication and owin external authentication to work together properly :-(
So far all alternatives don't seem enticing to me:
- switch to the identity framework (in our specific environment, this is absolutly NOT an option. I just mention it for the sake for completness)
- try to use web.api or something similar (which probably has the same problem)
- let go of owin external authentication and implement everything by hand
Has anyone been able to make this work? Any help is appreciated. Thanks in advance.

Halleluja, I found the solution:
From .Net 4.5 it is possible to prevent the forms redirect in the response:
Response.SuppressFormsAuthenticationRedirect = true;
so the working code would look like:
var owinContext = Context.GetOwinContext();
owinContext.Authentication.Challenge(properties, provider);
Response.SuppressFormsAuthenticationRedirect = true;
Response.StatusCode = 401;
Response.End();

Related

FormsAuthentication.SignOut() does not expire the FormsAuthenticationTicket

First off, this is not a problem with the ASP.NET session not expiring, we clear, abandon, and delete every cookie on logout.
This is about FormsAuthentication.SignOut() not expiring the ticket when called and allowing someone who copies the content of the cookie to manually create the cookie somewhere else and still be able to acces everything that is meant to now be blocked off after the logout.
Here is the gist of our logout method:
HttpContext.Current.User = null;
HttpContext.Current.Response.Cache.SetCacheability(HttpCacheability.NoCache);
HttpContext.Current.Session.Clear();
HttpContext.Current.Session.Abandon();
HttpContext.Current.Session.RemoveAll();
HttpContext.Current.Response.Cookies.Add(new HttpCookie("ASP.NET_SessionId", ""));
FormsAuthentication.SignOut();
FormsAuthentication.RedirectToLoginPage();
We also let ASP manage the creation of the ticket and the authentication via our Web.config and whatever else manages FormsAuthentication. Here is what is in the config:
<authentication mode="Forms">
<forms name="COOKIENAME" loginUrl="~/PAGE_THAT_REDIRECTS_TO_LOGIN.aspx" defaultUrl="~/PAGE_THAT_REDIRECTS_TO_LOGIN_OR_PROPER_PAGE_IF_LOGGED_IN.aspx" cookieless="UseCookies" timeout="60" />
</authentication>
Now, why is this an issue? simple, it's a security concern as if someone gets the cookie and keeps it alive, they can access whatever the user matching the cookie can, even though the user has been disconnected.
Is there is a proper way to force the FormsAuthenticationTicket to expire?
I tried decrypting it, but everything is readonly, and I also tried to create a new expired ticket and encrypting it, but it doesn't overwrite the old one.
Thanks
Versions: .NET 4.5.1, ASP.NET (not Core)
The basic problem is with Microsoft .net Core cookie Managemnt, it does not handle the lifetime of cookies correctly.
I had face this issue several times, and mostly with .Net core now.
To solve this issue we need to override their cookie management class, and implement ITicketStore interface.
https://github.com/aspnet/Security/blob/master/src/Microsoft.AspNetCore.Authentication.Cookies/CookieAuthenticationOptions.cs#L136
Below article can help you with detail implementation.
https://mikerussellnz.github.io/.NET-Core-Auth-Ticket-Redis/
I hope it helps.

Forms Authentication SSO - Why does User.Identity.IsAuthenticated == false

I'm trying to set up SSO on two separate IIS web sites (with a common domain) using Forms Authentication. I'm using the common approach of setting the auth cookie to the common domain and using matching machine key to enable decryption.
This works with no issues on test sites that I created. However, when trying to implement this in a legacy Web Forms site, I'm running into something that I don't understand.
When I log into one site, the test code on the second site has the following results:
var cookie = FormsAuthentication.GetAuthCookie("username", false); // works
var ft = FormsAuthentication.Decrypt(cookie.Value); // returns correct info
var isAuthentication = User.Identity.IsAuthenticated; // false
As an added bonus, whenever I sign in one site the other gets signed out (happens both ways.)
There must be something fundamental that I'm missing here.
Why is User.Identity.IsAuthenticated set to false, even though the FormsAuthentication ticket seems to be decrypting with no issues?
UPDATE: as pointed out below - FormsAuthentication.GetAuthCookie is not a valid way to obtain the existing auth cookie. Which brings me to this: I can see the top domain cookie in the browser, but it doesn't show up in the request. I suspect this is where the problem is occuring.
Solution: Both sites were not targeting the same version of .Net Framework as specified by the web.config:
<httpRuntime targetFramework="4.5" />
<compilation debug="true" targetFramework="4.5" />
Updating both sites to target the same framework fixed the problem.
The GetAuthCookie creates a new cookie:
http://msdn.microsoft.com/en-us/library/vstudio/3fay1e4k(v=vs.100).aspx
Creates an authentication cookie for a given user name. This does not set the cookie as part of the outgoing response, so that an application can have more control over how the cookie is issued.
No wonder it works, it doesn't look into the existing cookie.
My theory is that you have your new sites on an x64 machine and the legacy website sits in an x86. The encryption differs in such scenario even if keys are the same. Another possible reason is a different version of .net as the encryption algorithm has been changed in .net 4.

Accessing .ASPX page on password protected site from within same site

We recently migrated our staging server to a new hosting provider and setup forms authentication from within the hosting account control panel so that crawlers and unwanted visitors could not access it.
Now we are finding at least one area of the site's logic that is failing due to HTTP 401 Unauthorized.
There is a section of the site that generates PDF for site users. The conversion is from HTML to PDF. The source pages are .ASPX written in C#. The generate .HTM by downloading the ASPX files using their URL, such as http://www.mysite.com/mypage.aspx.
Now that the site is password protected, these routines fail with the HTTP 401 and I'm not sure how to overcome this. We don't want to remove the site authentication because we don't want anything accessing it.
Can anyone point me on how to code around this so that our internal routines will have access to local pages like we need?
EDIT
Some more detail. Since this is just a development site, I performed a quick and dirty configuration from within the hosting provider's control panel to enable Folder Security. I added the root folder '/' and then created 2 users. This works fine. When I go to the site I am prompted with what appears to be a forms authentication dialog box. I enter my username and password, access is granted.
I've noted that this configuration created 4 files in my root /' web site folder. They are .htaccess, .htpasswd, .htgroup, and .htfolders. This site has a lot of folders. Configuring each one in this manner would be time consuming and tedious. Therefore the '/' root configuration.
Our purpose is to block access to crawlers/search engines and also casual visitors who stumble onto the hostname.
This configuration causes the side effect that a small part of the site can no longer access it's own pages via http:// without getting an HTTP 401 error. What I would love to do is configure all of this using <security><ipSecurity>, blacklist all except for myself and the web site, but the provider doesn't install the needed IP module to do this.
The C# code that is receiving the HTTP 401 is:
webrequest = (HttpWebRequest)WebRequest.Create(url);
webrequest.UserAgent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; .NET CLR 1.1.4322)";
webrequest.Timeout = 600000;
resp = webrequest.GetResponse();
I've also tried:
CredentialCache credCache = new CredentialCache();
credCache.Add(new Uri(url), "Basic", new NetworkCredential("username", "password"));
webrequest = (HttpWebRequest)WebRequest.Create(url);
webrequest.Credentials = credCache;
webrequest.UserAgent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; .NET CLR 1.1.4322)";
webrequest.Timeout = 600000;
resp = webrequest.GetResponse();
Both methods receive HTTP 401 Unauthorized. I really don't want to fix this in the C# code because this security issue will not exist on the live site. I would much rather perform this configuration in web.config and/or the .ht* files if needed.
So, I am wondering, is there anything I can place in web.config that will help? Does anyone see a problem the way this is setup? (Other than it's not working! :P)
If you want you can remove authentication for just one page as follows.
<location path="mypage.aspx">
<system.web>
<authorization>
<allow users="*"/>
</authorization>
</system.web>
</location>

Single Sign-on SignOut problem : FormsAuthentication.SignOut() is not working

I used the Single Sign-on demo from: http://www.codeproject.com/KB/aspnet/SingleSignon.aspx
And I add a SignOut function for this demo, but found a problem:
when I set the cookie.Domain, FormsAuthentication.SignOut() is not working and the cookie can not be cleared.
If the cookie has not been set the cookie.Domain, FormsAuthentication.SignOut() works.
I used C# asp.net.
And could anybody tell me some simple and practical Single Sign-On and Single Sign-Off solutions using asp.net ?
In case you are using the authentication for the same domain and subdomain, try adding the domain name in the web.config instead of adding the domain through code. you will no have to code anything if you use this web.config entry
<authentication mode="Forms">
<forms loginUrl="~/Account/LogOn" timeout="2880" domain="abc.com"/>
</authentication>
This entry tells the asp.net engine that the authentication cookie will be used for all subdomains of abc.com . Try using this and see if it works.
This worked for me:
In the Logout event/ method of each site, use Cookies collection in Request object & delete the relevant cookies as below:
enter code hereHttpCookie cookie = Request.Cookies.Get(".CommonCookieName");
cookie.Expires = DateTime.Now.AddDays(-1);
HttpContext.Current.Response.Cookies.Add(cookie);
If all the sites in SSO use same cookie, then this is simple as described above.
If few or each site participating in SSO use their own cookie/ user name for same subject (person), then u need to remove all the cookies. (perhaps establish a central location with just the mapping of the usernames & cookie names in each site in SSO collection of sites.
This works for me
public virtual ActionResult LogOff()
{
FormsAuthentication.SignOut();
foreach (var cookie in Request.Cookies.AllKeys)
{
Request.Cookies.Remove(cookie);
}
foreach (var cookie in Response.Cookies.AllKeys)
{
Response.Cookies.Remove(cookie);
}
return RedirectToAction(MVC.Home.Index());
}

ASP.NET: Authenticating user in code

I'm playing around with authentication and authorization to prepare for some task. I've created two pages: Login.aspx and Default.aspx. In config file i've set authentication to forms and denied unauthenticated users access:
<authentication mode="Forms">
<forms name="aaa" defaultUrl="~/Login.aspx" />
</authentication>
<authorization>
<deny users="?"/>
</authorization>
Then I've written some simple code to authenticate my user in Login.aspx:
protected void Page_Load(object sender, EventArgs e)
{
GenericIdentity identity = new GenericIdentity("aga", "bbb");
Context.User = new GenericPrincipal(identity, new String[] { "User" }); ;
Response.Redirect("~/Default.aspx");
}
When i run it, the redirection doesn't take place. Instead Login.aspx is called over and over because the user is not authenticated (Context.User.Identity.IsAuthenticated is false at every load). What am i doing wrong?
Context.User only sets the principal for the current request. Once the redirect takes place, the current request ends and a new one begins with the non-overridden principal again (which is apparently not authenticated). So, setting Context.User doesn't actually authenticate anything.
Using FormsAuthentication.SetAuthCookie() will set the user's cookie to a valid value accepted by the FormsAuthentication provider, or put the token in the URL. You can redirect to your heart's content because the cookie obviously sticks with the user for future requests.
From MSDN (em added):
With forms authentication, you can use the SetAuthCookie method when you want to authenticate a user but still retain control of the navigation with redirects.
As stated, this does not necessarily require cookies - the name is a little misleading, because it will still work via the URL if FormsAuthentication is in cookieless mode:
The SetAuthCookie method adds a forms-authentication ticket to either the cookies collection, or to the URL if CookiesSupported is false.
Use FormsAuthentication.SetAuthCookie(..). Or FormsAuthentication.RedirectFromLoginPage(..).
You need to actually set the user as authenticated. All of the following methods will work and let you actually get away from your login screen.
FormsAuthentication.Authenticate()
FormsAuthentication.RedirectFromLoginPage()
FormsAuthentication.SetAuthCookie()
Lots of ways to get to the same result.
You need to actually make a call to the formsAuthentication provider to set the login.
FormsAuthentication.RedirectFromLoginPage(txtUser.Text, chkPersistLogin.Checked)
is a simple example
After creating the dummy Context.User, you need to perform a FormsAuthentication.SetAuthCookie or RedirectFromLoginPage method.

Resources