ASP.NET Facebook app session issue with safari - asp.net

Currently I am working on a Facebook app and it's developed by using ASP.NET.
This app works fine with IE(7,8 and 9) FF and Chrome.
The first page is default.aspx and it will handle the authentication then redirect to home.aspx
Now the only issue it has is that Safari doesn't accept cross-domain cookies. I've changed the web.config file and add it in order to avoid the use of cookies.
After that, the URL comes to
http://www.testdomain.com/(S(gvsc2i45pqvzqm3lv2xoe4zm))/default.aspx
It just can't be redirect from default.aspx to home.aspx automatically...
Anyone got a clue?
Or, is there anyway that i can deal with Safari with ASP.Net session in Facebook app?
Tons of thanks
PS. The code from default.aspx page
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
if (!string.IsNullOrEmpty(Request.Params["signed_request"]))
{
string signed_request = Request.Params["signed_request"];
if (!string.IsNullOrEmpty(signed_request))
{
// split signed request into encoded signature and payload
string[] signedRequestParts = signed_request.Split('.');
string encodedSignature = signedRequestParts[0];
string payload = signedRequestParts[1];
// decode signature
string signature = decodeSignature(encodedSignature);
// calculate signature from payload
string expectedSignature = hash_hmac(payload, Facebook.FacebookApplication.Current.AppSecret);
if (signature == expectedSignature)
{
// signature was not modified
Dictionary<string, string> parameters = DecodePayload(payload);
if (parameters != null)
{
string UserId = parameters["user_id"];
Session.Add("UserId", _SystemUser.SystemUserId);
Session.Add("Username", _SystemUser.Username);
Response.Redirect("Home.aspx?user_id=" + UserId);
}
}
}
}
if (!String.IsNullOrEmpty(Request["error_reason"])) // user denied your request to login
{
logger.Debug("Error Reason: " + Request["error_reason"]);
//User denied access
}
if (String.IsNullOrEmpty(Request["code"])) // request to login
{
string url1 = String.Format("https://www.facebook.com/dialog/oauth?client_id={0}&redirect_uri={1}&scope={2}", Facebook.FacebookApplication.Current.AppId, callbackUrl, ext_perms);
Response.Redirect(url1);
}
}
}

When using cookieless sessions, ASP.Net will automatically redirect any requests without a session ID in the URL to the same page, but with a new SessionID in the URL. However, it redirects as a GET request, and thus does not forward on any POSTED parameters ... so after the redirect your "parameters" variable, from the decoded signed_request, will be missing because the page will no longer have the signed_request POSTed parameter.
There are two possible solutions to this (that I know of):
Intercept the initial redirect in Global.ascx, and instead do your own redirect with the new SessionID in the URL ... BUT, do this as a self-posting form in Javascript where the form also has a signed_request param with the value of the signed_request.
Turn cookie sessions back on, and in your first page redirect out of FB to a page. In this page set a Session variable (which will get ASP.Net to set a session cookie), and then redirect back into FB.
You may/will also need some code to handle any app_data, if this is on a tab page too.
Sorry I can't be more useful code wise. I've written my own handlers for my job, but my workplace now owns that code! I'm never sure how much is OK to share.

I used cookieless session, but as the initial page was getting refreshed, the Facebook "signed_request" POSTed to the landing page was lost.
As a workaround, I added an HTTPModule to override EndRequest() event. In the event if the page is "initial page" & contained "signed_request" POSTed, the value is added as querystring. In the page we would check the querystring value and set it into session, to be used in the application.
The EndRequest is as below:
void context_EndRequest(object sender, EventArgs e)
{
HttpContext cntxt = HttpContext.Current;
const string paramname = "signed_request";
const string initialPage= "/startapp.aspx";
if ((String.Compare(cntxt.Request.Url.AbsolutePath, initialPage, true) == 0) && (!String.IsNullOrEmpty(cntxt.Request[paramname])))
{
string strQuerySignedReq = paramname+"=" + cntxt.Request[paramname];
if (cntxt.Response.RedirectLocation.Contains(".aspx?"))
cntxt.Response.RedirectLocation = cntxt.Response.RedirectLocation + "&" + strQuerySignedReq;
else
cntxt.Response.RedirectLocation = cntxt.Response.RedirectLocation + "?" + strQuerySignedReq;
}
}
The initial page - "startapp.aspx", load event would be:
protected void Page_Load(object sender, EventArgs e)
{
signed_request = Request.QueryString["signed_request"];
}
The disadvantage of the code is that EndRequest() would execute for all requests. Also, only relative url should be used for links. I have had several annoying experiences on cookies and Facebook, due to various security levels on different browsers. Hence, I can live with the disadvantages. Hope this helps!

