Get AccessToken on the Landing Page using Facebook C# SDK - c#

Coding Platform ASP.NET 4.0 WebForms
I have two pages that are relevant here
Login.aspx
LandingPage.aspx
On Login.aspx when I click an ImageButton, I redirect to Facebook site with the following code
protected void FacebookLoginButton_Click(object sender, ImageClickEventArgs e)
{
try
{
Response.Redirect(GetFacebookLoginURL());
}
catch (System.Threading.ThreadAbortException)
{
throw;
}
catch (Exception err)
{
Elmah.ErrorSignal.FromCurrentContext().Raise(err);
}
}
private string GetFacebookLoginURL()
{
try
{
string baseURL = System.Configuration
.ConfigurationManager
.AppSettings["WebsiteURL"]
.ToString();
string[] extendedPermissions = new[] {
"publish_stream",
"offline_access"
};
var oauth = new FacebookOAuthClient {
ClientId = FacebookContext.Current.AppId
};
var parameters = new Dictionary<string, object>{
{ "response_type", "token" },
{ "display", "page" }
};
if (extendedPermissions != null && extendedPermissions.Length > 0)
{
var scope = new StringBuilder();
scope.Append(string.Join(",", extendedPermissions));
parameters["scope"] = scope.ToString();
}
parameters["redirect_uri"] = String.Format("{0}LandingPage.aspx", baseURL);
return oauth.GetLoginUrl(parameters).OriginalString;
}
catch (Exception err)
{
Elmah.ErrorSignal.FromCurrentContext().Raise(err);
return "";
}
}
That part is working properly. But I am clueless on how to access the user info at the LandingPage which is my redirect_uri. Have tried this.
FacebookOAuthClient cl = new FacebookOAuthClient(FacebookContext.Current);
FacebookOAuthResult result = null;
string url = Request.Url.AbsoluteUri;
// verify that there is a code in the url
if (FacebookOAuthResult.TryParse(url, out result))
{
if (result.IsSuccess)
{
var accesstoken = result.AccessToken;
}
else
{
var errorDescription = result.ErrorDescription;
var errorReason = result.ErrorReason;
}
}
But I doubt it wont work since I dont have window.hash.location at Server Side(Its not working anyway)
var client = new FacebookClient(FacebookContext.Current);
dynamic me = client.Get("me");
string firstName = me.first_name;
string lastName = me.last_name;
string email = me.email;
Although I am not sure, getting the Access Token will solve my problem, won't it?
This is the error I am getting.
(OAuthException) An active access
token must be used to query
information about the current user.
What am I doing wrong?

here's a sample of a standalone website using webforms. Check out Default.aspx.cs and Web.config. Please note that this sample could be modified for use with the latest source code and might not work with the latest release (5.0.3 beta).

I just recently did this implementation. To access "me", you need to have an Access Token.
The implementation is pretty straighforward, however, I will say that I found the major stumbling block for me was to make sure that my redirect_uri from my "Login.aspx" url matched the redirect_uri from my landing page.

It must be simple, but I dodnt get it. So I used the lesser used Facebook C# SDK by Facebook. I pretty much did what this blog suggested. Everything is fine now.

Related

Google.Apis.Requests.RequestError Insufficient Permission [403]

