OAuth 1.0 A Invalid Signature when requesting request token (SalesForce/JIRA Integration) - oauth

I've been trying my darndest to avoid asking a question. I'm recieving an invalid signature error when trying to request a request token from JIRA from within Salesforce. The response I'm getting is:
08:15:24:201 USER_DEBUG [30]|DEBUG|
oauth_problem=signature_invalid
&oauth_signature=RaOPaBPznmS7aDUuJN1pOclcyHWP4uRYYx4j%2F%2BdtKB1SD4l1JcCCys3P3RAa8P7377MwLsIMBtkHfL62jaXr5LV30RndjVUSlCsVl3h47CvinHgDtTMwWGukQJXhOmSsLPvccCqD7qsRmiQnnjSJCwd9YKJEi2cxNcwMqhnceqAwJdzn3RD%2FBBeNpf3G97wqWhJx%2FtyEhQGk35OTu00fhtw%2BIX025STN3iiGNbyANlmoImAGWqAJeqJOPUVva7TR2OAInysL9%2BHmYkQTOcb7a9sn5rRWUqE4Jua6EoVyPgzJnSgZ4pFEIMzU0eTWQqhSFdQwOdnoq5EQU8W%2BPJM8uA%3D%3D
&oauth_signature_base_string=POST%26https%253A%252F%252F.atlassian.net%252Fplugins%252Fservlet%252Foauth%252Frequest-token%26oauth_consumer_key%253D3MVG9KI2HHAq33RwCPH5bNzAHbOgfiicjJ6HjvVfNhGU8aWXGl6ps.vsEzobCPqRXuDcmeV2Baw%25253D%25253D%2526oauth_nonce%253D-6826921263934288296%2526oauth_signature_method%253DRSA-SHA1%2526oauth_timestamp%253D1492182924
&oauth_signature_method=RSA-SHA1
The code I'm using to try to generate the signature is:
// GitHub OAuth Playground: https://github.com/jesperfj/sfdc-oauth-playground/blob/master/OAuth/src/classes/OAuth.cls
// OAuth Bible: http://oauthbible.com/
// Static Values
String consumerKey = '3MVG9KI2HHAq33RwCPH5bNzAHbOgfiicjJ6HjvVfNhGU8aWXGl6ps.vsEzobCPqRXuDcmeV2Baw==';
String endpoint = 'https://<AtlassianSiteName>.atlassian.net/plugins/servlet/oauth/request-token';
// Params
String callbackParam = '&oauth_callback=' + EncodingUtil.urlEncode('https://localhost/', 'UTF-8');
String consumerKeyParam = '&oauth_consumer_key=' + EncodingUtil.urlEncode(consumerKey, 'UTF-8');
String signatureMethodParam = '&oauth_signature_method=RSA-SHA1';
String timestampParam = '&oauth_timestamp=' + String.valueOf(DateTime.now().getTime()/1000);
String nonceParam = '&oauth_nonce=' + String.valueOf(crypto.getRandomLong());
String versionParam = '&oauth_version=1.0';
String paramString = consumerKeyParam + nonceParam + signatureMethodParam + timestampParam + versionParam + callbackParam;
// Get baseString
String baseString = 'POST&' + encodingUtil.urlEncode(endpoint, 'UTF-8') + paramString;
system.debug(baseString);
blob sig = crypto.signWithCertificate('RSA-SHA1', blob.ValueOf(baseString), 'SelfSignedCert_26Oct2015_184625');
String signature = EncodingUtil.urlEncode(EncodingUtil.base64encode(sig), 'UTF-8');
httpRequest req = new httprequest();
http http = new http();
req.setEndPoint(endpoint);
req.setMethod('GET');
req.setBody(paramString +
'&oauth_signature=' + signature);
httpresponse res = http.send(req);
system.debug(res.getBody());
Note: Yes I understand that I probably shouldn't be sharing some of this information but these are my personal test orgs that contain nothing important and I'm not providing the SSL used. You'll notice some comments at the top of my code containing a few urls to my favorite resources that have gotten me this far. These are not the only resources I've used, just the most useful ones.

Related

Bad request 400 when exchanging refresh_token for access_token with box.com

