I am trying to find the general principles of using the Facebook C# SDK to write a canvas application.
My approach is as follows. I have a master page which I attempt to authorise on the oninit page. All is well. The first time round we are not authorized and the second time round we are. When I post back it all goes wrong. I would have expected the authorization / cookies / session item / viewstate item to hang around and my authorizer item to remain authorised.
This appears not to be the case.
Should I only check this authorization once and then stick this auth token in memory? then when I want to get information / post information, pass this auth token in manually?
Is the general principle that when I want to post / get I create a new instance of facebookapp and pass in the auth token?
Here is the code i was using.
Can I get some general principles specific to c#, asp.net, codeplex SDK dlls
Many thanks
Spiggers
public partial class facebook : System.Web.UI.MasterPage
{
protected FacebookApp fbApp;
protected CanvasAuthorizer authorizer;
protected FacebookSettings oSettings = new FacebookSettings();
protected override void OnInit(EventArgs e) {
//not sure that we need this, or indeed if this works
//HttpContext.Current.Response.AddHeader("p3p", "CP=\"IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT\"");
oSettings.AppId = Config.Facebook.FacebookApplicationID;
oSettings.AppSecret = Config.Facebook.FacebookSecret;
fbApp = new FacebookApp(oSettings);
authorizer = new CanvasAuthorizer(fbApp);
if (!authorizer.IsAuthorized()) // postbacks always fail this, to it reposts itself and the postback event does not fire!!!
{
string scope = "publish_stream,email,user_birthday";
var url = Common.Facebook.Authorization.AuthURL(Config.Facebook.FacebookApplicationID, Config.Facebook.RedirectURL, scope);
var content = CanvasUrlBuilder.GetCanvasRedirectHtml(url);
Response.ContentType = "text/html";
Response.Write(content);
Response.End();
}
base.OnInit(e);
}
}
Related
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.
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 +"'");
I am using WF 4 with ASP.NET and as part of the workflow the system may need to redirect to other pages for the user to input additional information under certain circumstances. Once they have entered that information, the system needs to resume the workflow where it left off.
I have this code so far in the initial page that kicks off the process and an activity in the workflow that sets a bookmark.
static InstanceStore instanceStore;
static AutoResetEvent instanceUnloaded = new AutoResetEvent(false);
static Guid id;
protected void Page_Load(object sender, EventArgs e)
{
SetupInstanceStore();
}
protected void btnStartWorkflow_Click(object sender, EventArgs e)
{
app = Session["applicant"];
Dictionary<string, object> workflowInputs = new Dictionary<string, object>();
workflowInputs.Add("Applicant", app.Applicant);
WorkflowApplication workflowApplication = new WorkflowApplication(new IdentityCheckActivites.IdentityCheckWorkflow(), workflowInputs);
workflowApplication.InstanceStore = instanceStore;
//returning IdleAction.Unload instructs the WorkflowApplication to persist application state and remove it from memory
workflowApplication.PersistableIdle = (a) =>
{
return PersistableIdleAction.Persist;
};
workflowApplication.Unloaded = (a) =>
{
instanceUnloaded.Set();
};
workflowApplication.Completed = (a) =>
{
instanceUnloaded.Set();
};
workflowApplication.Persist();
id = workflowApplication.Id;
workflowApplication.Run();
Session["id"] = id;
workflowApplication.Idle = (a) =>
{
instanceUnloaded.Set();
};
instanceUnloaded.WaitOne();
var bookmarks = workflowApplication.GetBookmarks();
if (bookmarks != null && bookmarks[0].OwnerDisplayName == "CC")
{
workflowApplication.Unload();
Context.Response.Redirect("SecondPage.aspx");
}
Context.Response.Redirect("FinalPage.aspx");
}
private static void SetupInstanceStore()
{
instanceStore = new SqlWorkflowInstanceStore(#"Data Source=xxx;Initial Catalog=SampleInstanceStore;User Id=xxx;Password=xxx;Asynchronous Processing=True");
InstanceHandle handle = instanceStore.CreateInstanceHandle();
InstanceView view = instanceStore.Execute(handle, new CreateWorkflowOwnerCommand(), TimeSpan.FromSeconds(30));
handle.Free();
instanceStore.DefaultInstanceOwner = view.InstanceOwner;
}
This seems to work very well in that it persists the workflow to the database and if the bookmark is set I want to redirect to a second page for the user to enter more data.
This is the part of the code that I am having problems with: -
var bookmarks = workflowApplication.GetBookmarks();
if (bookmarks != null && bookmarks[0].OwnerDisplayName == "CC")
{
workflowApplication.Unload();
Context.Response.Redirect("SecondPage.aspx");
}
Context.Response.Redirect("FinalPage.aspx");
If there's a bookmark set, I redirect to an intermediary page, if not and no user intervention was necessary, the page will just redirect to the final page.
This works if the bookmark is set, but if not the workflowApplication.GetBookmarks() statement throws an exception telling me that the workflow has completed.
I can't seem to find a way to detect at this stage which state the workflow is in so that I can redirect to the relevant page.
Maybe I have the wrong idea in general, as much as I search though, I cannot seem to find a lot of guidance on this subject.
Any ideas?
Thanks,
Jim.
I don't think there is a way to directly determine if the workflow is completed from WorkflowApplication (except for catching and inspecting the exception that is thrown).
But you could set a flag in side your Completed delegate which is executed only if the there is no bookmark set and the workflow is completed. You could then check this flag before calling GetBookmarks().
Not sure if I understand exactly, but it seems that your page controller is looking at the state of the workflow to understand what page to redirect to? The problem is that the state may be non-existent if the WF instance has ended?
If the above is correct then perhaps the approach is wrong. A more appropriate approach might be to have a WCF WF service on AppFabric (correlated by session id) handle the website request directly. (If a user in a particular session visits the site, then the WF determines what page to render, and if the user hits a certain button, then send a WCF WF message using net pipe binding)
instead of
workflow.idle
you need
wfApp.PersistableIdle
and don't forget
instanceUnloaded.Set();
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.
I have developed an application on asp.net. I uploaded it to my host.
lets say http://myhost/app. My manager wrapped this address into an empty frameset on http://anotherhost/somename and sets the src of frame to http://myhost/ap. And so nobody can login. When the button is hit, it posts back (browser loads stuff, progress bar fills up and ends) but nothing happens. Does not redirect.
(I have set IE to alwaysAllowCookies and it now does work, but other people still cannot)
I think there is something that I have no clue about framesets and ASP.NET
ps: I never use frames but could not convince my manager in doing so. He likes to develop in front page :)
Whatz happening?
Thx in advance
protected void btnLogin_Click(object sender, ImageClickEventArgs e)
{
Member member = Logic.DoLogin(txtUsername.Text.Trim(), txtPassword.Text.Trim());
if (null == member) {
lblError.Text = "Invalid Login !"; return;
}
CurrentMember = member; ///CurrentMember is an inherited property that accesses Session["member"] = member
Response.Redirect("Default.aspx");
}
I've discovered that it's a new standart that IE gets to support. Its called P3P and that blocks cookies from other domains.
I've added this code to my page class constructor
HttpContext.Current.Response.AddHeader("p3p", "CP=\"IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT\"");