NetSuite OAUTH1 POST request works in Postman but fails in Restsharp - c#

I'm using RestSharp in .NET 6 to execute a POST request to NetSuite in a c# console application.
I'm using Token Based Authentication and OAuth1
When I execute the request using the same credentials (consumer key, consumer secret, access token, access token secret and realm) in C#, for GET requests, it works. I'm able to authenticate and get a response.
When I try a POST in C#, I get a 401, 'Unauthorized' with an error message stating that the token was rejected. The same POST request, with the same auth values and URL works in Postman however.
I feel like Postman is doing something to the authentication header in a different way to Restsharp, but that still doesn't explain why GET requests are working with RestSharp
public string ExecuteRequest(string url, int httpMethod, string body = "")
{
var client = new RestClient(url);
client.Authenticator = GetOAuth1Authenticator();
Method method = (Method)httpMethod;
var request = new RestRequest(url, method);
client.AddDefaultHeader("Accept", "*/*");
client.Options.MaxTimeout = -1;
request.AddHeader("Cookie", "NS_ROUTING_VERSION=LAGGING");
request.AddHeader("ContentType", "application/json");
if (string.IsNullOrEmpty(body) == false)
{
request.AddParameter("application/json", body, ParameterType.RequestBody);
}
var response = client.Execute(request);
if (response.IsSuccessful == false)
{
throw new HttpRequestException($"ERROR: {response.ErrorMessage} - RESPONSE CONTENT: {response.Content}");
}
if (response.Content == null)
{
throw new NullReferenceException("API RESPONSE IS NULL");
}
return response.Content;
}
private OAuth1Authenticator GetOAuth1Authenticator()
{
OAuth1Authenticator authenticator = OAuth1Authenticator.ForAccessToken(consumerKey: Credential.consumer_key,
consumerSecret: Credential.consumer_secret,
token: Credential.access_token, tokenSecret: Credential.access_token_secret, signatureMethod: RestSharp.Authenticators.OAuth.OAuthSignatureMethod.HmacSha256);
authenticator.Realm = Credential.accountId;
return authenticator;
}
For anyone who knows SuiteTalk REST API for NetSuite, I'm trying to do a POST request to transform a PO into a VendorBill, using this endpoint:
[netsuite host url]/purchaseOrder/{id}/!transform/vendorBill

try
var client = new RestClient(urlString);
var request = new RestRequest(Method.POST);
btw, check your oauth method, when you are generating the signature you must specify the method you are using ("POST")

Related

Postman Generate Valid Token, C# Web Client Token from Newtonsoft JToken doesnt work

