I am trying to get a webhook setup with Xero working locally. I am using ngrok to allow xero to call my localhost. To get the webhook working I must correctly return the "intent to receive"
It seems to be working fine in that I can debug it and follow it through. However when I try to return 200 for success (hashes match) or 401 unauthorized (hashes don't match) the receiving Xero still doesn't accept it. All it says is: "Response contained a body"
According to the Xero webhook docs my endpoint must ensure:
It uses HTTPS
It responds within 5 seconds with a 200 O.K status code
There is no body in the response
There are no cookies in the response headers
If the signature is invalid a 401 Unauthorised status code is returned
I have tried returning the code in various ways:
public IHttpActionResult WebHooks()
{
//if hash check fails I tried these
return Unauthorized();
return Request.CreateResponse((int)HttpStatusCode.Unauthorized);
return StatusCode(HttpStatusCode.Unauthorized);
//if hash check matched I tried the following
return Ok();
return Request.CreateResponse((int)HttpStatusCode.OK);
return StatusCode(HttpStatusCode.OK);
}
In seething desperation I also tried
public int WebHooks()
{
//if hash check matches
return 200;
//if hash check fails
return 401;
}
Thank you in advance for your help. I spent too long searching for an existing answer, but I couldn't find any. What am I doing wrong? As far as I can see my webapi should work.
Figured it out. This is what I did to get it working:
if (hashedPayload != xeroSignature)
{
ControllerContext.HttpContext.Response.Clear();
ControllerContext.HttpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
ControllerContext.HttpContext.Response.End();
return Content(string.Empty);
}
ControllerContext.HttpContext.Response.Clear();
ControllerContext.HttpContext.Response.StatusCode = (int)HttpStatusCode.OK;
return Content(string.Empty);
Nothing I tried worked and my swear-jar overfloweth. I noticed through ngrok that the "intent to receive" would return a 302 (as opposed to 401) and then would redirect to Account/Login.
This led me down a rabbit hole to where I discovered the answer
To quote:
If you are adding asp.net WebApi inside asp.net MVC web site you
probably want to respond unauthorized to some requests. But then
ASP.NET infrastructure come into play and when you try to set response
status code to HttpStatusCode.Unauthorized you will get 302 redirect
to login page.
That was exactly the behaviour I saw. When I tried the webhook in a new webapi project with only one method - it worked. However I want to get it working in my MVC site. I added what this post suggested and it now works perfectly for me.
Many thanks to #Jacob Alley
Related
I have a problem loading a 3D model on an online server, the error shown is related to accessing the Forge API, locally works smoothly however when mounted on the server or a website is made marks the following error "Failed to load resource: the server responded with a status of 404 (Not Found)", then "onDocumentLoadFailure() - errorCode:7".
As I comment, what I find stranger is that, locally, it works. Attached the segment of the code where it displays the error.
function getAccessToken() {
var xmlHttp = null;
xmlHttp = new XMLHttpRequest();
xmlHttp.open("GET", '/api/forge/toke', false); //Address not found
xmlHttp.send(null);
return xmlHttp.responseText;
}
Thank you very much in advance.
Are you sure the code you're running locally and the code you've deployed are really the same?
The getAccessToken function doesn't seem to be correct, for several reasons:
First of all, there seems to be a typo in the URL - shouldn't it be /api/forge/token instead of /api/forge/toke?
More importantly, the HTTP request is asynchronous, meaning that it cannot return the response immediately after calling xmlHttp.send(). You can find more details about the usage of XMLHttpRequest in https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest.
And finally, assuming that the function is passed to Autodesk.Viewing.Initializer options, it should return the token using a callback parameter passed to it (as shown in https://forge.autodesk.com/en/docs/viewer/v7/developers_guide/viewer_basics/initialization/#example).
With that, your getAccessToken should probably look more like this (using the more modern fetch and async/await):
async function getAccessToken(callback) {
const resp = await fetch('/api/forge/token');
const json = await resp.json();
callback(json.access_token, json.expires_in);
}
I've already found the issue. When I make the deploy I have to change the url where the request is made for the public or the name of the domain. For example: mywebsite.com/aplication-name/api/forge/token.
My ASP.NET Core API is only returning the first two rows out of 40.
I have tried
services.AddMvc().AddJsonOptions(options => options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore);
It sent VS code in to an endless loop. Any ideas on where to look to fix this? I am using the browser and postman to consume the api with the same results.
[EnableCors]
[HttpGet]
public ActionResult<IEnumerable<Fuaxclient>> GetClient()
{
return _context.clientList;
}
I am expecting to get proper json code of about 48 rows. Instead I'm getting 2 rows and broken json. I get this error from FireFox Web Console
error: null
headers: Object { normalizedNames: Map(0), lazyUpdate: null, headers: Map(0) }
message: "Http failure response for https://fuaxclientapi.azurewebsites.net/api/fuaxclient: 500 Internal Server Error"
name: "HttpErrorResponse"
ok: false
status: 500
statusText: "Internal Server Error"
url: "https://fuaxclientapi.azurewebsites.net/api/fuaxclient"
The API is still running if you would like to try the API to see what I'm talking about.
Ok after much digging and researching and banging my head on the desk, I have found my issue. I had NULL in my database and the API stopped at the NULL.
status: 500
statusText: "Internal Server Error"
As the error 500 itself suggests there error is on the server, in this case the url( endpoint api) stated as https://fuaxclientapi.azurewebsites.net/api/fuaxclient has issue.
It returns invalid json as validated here.
The return value need to be fixed in the api.
So I have this code inside the controller of my MVC for a page
[HttpGet, Route]
[Authorize(nameof(Access))]
public async Task<ActionResult> ListStuff()
{
var canRead = HasAccess()
if(!canRead)
{
throw new HttpResponseException(HttpStatusCode.Unauthorized);
}
}
I'm using C# attributes for security validation and what I want is if the attribute with the 'HasAccess()' function returns false then the page show show an 'unauthorized' error, as you guys can see I tried throwing an HttpResponseException, I'm pretty sure this isn't the proper way to do it. Any suggestions?
You send a GET http request to your running service, eg:http://localhost:8080/Myservice.svc/$metadata. I've used postman in the past to help with sending http requests.
Link to free postman
I have a Web API in my Azure server and I'm making calls from an ASP.NET Webforms website.
I seem to be able to perform GET with no trouble. Now for the PUT, it's giving me this error:
The page you are looking for cannot be displayed because an invalid
method (HTTP verb) is being used
I was not able to DELETE either. I see some other topics where people disable some WebDav and stuff on their IIS servers and it works. But on Azure?
Below my code for the PUT:
HttpResponseMessage response = client.GetAsync("api/People/" + id).Result;
if (response.IsSuccessStatusCode)
{
var yourcustomobjects = response.Content.ReadAsAsync<People>().Result;
Uri peopleUrl = response.Headers.Location;
yourcustomobjects.name= "Bob";
response = await client.PutAsJsonAsync(peopleUrl, yourcustomobjects);
tbDebug.Text += await response.Content.ReadAsStringAsync();
}
Alright I grew tired of trying to fix this issue by enabling PUT.
So what I did, was I wrote a GET that makes the needed change in the database.
Cheers
Why is the Web Security is working differently on different browser:
Details:
I have two applications
One is a simple HTML application and another one is an ASP.NET MVC4 WebApi application and the projects are inside of same solution and i have set multiple start-up project for run the application for same time .
Working version:
I have Used Web Security in the Web API project. I did full implementation of web security...
Login Action Code
// GET api/company
[System.Web.Http.AcceptVerbs("Post")]
[System.Web.Http.HttpPost]
public HttpResponseMessage Login(LoginRequest loginRequest)
{
try
{
if (WebSecurity.Login(loginRequest.EmailAddress, loginRequest.Password, true))
{
var userDetails = new string[2];
userDetails[0] = loginRequest.EmailAddress;
var currentUSerRole = Roles.GetRolesForUser(loginRequest.EmailAddress);
userDetails[1] = currentUSerRole[0].ToString();
HttpResponseMessage response =
Request.CreateResponse(HttpStatusCode.Accepted, userDetails);
return response;
}
else
{
HttpResponseMessage response
= Request.CreateResponse(HttpStatusCode.Unauthorized);
return response;
}
}
catch (Exception e)
{
HttpResponseMessage response
= Request.CreateResponse(HttpStatusCode.Unauthorized);
return response;
}
}
*WebSecurity.Login* is working on all browsers when i call the login method using Ajax.
But I have another method in another controller, That named as CurrentDateAndUser
Code:
[AllowAnonymous]
[System.Web.Http.AcceptVerbs("Get")]
[System.Web.Http.HttpGet]
public HttpResponseMessage CurrentDateAndUser()
{
if (WebSecurity.IsAuthenticated)
{
int userId = WebSecurity.CurrentUserId;
string[] currentDateAndUSerId = new string[2];
currentDateAndUSerId[0] = userId.ToString();
currentDateAndUSerId[1] = DateTime.UtcNow.ToString();
HttpResponseMessage response =
Request.CreateResponse(HttpStatusCode.Accepted, currentDateAndUSerId);
return response;
}
HttpResponseMessage responseNew =
Request.CreateResponse(HttpStatusCode.NotAcceptable);
return responseNew;
}
Issue:
If I call the CurrentDateAndUser method from Microsoft Internet Explorer Using an Ajax call, then everything works. The WebSecurity.IsAuthenticated returns true and is working well.
However,
If I call the CurrentDateAndUser method from Google Chrome Or Mozilla Firefox using an Ajax call, then nothing works. The WebSecurity.IsAuthenticated always returns false.
I don't know why. If you have any idea, then please let me know.
I also found a similar problem (not sure if it is a real issue):
When I run my application with Fiddler, I see a different result:
When i call the CurrentDateAndUser method from IE, the request is:
I can see the Cooke/Login values in above image
But When i call the CurrentDateAndUser method from Chrome And Firefox , the request is:
I can't see the cookie values, meaning that the Web Security.IsAuthenticated property is returning false.
Is it Bug in WebSecurity?????
Edit
My Ajax request code is
function GetCurrentUserId() {
return $.ajax({
method: 'GET',
url: rootUrl + '/api/Common/CurrentDateAndUser',
async: false
}).success(function (response) {
return response[0];
}).error(function () {
toastr.error('Somthing is wrong', 'Error');
})
}
This request does not send the Auth Cookie values to Web API method when I run the application in Chrome and Firefox, however, this request sends the cookie values to the API method, if it is run in IE
i have posted the Image , Please take a look at the above image
The issue is not with web security at all, it's with the way you implement your security. You should never be using a userid, email, or anything important in the cookies.
I would suggest you use the FormsAuthentication class to encrypt and decrypt your cookies, and even so, only store something such as the SessionID plus a custom hash of that session ID to verify your self when you decrypt the cookie
Here is a site that gives a pretty good example: http://www.c-sharpcorner.com/uploadfile/nipuntomar/update-formsauthenticationticket/
There are 3 things around it:
WebSecurity.IsAuthenticated actually returns the value of HttpRequest.IsAuthenticated, which is true if the Forms Authentication cookie has been set and is current. It's not available until the user makes the next request after successfully logging in, which is why you are seeing the behaviour that you describe.
I remember reading on MSDN or someplace, the WebSecurity.IsAuthenticated does not work until the page is fully loaded. Meaning if you login a user in a page and in the same flow of code you check IsAuthenticated, it will NOT return True. For IsAuthenticated to be True the page has to be reloaded or use the better practice; which is to redirect the user to another secured page as soon as the login is successful and in that page check IsAuthenticated.
We had the same issue with Chrome (version 21.0.1180). Despite that we see expiration date on Header, some Chrome in Windows XP ignored it. Then we removed the Expiration Date and Chrome accepted keep the session cookie without problems.
So what to do is:
After login try to check this on new page not on same page.
Also try to set cookie explicitly
System.Web.Security.FormsAuthentication.SetAuthCookie(user.Username, false);
I don't know if this will help or not.
But I remember I was learning jQuery ajax
So I setup a simple project on my laptop. When I tested it, it worked fine on IE, but failed in Chrome. After searching for hours, I found that Chrome will not allow AJAX requests from the local machine. When I tested it using an actual web server it worked fine for IE and Chrome.
So my question and advice is: are you testing on the same machine?
Try to deploy it to a machine running a web server with a unique domain name and test your application!