I have code to allow us to use the NetSuite REST API using OAuth 1.0. Everything works fine, except one call. When trying to do /salesorder/{id}/!transform/itemFulfillment It fails with 401. All other calls work fine. When I execute the same call from Postman it works fine too. What am I missing?
Here is my Code:
private static async Task CreateItemFulFillmentsAsync(NetSuiteJob job, int id, Item item)
{
RestRequest request = new RestRequest($"{job.RecordUrl}/salesorder/{id}/!transform/itemFulfillment", Method.Post);
request.AddBody(item);
RestHelper restHelper = new RestHelper();
RestResponse response = await restHelper.ExecuteRestRequest(request, job);
if (response == null || !response.IsSuccessful)
{
throw new Exception($"Failed to create the Item Fulfillment for the Sales Order: {id}.\r\n" + response.Content);
}
}
And the Helper Class:
public async Task<RestResponse> ExecuteRestRequest(RestRequest request, NetSuiteJob job)
{
RestClient client = new RestClient(job.BaseUrl) { Authenticator = GetOAuth1Authenticator(job) };
RestResponse response = await client.ExecuteAsync(request);
return response;
}
private OAuth1Authenticator GetOAuth1Authenticator(NetSuiteJob job)
{
OAuth1Authenticator oAuth1 = OAuth1Authenticator.ForAccessToken(
consumerKey: job.ConsumerKey,
consumerSecret: job.ConsumerSecret,
token: job.TokenId,
tokenSecret: job.TokenSecret,
OAuthSignatureMethod.HmacSha256);
oAuth1.Realm = job.Realm;
return oAuth1;
}
The results are:
{"type":"https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.2",
"title":"Unauthorized","status":401,
"o:errorDetails":[{"detail":"Invalid login attempt. For more details,
see the Login Audit Trail in the NetSuite UI at Setup > Users/Roles
> User Management > View Login Audit Trail.","o:errorCode":"INVALID_LOGIN"}]}
In NetSuite's Login Audit Trail, this call is logged as a failure and Role is blank, but the other calls using different action shows the Role like it should. The working routines use the same helper class but are doing it with a different URL and Body. I've verified the content being passed matches what I did manually in Postman too.
Related
I'm working on a multilanguage project for accademic purpose. I've written a simple Python Client that make requests to an API server written in ASP.NET. The server retrives spotify info about users. The server interacts with a DB filled by a Golang server that only makes scraping on API's exposed from Spotify. I'm aware that it's a misuse and there are better solutions
Clearly, Golang server, in order to make requests to Spotify API's, needs to know the access token returned from spotify Authorization Code Flow. Overlooking about spotify token expire time, the idea is: after user authentication through Identity module of ASP.NET server (using JWT token), associate the access token obtained calling https://accounts.spotify.com/api/token to user's informations. So, i expose an API in ASP.NET server like this
[AllowAnonymous]
[HttpPost("token")]
public async Task<ContentResult> getTokenAsync(string? code = null)
{
//to retrive information about who is the user that making call -> need later for associate spotifytoken
string accessToken = Request.Headers[HeaderNames.Authorization].ToString().Replace("Bearer ", "");
JwtSecurityTokenHandler t = new JwtSecurityTokenHandler();
var token = t.ReadJwtToken(accessToken);
var user = _userManager.FindByIdAsync(token.Subject).Result;
string s = "https://accounts.spotify.com/api/token";
if (code == null)
{
var qb = new QueryBuilder();
qb.Add("response_type", "code");
qb.Add("client_id", _config["SpotiSetting:clientId"]);
qb.Add("scope", "user-read-private user-read-email user-library-read");
qb.Add("redirect_uri", _config["SpotiSetting:redirectUser"]);
qb.Add("show_dialog", "true");
return new ContentResult
{
ContentType = "text/html",
Content = "https://accounts.spotify.com/authorize/" + qb.ToQueryString().ToString()
//Content = JsonConvert.SerializeObject(user.Result)
};
} else
{
//if i'm here, api is the callback designed for spotify
var qb = new QueryBuilder();
qb.Add("grant_type", "authorization_code");
qb.Add("code", code);
qb.Add("redirect_uri", "https://localhost:44345/spotify/token");
var client = new HttpClient();
var req = new HttpRequestMessage(HttpMethod.Post, s);
req.Content = new FormUrlEncodedContent(qb);
req.Headers.Authorization = new AuthenticationHeaderValue("Basic", "here_my_secret_encoded_CLIENTID:CLIENT_SECRET");
var response = await client.SendAsync(req);
var result = response.Content.ReadAsStringAsync().Result;
AccessToken json = JsonConvert.DeserializeObject<AccessToken>(result);
user.spotifyInformation.authToken = code;
user.spotifyInformation.accessToken = json;
var res = _userManager.UpdateAsync(user);
if (res.IsCompletedSuccessfully)
{
return Content("ok");
}
else
{
Content("Problem");
}
} return Content("");
}
The problem is that the second time that API is invoked, it's spotify that is sending the first authorization token (needed to request access_token), so I lost user information retrived in the first request. Should be better write two distinct API and separate callback from user request?
It's my first question here, so please to have mercy
I am connecting to a 3rd party API that requires calls to retrieve an authorization code and a token.
The API requires a redirect URL, which when returned, contains the authorization code attached to the query string.
So, on my side so I set up this controller below that will read the queryString.
Then, my app needs to fire a POST request to the API to get the token, and you will see me calling the controller the contains the POST request.
When I click my button to connect to the API, in my browser window, I do see that I am redirected to the GetGeologicalPeriod() controller below because I see the message:
you have reached the GeologicalPeriod endpoint.
And I do see the authorization code in the query string.
But I don't see anything else, no errors, nothing.
I was expecting to see the results retuned from the call to GetGeologicalPeriodToken, or at least an error that it failed, but I am getting nothing...not even in the browser console window.
So I am kind of at a loss as to what is actually happening.
Since this is on a development server, I can't step through it locally in Visual Studio.
Is there anyway to show messages or write to console so I can see what's going on?
Thanks!
[ApiController]
public class GeologicalPeriodController : ControllerBase
{
[HttpGet]
public ActionResult<String> GetGeologicalPeriod()
{
string getTest = "you have reached the GeologicalPeriod endpoint.";
var queryString = Request.Query["code"];
var postResult = GetGeologicalPeriodToken(queryString);
return postResult;
}
[HttpPost("AuthRequest")]
public ActionResult<String> GetGeologicalPeriodToken(string authCode)
{
string authToken = authCode;
string authString = "admin";
var queryString = Request.Query["code"];
var client = new RestClient("https://geologicalPeriod.geo.gov/oauth/token?accessType=professor&code=" + authCode + "&redirect_uri=https://jamaica.round.astro.edu/api/geologicalPeriodauth/geologicalPeriod/AuthRequest");
var request = new RestRequest(Method.POST);
request.AddHeader("Authorization", authString);
IRestResponse response = client.Execute(request);
var apiResponse = response;
return apiResponse.Content.ToString();
}
I recently implemented custom authentication with Azure Mobile App - All the server side works fine and also my web application which is using that mobile app service is working fine. I tested the server-side in details with POSTMAN and with different scenarios, everything works fine until I try to LoginAsync on Xamarin.
When I pass email and password in POSTMAN, I get the following response as a clear indication that it is working
but when I send a request from my app using LoginAsync I get the following error.
Cannot access child value on Newtonsoft.Json.Linq.JValue
My code to send request is fairly simple as following
public async Task<bool> Authenticate()
{
string username = "todo#gmail.com";
string password = "todo";
string message = string.Empty;
var success = false;
var credentials = new JObject
{
["email"] = username,
["password"] = password
};
try
{
MobileServiceUser user = await client.LoginAsync("CustomAuth", credentials);
if (user != null)
{
success = true;
CreateAndShowDialog("OK", "Auth");
}
}
catch (Exception ex)
{
CreateAndShowDialog(ex, "Auth Error");
}
return success;
}
where I am calling it as follows
private MobileServiceClient client;
client = new MobileServiceClient(applicationURL);
await Authenticate();
Any idea why I am getting Cannot access child value on Newtonsoft.Json.Linq.JValue error?
Cheers
EDIT POST
As a workaround, I am temporarily using InvokeApiAsync with JObject.FromObject instead of LoginAsync
await client.InvokeApiAsync("/.auth/login/CustomAuth", JObject.FromObject(credentials), HttpMethod.Post, null);
I am still not sure why LoginAsync does not work - Until I find a solution I will keep using InvokdeApiAsync as a workaround
AFAIK, your initialization for credentials is correct. For the below error:
Cannot access child value on Newtonsoft.Json.Linq.JValue
I checked your testing result via POSTMAN and found that you did not return userId to your client. The essential properties returned to your client would look like as follows:
{
"authenticationToken":"***",
"user":{
"userId":"***"
}
}
When using MobileServiceClient.LoginAsync, the client SDK would internally invoke LoginAsync() method under MobileServiceAuthentication.cs as follows:
JToken authToken = JToken.Parse(response);
// Get the Mobile Services auth token and user data
this.Client.CurrentUser = new MobileServiceUser((string)authToken["user"]["userId"]);
this.Client.CurrentUser.MobileServiceAuthenticationToken = (string)authToken[LoginAsyncAuthenticationTokenKey];
You would find that it would try to extract the userId property under user to construct the MobileServiceUser instance and assign to MobileServiceClient.CurrentUser.
I am trying to port an application from an azure mobile service to an azure web app. (the mobile service was working). I have added microsoft account authentication to the web-app, and the web app api has a MobileAppController attribute. I have a Universal windows app front end that calls the api. The app first checks if a player is in the database, if not I get a not found response. If I call the method using the following code with the MobileServiceClient I get an exception.
private async Task<HttpResponseMessage> GetAZMAsyncP(string apiext, IDictionary<string,string> param )
{
string myuri = String.Format("{0}{1}", urlbase, apiext);
//client is the MobileServiceClient that is correctly logged in
//I do not get response which is 404 not found, I get an exception "The request could not be completed, Not Found"
var response = await client.InvokeApiAsync(myuri, System.Net.Http.HttpMethod.Get, param);
return response;
}
If I call the api from an httpclient and add my own headers, which the mobile client is supposed to do for me, then I get the response as requested. Here is the code:
private async static Task<HttpResponseMessage> GetAZAsync(string apiext)
{
string completeUrl = String.Format("{0}{1}", urlbase, apiext);
// Call out to AZ
using (var http = new HttpClient())
{
// http.BaseAddress = new Uri(completeUrl);
HttpRequestMessage rq = new HttpRequestMessage()
{
RequestUri = new Uri(completeUrl),
Method = HttpMethod.Get
};
addauthheader(rq);
var response = await http.SendAsync(rq);
return response;
}
}
private static void addauthheader(HttpRequestMessage rq)
{
MobileServiceUser user = App.client.CurrentUser;
rq.Headers.Add("X-ZUMO-FEATURES", "AT,QS");
rq.Headers.Add("X-ZUMO-INSTALLATION-ID",
"ff90f37e-0c03-4c52-a343-af711752e383");
rq.Headers.Add("X-ZUMO-AUTH", user.MobileServiceAuthenticationToken);
rq.Headers.Add("Accept", "application/json");
rq.Headers.Add("User-Agent", "ZUMO/2.1");
rq.Headers.Add("User-Agent",
"(lang = Managed; os = Windows Store; os_version = --; arch = X86; version = 2.1.40707.0)");
rq.Headers.Add("X-ZUMO-VERSION",
"ZUMO/2.1(lang = Managed; os = Windows Store; os_version = --; arch = X86; version = 2.1.40707.0)");
rq.Headers.Add("ZUMO-API-VERSION", "2.0.0");
}
You can try this out as it is live (and buggy).
https://gamenote2.azurewebsites.net/api/Players?displayname=Paul Goldschmidt&teamid=arizona-diamondbacks
Should give you a 404,
https://gamenote2.azurewebsites.net/api/Players?displayname=Chase Utley&teamid=los-angeles-dodgers
should give you a chase utley object. (YOu will be asked to log into a Microsoft Account).
So my questions: 1. Can I fix the mobileclient call to get a response instead of an execption
2. Is there any good reason for me to be spending so much time on this.
If you examine the exception, you will note that the status code is in there - it's just in a property that is not serialized. Just surround your InvokeApiAsync() call with a try/catch and test for the StatusCode. It should be a lot easier than writing your own HTTP Client code for the same purpose.
Specifically, MobileServiceInvalidOperationException contains the HttpResponse of the failed request, so you can check exception.Response.StatusCode value.
I have a WebApi that I want to authorize my user with his linkedin information (as in create an access token and inject it in to my owin).
So far I have tried to work with Sparkle.Linkedin and this is what I have
public LinkedInLogic() {
// create a configuration object
_config = new LinkedInApiConfiguration(ApiKey, ApiSecret);
// get the APIs client
_api = new LinkedInApi(_config);
}
public Uri GetAuthUrl() {
var scope = AuthorizationScope.ReadBasicProfile;
var state = Guid.NewGuid().ToString();
var redirectUrl = "http://localhost:1510/api/login/RedirectAuth";
return _api.OAuth2.GetAuthorizationUrl(scope, state, redirectUrl);
}
public void GetAccessToken(string code) {
//If I do api.GetAccessToken(code); here I get an access token
var request = System.Net.WebRequest.Create("http://localhost:1510/api/token?grant_type=authorization_code&code=" + code);
request.GetResponse(); // my owin authorization
}
So I first get the Authorization Url -> it opens a popup -> I enter my data and it goes back to a controller which fires up GetAccessToken.
Problem is even if I completely authorize with linkedin I am not sure how to authorize with my own webapi. So I tried to send an http request to my owin token giver but it doesn't like it. There is also doesn't seem to be anyway I can return the access token back to the user so he can use it in his session.
Any ideas?
Not too sure if the sparkle is working anymore since the changes that where made by Linkedin on May 2015