How to use custom error settings for JWT middleware - go

I have followed the cook books guide to the letter, found here https://echo.labstack.com/cookbook/jwt
But when using the JWT middleware I am having some issues with adding custom error messages. Login works fine, even to the point of not giving details (username & password) that returns a 404.
But when the JWT is missing it returns a 400, I want it to also return a 404.
So in my research I found this, https://forum.labstack.com/t/custom-error-message-in-jwt-middleware/325/3 which lists the following middleware.ErrJWTMissing & middleware.ErrJWTInvalid But is very unclear on how to set these?
I have tried setting them as vars on the router file, like so
var (
ErrJWTInvalid = echo.NewHTTPError(http.StatusTeapot, "test 104")
ErrJWTMissing = echo.NewHTTPError(http.StatusTeapot, "test 103")
)
But the error that sill comes back to me is a 400 and not a 418 (as this is just a test). So what am I doing wrong?

You can change the HTTP code and message this way.
func init() {
middleware.ErrJWTMissing.Code = 401
middleware.ErrJWTMissing.Message = "Unauthorized"
}

First, a point on your statement that you want to return a 400 and also a 404 error - you cannot do this. You're sending one response from the server so it gets exactly one response code. You could send a 207, but we're not really talking about multiple resources here, so don't do that. In my opinion, a 400 error is indeed the correct response for a missing JWT as that constitutes a bad request. A 404 "Not Found" means that the requested resource (the thing on the server side) could not be found. It does not mean that something in the request could not be found.
As for setting your custom error message, you're likely to be out of luck without altering the source code for Echo. That specific response is coming from within the middleware handlers of the package itself (you can see it here). This is mostly abstracted away from you, so without looking at the inner workings of the package, there would be no way to tell where this was coming from, and frankly there's not a lot that you can easily do about it. ErrJWTMissing is indeed the variable that the package uses internally for this error message, but Echo does not appear to provide an exported setter method for you to change this value, so you're stuck with what it is.
If you truly wanted to set a custom error method for this case I think your options would be to:
Write your own middleware to intercept the request before it was handled by Echo's middleware, where you could handle the request however you wanted.
Edit the Echo source to work how you wanted it to work -- specifically, all you would have to do is edit ErrJWTMissing.
Basically, Echo is trying to do you favors by handling all of this middleware processing for you, and it's a lot of work or hackery to un-do that work while still using Echo.

Related

Correct way to return REST Api response