I'm trying to get the token from OAuth 2.0 Server and then pass to REST API with Authorization header.
It gets the token from the server and upon API call, it says the Token is Invalid.
Using token generated from Postman, and calling rest API from postman by that token works fine. Even if I try to paste the token generated by C# Client in Postman, Postman Rest API informed token is invalid.
I'm using .NET Core 2.1 and generating token like this.
var values = new Dictionary<string, string> {
{ "resource", baseUrl.Value.Replace("/commerce", "").Trim() },
{ "client_id", Startup.Configuration["clientId"] },
{ "grant_type", "client_credentials" },
{ "client_secret", Startup.Configuration["clientSecret"] }
};
var content = new FormUrlEncodedContent(values);
HttpClient client = new HttpClient();
var responseString = await client.PostAsync(tenantUrl.Value + "/oauth2/token", content);
string responseBody = await responseString.Content.ReadAsStringAsync();
JObject resJson = JObject.Parse(responseBody);
string token = resJson["access_token"].ToString();
Passing the Token to REST API by the following code.
When I insert token generated from POSTMAN, the REST API returns fine. There is some problem in C# Web Client Token Generation.
client = new HttpClient();
var tt = new StringContent(requestBody, Encoding.UTF8, "application/json");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var ounHeader = request.Headers.Where(m => m.Key == "OUN").FirstOrDefault();
client.DefaultRequestHeaders.Add(ounHeader.Key, ounHeader.Value.First());
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
var rsActionResponse = await client.PostAsync(oDataRequest, tt);
responseBody = await rsActionResponse.Content.ReadAsStringAsync();
JObject rsActionResponseJSON = JObject.Parse(responseBody);
Token from Postman (Working) :
eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6ImlCakwxUmNxemhpeTRmcHhJeGRacW9oTTJZayIsImtpZCI6ImlCakwxUmNxemhpeTRmcHhJeGRacW9oTTJZayJ9.eyJhdWQiOiJodHRwczovL3ZzaWZhc2hpb251cDgyNWM4Y2Y0OTQ3YzBkOTZmcmV0LmNsb3VkYXguZHluYW1pY3MuY29tLyIsImlzcyI6Imh0dHBzOi8vc3RzLndpbmRvd3MubmV0Lzc1NjY4ZjM2LTY1ZDMtNGQ5MC1hOTkwLTRiYjhlYzE2NzI4Zi8iLCJpYXQiOjE1MjU0MjUyMjUsIm5iZiI6MTUyNTQyNTIyNSwiZXhwIjoxNTI1NDI5MTI1LCJhaW8iOiJZMmRnWUZBNEUvTXRraStSZWZISHZZeHE1NktlQVFBPSIsImFwcGlkIjoiNDZmMTRlMTgtNDA5MS00ODRiLTk0NzYtNjM3ODc4NDE5MWQxIiwiYXBwaWRhY3IiOiIxIiwiaWRwIjoiaHR0cHM6Ly9zdHMud2luZG93cy5uZXQvNzU2NjhmMzYtNjVkMy00ZDkwLWE5OTAtNGJiOGVjMTY3MjhmLyIsIm9pZCI6ImJjODVmMGYzLTc5NjgtNDk4Ni1hMDMzLTViZTBjZDFiN2Y1NiIsInN1YiI6ImJjODVmMGYzLTc5NjgtNDk4Ni1hMDMzLTViZTBjZDFiN2Y1NiIsInRpZCI6Ijc1NjY4ZjM2LTY1ZDMtNGQ5MC1hOTkwLTRiYjhlYzE2NzI4ZiIsInV0aSI6InVnVkljaVNNaEV5YUhoaWpERFFHQUEiLCJ2ZXIiOiIxLjAifQ.fWJiUaOmPuRD21EcuGnUBWCNYl0TaUZ7OxIFQXHYmMvnivHiys5j9UjL3ZBRSZAVzrjrBS-v-0xyyzT_502NEkM0H77vnaxXFB2lrZz1GRcXr5oFSW4gfDTvPinByLr5LBglxZG6_PCP4oqChKQgigxR7xBjok1XXQOD2_h-gYZbmnDNexjzkxZAl4kqTCfSfoRJxUZxX1pgD5PRAAkx1eanc1jiJ4KQA6kvnSDL0PyGGmmQe36RrMKH5bclH3sMLO3wilgvLMp3ekKDj51P2emW9dhDx2BrblowpCcLGe3Q6PikuZrYOkx44WqJKAS6QovJwijxCEq9XKAzDEZbig
Token from C# Web Client (Not Working)
eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6ImlCakwxUmNxemhpeTRmcHhJeGRacW9oTTJZayIsImtpZCI6ImlCakwxUmNxemhpeTRmcHhJeGRacW9oTTJZayJ9.eyJhdWQiOiJodHRwczovL3ZzaWZhc2hpb251cDgyNWM4Y2Y0OTQ3YzBkOTZmcmV0LmNsb3VkYXguZHluYW1pY3MuY29tIiwiaXNzIjoiaHR0cHM6Ly9zdHMud2luZG93cy5uZXQvNzU2NjhmMzYtNjVkMy00ZDkwLWE5OTAtNGJiOGVjMTY3MjhmLyIsImlhdCI6MTUyNTQyNTA2MiwibmJmIjoxNTI1NDI1MDYyLCJleHAiOjE1MjU0Mjg5NjIsImFpbyI6IlkyZGdZUGhvZjdKaHI5YU9xZjBxVmJyTTk0NitBZ0E9IiwiYXBwaWQiOiI0NmYxNGUxOC00MDkxLTQ4NGItOTQ3Ni02Mzc4Nzg0MTkxZDEiLCJhcHBpZGFjciI6IjEiLCJpZHAiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC83NTY2OGYzNi02NWQzLTRkOTAtYTk5MC00YmI4ZWMxNjcyOGYvIiwib2lkIjoiYmM4NWYwZjMtNzk2OC00OTg2LWEwMzMtNWJlMGNkMWI3ZjU2Iiwic3ViIjoiYmM4NWYwZjMtNzk2OC00OTg2LWEwMzMtNWJlMGNkMWI3ZjU2IiwidGlkIjoiNzU2NjhmMzYtNjVkMy00ZDkwLWE5OTAtNGJiOGVjMTY3MjhmIiwidXRpIjoidWh1T3dmaE41RS1uOTZWSXVWc0VBQSIsInZlciI6IjEuMCJ9.Bulbv3HZyufQevjMPI-OU5_0NCrtFXPU9PIxXHriWLg_Mj_uUtoFHVslUtNH4FTwMEq2lTheE87N5jDkTra3Z-aTOQhsj3sz-6wqA4HrDGpbPyHaGscFfHkitpUyzV4_HfLaVA4vWAHbwKEIqs1gaVp-81m3oMka0OmDh1Jjgg-Lvcr-TMOkdP1qsgSdcRmqVWwmjYTp7HSPS997poC54md_Bdx7hFRwEA7WNmCdSCLZ44izgFHb3ou47r3agXprERYDBo6Vi6ofSp4zAsvYdsxoFrM6LiOwWSKbqilYotgDKjUQpA7u41iwy6fjgV1wBerOJUBWCWN8w3Vs4cmhXA
Fixed:
Token was received from Url : www.xyz.com
URL was registered as www.xyz.com/
Need to call www.xyz.com/ to get the valid token and pass to other API methods.
There is difference between www.xyz.com/ and www.xyz.com in OAuth registered.

