I've done Oauth with Hammock, I succeed to get access token, access token secret and session handle but now I must get the refresh access token when the token expired.
I've followed the instruction and I tried to pass the access token with urldecode and without urldecode but I can't get the token, I obtain
oauth_problem=token_rejected
UPDATE:
that's my code:
##the call##
var AccessTokenQuery = OAuthUtil.GetAccessTokenQueryRenewal(accessToken, session_handle, accessTokenSecret);
AccessTokenQuery.RequestAsync(AppSettings.AccessTokenUri, null);
AccessTokenQuery.QueryResponse += new EventHandler<WebQueryResponseEventArgs>(AccessTokenQuery_QueryResponse);
internal static OAuthWebQuery GetAccessTokenQueryRenewal(string oauth_token,string session_handle, string oauth_token_secret)
{
var oauth = new OAuthWorkflow
{
AccessTokenUrl = AppSettings.AccessTokenUri,
ConsumerKey = AppSettings.consumerKey,
ConsumerSecret = AppSettings.consumerKeySecret,
SignatureMethod = OAuthSignatureMethod.HmacSha1,
ParameterHandling = OAuthParameterHandling.HttpAuthorizationHeader,
TokenSecret = oauth_token_secret,
Token = oauth_token,
SessionHandle = session_handle,
Version = AppSettings.oAuthVersion
};
var info = oauth.BuildAccessTokenInfo(WebMethod.Post);
var objOAuthWebQuery = new OAuthWebQuery(info, false);
objOAuthWebQuery.HasElevatedPermissions = true;
objOAuthWebQuery.SilverlightUserAgentHeader = "Hammock";
return objOAuthWebQuery;
}
void AccessTokenQuery_QueryResponse(object sender, WebQueryResponseEventArgs e)
{
try
{
StreamReader reader = new StreamReader(e.Response);
string strResponse = reader.ReadToEnd();
var parameters = MainUtil.GetQueryParameters(strResponse);
accessToken = parameters["oauth_token"];
accessTokenSecret = parameters["oauth_token_secret"];
session_handle = parameters["oauth_session_handle"];
MainUtil.SetKeyValue<string>("AccessToken", accessToken);
MainUtil.SetKeyValue<string>("AccessTokenSecret", accessTokenSecret);
MainUtil.SetKeyValue<string>("SessionHandle", session_handle);
userLoggedIn();
}
catch (Exception ex)
{
Dispatcher.BeginInvoke(() =>
{
MessageBox.Show(ex.Message);
});
}
}
Quick possibility you could try: you said you 'tried to pass the access token with urldecode'. Have you tried using urlencode? Urldecode is used for decoding urls returned from a web call, encoding is what is done before passing to a web call.
Also, note that the encoding scheme for Oauth is slightly different than the one used in .NET 's default encoding. You can easily write your own encoding routine though, check out the oauth spec for details.
Ex:
private string UrlEncode(string value)
{
string unreserved = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~";
StringBuilder result = new StringBuilder();
foreach (char symbol in value)
{
if (unreserved.IndexOf(symbol) != -1)
result.Append(symbol);
else
result.Append('%' + String.Format("{0:X2}", (int)symbol));
}
return result.ToString();
}
Related
I'm creating a proof of concept that leverages the Etsy API via C# along with Angular.
I'm currently having issues performing authenticated requests to the Etsy API and receive the following response when requesting the authenticated user's profile:
"No logged in user; please specify a user id instead"
Below is the Controller action that I am calling.
You'll notice that if __SELF__ is passed in as the userId that the authentication code is called. Otherwise that code is skipped.
I am able to retrieve user profile's when I pass in a valid user name/id.
[HttpGet("{userId}")]
public async Task<IActionResult> UserProfile(string userId)
{
string requestUrl = String.Format("https://openapi.etsy.com/v2/users/{0}/profile?api_key={1}", userId, AccountController.ConsumerKey);
string token = Request.Headers["Access_Token"];
string tokenSecret = Request.Headers["Access_Verifier"];
if (userId.Equals("__SELF__"))
{
var client = new OAuthRequest
{
Method = "GET",
Type = OAuthRequestType.ProtectedResource,
SignatureMethod = OAuthSignatureMethod.HmacSha1,
ConsumerKey = AccountController.ConsumerKey,
ConsumerSecret = AccountController.ConsumerSecret,
Token = token,
TokenSecret = tokenSecret,
RequestUrl = requestUrl,
};
requestUrl += "&" + client.GetAuthorizationQuery();
}
using (HttpClient client = new HttpClient())
using (HttpResponseMessage res = await client.GetAsync(requestUrl))
using (HttpContent content = res.Content)
{
string data = await content.ReadAsStringAsync();
if (data != null)
{
try
{
UserModel userInfo = JsonConvert.DeserializeObject<UserModel>(data);
if (userInfo != null && userInfo.Count == 1 && userInfo.Results != null && userInfo.Results.Count == 1)
{
return Ok(userInfo.Results[0]);
}
}
catch (Exception)
{
return NotFound(data);
}
}
return NotFound();
}
}
I'm assuming that I am leveraging the OAuth Specific code incorrectly here. I have tried using the token and verifier from the client as well as the token and token secret, but none appear to work.
I was able to resolve this by removing the api key from the request uri when a request is made with OAuth credentials.
string requestUrl = String.Format("https://openapi.etsy.com/v2/users/{0}/profile", userId);
if (userId.Equals("__SELF__"))
{
var client = new OAuthRequest
{
Method = "GET",
Type = OAuthRequestType.ProtectedResource,
SignatureMethod = OAuthSignatureMethod.HmacSha1,
ConsumerKey = AccountController.ConsumerKey,
ConsumerSecret = AccountController.ConsumerSecret,
Token = token,
TokenSecret = tokenSecret,
RequestUrl = requestUrl,
};
requestUrl += "&" + client.GetAuthorizationQuery();
} else {
requestUrl += String.Format("?api_key={0}", AccountController.ConsumerKey);
}
When calling this method please help by advising what values to pass through such as GmailService? Im guessing userID is the Gmail account and the messageID( i want to download all of them) .
How can i change this to download all the attachments in the inbox.
Thank you in advance and I hope someone can help me.
Method im using is below.
public static void GetAttachments(GmailService service, String userId, String messageId, String outputDir)
{
try
{
Message message = service.Users.Messages.Get(userId, messageId).Execute();
IList<MessagePart> parts = message.Payload.Parts;
foreach (MessagePart part in parts)
{
if (!String.IsNullOrEmpty(part.Filename))
{
String attId = part.Body.AttachmentId;
MessagePartBody attachPart = service.Users.Messages.Attachments.Get(userId, messageId, attId).Execute();
// Converting from RFC 4648 base64 to base64url encoding
// see http://en.wikipedia.org/wiki/Base64#Implementations_and_history
String attachData = attachPart.Data.Replace('-', '+');
attachData = attachData.Replace('_', '/');
byte[] data = Convert.FromBase64String(attachData);
File.WriteAllBytes(Path.Combine(outputDir, part.Filename), data);
}
}
}
catch (Exception e)
{
Console.WriteLine("An error occurred: " + e.Message);
}
}
I think the answer to your question can be solved using Get in PostMan
In your header in PostMan, use key as Authorization and pass your token generated to it as value
or goto Authorization and pick bearer token and pass your token.
Note that messageId is the Id in string of the message you are trying to fetch and {userId} =me or user according to google and I believe you can fetch Attachment Id by the method you use above.
https://www.googleapis.com/gmail/v1/users/me/messages/messageId/attachments/id
public async Task<TResult> GetGmailInboxAttachmentById<TResult>(string messageId, string token, string id, string attachId)
{
using (var httpClient = new HttpClient())
{
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
var url = "https://www.googleapis.com/gmail/v1/users/me/messages/" + messageId + "/" + id + "/attachments" + "/" + attachId;
HttpResponseMessage response = await httpClient.GetAsync(url);
return await response.Content.ReadAsAsync<TResult>();
}
}
foreach (var part in inbox.Payload.Parts)
{
if (!String.IsNullOrEmpty(part.Filename))
{
var attachId = part.Body.AttachmentId;
var attach = _gmailService.GetGmailInboxAttachmentById<MessagePartBody>(id, token, part.PartId, attachId).Result;
// Converting from RFC 4648 base64 to base64url encoding
// see http://en.wikipedia.org/wiki/Base64#Implementations_and_history
string attachData = attach.Data.Replace('_', '+');
attachData = attachData.Replace('_', '/');
byte[] data = Convert.FromBase64String(attachData);
string file = Convert.ToBase64String(data);
GetAttach.Add(file);
}
}
Hope this solve it for you because I solved it with this method
I know, it's could be an off-topic, but using PostMan in case when programmer use Google API is little but contraproductive :) APIs also have attachments.Get Method, and you will not need to call HTTP request and allow non OAuth 2.0 authentication especially when you're using multi-phase authentication.
Here is example:
https://developers.google.com/gmail/api/v1/reference/users/messages/attachments/get
foreach (var part in m.Payload.Parts)
{
if (!string.IsNullOrEmpty(part.Filename))
{
var attachId = part.Body.AttachmentId;
MessagePartBody attachPart = service.Users.Messages.Attachments.Get(userId,
message.Id,
attachId).Execute();
byte[] data = GetBytesFromPart(attachPart.Data);
File.WriteAllBytes(Path.Combine(#"c:\teste\",
$"{DateTime.Now:yyyyMMddHHmmss}-{part.Filename}"), data);
}
}
private static string DecodedString(string messagePart)
{
try
{
var data = GetBytesFromPart(messagePart);
string decodedString = Encoding.UTF8.GetString(data);
return decodedString;
}
catch (System.Exception e)
{
// ignored
return string.Empty;
}
}
private static byte[] GetBytesFromPart(string messagePart)
{
var attachData = messagePart.Replace('-', '+');
attachData = attachData.Replace('_', '/');
byte[] data = Convert.FromBase64String(attachData);
return data;
}
I am trying to write an interface to our Phabricator install to allow out internal improvement system to create tasks. However, I cannot figure out why I keep getting a certificate error.
"{\"result\":null,\"error_code\":\"ERR-INVALID-CERTIFICATE\",\"error_info\":\"Your authentication certificate for this server is invalid.\"}"
The following is my code;
private void CreateSession()
{
int token = (int)((DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds);
var result = this.Do(
"conduit.connect",
new
{
client = this.ClientName,
clientVersion = this.ClientVersion,
clientDescription = "HIS to Fabricator Connector",
user = this.User,
authToken = token,
authSignature = SHA1HashStringForUTF8String(token + this.Certificate)
});
this.m_SessionKey = result.sessionKey;
this.m_ConnectionID = result.connectionID;
}
public static string SHA1HashStringForUTF8String(string s)
{
byte[] bytes = Encoding.UTF8.GetBytes(s);
var sha1 = SHA1.Create();
byte[] hashBytes = sha1.ComputeHash(bytes);
return HexStringFromBytes(hashBytes);
}
public static string HexStringFromBytes(byte[] bytes)
{
var sb = new StringBuilder();
foreach (byte b in bytes)
{
var hex = b.ToString("x2");
sb.Append(hex);
}
return sb.ToString();
}
This returns the following JSON;
"{\"client\":\"HIS\",\"clientVersion\":\"1\",\"clientDescription\":\"HIS to Fabricator Connector\",\"user\":\"KYLIE\",\"authToken\":1449486922,\"authSignature\":\"ec020edbd5082d3971c2c11ef4f4917244fc4a78\"}"
I think the issue is the certificate I am passing. I am using;
api-3ydcae2gtmf6u6uer2zow465j6px
which I obtained from the Conduit API Tokens page.
Any pointers?
You have to get the token via .../conduit/token
Use that token to query .../api/conduit.getcertificate
As result you get the certificate -> profit! :)
PS: it's neither api- nor cli- token to query the certificate! ;)
I try to call a share REST API with Hammock library function in my MVC 4 application.
Please see my code below
public ActionResult SharePost()
{
string content = "";
try
{
var credentials = new OAuthCredentials
{
ConsumerKey = "xxxxxxxxxxxxxx",
ConsumerSecret = "xxxxxxxxxxxxxxxx",
Token = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
TokenSecret = "xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
Verifier = verifier,
Type = OAuthType.AccessToken,
ParameterHandling = OAuthParameterHandling.HttpAuthorizationHeader,
SignatureMethod = OAuthSignatureMethod.HmacSha1,
Version = "1.0"
};
var client = new RestClient { Authority = "http://api.linkedin.com/v1", Credentials = credentials, Method = WebMethod.Post };
var request = new RestRequest { Path = "/people/~/shares" };
StringBuilder sbAppend = new StringBuilder();
sbAppend.AppendLine("<?xml version=1.0 encoding=UTF-8?>");
sbAppend.AppendLine("<share><comment>Check out the LinkedIn Share API!</comment><content><title>LinkedIn Developers Documentation On Using the Share API</title><description>Leverage the Share API to maximize engagement on user-generated content on LinkedIn</description><submitted-url>https://developer.linkedin.com/documents/share-api</submitted-url><submitted-image-url>http://m3.licdn.com/media/p/3/000/124/1a6/089a29a.png</submitted-image-url></content><visibility><code>anyone</code></visibility></share>");
client.AddHeader("Content-Type", "text/xml");
byte[] msg = Encoding.Default.GetBytes(sbAppend.ToString());
client.AddPostContent(msg);
RestResponse response = client.Request(request);
content = response.Content;
}
catch (Exception ex)
{
throw ex;
}
return Content(content);
}
But i get a error responce.content
Edit
I use double quotes in my xml header. but always shows the same error.
Is there any thing wrong.
I didn't see the post xml values in fiddller. Please see this image
Please help.
I found error in your XML you are missing ""
Try this
sbAppend.AppendLine('<?xml version="1.0" encoding="UTF-8"?>');
I want to post an image to my Twitter account via C#. I can get access token code, everything is fine but I investigated a PHP code
$tmhOAuth = new tmhOAuth(array(
'consumer_key' => OAUTH_CONSUMER_KEY,
'consumer_secret' => OAUTH_CONSUMER_SECRET,
'user_token' => $oauth_token,
'user_secret' => $oauth_token_secret,
));
$image = "{$_FILES['image']['tmp_name']};type={$_FILES['image']['type']};filename={$_FILES['image']['name']}";
$code = $tmhOAuth->request('POST', 'https://upload.twitter.com/1/statuses/update_with_media.json',
array(
'media[]' => "#{$image}",
'status' => " " . $status, //A space is needed because twitter b0rks if first char is an #
'lat' => $lat,
'long' => $long,
),
true, // use auth
true // multipart
In PHP code, the OAuth class has a request method. In C# side, I used Twitterizer library which hasn't any request method in OAuth class. Then I used Webclient instead of request method. But I need to some Credentials to post data. But I don't know what/why I use username and password. Actually, I don't want to use any credentials. What can I use instead of credentials?
Second problem is, I always get an authorized errors (401) here is code
OAuthTokenResponse responseToken = OAuthUtility.GetAccessToken(ConsumerKey, ConsumerSecret, oauth_token, oauth_verifier);
OAuthTokens accessToken = new OAuthTokens();
accessToken.AccessToken = responseToken.Token;
accessToken.AccessTokenSecret = responseToken.TokenSecret;
accessToken.ConsumerKey = ConsumerKey;
accessToken.ConsumerSecret = ConsumerSecret;
TwitterResponse<TwitterUser> twitterResponse = TwitterAccount.VerifyCredentials(accessToken);
System.Net.ServicePointManager.Expect100Continue = false;
if (twitterResponse.Result != RequestResult.Unauthorized)
{
try
{
string URL = "https://upload.twitter.com/1/statuses/update_with_media.json";
WebClient client = new WebClient();
client.Credentials = new System.Net.NetworkCredential(uName, pass);
NameValueCollection postData = new NameValueCollection();
postData.Add("status", status);
postData.Add("media[]", Encoding.ASCII.GetString(bytesOfImage));
byte[] b = client.UploadValues(URL, "POST", postData); // 401 error.
}
catch (Exception e)
{
return e.Message;
}
So where is the problem in my code?
You can do this in LINQ to Twitter, using the TweetWithMedia method, like this:
static void TweetWithMediaDemo(TwitterContext twitterCtx)
{
string status = "Testing TweetWithMedia #Linq2Twitter " + DateTime.Now.ToString(CultureInfo.InvariantCulture);
const bool possiblySensitive = false;
const decimal latitude = StatusExtensions.NoCoordinate; //37.78215m;
const decimal longitude = StatusExtensions.NoCoordinate; // -122.40060m;
const bool displayCoordinates = false;
const string replaceThisWithYourImageLocation = #"..\..\images\200xColor_2.png";
var mediaItems =
new List<Media>
{
new Media
{
Data = Utilities.GetFileBytes(replaceThisWithYourImageLocation),
FileName = "200xColor_2.png",
ContentType = MediaContentType.Png
}
};
Status tweet = twitterCtx.TweetWithMedia(
status, possiblySensitive, latitude, longitude,
null, displayCoordinates, mediaItems, null);
Console.WriteLine("Media item sent - Tweet Text: " + tweet.Text);
}
Joe