On a mobile app, I want to have a Share button that posts an image on behalf of the user, but instead of posting it on this user's timeline, it must be posted on a specific Facebook Page timeline.
By using the Share dialog, it seems there is no way to configure the target of the post (in this case, the Facebook Page). Or at least I couldn't find it.
How would you do it?
Note: the app is made in Unity/C#, although I don't think this would matter much
Try this code :)
<div id="fb-root"></div>
<script>
window.fbAsyncInit = function() {
FB.init({
appId : '**appId**',
channelUrl : '//local.facebook-test/channel.html',
status : true,
xfbml : true,
oauth : true
});
FB.login(function(response)
{
if (response.authResponse)
{
var opts = {
message : '**message**',
access_token: '**PageAccessToken**',
name : '**name**',
link : 'https://developers.facebook.com/docs/reference/dialogs/',
description : '**description**',
picture : 'http://url/to/pic.jpg'
};
FB.api('/me/feed', 'post', opts, function(response)
{
if (!response || response.error)
{
console.log(response.error);
alert('Posting error occured');
}else{
alert('Success - Post ID: ' + response.id);
}
});
} else {
alert('Not logged in');
}
}, { scope: 'manage_pages, publish_actions, user_photos' });
};
(function (d, s, id) {
var js, fjs = d.getElementsByTagName(s)[0];
if (d.getElementById(id)) { return; }
js = d.createElement(s); js.id = id;
js.src = "//connect.facebook.net/en_US/all.js";
fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));
</script>
Related
So basically I have an ajax call which hits a method which returns a partial view and to display the data I use jQuery.html().
We are running our code through a veracode security tool and it's raising a XSS vulnerability.
Here is the jquery:
$.ajax({
url: "ReturnTransNoInformation",
data: { __RequestVerificationToken: ReturnAntiForgeryToken(), TransNo: $("[data-trans-no]").val() },
type: "POST",
beforeSend: function () {
TransNoInfoAjaxBegin("[data-view-trans-no-container]");
},
success: function (data) {
if (data.Success === false) {
if (data.ErrorMessage !== undefined && data.ErrorMessage !== null && data.ErrorMessage !== "") {
Error(data.ErrorMessage);
}
else {
Error("Something went wrong while loading the TransNo info, please refresh the page and try again.")
}
$("[data-transno-partial]").html(null); //Potential XSS
}
else {
$("[data-transno-partial]").html(data); //Potential XSS
}
},
error: function () {
Error("Something went wrong while loading the TransNo info, please refresh the page and try again.")
},
complete: function () {
AjaxComplete("[data-view-trans-no-container]");
}
});
My C# code:
[AuthorizeUsers(new UserLevel[] { UserLevel.SubRepMaster, UserLevel.Regional })]
[ValidateAntiForgeryToken]
public async Task<ActionResult> ReturnTransNoInformation(string transNo) {
if (string.IsNullOrWhiteSpace(transNo)) return Json(new { Success = false, ErrorMessage = "Error: TransNo cannot be empty" });
var info = await _iAccountingManager.ReturnTransNoInfoAsync(transNo, SessionInfo.CIDs);
if (info == null) return Json(new { Success = false, ErrorMessage = "Error: TransNo does not exist" });
return PartialView("~/Views/Accounting/Partials/_TransNoInfo.cshtml", new TransNoInfoViewModel(info));
}
My partial view is entirely controller by me with no JS in the partial view. Is this an actual XSS vulnerability?
If you are under control of the code being displayed, then no.
XSS relies on a malicious third party inputting code into your website.
If there are no third parties which can change the input to their desired input, then there is no problem with XSS on your site.
I have the following code in my view :
<script type="text/javascript">
function OnCancelClick(e)
{
var jobId = e;
var flag = confirm('You are about to cancel job : ' + jobId + '. Are you sure you want to cancel this job?');
if (flag) {
$.ajax({
url: '/job/CancelJob',
type: 'POST',
data: { jobId: jobId },
dataType: 'html',
success: function (result) { alert('Job ' + jobId + ' was cancelled.'); document.location = "#Url.Action("Index", "Job")"; },
error: function () { alert('Something went wrong. Check the log for more information.'); }
});
}
return false;
}
</script>
In my view I also have :
<input type="submit" id="cancelButton" value="Cancel" onclick="javascript: return OnCancelClick(#Model.Id);" />
In my controller I have :
[HttpPost]
public ActionResult CancelJob(int jobId)
{
try
{
logger.LogInfo(string.Format("<start> Cancel-button clicked for job : {0}", jobId), jobId);
JobCommandService.ChangeStatus(jobId, 6);
logger.LogInfo(string.Format("<end> Cancel-button clicked for job : {0}", jobId), jobId);
return RedirectToAction("Index", "Job");
}
catch (Exception ex)
{
logger.LogError(ex.Message, ex, jobId);
Response.StatusCode = (int)HttpStatusCode.InternalServerError;
return Json(new { Success = false, Message = ex.Message });
}
}
When I run this in my VS2012 it works just fine.
When I deploy it to the server, I'm getting the message that something went wrong.
In my logging there is no trace of the button being clicked.
As per your comment, when deployed your app is installed in accindigoapps.blabla.lok/jobmonitor.
However your script has the url hardcoded as url: '/job/CancelJob'. That will mean:
when you are debugging from VS your script will work because the request is being sent to a url like http://localhost:XXX/job/CancelJob
however in production, the request will be sent to http://accindigoapps.blabla.lok/job/CancelJob, missing the jobmonitor part.
You need a way to inform your JS code about the base url of your application:
You could generate the Url in a Razor view using Url.Action("CancelJob","job") and pass that Url into your javascript code.
Another option would be to use Url.Content("~/") in some javascript of your base layout. That helper Url.Content("~/") will return only your application folder, / in your dev environment and /jobmonitor/ when deployed. That way you will have your app root-relative url available to any script, so you can use it to build root-relative urls as you were doing in your script:
<script>
var myApp = {};
myApp.BaseUrl = '#Url.Content("~/")';
</script>
//Some other script like yours would be able to keep using root-relative urls as:
$.ajax({
url: myApp.BaseUrl + 'job/CancelJob',
...
If you prefer to generate full urls, you could follow a similar approach. Have a look at this question
Hope it helps!
I know this is duplicate but I could not get reliable solution(for asp.net web).
I just want to redirect to the login page if session expires.
I have tried following:
1. using jquery status code
$.ajax({
type: "POST",
url: "stream.asmx/SomeMethod",
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (msg) {
//success msg
},
error: function (request, status, error) {
if (status = 403) {
location.href = 'login.aspx';
}
}
});
Problem: this returns same status code(403) for other errors too, which I only expect for session timeout.
2. Sending json message whether session expired
code behind:
if (!object.Equals(HttpContext.Current.Session["User"], null))
{
Id = int.Parse(HttpContext.Current.Session["User"].ToString());
}
else
{
result = from row in dtscrab.AsEnumerable()
select new
{
redirectUrl = "login.aspx",
isRedirect = true
};
}
on $.ajax success:
success: function (msg) {
if (msg.d[0].isRedirect) {
window.location.href = msg.d[0].redirectUrl;
}
else {
//load containt
}
}
Problem: It's somehow desn't invoke ajax success line if session expires(it does return correct json). And even this is not a proper way if I have many number of ajax request in the page(should be handled globally).
However, I saw this post which is really good soltion but it's for mvc using AuthorizeAttribute: handling-session-timeout-in-ajax-calls
So, Is there I can use same concept used in mvc using AuthorizeAttribute in asp.net web api? If not, how I can troubleshoot those issue which I'm facing (any of above two mentioned)?
A 403 status code is going to cause jQuery to call the failure method. Keep the same code behind from your second try, but move the redirect handler to the failure method instead of the success method. In the success method, treat it as you normally would.
Problem:
I had same problem in my Razor MVC Application throwing exceptions while ajax calls made when session timed out.
The way I have managed to get this issue sorted is by monitoring each ajax requests by using a simple light weight Action Method (RAZOR MVC) returning a bool variable whether the Request is Authenticated or not. Please find the code below..
Layout/Master Page / Script file:
<script>
var AuthenticationUrl = '/Home/GetRequestAuthentication';
var RedirectUrl = '/Account/Logon';
function SetAuthenticationURL(url) {
AuthenticationUrl = url;
}
function RedirectToLoginPage() {
window.location = RedirectUrl;
}
$(document).ajaxStart(function () {
$.ajax({
url: AuthenticationUrl,
type: "GET",
success: function (result) {
if (result == false) {
alert("Your Session has expired.Please wait while redirecting you to login page.");
setTimeout('RedirectToLoginPage()', 1000);
}
},
error: function (data) { debugger; }
});
})
Then in Home Controller/Server side you need a method to verify the request and return the boolean variable..
public ActionResult GetAuthentication ( )
{
return Json(Request.IsAuthenticated, JsonRequestBehavior.AllowGet);
}
This will validate each ajax request and if the session got expired for any ajax request, it will alert the user with a message and redirect the user to the login page.
I would also suggest not to use standard Alert to Alert. User some Tool tip kind of formatted div Alerts. Standard JS Alerts might force the user to click OK before redirection.
Hope it helps.. :)
Thanks,
Riyaz
Finally, I ended up following.
public class IsAuthorizedAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (filterContext.HttpContext.Request.IsAjaxRequest())
{
var sessions = filterContext.HttpContext.Session;
if (sessions["User"] != null)
{
return;
}
else
{
filterContext.Result = new JsonResult
{
Data = new
{
status = "401"
},
JsonRequestBehavior = JsonRequestBehavior.AllowGet
};
//xhr status code 401 to redirect
filterContext.HttpContext.Response.StatusCode = 401;
return;
}
}
var session = filterContext.HttpContext.Session;
if (session["User"] != null)
return;
//Redirect to login page.
var redirectTarget = new RouteValueDictionary { { "action", "LogOn" }, { "controller", "Account" } };
filterContext.Result = new RedirectToRouteResult(redirectTarget);
}
}
Handling client side
<script type="text/javascript">
$(document).ajaxComplete(
function (event, xhr, settings) {
if (xhr.status == 401) {
window.location.href = "/Account/LogOn";
}
});
</script>
you can set session time out expire warning some thing like ....
<script type="text/javascript">
//get a hold of the timers
var iddleTimeoutWarning = null;
var iddleTimeout = null;
//this function will automatically be called by ASP.NET AJAX when page is loaded and partial postbacks complete
function pageLoad() {
//clear out any old timers from previous postbacks
if (iddleTimeoutWarning != null)
clearTimeout(iddleTimeoutWarning);
if (iddleTimeout != null)
clearTimeout(iddleTimeout);
//read time from web.config
var millisecTimeOutWarning = <%= int.Parse(System.Configuration.ConfigurationManager.AppSettings["SessionTimeoutWarning"]) * 60 * 1000 %>;
var millisecTimeOut = <%= int.Parse(System.Configuration.ConfigurationManager.AppSettings["SessionTimeout"]) * 60 * 1000 %>;
//set a timeout to display warning if user has been inactive
iddleTimeoutWarning = setTimeout("DisplayIddleWarning()", millisecTimeOutWarning);
iddleTimeout = setTimeout("TimeoutPage()", millisecTimeOut);
}
function DisplayIddleWarning() {
alert("Your session is about to expire due to inactivity.");
}
function TimeoutPage() {
//refresh page for this sample, we could redirect to another page that has code to clear out session variables
location.reload();
}
4xx are HTTP error status codes and would cause jquery to execute the onFailure callback.
Also, beware of using 3xx for redirects when you want to process the payload. Internet Explorer, in my experience, just does a redirect (without looking at the payload) when a 3xx status code is returned.
I'd say, throw a 403 and handle the situation. To the client 403 implies the resource access is forbidden. There can be multiple reasons, which is OK I guess.
For those using a ScriptManager, you can easily check for ajax request and then redirect with the following code:
private void AjaxRedirect(string url)
{
Response.StatusCode = 200;
Response.RedirectLocation = url;
Response.Write("<html></html>");
Response.End();
}
Then check for request type and redirect accordingly (using routes here):
if (ScriptManager.GetCurrent(Page).IsInAsyncPostBack)
{
var redirectUrl = RouteTable.Routes.GetVirtualPath(null, "Default", null).VirtualPath;
AjaxRedirect(redirectUrl);
}
else
{
Response.RedirectToRoute("Default");
}
The "Default" route is a route defined in the routes collection:
routes.MapPageRouteWithName("Default", "", "~/default.aspx");
If you prefer, instead of using ScriptManager for ajax request check, you can use:
if (Request.Headers["X-Requested-With"] == "XMLHttpRequest") {
code here...
}
My goal is to use the FB login button so that FB users can log into my ASP.NET MVC 3 website. It seems that things have changed recently with the Facebook C# SDK and all the old examples will not work with the new version. I've tried for a day to get them to work... I'm working off of the tutorial Getting Started with the Facebook C# SDK for ASP.NET
Currently when I browse to http://localhost:8033/ it seems to automatically log me in (even after a fresh restart of Chrome) because it shows "my-name uses my-app-name" and shows my picture. I expected it to instead show a FB login button. And when I go to http://localhost:8033/Home/About I get an error that Session["AccessToken"] is null (which makes sense because it's clearly not getting set).
Here's what I have:
HomeController.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Facebook;
namespace FacebookTest.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
ViewBag.Message = "Welcome to ASP.NET MVC!";
return View();
}
public ActionResult About()
{
var accessToken = Session["AccessToken"].ToString();
var client = new FacebookClient(accessToken);
dynamic result = client.Get("me", new { fields = "name,id" });
string name = result.name;
string id = result.id;
ViewBag.Message = "Hello id: " + id;
return View();
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult FacebookLogin(HttpContext context)
{
var accessToken = context.Request["accessToken"];
context.Session["AccessToken"] = accessToken;
return RedirectToAction("About");
}
}
}
Index.cshtml
#{
ViewBag.Title = "Home Page";
}
<h2>#ViewBag.Message</h2>
<p>
To learn more about ASP.NET MVC visit http://asp.net/mvc.
</p>
<div id="fb-root"></div>
<script>
window.fbAsyncInit = function () {
FB.init({
//appId: 'YOUR_APP_ID', // App ID
appId: '<MY-NUMBER-REMOVED>', // App ID
status: true, // check login status
cookie: true, // enable cookies to allow the server to access the session
xfbml: true // parse XFBML
});
// Additional initialization code here
FB.Event.subscribe('auth.authResponseChange', function (response) {
if (response.status === 'connected') {
// the user is logged in and has authenticated your
// app, and response.authResponse supplies
// the user's ID, a valid access token, a signed
// request, and the time the access token
// and signed request each expire
var uid = response.authResponse.userID;
var accessToken = response.authResponse.accessToken;
// TODO: Handle the access token
// Do a post to the server to finish the logon
// This is a form post since we don't want to use AJAX
var form = document.createElement("form");
form.setAttribute("method", 'post');
//form.setAttribute("action", '/FacebookLogin.ashx');
form.setAttribute("action", '/Home/FacebookLogin');
var field = document.createElement("input");
field.setAttribute("type", "hidden");
field.setAttribute("name", 'accessToken');
field.setAttribute("value", accessToken);
form.appendChild(field);
document.body.appendChild(form);
form.submit();
} else if (response.status === 'not_authorized') {
// the user is logged in to Facebook,
// but has not authenticated your app
} else {
// the user isn't logged in to Facebook.
}
});
};
// Load the SDK Asynchronously
(function (d) {
var js, id = 'facebook-jssdk', ref = d.getElementsByTagName('script')[0];
if (d.getElementById(id)) { return; }
js = d.createElement('script'); js.id = id; js.async = true;
js.src = "//connect.facebook.net/en_US/all.js";
ref.parentNode.insertBefore(js, ref);
} (document));
</script>
<div class="fb-login-button" data-show-faces="true" data-width="400" data-max-rows="1"></div>
About.cshtml
#{
ViewBag.Title = "About Us";
}
<h2>About</h2>
<p>
#ViewBag.Message
</p>
Can you tell me how to fix this so that a FB login button is displayed, and when clicked it asks the users to do a FB authentication, sends them back, and then my app recognizes them as a logged in user?
As for the Login button, if you are logged in to Facebook prior to visiting your app you will see the faces instead of the login button, the only way to get the Login button back is to go to facebook.com and do a logout or possibly do a facebook logout using the C# SDK. Depending on your requirements this may or may not be what you wanted. There is a bit about Re-Authentication in the SDK documentation if that is what you really want.
I've tweaked your app by removing the submit() and replaced it with an ajax post, The FacebookLogin action was changed and I added some error handling on the About action. Your original app will work but it will automatically redirect to About if you are logged in to Facebook.
Update Added a login link which does not use Javascript, insert appid and appsecret and adjust portnumber accordingly. This was adapted from the server side login sample found here which is far prettier than this code :)
Note The state value being passed in the server side flow should be a unqiue value that you should validate in the ConnectResponse() method, i.e. generate a value in FacebookLoginNoJs and make sure it's the same in ConnectResponse to prevent cross site request forgery
HomeController.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using Facebook;
namespace FacebookTest.Controllers
{
public class HomeController : Controller
{
public ActionResult Index()
{
ViewBag.Message = "Welcome to ASP.NET MVC!";
return View();
}
public ActionResult About()
{
ViewBag.Message = "Please log in first";
if (Session["AccessToken"] != null)
{
var accessToken = Session["AccessToken"].ToString();
var client = new FacebookClient(accessToken);
try
{
dynamic result = client.Get("me", new { fields = "name,id" });
string name = result.name;
string id = result.id;
ViewBag.Message = "Hello id: " + id + " aka " + name;
}
catch (FacebookOAuthException x)
{
}
}
return View();
}
public void FacebookLogin(string uid, string accessToken)
{
var context = this.HttpContext;
context.Session["AccessToken"] = accessToken;
}
public ActionResult FacebookLoginNoJs()
{
return Redirect("https://www.facebook.com/dialog/oauth?client_id=MY-APPID-REMOVED&redirect_uri=http://localhost:45400/Home/ConnectResponse&state=secret");
}
public ActionResult ConnectResponse(string state, string code, string error, string error_reason, string error_description, string access_token, string expires)
{
if (string.IsNullOrEmpty(error))
{
try
{
var client = new FacebookClient();
dynamic result = client.Post("oauth/access_token",
new
{
client_id = "MY-APPID-REMOVED",
client_secret = "MY-APP-SECRET-REMOVED",
redirect_uri = "http://localhost:45400/Home/ConnectResponse",
code = code
});
Session["AccessToken"] = result.access_token;
if (result.ContainsKey("expires"))
Session["ExpiresIn"] = DateTime.Now.AddSeconds(result.expires);
}
catch
{
// handle errors
}
}
else
{
// Declined, check error
}
return RedirectToAction("Index");
}
}
}
Index.cshtml
#{
ViewBag.Title = "Home Page";
}
<h2>#ViewBag.Message</h2>
<p>
To learn more about ASP.NET MVC visit http://asp.net/mvc.
</p>
<div id="fb-root"></div>
<script>
window.fbAsyncInit = function () {
FB.init({
//appId: 'YOUR_APP_ID', // App ID
appId: 'MY-APPID-REMOVED', // App ID
status: true, // check login status
cookie: true, // enable cookies to allow the server to access the session
xfbml: true // parse XFBML
});
// Additional initialization code here
FB.Event.subscribe('auth.authResponseChange', function (response) {
if (response.status === 'connected') {
var uid = response.authResponse.userID;
var accessToken = response.authResponse.accessToken;
var url = '/Home/FacebookLogin';
$.post(url, { uid: uid, accessToken: accessToken }, function (data) {
});
} else if (response.status === 'not_authorized') {
// the user is logged in to Facebook,
// but has not authenticated your app
} else {
// the user isn't logged in to Facebook.
}
});
};
// Load the SDK Asynchronously
(function (d) {
var js, id = 'facebook-jssdk', ref = d.getElementsByTagName('script')[0];
if (d.getElementById(id)) { return; }
js = d.createElement('script'); js.id = id; js.async = true;
js.src = "//connect.facebook.net/en_US/all.js";
ref.parentNode.insertBefore(js, ref);
} (document));
</script>
<div class="fb-login-button" data-show-faces="true" data-width="400" data-max-rows="1"></div>
#Html.ActionLink("The NoJs Login", "FacebookLoginNoJs", "Home")
About.cshtml
#{
ViewBag.Title = "About Us";
}
<h2>About</h2>
<p>
#ViewBag.Message
</p>
I am using the following code in my facebook application. The when loading the application in facebook has no problem in chrome/firefox/ie8. When it runs in IE9 it is reporting that OAuthException has been thrown.
public string GetFacebookId() {
if (!FacebookWebContext.Current.IsAuthorized())
return string.Empty;
var client = new FacebookWebClient();
dynamic me = client.Get("me");
return me.id;
}
(OAuthException) An active access token must be used to query
information about the current user.
any suggestions would be appreciated.
thanks.
EDIT:
window.fbAsyncInit = function () {
FB.init({
appId: '#(Facebook.FacebookApplication.Current.AppId)', // App ID
//channelURL: '//facebook.thefarmdigital.com.au/moccona/premium/FacebookChannel/', // Channel File
status: true, // check login status
cookie: true, // enable cookies to allow the server to access the session
oauth: true, // enable OAuth 2.0
xfbml: true // parse XFBML
});
FB.Canvas.setAutoGrow();
};
$(function () {
$('#custom_login').click(function () {
FB.getLoginStatus(function (response) {
if (response.authResponse) {
//should never get here as controller will pass to logged in page
} else {
FB.login(function (response) {
if (response.authResponse) {
window.location = '#(Request.Url.AbsoluteUri.Replace(Request.Url.PathAndQuery, ""))' + $('#custom_login').attr('href');
} else {
window.location = '#(Request.Url.AbsoluteUri.Replace(Request.Url.PathAndQuery, ""))' + $('#custom_login').attr('dataFail');
}
}, { scope: 'publish_stream' });
}
});
return false;
});
});
I'm not familiar with FB's C# SDK, but judging from the code you gave, it does not seem that you are doing any user authentication with FB. It might be that it works in Chrome and Firefox only because you are somehow already logged into your FB app in those browsers.