How to get User json data with google Oauth2 - c#

I'm attempting to send back my token to request further information about the user logging in. I want to get json back.
using the code below I get an exception "The given key was not present in the dictionary."
What am I doing wrong?
public void Login(Action<Mobile.Google.Account> googleLoginCompleted, Action googleLoginFailed)
{
var auth = new OAuth2Authenticator(
clientId: "myid",
clientSecret: "secret",
scope: "https://www.googleapis.com/auth/plus.login",
authorizeUrl: new Uri("https://accounts.google.com/o/oauth2/v2/auth"),
redirectUrl: new Uri("http://adults.wicareerpathways.org"),
accessTokenUrl: new Uri("https://www.googleapis.com/oauth2/v4/token"));
auth.Completed += (sender, e) =>
{
if (e.IsAuthenticated)
{
var values = e.Account.Properties;
var access_token = values["access_token"];
var googleAccount = new Mobile.Google.Account
{
Username = e.Account.Username,
Properties = e.Account.Properties
};
try
{
var request = HttpWebRequest.Create(string.Format(#"https://www.googleapis.com/oauth2/v2/userinfo?access_token=" + access_token + "&format=json", ""));
request.ContentType = "application/json";
request.Method = "GET";
var user_ID = values["user_id"];
using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
{
System.Console.Out.WriteLine("Stautus Code is: {0}", response.StatusCode);
using (StreamReader reader = new StreamReader(response.GetResponseStream()))
{
var content = reader.ReadToEnd();
GoogleAccountDetails result = Newtonsoft.Json.JsonConvert.DeserializeObject<GoogleAccountDetails>(content);
googleAccount.Username = result.id;
if (googleLoginCompleted != null)
{
googleLoginCompleted(googleAccount);
}
}
}
}
catch (Exception exx)
{
System.Console.WriteLine(exx.ToString());
}
}

Rather than directly solving your problem, let me give you some advice on how to solve this yourself.
It seems obvious that a key isn't present. The first important thing is to figure out which line exactly the problem is. I see two possibilities, "user_id" and "access_token". One of those two items must not be included. Both come from values. Try printing out values, and see what you come up with. In particular, print the keys, and see if you have a typo.
Looking at your code, I suspect your error is in the second, var user_ID = values["user_id"]; First of all, you don't seem to use it anywhere. Secondly, it is different than the clientId used earlier. In fact, I would suggest just using clientId, if that is what you intend, which will give you consistency in your requesting values.

Related

C# eBay Listing Recommendation API

I'm having trouble getting any type of response from the Listing Recommendation API. I keep getting a generic 500 message on my return. I've set up my headers the way they recommend here: https://developer.ebay.com/devzone/listing-recommendation/Concepts/MakingACall.html
I've tried using the information from the documentation on the call here: https://developer.ebay.com/devzone/listing-recommendation/CallRef/itemRecommendations.html#Samples
But every variation of my code comes up bad. Below is a sample of the code. I've tried it with and without the commented out line of code with the same results. It always fails on the line response.GetResponseStream. Thanks for your help.
public static void test(string AuthToken, string listing, log4net.ILog log)
{
string url = "https://svcs.ebay.com/services/selling/listingrecommendation/v1/item/" + listing + "/itemRecommendations/?recommendationType=ItemSpecifics";
var listingRecommendationRequest = (HttpWebRequest)WebRequest.Create(url);
listingRecommendationRequest.Headers.Add("Authorization", "TOKEN " + AuthToken);
listingRecommendationRequest.ContentType = "application/json";
listingRecommendationRequest.Accept = "application/json";
listingRecommendationRequest.Headers.Add("X-EBAY-GLOBAL-ID", "EBAY-US");
listingRecommendationRequest.Method = "GET";
//listingRecommendationRequest.Headers.Add("recommendationType", "ItemSpecifics");
var response = (HttpWebResponse)listingRecommendationRequest.GetResponse();
string result;
using (var streamReader = new StreamReader(response.GetResponseStream()))
{
result = streamReader.ReadToEnd();
}
var reader = new JsonTextReader(new StringReader(result));
while (reader.Read())
{
if (reader.Value != null)
{
// Read Json values here
var pt = reader.Path;
var val = reader.Value.ToString();
}
}
}
Edit: Adding image of what I'm trying to accomplish. I'm trying to get the item specifics recommendations that are suggested by eBay when manually editing a listing. These suggestions change based on what is in your title.

401 Unauthorized when calling Stormpath REST API with WebRequest object?

I am using Stormpath for my authentication service. I call the RestAPI of Stormpath by using HttpWebRequest.
I am also using HttpWebRequest to call the RestAPI but it does not work.
private void BtnGetResetApiClick(object sender, EventArgs e)
{
var username = "aaaa";
var password = "bbbb";
ServicePointManager.ServerCertificateValidationCallback = Callback;
var request = WebRequest.Create("https://api.stormpath.com/v1/tenants/current") as HttpWebRequest;
request.UserAgent = ".NET SDK";
request.Method = "GET";
request.Accept = "*/*";
var data = string.Format("{0}:{1}", username, HttpUtility.HtmlEncode(password));
var token = Convert.ToBase64String(Encoding.UTF8.GetBytes(data));
string authHeader = string.Format("Basic {0}", token);
request.Headers.Add("Authorization", authHeader);
request.ServerCertificateValidationCallback = Callback;
using (var response = request.GetResponse())
{
var stream = response.GetResponseStream();
if (stream != null)
{
var streamReader = new StreamReader(stream);
var str = streamReader.ReadToEnd();
streamReader.Close();
stream.Close();
}
}
}
private bool Callback(object obj, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors)
{
return true;
}
When calling:
var response = request.GetResponse()
I got an exception:
An unhandled exception of type 'System.Net.WebException' occurred in System.dll The remote server returned an error: (401) Unauthorized.
Can you help me to see if my code has something wrong?
Update - use the SDK, it's much easier!
If you're calling the Stormpath API from C# frequently, don't bother with writing requests by hand. Use the Stormpath .NET SDK instead. I'm the author. :)
Install it using install-package Stormpath.SDK from the Package Manager Console. Then, create an IClient object:
// In a production environment, you'll want to load these from
// environment variables or a secure file, instead of hardcoding!
var apiKey = ClientApiKeys.Builder()
.SetId("Your_Stormpath_API_key_ID")
.SetSecret("Your_Stormpath_API_key_secret")
.Build();
var client = Clients.Builder()
.SetApiKey(apiKey)
.Build();
Getting the tenant info is now just a simple call:
var tenant = await client.GetCurrentTenantAsync();
Console.WriteLine($"Current tenant is: {tenant.Name}");
If you really want to make raw requests, you can still do that! I'll explain below.
Constructing the Authorization header
A 401 Unauthorized response means that the API was not able to find a valid Authorization header in your request. To authenticate correctly, you need two things:
An authorization payload in the format apiKeyID:apiKeySecret
An Authorization header with value: Basic base64(payload)
Here's how to construct the complete header:
// API_KEY_ID and API_KEY_SECRET populated elsewhere
var authPayload = string.Format("{0}:{1}", API_KEY_ID, API_KEY_SECRET);
var authPayloadEncoded = Convert.ToBase64String(Encoding.UTF8.GetBytes(authPayload));
request.Headers.Add(HttpRequestHeader.Authorization, "Basic " + authPayloadEncoded);
You don't need the ServerCertificateValidationCallback = Callback stuff. With the above header, the request will be seen by the API as a valid request (assuming your API Key ID and Secret are correct, of course).
Redirection handling
One thing to watch out for (this tripped me up at first!) is that WebRequest will follow HTTP 302 redirects automatically, but will not apply the existing headers to the new request.
The solution is to disable redirect following:
request.AllowAutoRedirect = false;
This means you'll have to handle 302 responses yourself, but it's the only way to correctly apply the Authorization header to each request.
Working example
I created a simple working example in this gist. Since I'll be creating requests multiple times, I wrote a helper function:
private static HttpWebRequest BuildRequest(string method, string uri)
{
var request = WebRequest.Create(uri) as HttpWebRequest;
request.UserAgent = "dotnet/csharp web-request";
request.Method = method;
request.ContentType = "application/json";
// Important, otherwise the WebRequest will try to auto-follow
// 302 redirects without applying the authorization header to the
// subsequent requests.
request.AllowAutoRedirect = false;
// Construct HTTP Basic authorization header
var authPayload = string.Format("{0}:{1}", API_KEY_ID, API_KEY_SECRET);
var authPayloadEncoded = Convert.ToBase64String(Encoding.UTF8.GetBytes(authPayload));
request.Headers.Add(HttpRequestHeader.Authorization, "Basic " + authPayloadEncoded);
return request;
}
And a simple console app to demonstrate getting the current tenant URL and name:
// Get these from the Stormpath admin console
private static string API_KEY_ID = "Your_Stormpath_API_key_ID";
private static string API_KEY_SECRET = "Your_Stormpath_API_key_secret";
static void Main(string[] args)
{
// First, we need to get the current tenant's actual URL
string tenantUrl = null;
var getCurrentTenantRequest = BuildRequest("GET", "https://api.stormpath.com/v1/tenants/current");
try
{
using (var response = getCurrentTenantRequest.GetResponse())
{
tenantUrl = response.Headers["Location"];
}
}
catch (WebException wex)
{
Console.WriteLine("Request failed. {0}", wex.Message);
throw;
}
// Now that we have the real tenant URL, get the tenant info
string tenantData = null;
var getTenantInfoRequest = BuildRequest("GET", tenantUrl);
try
{
using (var response = getTenantInfoRequest.GetResponse())
using (var responseStream = response.GetResponseStream())
using (var reader = new StreamReader(responseStream))
{
tenantData = reader.ReadToEnd();
}
}
catch (WebException wex)
{
Console.WriteLine("Request failed. {0}", wex.Message);
throw;
}
// Use JSON.NET to parse the data and get the tenant name
var parsedData = JsonConvert.DeserializeObject<Dictionary<string, object>>(tenantData);
Console.WriteLine("Current tenant is: {0}", parsedData["name"]);
// Wait for user input
Console.ReadKey(false);
}
The code is pretty verbose because we're making raw requests to the API. Again, if you're making requests frequently, use the SDK instead!

HttpClient.PostAsync: API is returning missing parameter

I am trying to do a post with HttpClient to a standard REST API. The post method I am targeting requires two key/value pairs, name and size. When I perform the post using the code below, the API returns the error "Required keys missing: size." However, I've clearly added it to the FormUrlEncodedContent.
I'm wondering if there is an encoding issue, or if I am using one of wrong types of HttpContent.
public async Task<OHDrive> Create(OHDrive drive)
{
string json = "";
var values = new Dictionary<string, string>();
//name and size are required
if (string.IsNullOrEmpty(drive.Name) || drive.Size == 0)
throw new Exception("Name and Size are required for drive creation");
values["name"] = drive.Name;
values["size"] = drive.Size.ToString();
if (drive.UserID != null)
values["user"] = drive.UserID;
if (drive.Encryption != null)
values["encryption:cipher"] = drive.Encryption;
var content = new FormUrlEncodedContent(values);
using (HttpClient client = OHUtilities.CreateClient(userID, secretKey))
{
var url = urlBase + "create";
var response = await client.PostAsync(url, content);
json = await response.Content.ReadAsStringAsync();
}
return JsonConvert.DeserializeObject<OHDrive>(json);
}
Update:
I tried switching the order of the key/value pairs, like this:
values["size"] = drive.Size.ToString();
values["name"] = drive.Name;
I then got the opposite error message: "Required keys missing: name." It's like the API is not seeing the second param for some reason. I checked the request in Fiddler and everything appears to be correct.

Loop through returned XML Result and assign values

I'm currently calling a external MVC Controller and passing in two parameters which passes back an XML Result how I'm currently doing this is as follows, please be aware my experience with XML is pretty much 0
try
{
var url = ConfigurationManager.AppSettings["url"] + String.Format("/ws/RetailerBrandsFiltered?postcode={0}&countryCode={1}", model.Postcode, model.SelectedCountry);
// Create the request to call the external mvc controller to retrieve the retailers
var req = (HttpWebRequest)WebRequest.Create(url);
req.Method = "GET";
req.ContentType = "xml";
// Do the request to external controller
var stIn = new StreamReader(req.GetResponse().GetResponseStream());
var responce = stIn.ReadToEnd();
foreach (var item in responce)
{
string urn = item["URN"].InnerText
}
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
I'm trying to loop through the response and get the XML nodes out and assign them to a model (which I haven't implemented yet) but for testing I'm trying to get the first value and assign it to a string.
Which isn't working the message is Cannot apply indexing to type char, ideally my for loop would look something like this.
foreach (var item in responce)
{
string urn = item["URN"].InnerText;
string name = item["NAME"].InnerText;
string address = item["ADDRESS"].InnerText;
}
Again once this is working, I will replace the string's with model binding. any help would be appreciated
The variable reponce is a string and when you try to iterate over it, you're implicitly casting the string to a char[]and iterating over that.
Load the stream into a XDocument and use Linq to get the values:
try
{
var url = ConfigurationManager.AppSettings["url"] + string.Format("/ws/RetailerBrandsFiltered?postcode={0}&countryCode={1}", model.Postcode, model.SelectedCountry);
// Create the request to call the external mvc controller to retrieve the retailers
var req = (HttpWebRequest)WebRequest.Create(url);
req.Method = "GET";
req.ContentType = "xml";
// Do the request to external controller
var xDoc = XDocument.Load(req.GetResponse().GetResponseStream());
// Get some nodes
var items = xDoc.Descendants("item");
foreach(var item in items)
{
string urn = item.Element("URN").Single().Value;
// Do more stuff
}
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
Se MSDN article and this Stack Overflow question for more details.

facebook c# sdk - The user hasn't authorized the application to perform this action

I am building a console app that will publish streams to a page's wall.
Issue: I'm getting "The user hasn't authorized the application to perform this action". I'm using opengraph to get the access token.
Am I missing something? Any help is greatly appreciated. Thanks!
// constants
string apiKey = "XXX";
string secret = "XXX";
string pageId = "XXX";
// get access token
string url = string.Format("https://graph.facebook.com/oauth/access_token?client_id={0}&client_secret={1}&grant_type=client_credentials", apiKey, secret); // todo: figure out open graph url
WebRequest req = WebRequest.Create(url);
WebResponse resp = req.GetResponse();
StreamReader reader = new StreamReader(resp.GetResponseStream());
string respStr = reader.ReadToEnd();
string accessToken = respStr.Replace("access_token=", "");
// construct the post
dynamic messagePost = new ExpandoObject();
messagePost.access_token = accessToken;
messagePost.picture = "www.google.com/pic.png";
messagePost.link = "www.google.com";
messagePost.name = "some name";
messagePost.captiion = "some caption";
messagePost.description = "some description";
messagePost.req_perms = "publish_stream";
messagePost.scope = "publish_stream";
// using client
FacebookClient client = new FacebookClient(accessToken);
try // to post the post to the page's wall
{
var result = client.Post(string.Format("/{0}/feed", pageId), messagePost);
}
catch (FacebookOAuthException ex)
{
// getting caught here, with error msg = "The user hasn't authorized the application to perform this action"
}
catch (FacebookApiException ex)
{
// ignore
}
After visiting the following links, I was able to run the code and have it successfully publish to the page's wall, after which it shows up in the Likers' news feeds.
http://www.facebook.com/login.php?api_key={API_KEY_GOES_HERE}&next=http://www.facebook.com/connect/login_success.html&req_perms=read_stream,publish_stream
http://www.facebook.com/connect/prompt_permissions.php?api_key={API_KEY_GOES_HERE}&next=http://www.facebook.com/connect/login_success.html?xxRESULTTOKENxx&display=popup&ext_perm=publish_stream&profile_selector_ids={PAGE_ID_GOES_HERE}
Thanks to the answer on Authorizing a Facebook Fan Page for Status Updates
"Liking your application" and "allowing your application to post to his wall" are two different sentences. There is no magic wand that allows you to override a user's preference whether they like you or not.
Your best bet is to handle the "failure" and move on to the next person. If you want to know likers that don't want you to post to their wall, you can get that information in the return and store it in some type of "log".

Categories

Resources