I'm developing a public website and what I want to do is pretty straightforward, but I'm pulling my hair out trying to get everything working right.
I administer an open Facebook group and I want to display the public facebook events of this group on my website.
I can't seem to figure out how to setup my authentication so that I can access the event data. Here is my code for using my application to get an auth token:
var fb = new FacebookClientWrapper();
dynamic result = fb.Get("oauth/access_token", new
{
client_id = AppSettings.AppID,
client_secret = AppSettings.AppSecret,
grant_type = "client_credentials"
});
fb.AccessToken = result.access_token;
I know this works fine because I can access some information - for example, if I access a specific event by its ID, I can retrieve that information.
The problem occurs when I try to retrieve a list of events with fields within a date range:
[HttpGet]
public object GetEventDetails(string unixStartDateTime, string unixEndDateTime)
{
var parms = new Dictionary<string, object>();
parms.Add("fields", new[] { "id","name","description","start_time","venue" });
if (!String.IsNullOrEmpty(unixStartDateTime)) { parms.Add("since", unixStartDateTime); }
if (!String.IsNullOrEmpty(unixEndDateTime)) { parms.Add("until", unixEndDateTime); }
var eventsLink = String.Format(#"/{0}/events", AppSettings.GroupID);
return ObjectFactory.GetInstance<IFacebookClient>().Get(eventsLink,parms);
}
(I'm aware that even if this did succeed, the return value wouldn't be serializable - I'm not concerned about that quite yet).
This GET request returns the following message:
(OAuthException - #102) A user access token is required to request this resource.
So the message is quite clear: I need a user access token to get the data I've requested. The question is - what is the best way to do this? Can I give my application a certain permission to read this data? I've looked over all the permissions available to apps, but I don't see one that would do the trick.
I don't want to require people to log onto Facebook to look at public event data, and I love the idea of allowing people with no technical experience to essentially update the website content by posting Facebook events to the group. Right now, I have to duplicate anything they do.
I would think this kind of application would be very common, but no matter what I've read or tried, I can't quite find an example of the same thing that works.
From the docs at https://developers.facebook.com/docs/graph-api/reference/v2.0/group/events you need
A user access token for a member of the group with user_groups permission.
To avoid the hassle, you could create such an Access Token via the Graph Explorer and then store it in your application. Remember to exchange that Access Token to a long-lived one (https://developers.facebook.com/docs/facebook-login/access-tokens/#extending), and that you have to renew the Access Token every 60 days afterwards.
Related
I have a nice Azure Active Directory set up with a dozen users. (All me!) So I have a Tenant ID, client ID and Client Secret.
I am also working on a simple console application that will function as a public client for this directory. This client also holds a list of usernames and passwords as this is just meant as a simple experiment. Not secure, I know. But I first need to understand how it works...
I do this:
IConfidentialClientApplication client = ConfidentialClientApplicationBuilder
.CreateWithApplicationOptions(options).Build();
And this creates my client app. Works fine.
I also get a token using "https://graph.microsoft.com/.default" and can use this to get all users as JSON:
string result = await GetHttpContentWithToken("https://graph.microsoft.com/v1.0/users",
token.AccessToken);
Although I might want it to be more user-friendly, JSON is fine for now.
How can I check if user is an authorized user?
And no, I don't want complex solutions that require various nuget packages. Just a plain and simple step-by-step explanation. I could probably Google this but I ended up with thousands of results and none were helpful... This should be easy, right?
[EDIT] I first wanted to get a list of users nut that failed because of a typo... (There's a dot before 'default'...)
It took some fooling around but it's not too difficult after all. There are a lot of libraries around Azure but it is all basically just a bunch of HTTP requests and responses. Even in a console application...
I started with making a PublicClientApplicationBuilder first:
var options = new PublicClientApplicationOptions()
{
ClientId = <**clientid**>,
TenantId = <**tenantid**>,
AzureCloudInstance = AzureCloudInstance.AzurePublic,
};
var client = PublicClientApplicationBuilder.CreateWithApplicationOptions(options).Build();
I can also create a ConfidentialClientApplication instead, but this allows me to log in interactively, if need be.
Next, set up the scopes:
var scopes = new List<string>() { "https://graph.microsoft.com/.default" };
As I wanted to log in using username and password, I have to use this:
var token = await client.AcquireTokenInteractive(scopes).ExecuteAsync();
But if I want to log in using code, I can also use this:
var password = new SecureString();
foreach (var c in <**password**>) { password.AppendChar(c); }
var token = await client.AcquireTokenByUsernamePassword(scopes, <**account**>, password).ExecuteAsync();
At this point, I'm authorized as the specified user. So, now all I need is to get whatever data I like, in JSON strings...
public static async Task<string> ExecCmd(string name, string url, string token)
{
HttpClient httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("bearer", token);
string result = await GetHttpContentWithToken(url, token);
JObject json = JsonConvert.DeserializeObject(result) as JObject;
File.WriteAllText(name, json.ToString());
return result;
}
As I just want to read the data as text files, I just execute the action in using a specific and write it as formatted JSON to the file . So, using this simple method I can now use this:
await ExecCmd("Profile.txt", "https://graph.microsoft.com/v1.0/me/", token.AccessToken);
await ExecCmd("Groups.txt", "https://graph.microsoft.com/v1.0/groups", token.AccessToken);
await ExecCmd("Users.txt", "https://graph.microsoft.com/v1.0/users", token.AccessToken);
These will provide me with (1) the profile of the current user, (2) the AD groups and (3) the AD users. And probably a bit more...
I can use this ExecCmd to retrieve a lot more data, if I want to. But there's something else to keep in mind! For it all to work, you also need to configure the Azure application and make sure all access rights are assigned and approved!
So, in Azure AD you have to add an "App registration" and fiddle around with the settings... (The Azure experts are horribly shocked now, but when you want to learn, you'd just have to try and fail until you succeed...)
Also set "Default client type" to "public client" for the registered app.
In Azure, with the registered app, you also need to set the proper API permissions! Otherwise, you won't have access. And as I want access to Active Directory, I need to add permissions to "Azure Active Directory Graph". I can do this inside Azure or by using the scope when I call AcquireTokenInteractive(). For example, by using "https://graph.windows.net/Directory.Read.All" instead of "https://graph.windows.net/.default".
Once you've accessed a token interactively, you can also get more tokens using client.AcquireTokenSilent(). It gets a bit tricky from here, especially if you want to access a lot of different items. Fortunately, Active Directory is mostly the directory itself, groups, users and members.
Personally, I prefer to grant access from the Azure website but this is quite interesting.
Anyways, I wanted to authenticate users with Azure and now I know how to do this. It still leaves a lot more questions but this all basically answers my question...
I'll use this as answer, as others might find it useful...
I've been searching the Graph API Explorer and documentation for a long time, but I really can't find anything.
I need a simple GET request like the me/subscribes just with page-id/subscribers or something like that. Does anyone know the get I must send to get a Count of subscribers of a page?
BTW I'm using Facebook SDK and I'm using this as GET:
var fb = new FacebookClient(useraccesstoken);
dynamic result = fb.Get("i want to get subscribers of a page id what to do??");
The /me/subscribers end-point was available to users' only, and has since been removed with Graph API 2.0, along with the user_subscriptions permission.
You can get a count of the subscribers / likes for a page as follows:
/{page-id}?fields=likes
This will return an a count for the total number of likes for a given {page-id}:
{
"likes": 123456,
"id": "{page-id}"
}
Trying to access the subscribers of a page is the same as trying to access all the users that have liked the page, which Facebook doesn't allow.
I've read a lot of posts about how to handle posting to FB without logging in - specifically in my instance to a managed page, some using the defunct "offline_access" method, others relating to tokens.
I've created a token from my graph API - https://developers.facebook.com/tools/explorer
I've also created a little test in some code e.g.
FacebookClient fb = new FacebookClient("Access_Token"]);
Dictionary<string, object> argList = new Dictionary<string, object>();
argList["message"] = "my message";
fb.Post("mypage/feed", argList);
This works perfectly as expected and posts to the page wall in question. If however I log out of Facebook, it doesn't work and in turn throws an error.
Facebook.FacebookOAuthException: (OAuthException - #190)
Error validating access token: This may be because the user logged out
I'm stuck about why this happens when I've set the permissions correctly in the https://developers.facebook.com/tools/explorer area e.g. "publish_stream", "manage_pages" etc.
I've even assigned the token to an app I've created too which still doesn't help.
Has anyone got an example in C# that illustrates how to post when you are not logged in?
Thanks in advance,
Deeds.
Its may be because of access token validation expired, if you have application Id and Secret key of which you used to generate access token, You can use the following code to extend the access token validation period upto 60 days .
public string exchange_access_token(string actoken)
{
var fb = new FacebookClient();
dynamic result = fb.Get("oauth/access_token", new
{
client_id = "xxxxxxxxx",
client_secret = "xxxxxxxxxxxx",
grant_type = "fb_exchange_token",
fb_exchange_token = actoken
});
return result;
}
But if you dont have application details you cant do this ...
The exception I am getting is "The user hasn't authorized the application to perform this action". I know this is a well published exception but there are no rules which I can follow to get this code to work. I am trying to post to a friends wall via the API.
AuthenticationResult result = OAuthWebSecurity.VerifyAuthentication(Url.Action("ExternalLoginCallback", new { ReturnUrl = returnUrl }));
String accessToken = result.ExtraData["accesstoken"];
FacebookClient client = new FacebookClient(accessToken);
dynamic parameters = new ExpandoObject();
arameters.message = "Testing";
I have managed to get my friends facebook ids and this is facebookFriendID
object resTest = client.Post("/" + facebookFriendID + "/feed", parameters);
This is throwing the exception. Do I need to set any special options in my app to allow this to post to friends walls and/or do the users receving the post need to accept the app first? Is there any other params I need to send?
Thanks in advance
Posting to a friend's wall has been disabled
Post to friends wall via the API generate a high levels of negative user feedback, including “Hides” and “Mark as Spam" and so we are removing it from the API. If you want to allow people to post to their friend’s timeline from your app, you can invoke the feed dialog. Stories that include friends via user mentions tagging or action tagging will show up on the friend’s timeline (assuming the friend approves the tag).
https://developers.facebook.com/blog/post/2012/10/10/growing-quality-apps-with-open-graph/
ensure which authorization check the user has access to only his/her pages or whole application.
For basic authorization you can do like this
[BasicAuthorize]
public ActionResult Index()
{
// code will go here
}
For Anonymous
[AllowAnonymous]
public ActionResult Index()
{
// code will go here
}
[BasicAuthorize] requires at least user should login
[AllowAnonymous] Allow Every one to application
I think the exception is pretty explicit: your app must ask the target user for an authorization to post on its wall, and the user has to approve it. Imagine how would Facebook it be if any app could just post whatever it wanted on anyone's behalf in anyone's wall.
Depending on your implementation, you will need to ask for the publish_stream, status_update, or even other permission.
Do I pass this as a param? – CR41G14
I think it's more complicated than that, as you have to ask for the permission before acting. Check out this question for some information that may help you (here in SO there are several other questions about the topic, too).
What's happening is say user A Logs in and provides the rights for the app to access his/her Data we extract his data then..
But what the problem is when another person Logs into his/her account using the same computer and grants permission we are getting the Data of User A and not B..
if(Request.Params["code"] != null)
{
Facebook.FacebookAPI api = new Facebook.FacebookAPI(GetAccessToken());
string me = api.Get("/me");
string meFriends = api.Get("/me/friends/");
}
I am accessing the user info by the above code ..
What should we do about it?
It's a known problem with the SDK saving the data into the session. I'm not 100% sure for C# but I guess it's the same as PHP. The SDK (in PHP ) checks if user's Signed Request is available in session:
public function getSignedRequest() {
if (!$this->signedRequest) {
if (isset($_REQUEST['signed_request'])) {
$this->signedRequest = $this->parseSignedRequest(
...
What I do is everytime I check the validity of the current user's access token. But someone mentioned that's not effective (although I tend to disagree:) ).
In short SDK doesn't check for validity and takes the data from session as first source. That's why 2nd user has the 1st user data.