I know this is an old question, but I had exactly the same problem and found a solution.
The solution here works if you're using a SQL Server in your application.
Using cookieless to store your SessionId in the URL will avoid the cookie problem, but still missing the Session issue in Safari.
Well, you'll need to set a SQL SessionState, this will make your application communicate with your Database to store the Sessions. This will work for facebook canvas apps in Safari.
Setting this is simple:
Registering: run aspnet_regsql.exe (in C:/Windows/Microsoft.NET/Framework/'Framework version'/)
Check parameters in https://msdn.microsoft.com/en-us/library/ms229862.aspx (the main ones are -S –ssadd)
In the same path, there is a InstallSqlState.SQL script. Run it on your Database Server.
Now, set this tag in your Web.Config file:
<configuration>
<system.web>
<sessionState mode="StateServer" stateConnectionString="tcpip=127.0.0.1:42424" timeout="120" cookieless="true" />
</system.web>
</configuration>
And the magic is done!
There is something to remember. You can't do WebRequests to facebook from Server side to request for access tokens, because facebook redirects the calls to the "Valid OAuth redirect URIs", and completely ignores the SessionId parameters in the Request URI. You still can make WebRequests to APIs, but the authentication will need to be assyncronous, using Javascript.

Related

Restrict access to all asp.net pages

I am mainlining one asp.net Project, this project is configured in IIS. The website is open for everyone, when i review the code in asp.net page, its checking window login "enterprise id" and allowing all users to view the all the aspx pages.
Now, my management team requested us to restrict those who are under junior level employees.(Junior engg, Developer, software engg).
I have written the query, passing enterprise id and validate grade, if its junior level , returning "0" values,else returning "1" values.
My questions is, I do not want go and edit each page and check this query and restrict each page.
can you please suggest , how can i implement simplest and best way to restric the users.
Thanks,
--------------------------------------- Update on 09/24/2015
Index.aspx
protected void Page_Load(object sender, EventArgs e)
{
string UserStatus = UtilFunctions.ValidateUser();
Response.Write(UserStatus);
if (UserStatus == "0")
{
Response.Write("<div><font color=red><h1>You are not authorized to view this page</h1></font></div>");
Response.End();
}
}
Utilifunctions.cs
public static String ValidateUser()
{
string CurrentUser = getLoggedOnUser();
using (System.Data.SqlClient.SqlConnection myConnection = new SqlConnection(ConfigurationManager.ConnectionStrings["TestDB"].ConnectionString))
{
using (SqlCommand myCommand = myConnection.CreateCommand())
{
myConnection.Open();//Opens the Connection
myCommand.CommandText = "Select Permission From Temp_Validate Where EnterpriseId='" + CurrentUser + "'";
SqlDataReader IDReader = myCommand.ExecuteReader(); //Gets the ID
IDReader.Read();
string UserStatus = IDReader["Permission"].ToString();
IDReader.Close();
return UserStatus;
}
}
I implemented the above functionalite in my index.aspx page, if the userstatus equal to "0" , it will display the "You are not authrized to view this message" and it will end.
I have around 30 aspx page,its currently running in Production. I do not want go include the same code (index.aspx) in every page load to stop the user validation.
could you please suggest how can i implement without editing all pages.
Updated on 09/28 : Utilifunction.cs
public static String getLoggedOnUser()
{
String user = HttpContext.Current.User.Identity.Name.Substring(HttpContext.Current.User.Identity.Name.IndexOf("\\") + 1);
if (user == "") user = "anonymous";
string UserStatus = IsValidUser(user);
if (UserStatus == "0")
{
HttpContext.Current.Response.Redirect("PSF_Error.aspx", true);
}
return user;
}
public static String IsValidUser(string currentUser)
{
using (System.Data.SqlClient.SqlConnection myConnection = new SqlConnection(ConfigurationManager.ConnectionStrings["Test"].ConnectionString))
{
using (SqlCommand myCommand = myConnection.CreateCommand())
{
//Gets email of the creator of current user
myConnection.Open();//Opens the Connection
myCommand.CommandText = "Select Permission From Temp_Validate Where EnterpriseId='" + currentUser + "'";
SqlDataReader IDReader = myCommand.ExecuteReader(); //Gets the ID
IDReader.Read();
string UserStatus = IDReader["Permission"].ToString();
IDReader.Close();
return UserStatus;
}
}
}
Index.aspx
Page_load
{
string CurrentUser = UtilFunctions.getLoggedOnUser();
}
You have a few options, here:
1) Set up role-based access with Owin or AspNet.Identity. This is probably your best option, but I couldn't find a good tutorial for you. Those packages are well-documented, however, and I'm sure you can figure them out with some effort.
2) Build a Roles table, and customize access yourself. The best example I found was here: http://www.codeproject.com/Articles/875547/Custom-Roles-Based-Access-Control-RBAC-in-ASP-NET
3) Redirect unauthorized users without the use of roles. So something like:
public ActionResult SecurePage(User u)
{
if(u.level == "junior"){
return RedirectToAction("CustomErrorPage");
} else {
return View();
}
}
I'm not sure that that option is terribly secure, but it should work.
Hope that helps!
after setting up roles you can use a web.config file in every directory specifying authorization and/or use the 'location' element in the web.config file.
First off, sorry about the confusing code. I've been using MVC, and you've clearly posted your code behind.
I don't think that you can achieve what you are trying to do, without adding your code to each page, or learning about roles. You could reduce some code duplication in a number of clever ways, but I can't think of anything that doesn't seem like a total hack.
If you want to, say, put all of your secure pages in the same directory, and restrict low-level access to that directory, you are going to have to filter by specific users or, if you can implement them, roles. As I understand it, the deny and allow nodes in your web.config file are setting server side (so IIS, probably) authorization rules, so the keywords and rules you can use are limited. Check this page out, for some basics:
http://weblogs.asp.net/gurusarkar/setting-authorization-rules-for-a-particular-page-or-folder-in-web-config
While it is likely POSSIBLE to build a rule based on values in your DB, doing so would probably be far more work than it would be worth.
Sorry that I can't offer a more satisfactory answer, but I would recommend: 1) Get to work, and add a check to the code behind for each page, or 2) (and I highly suggest this option) close this question, and post another, about implementing roles in .net, and assigning roles to users, in code. If, say, you can use your login page to assign every junior-level user the custom role of Junior, and place all of your secure pages in a directory named SecurePages you could add the following code to your web.config, and achieve exactly what you are trying to do:
<location path="SecurePages">
<system.web>
<authorization>
<deny roles="Junior">
<deny users="*">
</authorization></system.web></location>
Good luck!