I am getting below error, I wanted to get all the comments posted on a youtube video.
So basically I am passing video id and I wanted to get all the comments associated with that video
Google.Apis.Requests.RequestError
Insufficient Permission [403]
Errors [Message[Insufficient Permission] Location[ - ] Reason[insufficientPermissions] Domain[global]]
Here is my code:
protected void btnGetVideoDesc_Click(object sender, EventArgs e)
{
string videoId = txtVideoID.Text;
YoutubeVideo video = new YoutubeVideo(videoId);
lblTitle.Text = video.title;
lblPublishedDate.Text = video.publishdate.ToShortDateString();
}
public class YoutubeVideo
{
public string id, title, description ;
public DateTime publishdate;
public YoutubeVideo(string id)
{
this.id = id;
YoutubeAPI.GetVideoInfo(this);
}
}
public class YoutubeAPI
{
private static YouTubeService ytService = Auth();
private static YouTubeService Auth()
{
UserCredential creds;
var service = new YouTubeService();
try
{
using (var stream = new FileStream(#"C:\v-mmarat\Project\EMEA_Development\YoutubeWebCrawling\YoutubeWebCrawling\youtube_client_secret.json", FileMode.Open, FileAccess.Read))
{
creds = GoogleWebAuthorizationBroker.AuthorizeAsync(GoogleClientSecrets.Load(stream).Secrets,
new[] { YouTubeService.Scope.YoutubeReadonly }, "user", CancellationToken.None,
new FileDataStore("YoutubeAPI")
).Result;
}
service = new YouTubeService(new BaseClientService.Initializer()
{
HttpClientInitializer = creds,
ApplicationName = "YoutubeAPI",
ApiKey = "My_API_Key"
});
}
catch (Exception e)
{ }
return service;
}
public static void GetVideoInfo(YoutubeVideo video)
{
try
{
//This code work perfectly
var videoRequest = ytService.Videos.List("snippet");
videoRequest.Id = video.id;
var response = videoRequest.Execute();
if (response.Items.Count > 0)
{
video.title = response.Items[0].Snippet.Title;
video.description = response.Items[0].Snippet.Description;
video.publishdate = response.Items[0].Snippet.PublishedAt.Value;
}
else
{
//error
}
var CommentRequest = ytService.Comments.List("snippet");
videoRequest.Id = video.id;
//Getting error at this line after CommentRequest.Execute();
var Commentresponse = CommentRequest.Execute();
if (Commentresponse.Items.Count > 0)
{
video.title = Commentresponse.Items[0].Snippet.ChannelId;
video.description = Commentresponse.Items[0].Snippet.TextDisplay;
video.publishdate = Commentresponse.Items[0].Snippet.PublishedAt.Value;
}
else
{
//error
}
}
catch (Exception e)
{ }
}
In GoogleWebAuthorizationBroker.AuthorizeAsync change "user" to "admin".
This is a really late response but I had a similar issue. My project was using the YouTube API to upload videos to an account and in a separate section of the code was using it again to search for a video by ID to check its status.
My issue was I was using the same OAuth credentials to upload and then also for searching for a video.
This was failing because I had already set the YouTube Scope when uploading which was not the correct scope for searching for a video.
My simple solution for this was to create another set of OAuth credentials (Of type "Other") via the Google Developer Console, download the json file and use these details to obtain a different access token for the searching part of my code.
Hopefully this helps someone out.
I know this answer is a little late, but this is what fixed it for me. Hopefully it helps someone else.
I was receiving the same permissions denied error. After adding the YouTubeService.Scope.YoutubeForceSsl item to the scopes list, I was able to pull the comments.
Also, it looks like your code is not quite right. You will want to pull the CommentThreads based on the VideoId and include the replies (if you want them).
You won't be able to pull the comments for a video using Comments.
var threadsRequest = Client.CommentThreads.List("snippet,replies");
threadsRequest.VideoId = videoId;
var response = threadsRequest.Execute();
Same here.
Got it solved by specifying a the "dataStore" parameter in "GoogleWebAuthorizationBroker.AuthorizeAsync".
The service than wrote an acces-token JSON file into that folder.
Seems the storage option was required for authorization.
Also when authorization succeded, the browser jumped to an Google-authorisation URL where I had to logon and allow the levels of access I had requested from the API.
I used the API all the time before, but only for readonly ops.
Seems the "action" stuff (insert, update, delete, upload) requires more grants.

Linq2Twitter - 401 : bad authentication data

I have been working with Linq2Twitter (v. 2), using the Search API and
I wanted to switch to the Stream API. I updated to v. 3 but since then I don't manage to authenticate anymore. I don't think the Stream API or the version could be the problem, because I've tried to go back to the previous version, previous authentication methods, and it doesn't work anymore either. I get a 401 : bad authentication data.
So, here is my current code :
var auth = new SingleUserAuthorizer
{
CredentialStore = new SingleUserInMemoryCredentialStore()
{
ConsumerKey = ConfigurationManager.AppSettings["twitterConsumerKey"],
ConsumerSecret = ConfigurationManager.AppSettings["twitterConsumerSecret"],
OAuthToken = ConfigurationManager.AppSettings["twitterOAuthToken"],
AccessToken = ConfigurationManager.AppSettings["twitterAccessToken"]
}
};
TwitterContext _twitterCtx = new TwitterContext(auth);
try
{
var verifyResponse =
await
(from acct in _twitterCtx.Account
where acct.Type == AccountType.VerifyCredentials
select acct)
.SingleOrDefaultAsync();
if (verifyResponse != null && verifyResponse.User != null)
{
User user = verifyResponse.User;
Console.WriteLine(
"Credentials are good for {0}.",
user.ScreenNameResponse);
}
}
catch (TwitterQueryException tqe)
{
Console.WriteLine(tqe.Message);
}
Of course, I checked the credentials several times, printed them out and all.
I tried with ApplicationOnlyAuthorizer, v.2, v.3 as well, it doesn't change anything.
What scares me the most is that what used to work (v2 + ApplicationOnly + Search API) doesn't work either.
Through my research I've heard of a problem caused by unsynchronized timestamps, or something like that. But I don't understand how I can change that.
The program is not on a server, it's locally stored.
Thank you for reading.
Here's how to use SingleUserAuthorizer in v3.0:
var auth = new SingleUserAuthorizer
{
CredentialStore = new SingleUserInMemoryCredentialStore
{
ConsumerKey = ConfigurationManager.AppSettings["consumerKey"],
ConsumerSecret = ConfigurationManager.AppSettings["consumerSecret"],
AccessToken = ConfigurationManager.AppSettings["accessToken"],
AccessTokenSecret = ConfigurationManager.AppSettings["accessTokenSecret"]
}
};
Notice here that I'm setting AccessToken and AccessToken secret. I also have a FAQ with suggestions for resolving 401 problems:
https://linqtotwitter.codeplex.com/wikipage?title=LINQ%20to%20Twitter%20FAQ&referringTitle=Documentation

LinkedIn OAUTH - Still can't get email-address using DotNetOpenAuth.AspNet

Having read that since late last year LinkedIn finally have finally allowed us to retrieve the email address for the currently logged on user I've been failing to do so. I've read all the posts I can find on SO and elsewhere and as far as I can tell my code should be working. It returns just fine with all the other fields,
however, the email address field is always empty.
Here's my LinkedInClient class;
public class LinkedInClient2 : OAuthClient
{
public static readonly ServiceProviderDescription LinkedInServiceDescription = new ServiceProviderDescription
{
AccessTokenEndpoint =
new MessageReceivingEndpoint(
"https://api.linkedin.com/uas/oauth/accessToken",
HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.AuthorizationHeaderRequest),
RequestTokenEndpoint =
new MessageReceivingEndpoint(
"https://api.linkedin.com/uas/oauth/requestToken?scope=r_basicprofile+r_emailaddress",
HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.AuthorizationHeaderRequest),
UserAuthorizationEndpoint =
new MessageReceivingEndpoint(
"https://www.linkedin.com/uas/oauth/authenticate",
HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.AuthorizationHeaderRequest),
TamperProtectionElements = new ITamperProtectionChannelBindingElement[] { new HmacSha1SigningBindingElement() },
ProtocolVersion = ProtocolVersion.V10a
};
public LinkedInClient2(string consumerKey, string consumerSecret, IConsumerTokenManager tokenManager)
: base("linkedIn", LinkedInServiceDescription, tokenManager)
{
}
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "We don't care if the request fails.")]
protected override AuthenticationResult VerifyAuthenticationCore(AuthorizedTokenResponse response)
{
// See here for Field Selectors API http://developer.linkedin.com/docs/DOC-1014
const string ProfileRequestUrl = "https://api.linkedin.com/v1/people/~:(id,first-name,last-name,email-address,headline,industry,summary,picture-url)";
string accessToken = response.AccessToken;
var profileEndpoint = new MessageReceivingEndpoint(ProfileRequestUrl, HttpDeliveryMethods.GetRequest);
HttpWebRequest request = this.WebWorker.PrepareAuthorizedRequest(profileEndpoint, accessToken);
try
{
using (WebResponse profileResponse = request.GetResponse())
{
using (Stream responseStream = profileResponse.GetResponseStream())
{
XDocument document = LoadXDocumentFromStream(responseStream);
string userId = document.Root.Element("id").Value;
// User Profile Fields - https://developer.linkedin.com/documents/profile-fields
string firstName = document.Root.Element("first-name").Value;
string lastName = document.Root.Element("last-name").Value;
string userName = document.Root.Element("email-address").Value; // <<<<<< ERROR - always empty
var extraData = new Dictionary<string, string>();
extraData.Add("accesstoken", accessToken);
extraData.Add("name", userName);
extraData.AddDataIfNotEmpty(document, "picture-url");
extraData.AddDataIfNotEmpty(document, "location");
extraData.AddDataIfNotEmpty(document, "headline");
extraData.AddDataIfNotEmpty(document, "summary");
extraData.AddDataIfNotEmpty(document, "industry");
return new AuthenticationResult(
isSuccessful: true, provider: this.ProviderName, providerUserId: userId, userName: userName, extraData: extraData);
}
}
}
catch (Exception exception)
{
return new AuthenticationResult(exception);
}
}
internal static XDocument LoadXDocumentFromStream(Stream stream)
{
const int MaxChars = 0x10000; // 64k
XmlReaderSettings settings = new XmlReaderSettings()
{
MaxCharactersInDocument = MaxChars
};
return XDocument.Load(XmlReader.Create(stream, settings));
}
}
}
I realise that I'm supposed to add the scope=r_emailaddress to the RequestTokenEndpoint (which I have) but from the fiddler traces I can't even see that endpoint being fetched. Basically, it only every uses the AccessTokenEndpoint which presumably is something to do with my problem.
This is approximately how my ASP.Net MVC4.5 controller looks;
[AllowAnonymous]
public virtual ActionResult LinkedIn(string returnUrl)
{
var tokenMgr = new RepoOAuthTokenManager(_iOtk, LinkedInAppKey, LinkedInAppSecret);
var iacp = new LinkedInClient2(LinkedInAppKey, LinkedInAppSecret, tokenMgr); // if none specified, LinkedInClient uses the AuthenticationOnlyCookieOAuthTokenManager which doesn't work for APIs
var ioadp = new MyOauthDataProvider();
var oasm = new OpenAuthSecurityManager(this.HttpContext, iacp, ioadp);
var redirectUri = Url.ActionFullyQualified(this.nameof(c => c.LinkedIn(null)), null, new RouteValueDictionary(new { returnUrl = returnUrl }));
AuthenticationResult ar = oasm.VerifyAuthentication(redirectUri);
if (ar.Error == null)
{
if (ar.IsSuccessful)
DoSomethingResultingInRedirect(redirectUri); // OK
else
oasm.RequestAuthentication(redirectUri);
}
else
ModelState.AddModelError("", ar.Error.Message);
return View(this.nameof(c=>c.Login(null)));
}//LinkedIn
I can't say I completely understand the extensibility mechanism in DotNetOpenAuth and I may be misunderstanding something so I'd appreciate some pointers.
Am I missing a step somewhere?
I have two solutions to this, although I still don't understand how to get my existing code to work as I'd expect, but hopefully this may help someone else;
(1) I went to Making it easier for you to add default member permissions and clicked on the API admin page.
Here you can select what scopes you want requested by default. It didn't work until I clicked a box (now disappeared) that was worded along the lines of "[x] Make this permanent". Once I'd done that I started to get the email-address field populated as I was expecting.
(2) I tried using the OAuth2 URL instead from information here and it seemed to work. I have also found an implementation of an OAuth2 client here which looks like a good start. I suspect that in the long run, an OAuth2 upgrade (once the spec is more static) will yield better overall mileage.
For now though, I'm out of the pit of despair, but other answers are still welcome!
I had a similar issue.. maybe this is relevant for you:
My Request Token Call is:
https://api.linkedin.com/v1/people/~:(id,first-name,last-name,headline,member-url-resources,picture-url,location,public-profile-url,email-address)?format=json
but the json response is:
array(8) {
["emailAddress"]=>
string(18) "email#email.com"
["firstName"]=>
string(3) "Tim"
...
Note that in the first case email is named email-address, in the second emailAddress.

using google oauthutill in a desktop application to retrieve contacts

I am using oauth to get acces to google contacts from a desktop application. I have followed the instruction from google here: http://code.google.com/intl/iw-IL/apis/gdata/docs/auth/oauth.html#Examples but I am having problems
here is the code:
OAuthParameters parameters = new OAuthParameters()
{
ConsumerKey = CONSUMER_KEY,
ConsumerSecret = CONSUMER_SECRET,
Scope = SCOPE,
Callback = "http://localhost:10101/callback.htm.txt",
SignatureMethod = "HMAC-SHA1"
};
OAuthUtil.GetUnauthorizedRequestToken(parameters);
string authorizationUrl = OAuthUtil.CreateUserAuthorizationUrl(parameters);
Console.WriteLine(authorizationUrl);
var win = new GoogleAuthenticationWindow(authorizationUrl,parameters);
win.ShowDialog();
OAuthUtil.GetAccessToken(parameters);
inside the window I have the following:
private void BrowserNavigated(object sender, NavigationEventArgs e)
{
if (e.Uri.ToString().Contains("oauth_verifier="))
{
OAuthUtil.UpdateOAuthParametersFromCallback(e.Uri.ToString(), m_parameters);
Close();
}
}
at the last line (OAuthUtil.GetAccessToken(parameters);) I am getting a 400 bad request error and I have no idea why...
After much playing around... I think this is the easiest way to access google api:
Service service = new ContactsService("My Contacts Application");
service.setUserCredentials("mail#gmail.com", "password");
var token = service.QueryClientLoginToken();
service.SetAuthenticationToken(token);
var query = new ContactsQuery(#"https://www.google.com/m8/feeds/contacts/mail#gmail.com/full?max-results=25000");
var feed = (ContactsFeed)service.Query(query);
Console.WriteLine(feed.Entries.Count);
foreach (ContactEntry entry in feed.Entries)
{
Console.WriteLine(entry.Title.Text);
}
much easier than using oauth...

Facebook .NET SDK: How to authenticate with ASP.NET MVC 2

I am trying to get the grip on the Facebook SDK and at the same time transitioning from ASP.NET forms to MVC (finally). So please bear with me ..
I have created two controller actions:
FBLogon is execetued when the user clicks on the FB login button on the form.
He is then redirected to the FB login page.
Afterwards he gets sent back to the FBAuthorize page, which is supposed to parse the returned url for the access token. I get something like:
http://localhost:5000/account/FBAuthorize#access_token=199143326771791|827213759889396d5408fee6-100001815992604|BmYchAOMqSoZ2L0TYgCrtpoKP3M&expires_in=0
The problem I see, is that as the access_token is passed behind a #, asp.net cannot parse it on the server. Am I doing something fundamentaly wrong?
Code follows:
public ActionResult FBLogon()
{
var settings = ConfigurationManager.GetSection("facebookSettings");
IFacebookApplication current = null;
if (settings != null)
{
current = settings as IFacebookApplication;
if (current.AppId == "{app id}" || current.AppSecret == "{app secret}")
{
return View();
}
}
string[] extendedPermissions = new[] { "publish_stream", "offline_access" };
var oauth = new FacebookOAuthClient { ClientId = current.AppId, RedirectUri = new Uri("http://localhost:5000/account/FBAuthorize") };
var parameters = new Dictionary<string, object>
{
{ "response_type", "token" },
{ "display", "page" }
};
if (extendedPermissions != null && extendedPermissions.Length > 0)
{
var scope = new StringBuilder();
scope.Append(string.Join(",", extendedPermissions));
parameters["scope"] = scope.ToString();
}
var loginUrl = oauth.GetLoginUrl(parameters);
return Redirect(loginUrl.ToString());
}
public ActionResult FBAuthorize()
{
FacebookOAuthResult result;
if (FacebookOAuthResult.TryParse(Request.Url, out result))
{
if (result.IsSuccess)
{
var accesstoken = result.AccessToken;
}
else
{
var errorDescription = result.ErrorDescription;
var errorReason = result.ErrorReason;
}
}
return View();
}
Ok. The facebook docs say it quite clearly:
Because the access token is passed in
an URI fragment, only client-side code
(such as JavaScript executing in the
browser or desktop code hosting a web
control) can retrieve the token. App
authentication is handled by verifying
that the redirect_uri is in the same
domain as the Site URL configured in
the Developer App
from http://developers.facebook.com/docs/authentication/ ---> Client-side Flow Section.
So I'm sending the token back to my server to complete the authentication..
Update:
The sending back to the server I do using Javascript something like this:
var appId = "<%: Facebook.FacebookContext.Current.AppId %>";
if (window.location.hash.length > 0) {
accessToken = window.location.hash.substring(1);
var url = window.location.href.replace(/#/, '?');
window.location = url;
}
On the server then I have the following action. Not very nice but it works..
public ActionResult FBAuthorize()
{
FacebookOAuthResult result = null;
string url = Request.Url.OriginalString;
/// hack to make FacebookOuthResult accept the token..
url = url.Replace("FBAuthorize?", "FBAuthorize#");
if (FacebookOAuthResult.TryParse(url, out result))
{
if (result.IsSuccess)
{
string[] extendedPermissions = new[] { "user_about_me", "offline_access" };
var fb = new FacebookClient(result.AccessToken);
dynamic resultGet = fb.Get("/me");
var name = resultGet.name;
RegisterModel rm = new Models.RegisterModel();
rm.UserName = name;
rm.Password = "something";
rm.Email = "somethig";
rm.ConfirmPassword = "23213";
//Label1.Text = name;
//Response.Write(name);
//return RedirectToAction("register", "Account", rm);
ViewData["Register"] = rm;
return RedirectToAction("Register");
}
else
{
var errorDescription = result.ErrorDescription;
var errorReason = result.ErrorReason;
}
}
return View();
}
I found this post http://facebooksdk.codeplex.com/discussions/244568 on codeplex. I think this is what you need.
Note that instead of using the client-side flow, you need to use the server-side flow.
This is what you should do
Create a login link for server-side flow. After Authorization, facebook will return an url containing a code instead of a access token.
Then you request for a token from facebook using the code. this is my example
public ActionResult FBAuthorize()
{
FacebookOAuthClient cl = new FacebookOAuthClient(FacebookContext.Current);
FacebookOAuthResult result = null;
string url = Request.Url.OriginalString;
// verify that there is a code in the url
if (FacebookOAuthResult.TryParse(url, out result))
{
if (result.IsSuccess)
{
string code = result.Code;
// this line is necessary till they fix a bug *see details below
cl.RedirectUri = new UriBuilder("http://localhost:5000/account/FBAuthorize").Uri;
var parameters = new Dictionary<string, object>();
//parameters.Add("permissions", "offline_access");
Dictionary<String, Object> dict = (Dictionary<String, Object>)cl.ExchangeCodeForAccessToken(code, new Dictionary<string, object> { { "redirect_uri", "http://localhost:5000/account/FBAuthorize" } });
Object Token = dict.Values.ElementAt(0);
TempData["accessToken"] = Token.ToString();
return RedirectToAction ("ShowUser");
}
else
{
var errorDescription = result.ErrorDescription;
}
}
else
{
// TODO: handle error
}
return View();
}
*There is bug when using IIS in localhost, see the original post for details (the redirect uri when asking for the token must be the same as the one used asking for the code)
It is highly recommended to use IIS and not visual studio web server. There are many things that wont work in visual studio web server.
I am in the same spot you are at the moment.
We never get the Request.QueryString populated becasue of the "fragment" or # in the url.
Love to know if you solved this and how.
It does not look like the FacebookOAuthResult class was written to be used in web applications of any sort.
you can change the response type in you scope paramas to be "code" then it will send back a code in the querystring in which you can swap for a token.

Categories

Resources