I have successfully done this in the past for other services, however with box.com I get an error and I've tried everything I could think off and that others have suggested here.
I'm using .NET C#;
string postdata = "";
postdata += "client_id=" + HttpUtility.UrlEncode(client_id) + "&";
postdata += "client_secret=" + HttpUtility.UrlEncode(client_secret) + "&";
postdata += "refresh_token=" + HttpUtility.UrlEncode(refreshToken) + "&";
postdata += "redirect_uri=" + HttpUtility.UrlEncode(_redirectUri) + "&";
postdata += "grant_type=refresh_token";
var json = PostResponse(new Uri(#"https://www.box.com/api/oauth2/token"), postdata);
I've tried both with and without urlencoding of the values. Normally urlencoding is not needed in my experience.
I've also tried different order of parameters.
private string PostResponse(Uri uri, string postdata)
{
var bytes = Encoding.UTF8.GetBytes(postdata);
var request = (HttpWebRequest)WebRequest.Create(uri);
request.Method = WebRequestMethods.Http.Post;
request.ContentType = "application/x-www-form-urlencoded; charset=utf-8";
request.ContentLength = bytes.Length;
Stream OutputStream = request.GetRequestStream();
OutputStream.Write(bytes, 0, bytes.Length);
var response = request.GetResponse();
var reader = new StreamReader(response.GetResponseStream());
return reader.ReadToEnd();
}
This code fails with error 400 (bad request). Similar code works fine with for example Google Drive.
Can anyone spot what I'm doing wrong with box.com? Thanks!
Got it working using box_device_id and box_device_name (indirectly part of my problem) plus examining the response in details which showed that a json error message was returned stating that the refresh token had expired. Turns out that Box expires refresh tokens when using them, issuing a new one. This is different from the other cloud drives I've integrated with.

Connecting to Twitter - RestSharp OAuth2

I am attempting to connect to the Twitter API with these instructions
https://dev.twitter.com/docs/auth/application-only-auth
Here is my code:
var baseUrl = "http://api.twitter.com/";
var client = new RestClient(baseUrl);
var request = new RestRequest("/oauth2/token", Method.POST);
var concat = ConfigurationManager.AppSettings["TwitterConsumerKey"] + ":" +
ConfigurationManager.AppSettings["TwitterConsumerSecret"];
string encodeTo64 = concat.EncodeTo64();
request.AddHeader("Authorization", "Basic " + encodeTo64);
request.AddHeader("Content-Type", "application/x-www-form-urlencoded;charset=UTF-8");
request.AddBody("grant_type=client_credentials");
IRestResponse restResponse = client.Execute(request);
EncodeTo64
static public string EncodeTo64(this string toEncode)
{
byte[] toEncodeAsBytes
= System.Text.ASCIIEncoding.ASCII.GetBytes(toEncode);
string returnValue
= System.Convert.ToBase64String(toEncodeAsBytes);
return returnValue;
}
Response.Content is the following
"{\"errors\":[{\"code\":170,\"label\":\"forbidden_missing_parameter\",\"message\":\"Missing required parameter: grant_type\"}]}"
Is this part wrong?
request.AddBody("grant_type=client_credentials");
I have verified that my credentials are correct (I got that error before, but resolved it, so it should be OK).
The instructions on the Twitter page confused me. "The body of the request must be grant_type=client_credentials."
As for Restsharp, it's not AddBody, but AddParameter.
So:
request.AddParameter("grant_type", "client_credentials");

How to attach files via ASP.net application to FogBugz with C#

I have an ASP.net application that allows the users to report bugs and attach files. The bug together with its detail and attachments should be saved in FogBugz.
I have managed to create everything except the file attachment part.
here is my code:
private void NewCaseWithFile()
{
string fbUrl = "https://test.fogbugz.com/api.asp";
string fbToken = logInFogBugz();
string param = "";
param += "cmd=new";
param += "&token=" + fbToken;
param += "&sTags=" + "OnlineService,";
param += "&sTitle=" + "Testing";
param += "&sEvent=" + "This case is being created from Visual Studio";
param += "&nFileCount=" + "1";
param += "&File1=" + "Picture.png";
HttpWebRequest httpWebRequest = (HttpWebRequest)WebRequest.Create(fbUrl + "?" + param);
httpWebRequest.Method = WebRequestMethods.Http.Post;
httpWebRequest.ContentType = "multipart/form-data";
httpWebRequest.Accept = "application/xml";
httpWebRequest.ContentLength = 0;
HttpWebResponse response = (HttpWebResponse)httpWebRequest.GetResponse();
StreamReader streamReader = new StreamReader(response.GetResponseStream());
XDocument doc = XDocument.Load(streamReader);
}
I have tried all instructions under "Editing Cases" but it did not help. In fact I have no idea what are File 1, File 2 and how to send them to FogBugz.
Can anyone help me with this?
Many thanks!
File1 should be specified in the body of your multipart/form-data post (not as a querystring parameter).
You actually have to specify all the bytes in the file.
There's an answer on fogbugz.stackexchange.com as well as a C# FogBugz API wrapper that will handle all the parts for you.
The form parts in the body of your post would look like
--sdfjdsjsdflk SOME BOUNDARY--
Content-Disposition: form-data; name="File1"; filename="foo.jpg"
Content-Transfer-Encoding: base64
Content-Type: image/png
slfkajdflksjflajfdj
sldfjsd;aljfds
these are actual data bytes from the foo.jpg file
slfkjdsfljds
sdflajsdfs
Or you can look at this question which points to an RFC with an example.

Oauth not working CX api

I'm trying to use the oauth for CX exposed api, I followed their documentation, still I'm getting HTTP "BAD REQUEST" error, Here is the code -
String method = "POST";
String code = "";
NameValuePair[] data = {
new NameValuePair("grant_type", "authorization_code"),
new NameValuePair("code", code),
new NameValuePair("redirect_uri",URLEncoder.encode(CALLBACK_URL, "UTF-8"))
};
String secret = CONSUMER_KEY+":"+CONSUMER_SECRET;
String encodedSecret = Base64.encodeBase64String(secret.getBytes("UTF-8"));
org.apache.commons.httpclient.HttpClient httpClient = new org.apache.commons.httpclient.HttpClient();
PostMethod httpMethod = new PostMethod(ACCESS_TOKEN_ENDPOINT_URL);
httpMethod.addRequestHeader("Authorization","Basic "+encodedSecret);
httpMethod.setRequestBody(data);
System.out.println("HTTP call -- " + method + " " + ACCESS_TOKEN_ENDPOINT_URL);
httpClient.executeMethod(httpMethod);
Thanks,
Hemant
I've tested the following slight modification of your code and it works. You might double check that
Your key has been approved (this shouldn't be the problem given the
error you are seeing).
You are using the correct ACCESS_TOKEN_ENDPOINT_URL
Try having the redirect_uri be the same for both the auth_code response and the token request
String method = "POST";
String authCode = "[AUTH-CODE-HERE]";
String CONSUMER_KEY="[YOUR-KEY-HERE]";
String CONSUMER_SECRET="[YOUR-SECRET-HERE]";
String ACCESS_TOKEN_ENDPOINT_URL="https://api.cx.com/1/oauth/token";
String REDIRECT_URI="[YOUR-REDIRECT-HERE]";
NameValuePair[] data = {
new NameValuePair("grant_type", "authorization_code"),
new NameValuePair("code", authCode),
new NameValuePair("redirect_uri", REDIRECT_URI)
};
String secret = CONSUMER_KEY+":"+CONSUMER_SECRET;
String encodedSecret = Base64.encodeBase64String(secret.getBytes("UTF-8"));
PostMethod httpMethod = new PostMethod(ACCESS_TOKEN_ENDPOINT_URL);
httpMethod.addRequestHeader("Authorization","Basic "+encodedSecret);
httpMethod.setRequestBody(data);
System.out.println("HTTP call -- " + method + " " + ACCESS_TOKEN_ENDPOINT_URL);
int responseCode = httpClient.executeMethod(httpMethod);
System.out.println(responseCode);
System.out.println(httpMethod.getResponseBodyAsString());
If you are still running into issues, can you post the result of the following line: System.out.println(httpMethod.getResponseBodyAsString());
The CX developer API has been discontinued.
Sorry for the inconvenience.

How to remove an attachment from Jira 4.4 using Http

I have been looking for a way to remove an attachment from Jira using the SOAP Api, but it seems that this is not possible natively, and I would prefer not having to implement a new plugin for Jira, as suggested in the accepted answer to this question, or recompiling the existing plugin to support this as mentioned here.
This answer to the abovementioned question seems to do exactly what I want, but alas, I can't get i to work. The response i get is an error stating that:
XSRF Security Token Missing
JIRA could not complete this action due to a missing form token.
You may have cleared your browser cookies, which could have resulted in the expiry of your current form token. A new form token has been reissued.
As I am using Asp.Net MVC C#, I have used the code from the answer, as is, with only the server url adjusted, as well as with different credentials (a Jira user) and the username/password passed through as request parameters using:
os_username=jirausername&os_password=xxxxxxx
The code I am currently using is as follows:
public void RemoveAttachment(string issueid, string attachmentid)
{
using (System.Net.WebClient client = new System.Net.WebClient())
{
//Compute jira server base url from WS url
string baseUrl = _service.Url.Substring(0, _service.Url.IndexOf("/rpc/"));
//Compute complete attachment url
string attachmenturl = baseUrl + "/secure/DeleteAttachment.jspa?id=" +
issueid + "&deleteAttachmentId=" + attachmentid;
client.Credentials = new System.Net.NetworkCredential("jirausername", "xxxxxxx");
string response = client.DownloadString(attachmenturl);
}
}
I ended up using a method that first requests the deletion confirmation form, then extracts a required token from the form, and finally posts something equivalent to the form content in order to delete the attachment. Code below.
public void RemoveAttachment(string issueid, string attachmentid)
{
//Compute jira server base url from WS url
string baseUrl = _service.Url.Substring(0, _service.Url.IndexOf("/rpc/"));
//Compute complete attachment deletion confirm url
string confirmurl = baseUrl + "/secure/DeleteAttachment!default.jspa?id=" +
issueid + "&deleteAttachmentId=" + attachmentid + "&os_username=jirauser&os_password=xxxxxx";
//Create a cookie container to maintain the xsrf security token cookie.
CookieContainer jiracontainer = new CookieContainer();
//Create a get request for the page containing the delete confirmation.
HttpWebRequest confirmrequest = (HttpWebRequest)WebRequest.Create(confirmurl);
confirmrequest.Credentials = System.Net.CredentialCache.DefaultCredentials;
confirmrequest.CookieContainer = jiracontainer;
//Get the response and the responsestream.
WebResponse confirmdeleteresponse = confirmrequest.GetResponse();
Stream ReceiveStream = confirmdeleteresponse.GetResponseStream();
// Open the stream using a StreamReader for easy access.
StreamReader confirmreader = new StreamReader(ReceiveStream);
// Read the content.
string confirmresponse = confirmreader.ReadToEnd();
//Create a regex to extract the atl/xsrf token from a hidden field. (Might be nicer to read it from a cookie, which should also be possible).
Regex atl_token_matcher = new Regex("<input[^>]*id=\"atl_token\"[^>]*value=\"(?<token>\\S+)\"[^>]*>", RegexOptions.Singleline);
Match token_match = atl_token_matcher.Match(confirmresponse);
if (token_match.Success)
{
//If we found the token get the value.
string token = token_match.Groups["token"].Value;
//Compute attachment delete url.
string deleteurl = baseUrl + "/secure/DeleteAttachment.jspa";
//Construct form data.
string postdata = "atl_token=" + HttpContext.Current.Server.UrlEncode(token) + "&id=" + issueid + "&deleteAttachmentId=" + attachmentid + "&Delete=Delete&os_username=jirauser&os_password=xxxxxx";
//Create a post request for the deletion page.
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(deleteurl);
request.KeepAlive = false;
request.CookieContainer = jiracontainer; // Remember to set the cookiecontainer.
request.ProtocolVersion = HttpVersion.Version10;
request.Method = "POST";
//Turn our request string into a byte stream
byte[] postBytes = Encoding.ASCII.GetBytes(postdata);
//Make sure you specify the proper type.
request.ContentType = "application/x-www-form-urlencoded";
request.ContentLength = postBytes.Length;
Stream requestStream = request.GetRequestStream();
//Send the post.
requestStream.Write(postBytes, 0, postBytes.Length);
requestStream.Close();
//Get the response.
WebResponse deleteresponse = request.GetResponse();
// Open the responsestream using a StreamReader for easy access.
StreamReader deleteresponsereader = new StreamReader(deleteresponse.GetResponseStream());
// Read the content.
string deleteresponsecontent = deleteresponsereader.ReadToEnd();
// do whatever validation/reporting with the response...
}
else
{
//We couldn't find the atl_token. Throw an error or something...
}
}
Edit:
Same thing works for removing comments. Replace 'attachment' with 'comment' and 'deleteAttachmentId' with 'commentId' and you should be good to go.

Resources