How can i keep users logged in when they return? [duplicate]

HI
I am using asp.net mvc with asp.net membership.
I want to have a checkbox that if clicked keeps the users signed in for 2 weeks(unless they clear their cookies).
So I know their is
FormsAuthentication.SetAuthCookie(userName, createPersistentCookie)
but I don't know how to set it up for 2week retention.
I rewrote most of the membership stuff. So I don't use stuff like Create() and VerifyUser().
Add a hash key or a random string to both the cookie and the database (both the same key). If the cookie and database value are the same, when the user starts a new session, sign him/her in again. When the user reaches the two weeks, remove the secret key from the database using a cronjob (Unix) or scheduled task (Windows).
Warning: Do not rely on the cookie expire date, since people can hack their browser.
Rule: NEVER, EVER trust ANY of your users!
You can set the global session timeout (the value is in minutes) in web.config eg.
<system.web>
<authentication mode="Forms">
<forms timeout="20160"/>
</authentication>
</system.web>
This will be for all authenticated users. If you want to use the 'Remember Me' functionality then you will need to write your own code to set the cookie/ticket. Something like this (taken from here):
protected void Page_Load()
{
if (Request.Cookies["username"] == null || Request.Cookies["username"].Value.ToString().Trim() == "")
{
Login1.RememberMeSet = true;
}
else
{
Login1.UserName = Request.Cookies["username"].Value.ToString().Trim();
Login1.RememberMeSet = true;
}
}
protected void RememberUserLogin()
{
// Check the remember option for login
if (Login1.RememberMeSet == true)
{
HttpCookie cookie = new HttpCookie("username");
cookie.Value = Login1.UserName.Trim();
cookie.Expires = DateTime.Now.AddHours(2);
HttpContext.Current.Response.AppendCookie(cookie);
Login1.RememberMeSet = true;
}
else if (Login1.RememberMeSet == false)
{
HttpContext.Current.Response.Cookies.Remove("username");
Response.Cookies["username"].Expires = DateTime.Now;
Login1.RememberMeSet = false;
}
}
Just use a simple cookie with 2 weeks expiration date.
Have you seen this?
http://forums.asp.net/t/1440824.aspx
Along similar lines to what Koning has suggested.
You can not use a session method to keep your users logged in, since browsers delete the session cookies when the browser is closed.
Do what user142019 offered and set the session's IdleTimeout parameter very short, up to 15 min. When the server receives any request from the browser, first check the session if it's alive. if not, try to get the cookie. If the cookie and database value are the same and not expired, assign it to the (new) session and return the response.
You can use onBeforeUnload listener to send a logout request when the user leaves your site. If logged out, delete the cookie and the db record, if not - assign a new hash for the next auto login and refresh that hash again when the user retunes to your website. You can also keep track of IP and the browser and link them to the hash in your db.
So, in case if the cookie is used with another browser or IP, and the hash code is valid, you can force them to login again.