Docusign /oauth/token endpoint return page instead of json with bearer C#

I went through the OAuth2 proccess in DocuSign API, I follow all the steps using official docs, but when I tried to perform the request in order to get the the AccessToken I received an HTML as response, indicating something like "DocuSign is temporarily unavailable. Please try again momentarily." Although the http response is 200(OK), The weird stuff is when I test with the same values on Postman I get the correct response.
This is my code
public static DocuSignBearerToken GetBearerToken(string AccessCode, bool RefreshToken = false)
{
string AuthHeader = string.Format("{0}:{1}", DocuSignConfig.IntegratorKey, DocuSignConfig.SecretKey);
var client = new RestClient("http://account-d.docusign.com");
client.Authenticator = new HttpBasicAuthenticator(DocuSignConfig.IntegratorKey, DocuSignConfig.SecretKey);
var request = new RestRequest("/oauth/token", Method.POST);
request.AddHeader("content-type", "application/x-www-form-urlencoded");
request.AddHeader("authorization", "Basic " + Base64Encode(AuthHeader));
if(!RefreshToken)
request.AddParameter("application/x-www-form-urlencoded", string.Format("grant_type=authorization_code&code={0}", AccessCode), ParameterType.RequestBody);
else
request.AddParameter("application/x-www-form-urlencoded", string.Format("grant_type=refresh_token&refresh_token={0}", AccessCode), ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
var responseString = response.Content;
DocuSignBearerToken Result = JsonConvert.DeserializeObject<DocuSignBearerToken>(responseString);
return Result;
}
Ok, this is awkward, reading the DocuSign docs they never specify if the authorization URL is http or https I assumed it was http, postman is smart enough to determine http or https when performs the request, my code doesn't, simply changing the Authorization URL from http:// to https:// solves the error.
If your tests using Postman work, then there is a problem with your code.
We've all been there, including me!
In these cases, I send my request to requestb.in to see what I'm really sending to the server. You'll find something is different from what you're sending via Postman.

Azure ACS get token using RestSharp 415 error

The following is the code sample provided by msdn for obtaining an SWT token from azure ACS (Access Control Service):
private static string GetTokenFromACS(string scope)
{
string wrapPassword = pwd;
string wrapUsername = uid;
// request a token from ACS
WebClient client = new WebClient();
client.BaseAddress = string.Format(
"https://{0}.{1}", serviceNamespace, acsHostUrl);
NameValueCollection values = new NameValueCollection();
values.Add("wrap_name", wrapUsername);
values.Add("wrap_password", wrapPassword);
values.Add("wrap_scope", scope);
byte[] responseBytes = client.UploadValues("WRAPv0.9/", "POST", values);
string response = Encoding.UTF8.GetString(responseBytes);
Console.WriteLine("\nreceived token from ACS: {0}\n", response);
return HttpUtility.UrlDecode(
response
.Split('&')
.Single(value => value.StartsWith("wrap_access_token=", StringComparison.OrdinalIgnoreCase))
.Split('=')[1]);
}
I am trying to replicate the code using RestSharp:
var request = new RestRequest("WRAPv0.9", Method.POST);
request.AddHeader("Content-Type", "application/x-www-form-urlencoded");
request.AddParameter("wrap_name", uid, ParameterType.RequestBody);
request.AddParameter("wrap_password", pwd, ParameterType.RequestBody);
request.AddParameter("wrap_scope", realm, ParameterType.RequestBody);
RestClient client = new RestClient(
string.Format(#"https://{0}.{1}", serviceNamespace, acsHostUrl));
client.ExecuteAsync(request, Callback);
I tried other variations of the above code but to no avail. I keep recieving a 415 error stating that:
415 Unsupported Media Type T8000 Content-Type 'text/plain' is not
supported. The request content type must be
'application/x-www-form-urlencoded'.
I am not a Fiddler expert but with my limited experience with it I was not able to inspect my outgoing http request because it is encrypted.
I would appreciate advice on solving the issue.
You can try to leave out the AddHeader method call and instead set the Content-Type as the first AddParameter.
The issue is described here.

Requesting OAuth request token from Twitter API with RestSharp returns 404

I'm trying to retrieve a Twitter OAuth request token using RestSharp, but no matter what I try the API seems to respond with a 404. Here's my method code:
public void GetRequestToken()
{
var client = new RestClient("https://api.twitter.com/1");
var authenticator = OAuth1Authenticator.ForRequestToken(_consumerKey, _consumerSecret);
authenticator.ParameterHandling = OAuthParameterHandling.HttpAuthorizationHeader;
client.Authenticator = authenticator;
var request = new RestRequest("/oauth/request_token", Method.POST);
request.AddHeader("oauth_callback", "http%3A%2F%2Fmarkashleybell.com");
var response = client.Execute(request);
var qs = HttpUtility.ParseQueryString(response.Content);
_token = qs["oauth_token"];
_tokenSecret = qs["oauth_token_secret"];
}
_consumerKey and _consumerSecret are retrieved from Web.config settings, and I've double-checked that they are correct. _token and _tokenSecret are private member variables of the class containing this method.
The API url being requested is correct. The request seems to be passing all the correct headers according to Twitter's documentation.
I've tried to use the APIGee console to make the same API call and see what I'm doing wrong, but even that returns 404.
What am I doing wrong here?
DOH. It turns out the Twitter API URLs for OAuth operations are in fact slightly different to those for all other Twitter API calls and omit the API version segment, so this was a genuine 404.
There was also a tidier way to supply the oauth_callback parameter built into the RestSharp authenticator constructor—for reference, the code below works perfectly:
public void GetRequestToken()
{
var client = new RestClient("https://api.twitter.com"); // Note NO /1
client.Authenticator = OAuth1Authenticator.ForRequestToken(
_consumerKey,
_consumerSecret,
"http://markashleybell.com" // Value for the oauth_callback parameter
);
var request = new RestRequest("/oauth/request_token", Method.POST);
var response = client.Execute(request);
var qs = HttpUtility.ParseQueryString(response.Content);
_token = qs["oauth_token"];
_tokenSecret = qs["oauth_token_secret"];
}

Getting signature_invalid calling oauth/request_token for Etsy's API using RestSharp

I'm trying to use RestSharp to access Etsy's API. Here's the code I'm using attempting to get an OAuth access token:
var authenticator = OAuth1Authenticator.ForRequestToken(
ConfigurationManager.AppSettings["ApiKey"],
ConfigurationManager.AppSettings["ApiSecret"]);
// same result with or without this next line:
// authenticator.ParameterHandling = OAuthParameterHandling.UrlOrPostParameters;
this.Client.Authenticator = authenticator;
var request = new RestRequest("oauth/request_token")
.AddParameter("scope", "listings_r");
var response = this.Client.Execute(request);
Etsy tells me that the signature is invalid. Interestingly enough, when I enter the timestamp and nonce values generated by the request into this OAuth signature validation tool, the signatures don't match. Moreover, the URL generated by the tool works with Etsy where the one generated by RestSharp doesn't. Is there something I'm doing wrong or something else I need to configure with RestSharp?
Note: I'm using the version of RestSharp provided by their Nuget package, which at the time of this posting is 102.5.
I finally was able to connect to the Etsy API with RestSharp using OAuth. Here is my code -- I hope it works for you...
RestClient mRestClient = new RestClient();
//mRestClient.BaseUrl = API_PRODUCTION_URL;
mRestClient.BaseUrl = API_SANDBOX_URL;
mRestClient.Authenticator = OAuth1Authenticator.ForRequestToken(API_KEY,
API_SHAREDSECRET,
"oob");
RestRequest request = new RestRequest("oauth/request_token", Method.POST);
request.AddParameter("scope",
"shops_rw transactions_r transactions_w listings_r listings_w listings_d");
RestResponse response = mRestClient.Execute(request);
if (response.StatusCode != System.Net.HttpStatusCode.OK)
return false;
NameValueCollection queryString = System.Web.HttpUtility.ParseQueryString(response.Content);
string oauth_token_secret = queryString["oauth_token_secret"];
string oauth_token = queryString["oauth_token"];
string url = queryString["login_url"];
System.Diagnostics.Process.Start(url);
// BREAKPOINT HERE
string oauth_token_verifier = String.Empty; // get from URL
request = new RestRequest("oauth/access_token");
mRestClient.Authenticator = OAuth1Authenticator.ForAccessToken(API_KEY,
API_SHAREDSECRET,
oauth_token,
oauth_token_secret,
oauth_token_verifier);
response = mRestClient.Execute(request);
if (response.StatusCode != System.Net.HttpStatusCode.OK)
return false;
queryString = System.Web.HttpUtility.ParseQueryString(response.Content);
string user_oauth_token = queryString["oauth_token"];
string user_oauth_token_secret = queryString["oauth_token_secret"];
The user_oauth_token and user_oauth_token_secret are the user's access token and access token secret -- these are valid for the user until the user revokes access.
I hope this code helps!

Categories