I am developing a REST API for a website, let's say i have a POST endpoint('/users/create') which i want to create the user if they don't already exist and return the user as a result.
What this would look like is:
#PostMapping(value = "/users/create")
public User createUser(#RequestBody UserDTO form)
The problem with this is that when the user is successfully created there is no problem just returning the user with a response code 200. The problem comes when I am doing input validation and for example a user with that email already exists and i want to return user: null and some sort of message as to what specifically failed in the user creation. I've thought about multiple posibilities such as creating a custom object that has fields for both an object of any type ( user in this case ) and a message along with it, as well as using HttpServletResponse / ResponseEntity. They all seem like they are overcomplicating something that should be realtively simple in my eyes and i don't know which approach is the best. First time posting so excuse me if i'm a bit all over the place.
there probaly is an solution which uses the Http error system but as an alternative I would use an if statement on both sides, which checks if the user already exists and if it does it sends user:error or something like that. The client side then has to check if the anwser is a user or error and if it is error a pre programmed message is shown

How to inject a header from the errormessage

I've been attempting to inject a custom header for a error response status (and failing).
I have a very simple lambda being used
exports.handler = (event, context, callback) => {
// TODO implement
//callback(null, 'Hello from Lambda');
var error = {
name:"error",
message:"I am a failure",
statusCode: 400
};
error["x-test"] = 'foo';
callback(JSON.stringify(error), null);
};
In the api gateway, I've done the following:
set up CORS to include x-test
responsetemplate = "$input.path('$.errorMessage')"
responseparameter to include:
method.response.header.x-test = integration.response.body.x-test
Also, I have a statusCode mapped using '.*statusCode.*?400.*'
This has turned out empty.
so I decided to take a step back and see what happens if I do:
method.response.header.x-test = integration.response.body
I found that I get the stringified response of errorMessage.
{"x-test":"{\"errorMessage\":\"{\\\"name\\\":\\\"error\\\",\\\"message\\\":\\\"I am a failure\\\",\\\"statusCode\\\":400,\\\"x-test\\\":\\\"foo\\\"}\"}"}
So I decided to change the responsetemplate to force it to json by doing the following:
responsetemplate = "$util.parseJson($input.path('$.errorMessage'))"
and I still get the stringified response:
{"x-test":"{\"errorMessage\":\"{\\\"name\\\":\\\"error\\\",\\\"message\\\":\\\"I am a failure\\\",\\\"statusCode\\\":400,\\\"x-test\\\":\\\"foo\\\"}\"}"}
My guess is that it doesn't transform as expected, but only for the final output.
So how would you take a value and shove it into a header?
Thanks,
Kelly
I think this is more of a design choice regarding the limitation imposed by both Lambda and APIGateway. I will try my best to walk through my thoughts.
First of all, in Lambda, callback(error, result) function can either take an error string as first argument, or an object as result response. If you want to pass along a simple error message, you could definitely just do that. However, in your case, as you tried to pass along an entire error object, choosing the second option is clearly a better solution (in contrast to stringifying an object and parse it into object again). As a result, the final line of your Lambda function should be:
callback(null, error);
Yes, in this case, if you test your function in Lambda, the output result will no longer be red and flag it as an error, but this won't matter as you can format your headers and response in APIGateway.
Now you need to set things up in APIGateway, in which you need to make use of the object passed by Lambda.
It's actually rather easy to use method execution interface to configure headers.
In Method Response, you need to add the headers you want to include in the response for a specific status code, which in your case is x-test. (If you want the API to return different status codes, you can also configure that in this panel.)
Then go to Integration Response, under the same status code, you will see the added header available. According to this documentation from AWS, you can use integration.response.body.JSONPath_EXPRESSION to assign the header value (this is another reason that you should return object rather than string in Lambda, as there is no formal API to parse object from string at this stage). This time, as your Lambda is passing an object, the value of x-test header is:
integration.response.body['x-test']
Now your API should have the proper header included.
NOTE: In order to set up different status code in APIGateway, you should leave some distinguishable data fields (your statusCode: 400 should work perfectly) in you response body, so you can use RegEx to match those fields to a specific status code.
So... above doesn't work with success message. I found this blog though talking about error handling design pattern. Apparently what they suggest is only mapping status code when there is an error, in which case no body should be passed (only the errorMessage), as browser won't care about response body for a status code other than 200 anyway.
I guess after all, it is impossible to customize both status code and header at the same time with Lambda passing an object to APIGateway?
This is due to the fact that you are stringifying the error object coming from your Lambda function. API Gateway attempts to resolve the JSON-Path expression and can't evaluate "x-test" in a string. If you return an object instead of a string, this should work.
You may want to consider using proxy integrations which allow you to control the headers and status directly from your Lambda function.
Update: I've written a blog post on this topic with sample code # https://rpgreen.wordpress.com/2017/01/25/how-to-send-response-headers-for-aws-lambda-function-exceptions-in-api-gateway/

JMeter. Why doesn't if statement work?

I am frustrated by my failure to get a simple IF statement to trigger and am begining to suspsect that I do not understand the way IF statements are implemented in JMeter.
What I am trying to do is query an API that returns 202 responses while it is processes a request, then returns a 200 when the results are returned. Finally a 404 is returned if I query again after getting the results.
I have a while controller with no condition that performs this query and it exits on the 404.
This seems to work OK.
What I want to do is confirm that I have valid JSON returned for the 202 and the 200 but ignore the 404.
I thought I could add an IF controller after each query in the loop to run a a BSF assertion when I have 202 or 200 but I cannot seem to get it to work. Even if I put in "true" or "1 == 1" as the condition the assertion is never called.
"${httpCode}" == "200"
In my assertion I have added a deliberate error "eval x;" that causes the assertion to fail if I drag and drop it to other parts of the suite -
try {
eval('var response = ' + prev.getResponseDataAsString());
eval x;
} catch(e) {
prev.setSuccessful(false);
prev.setResponseMessage("Invalid response. Expected a valid JSON.");
}
Like I said, even if I add true or 1 == 1 the assertion error never shows in the assertion results. Am I fundamentally misunderstanding the way the IF controller works? Certainly the Java programmers I work with cannot see what is wrong here.
Unfortunately this site won't let me post an image of the suite...
BTW
Sorry for asking a "vague" question but I have been evaluating JMeter for over a week now and find the online resources to be lacking in "How To" type tutorials. Lots of references to specific JMeter objects and basic "Getting Started" type posts but nothing detailed. Also most of the references seem to be circular and and out of date; I keep finding myself back at the same pages over again.
The apache site seems more geared up as an object reference.
Is anyone able to recommend a really good online resource?
What JMeter version do you have? In my ver.2.10 if controller with 1==1 works correctly.
Also I could recommend you to use If controller with JMeterThread.last_sample_ok function. It returns true in case previous sampler was ok.
You could use in as if condition.
Try the following:
${__BeanShell("${httpCode}".equals("200"))}

Example Content-Types for `POST` validation failures?

Suppose an HTTP server responds to a POST with a 400 response code because the request failed validation (e.g. email address not found). If the server wishes to provide more information to the client about the nature of the error, how should this be returned? For each possible content type used in requests, should there ideally be an associated "error" content type?
For example, given the request
POST /users
Content-Type: application/x-myuser
{
"email": "foo#example.com",
"name": "Michael"
}
a response might be
400 Bad Request
Content-Type: application/x-myuser-error
{
"email": "Email address foo#example.com not found"
}
Are there any good examples of "error" content types publicly available?
I don't have any examples, but it's good to always keep these in mind:
Always include a machine-readable error, and generalize as much as possible. A JSON structure like
{"error":"Email address not found!","code":"fielderror","field":"email","reason":"notfound"} (could be simplified to {"error":"...","code":"emailnotfound"})
allows API developers to properly present the error to the user (and act on the error) while it allows you to change the messages without breaking applications. It also really helps with translation of error messages, both on your end and the external developer's end.
A different approach is to simply don't return any body, and use HTTP headers to tell the user agent what went wrong. For example, you could use X-Error and X-Error-Code to show a human readable error, and a machine readable code.
Creating too many content types might be a bad thing. I personally prefer to always use application/json and let the user agent know the status by looking at the HTTP codes: 200, 400, 403, 404, 500, etc.
Definitely don't ever start making combinations of HTTP codes and content types. You don't want your users to have to learn that application/myapp/error means there's an error, UNLESS it's 200 in which case you're in the edit screen, OR when it's 302 it's not actually an error but a redirect. This is why you should probably stick with one content type.
Bottom line: always keep it simple. Make sure that there's one field which you have to look at, not two or three, when detecting a status. Once the user agent has determined the status it could choose to look at some other fields for extra info, but only after it has determined that something went wrong. Including a separate content type probably won't help there.

How do I parse a POST to my Rails 3.1 server manually?

Scenario:
I have a Board model in my Rails server side, and an Android device is trying to post some content to a specific board via a POST. Finally, the server needs to send back a response to the Android device.
How do I parse the POST manually (or do I need to)? I am not sure how to handle this kind of external request. I looked into Metal, Middleware, HttpParty; but none of them seems to fit what I am trying to do. The reason I want to parse it manually is because some of the information I want will not be part of the parameters.
Does anyone know a way to approach this problem?
I am also thinking about using SSL later on, how might this affect the problem?
Thank you in advance!! :)
I was trying to make a cross-domain request from ie9 to my rails app, and I needed to parse the body of a POST manually because ie9's XDR object restricts the contentType that we can send to text/plain, rather than application/x-www-urlencoded (see this post). Originally I had just been using the params hash provided by the controller, but once I restricted the contentType and dataType in my ajax request, that hash no longer contained the right information.
Following the URL in the comment above (link), I learned the how to recover that information. The author mentions that in a rails controller we always have access to a request variable that gives us an instance of the ActionDispatch::Request object. I tried to use request.query_string to get at the request body, but that just returned an empty string. A bit of snooping in the API, though, uncovered the raw_post method. That method returned exactly what I needed!
To "parse it manually" you could iterate over the string returned by request.raw_post and do whatever you want, but I don't recommend it. I used Rack::Utils.parse_nested_query, as suggested in Arthur Gunn's answer to this question, to parse the raw_post into a hash. Once it is in hash form, you can shove whatever else you need in there, and then merge it with the params hash. Doing this meant I didn't have to change much else in my controller!
params.merge!(Rack::Utils.parse_nested_query(request.raw_post))
Hope that helps someone!
Not sure exactly what you mean by "manually", posts are normally handled by the "create" or "update" methods in the controller. Check out the controller for your Board model, and you can add code to the appropriate method. You can access the params with the params hash.
You should be more specific about what you are trying to do. :)

Resources