How to pass data from Silverlight OOB application to asp.net website?

I created silver-light 4.0 application in that user can enter their username and password.
After submit this secret data(username, password ) from SL application,
it submitted to website with query string..
I want to pass as below URL string
for ex: -
http://testsite.com/mypage.aspx?<encrypted string>
I want to pass username and password in encrypted format from SL to Aspx page..
How I pass those information from SL application to asp.net website..
So you could just use the WebClient class and GET the page.
(I'm assuming your doing asp.net WebForms NOT MVC)
Your asp.net page should be a blank page, in your code behind you read your query string and do what you need with it, depending on success or failure you write the appropriate response with Response.Write();.
In your silverlight code, you will just need to request for your page, and you can then read the response from your asp.net page.
Asp.net:
var encyString = Request.QueryString["str"];
//some logic
Response.Write("Success");
Silverlight:
WebClient client = new WebClient();
client.DownloadStringCompleted +=
new DownloadStringCompletedEventHandler(
client_DownloadStringCompleted);
In Button1_Click, I call DownloadStringAsync, passing the complete URL that includes the number specified by the user.
private void Button1_Click(object sender, RoutedEventArgs e)
{
string encryptedString = "example";
client.DownloadStringAsync
(new Uri("http://testsite.com/mypage.aspx?"+encryptedString));
}
In the DownloadStringCompleted event-handler, I check that the Error property of the event args is null, and either output the response or the error message to the text block.
void client_DownloadStringCompleted(object sender,
DownloadStringCompletedEventArgs e)
{
if (e.Error == null)
resultBlock.Text = "Using WebClient: "+ e.Result;
//will be Response.Write();
else
resultBlock.Text = e.Error.Message;
}
Above code was plagiarized from this blog.
Remember, a sniffer can read your request. You may want to use SSL if you need better security. Possibly a more secure way to send this data would be to POST it to your asp.net page.
This article describes how to POST from silverlight to a page.
HTH
What I understood from the question is that you are authenticating user twice – First in SL app and then in ASP.Net app. Instead can you just authenticate user in SL and pass the result (True/False or token may be) to ASP.Net app? This is the safe way I feel.
You can use like HtmlPage.Window.Eval("window.location.href='"+ YOURURL +"'");

asp.net can you mix cookieless with cookie session stored session data?

Is it possible to use mixed cookieless sessions with cookie sessions?
I've an application that captured user details and then redirect for payment to an ssl page. I was wondering if this is possible?
http://www.mydomain.com/confirm.aspx
redirects to
https://www.mydomain.com/(S(za1tw2l2k02jer4fiskzlovd))/payment.aspx
Note: the session Id in the latter url.
So in essence, we use the standard cookie session for the majority of the application but when we transfer to an ssl page we pass the SessionId to the https url to pick up the session. I've tried this locally but it starts a new session.
Am I missing a trick?
Thanks
I've found a solution that seems to work
When transfering between http and https i've the following:
As you can see I'm passing the session id manually to the https page.
protected void btnPurchase_Click(object sender, EventArgs e)
{
// Confirm puchase code **
string sslPaymentPath = string.Format("https://{0}/payment.aspx?sid={1}", Request.Url.DnsSafeHost, Session.SessionID);
Response.Redirect(sslPaymentPath);
}
Upon reaching the ssl page, asp.net sees the request as a new session so I use the Start_Session method in the global.asax to abandon the newly created session and add a new session cookie with the session id passed in from the query string. Because the AquireSessionState which populates the session keyValue pair has already been run by this point I need to redirect the page back to itself to repopulate those values.
It seems to work really well :)
void Session_Start(object sender, EventArgs e)
{
bool isPaymentPage = (Request.Path.ToLower().IndexOf("payment.aspx") != -1);
// Code to load session over ssl. When changing between two sessions
if (isPaymentPage && Request.QueryString["sid"] != null && Request.IsSecureConnection)
{
string passedSessionId = Request.QueryString["sid"];
Session.Abandon();
Response.Cookies.Add(new HttpCookie("ASP.NET_SessionId", passedSessionId));
Response.Redirect(Request.Url.LocalPath, true);
}
}
Also with regard to somebody clicking on an external link whilst browsing the ssl purchase.aspx page i've written following in the global.asax to redirect traffic back to standard none ssl pages if it's not the payment page.
void Application_BeginRequest(object sender, EventArgs e)
{
bool isPaymentPage = (Request.Path.ToLower().IndexOf("payment.aspx") != -1);
// In the case someone has navigated away from the payment page redirect them back to the none secure protocol.
if (!isPaymentPage && Request.IsSecureConnection)
{
bool isAxdResource = (Request.Path.ToLower().IndexOf(".axd") != -1);
if (!isAxdResource)
{
string url = Request.Url.AbsoluteUri.ToLower().Replace("https://", "http://");
Response.Redirect(url,true);
}
}
}
Hope somebody finds this useful, I was stuck for a while trying to come up with a nice solution.
My inspiration came from this url.

How do I Keep a user logged in for 2 weeks?

HI
I am using asp.net mvc with asp.net membership.
I want to have a checkbox that if clicked keeps the users signed in for 2 weeks(unless they clear their cookies).
So I know their is
FormsAuthentication.SetAuthCookie(userName, createPersistentCookie)
but I don't know how to set it up for 2week retention.
I rewrote most of the membership stuff. So I don't use stuff like Create() and VerifyUser().
Add a hash key or a random string to both the cookie and the database (both the same key). If the cookie and database value are the same, when the user starts a new session, sign him/her in again. When the user reaches the two weeks, remove the secret key from the database using a cronjob (Unix) or scheduled task (Windows).
Warning: Do not rely on the cookie expire date, since people can hack their browser.
Rule: NEVER, EVER trust ANY of your users!
You can set the global session timeout (the value is in minutes) in web.config eg.
<system.web>
<authentication mode="Forms">
<forms timeout="20160"/>
</authentication>
</system.web>
This will be for all authenticated users. If you want to use the 'Remember Me' functionality then you will need to write your own code to set the cookie/ticket. Something like this (taken from here):
protected void Page_Load()
{
if (Request.Cookies["username"] == null || Request.Cookies["username"].Value.ToString().Trim() == "")
{
Login1.RememberMeSet = true;
}
else
{
Login1.UserName = Request.Cookies["username"].Value.ToString().Trim();
Login1.RememberMeSet = true;
}
}
protected void RememberUserLogin()
{
// Check the remember option for login
if (Login1.RememberMeSet == true)
{
HttpCookie cookie = new HttpCookie("username");
cookie.Value = Login1.UserName.Trim();
cookie.Expires = DateTime.Now.AddHours(2);
HttpContext.Current.Response.AppendCookie(cookie);
Login1.RememberMeSet = true;
}
else if (Login1.RememberMeSet == false)
{
HttpContext.Current.Response.Cookies.Remove("username");
Response.Cookies["username"].Expires = DateTime.Now;
Login1.RememberMeSet = false;
}
}
Just use a simple cookie with 2 weeks expiration date.
Have you seen this?
http://forums.asp.net/t/1440824.aspx
Along similar lines to what Koning has suggested.
You can not use a session method to keep your users logged in, since browsers delete the session cookies when the browser is closed.
Do what user142019 offered and set the session's IdleTimeout parameter very short, up to 15 min. When the server receives any request from the browser, first check the session if it's alive. if not, try to get the cookie. If the cookie and database value are the same and not expired, assign it to the (new) session and return the response.
You can use onBeforeUnload listener to send a logout request when the user leaves your site. If logged out, delete the cookie and the db record, if not - assign a new hash for the next auto login and refresh that hash again when the user retunes to your website. You can also keep track of IP and the browser and link them to the hash in your db.
So, in case if the cookie is used with another browser or IP, and the hash code is valid, you can force them to login again.

Resources