Custom Boom error messages - javascript

On my Hapi.js server, I'd like to send a specific message if an account does not have permission rights to access an api endpoint. The Boom message I have right now looks like this:
return reply(Boom.unauthorized("unauthorized access to this API."));
This returns a message that looks like this:
{
"statusCode": 401,
"error": "Unauthorized",
"message": "unauthorized access to this API."
}
I would like to make it more customized to look like this:
{
"success": false,
"message": "unauthorized access to this API.",
"csrf-decorator": "",
"redirect": ""
}
Do we have access to customize the Boom error messages?
Thanks!

Boom comes with built in response error transformation. So in order to achieve my results I had reformat my error reply in the following way:
const error = Boom.forbidden("Sorry, you are restricted in accesssing this API. No soup for you!.");
error.output.statusCode = 403; // Assign a custom error code
error.output.payload["csrf-decorator"] = request.headers["csrf-decorator"];
error.reformat();
return reply(error);

According to the Hapijs documentation you can reformat error messages to customize them:
Error transformation
Errors can be customized by changing their output content. The boom error object includes the following properties:
isBoom - if true, indicates this is a Boom object instance.
message - the error message.
output - the formatted response. Can be directly manipulated after object construction to return a custom error response. Allowed root keys:
statusCode - the HTTP status code (typically 4xx or 5xx).
headers - an object containing any HTTP headers where each key is a header name and value is the header content.
payload - the formatted object used as the response payload (stringified). Can be directly manipulated but any changes will be lost if reformat() is called. Any content allowed and by default includes the following content:
statusCode - the HTTP status code, derived from
error.output.statusCode.
error - the HTTP status message (e.g. 'Bad Request', 'Internal Server
Error') derived from statusCode.
message - the error message derived from error.message.
inherited Error properties.
It also supports the following method:
reformat() - rebuilds error.output using the other object properties.
const Boom = require('boom');
const handler = function (request, h) {
const error = Boom.badRequest('Cannot feed after midnight');
error.output.statusCode = 499; // Assign a custom error code
error.reformat();
error.output.payload.custom = 'abc_123'; // Add custom key
throw error;
});
When a different error representation is desired, such as an HTML page or a different payload format, the onPreResponse extension point may be used to identify errors and replace them with a different response object.
const Hapi = require('hapi');
const Vision = require('vision');
const server = Hapi.server({ port: 80 });
server.register(Vision, (err) => {
server.views({
engines: {
html: require('handlebars')
}
});
});
const preResponse = function (request, h) {
const response = request.response;
if (!response.isBoom) {
return h.continue;
}
// Replace error with friendly HTML
const error = response;
const ctx = {
message: (error.output.statusCode === 404 ? 'page not found' : 'something went wrong')
};
return h.view('error', ctx).code(error.output.statusCode);
};
server.ext('onPreResponse', preResponse);

Related

Reading 409 response json in production cannot read custom error message from server

I have an asp.net core MVC server set up, I am trying to enable some better error handling. However when I deploy my changes to production environment I have a discrepancy in my server responses when dealing with 4xx errors.
When running on my local host i am able to send custom response data back to the client and read this data no problem, however when i attempt the same thing after live deployment I cannot read the responses the same way And I do not understand why.
Controller
[HttpPost]
public JsonResult SaveRecord([FromBody]NewsLanguage newLanguage)
{
//NewsLanguage newLanguage = new NewsLanguage()
try
{
_context.NewsLanguages.Add(newLanguage);
_context.SaveChanges();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
Response.StatusCode = 409;
string errMsg = ex.Message;
if (ex.InnerException != null)
errMsg = ex.InnerException.Message;
return Json(new { status = "Error", message = errMsg });
}
Response.StatusCode = 200;
return Json(new { status = "success",
message = "New News Language Saved Successfully!" });
}
fetch request
try {
const response = await submitForm("/saverecord", newsLanguage, "POST");
console.log(response);
if (response.ok)
handleResponse(response, newsLanguage);
else {
const err = await response.json();
throw new Error(err.message || err.statusText)
}
} catch (err) {
console.log(err);
handleErrorResponse(err, newsLanguage);
}
function submitForm(route, newsLanguage, method) {
const requestOptions =
{
method: method,
headers:
{
'Content-Type': 'application/json'
},
body: JSON.stringify(newsLanguage)
};
return fetch(parurl + route, requestOptions);
}
async function handleResponse(response, newsLanguage, method) {
const data = await response.json();
console.log(response, data)
if (data.status === "success") {
//have to close modal this way since using
//jquery hide leave backdrop open and causes
//issue with subsequent modal openings
document.getElementById("ModalFormClose").click();
toastr.success(data.message, "PERLEWEB DATABASE INTERFACE");
if (method != "DELETE") {
let table = $('#example').DataTable();
table.row.add({ "id": newsLanguage.Id,
"languageName": newsLanguage.LanguageName }).draw();
} else {
var table = $('#example').DataTable();
table.row($(this).parents('tr')).remove().draw();
}
} else {
toastr.error(response.responseJSON.message, "ERROR!")
}
}
function handleErrorResponse(errorMsg) {
toastr.error(errorMsg, "ERROR!")
}
So it seems the custom error message i send when sending the 409 response is not there in the client in production, however, the success message is i can read it as expected and display the message, however when trying to read the response.json() after checking if response is ok (and when it is not) the response message is "SyntaxError: Unexpected token T in JSON at position 0" which based on some other research suggest it is undefined.
So my main questions are,
1- where is my error message for failures?
2- Is there a way i can get it display the error message, or can i only send http response code for error?
3- why does it work for success response but not error?
4- why is there this difference btwn localhost vs production is this a server configuration issue?
Thanks
After Much investigation, it turns out the source of the issue was in the web.configurations.
Since the project is being inside of another web app i had to add a section to my web.config which specifies a different custom error handling method than the rest of the site. specifically i added the below
<location path="webdb">
<system.webServer>
<validation validateIntegratedModeConfiguration="false" />
<httpErrors errorMode="DetailedLocalOnly" existingResponse="PassThrough" >
<clear/>
</httpErrors>
</system.webServer>
</location>
And Now i am able to parse the Error response custom text in my JS and display the message from the server

Stripe Webhook error 400 with netlify functions

I have set up payments with stripe using Netlify function by following this article https://www.netlify.com/blog/2020/04/22/automate-order-fulfillment-w/stripe-webhooks-netlify-functions/ and in my Stripe dashboard I get error saying that:
Webhook Error: No signatures found matching the expected signature for payload. Are you passing the raw request body you received from Stripe? https://github.com/stripe/stripe-node#webhook-signing
Now I am not sure if the user gets confirmation email, Sendgrid does not show any activity, however it has not shown any when I was testing this flow previously, and although I received confirmation email. Unfortunately back then I pressed resend by my webhook activity details in Stripe dashboard, and I am not sure if I should be resending those or do they go through. Would anyone be able to tell me what is wrong with my code?
const stripe = require("stripe")(process.env.STRIPE_SECRET_KEY);
const sgMail = require("#sendgrid/mail");
sgMail.setApiKey(process.env.SENDGRID_API_KEY);
exports.handler = async ({ body, headers }) => {
try {
const stripeEvent = stripe.webhooks.constructEvent(
body,
headers["stripe-signature"],
process.env.STRIPE_WEBHOOK_SECRET
);
if (stripeEvent.type === "charge.succeeded") {
const emailTo = stripeEvent.data.object.billing_details.email;
const msg = {
to: emailTo,
from: process.env.FROM_EMAIL_ADDRESS,
subject: `Thanks!`,
html: `elox`,
};
await sgMail.send(msg);
}
return {
statusCode: 200,
body: JSON.stringify({ received: true }),
};
} catch (err) {
console.log(`Stripe webhook failed with ${err}`);
return {
statusCode: 400,
body: `Webhook Error: ${err.message}`,
};
}
};
Thanks!
I got the same issue.
Locally with the stripes-cli everything works fine.
It seems that the lambda didn't hand over the raw body to the stripes.webhook.constructEvent.
Therefore my solution was to change the method signature to the following and use the event.body object.
exports.handler = async(event, context ) => {
try {
const stripeEvent = stripe.webhooks.constructEvent(
event.body,
event.headers['stripe-signature'],
endpointSecret
);....

AstraDB failed GET request

I have been working on a TikTok clone app. So I created my database with Astra DB and set up two functions inside a function folder to test out if my posts are working. I am using netlify dev to test out the applications. But when I redirect http://localhost:8888/.netlify/functions/addData
I get this failed get request error
Request from ::1: GET /.netlify/functions/addData
Error: Request Failed: [object Object]
Stack Trace: Request failed with status code 401
at axiosRequest (D:\tiktokclone\node_modules\#astrajs\rest\src\rest.js:126:11)
at processTicksAndRejections (internal/process/task_queues.js:93:5)
at async AstraClient._request (D:\tiktokclone\node_modules\#astrajs\rest\src\rest.js:199:22)
at async AstraClient.put (D:\tiktokclone\node_modules\#astrajs\rest\src\rest.js:263:12)
at async AstraCollection._put (D:\tiktokclone\node_modules\#astrajs\collections\src\collections.js:69:22)
at async Object.exports.handler (D:\tiktokclone\functions\addData.js:17:9)
Response with status 500 in 231 ms.
I quite don't understand what causes this. All the credentials inside my .env folder were correct.Here is the code I used to make the request
const { createClient } = require("#astrajs/collections");
const collection = "posts";
exports.handler = async function (event, context, callback) {
const astraClient = await createClient({
astraDatabaseId: process.env.ASTRA_DB_ID,
astraDatabaseRegion: process.env.ASTRA_DB_REGION,
applicationToken: process.env.ASTRA_DB_APPLICATION_TOKEN,
});
console.log(astraClient)
console.log(collection)
console.log('Hello')
const posts = astraClient
.namespace(process.env.ASTRA_DB_KEYSPACE)
.collection(collection);
try {
await posts.create("a post", {
title: "my first post",
});
return {
statusCode: 200,
};
} catch (e) {
console.error(e);
return {
statusCode: 500,
body: JSON.stringify(e),
};
}
};
I found a fix. For some reason, I was trying to call the API using an application token and it was giving me the 401 error. When I used username and password it worked.
const astraClient = await createClient({
astraDatabaseId: process.env.ASTRA_DB_ID,
astraDatabaseRegion: process.env.ASTRA_DB_REGION,
username: process.env.ASTRA_DB_USERNAME,
password: process.env.ASTRA_DB_PASSWORD,
});
username is the client ID and password is the client secret. This error happened because of a slight confusion with the REST API and the Document API. Astra DB uses application token for authenticating document API while REST API uses client ID and Password.

Return XML in Firebase Cloud Function

I am trying to set up a cloud function that returns an xml. I am able to create and log the xml, but it crashes with the following error when I try to return it.
TypeError: Converting circular structure to JSON
at Object.stringify (native)
at stringify (/var/tmp/worker/node_modules/express/lib/response.js:1119:12)
at ServerResponse.json (/var/tmp/worker/node_modules/express/lib/response.js:260:14)
at ServerResponse.send (/var/tmp/worker/node_modules/express/lib/response.js:158:21)
at cors (/user_code/index.js:663:21)
at cors (/user_code/node_modules/cors/lib/index.js:188:7)
at /user_code/node_modules/cors/lib/index.js:224:17
at originCallback (/user_code/node_modules/cors/lib/index.js:214:15)
at /user_code/node_modules/cors/lib/index.js:219:13
at optionsCallback (/user_code/node_modules/cors/lib/index.js:199:9)
My Function
exports.sendXMLResponeSample = functions.https.onRequest((request, response) => {
cors(request, response, () => {
// import xmlbuilder
const builder = require('xmlbuilder');
// create my object to convert to xml
var myFeedObject = {
"somekey": "some value",
"age": 59,
"eye color": "brown"
}
// convert myFeedObject to xml
const feed = builder.create(myFeedObject, { encoding: 'utf-8' })
console.log("feed.end({ pretty: true }) = (below)");
console.log(feed.end({ pretty: true }));
// return xml
return response.send(200, feed) // <<< error occurs here
})
})
I believe the error suggests the the firebase cloud function is expecting I return a JSON object in the response rather than an xml object, but I am unsure how to tell it to expect an xml object in the response.
Does anyone understand how to return an xml object in a firebase cloud function?
EDIT: The object is converted to an xml object without any issue. The error occurs when the xml object is attempted to be returned.
You can use the .contentType(type: string) on the response object that the cloud function returns to the caller.
Like so:
res.status(200)
.contentType('text/xml; charset=utf8')
.send(xmlString);
You may install the object-to-xml library, and then set the response data type in the response header to text/XML, something like res.header('Content-type','text/xml').
This is what I'm doing.
const xmlString =
'<?xml version="1.0" encoding="UTF-8"?><Response><Message><Body>This is the
response</Body></Message></Response>';
res
.set("Content-Type", "text/xml; charset=utf8")
.status(200)
.send(xmlString);
Works for me. I'm sure there is a better way to convert your XML to a string.

Accessing JSON values with jasmine-node / frisby

I am trying to construct a test that will validate the error message received for a bad request to an API. I am using node-jasmine and frisby to write the tests but having trouble accessing the JSON values with dot notation.
var frisby = require('frisby');
frisby.create('JSON Error')
.put('url', {
}, {json: true})
.expectStatus(400)
.expectHeaderContains('content-type', 'application/json')
.auth('username', 'password')
.afterJSON(function (body) {
//changed values
expect(body.error.newEmail).toMatch('missing-required-key'),
expect(body.error.oldEmail).toMatch('missing-required-key')
})
.toss();
Expected JSON response body
{
"error": {
"newEmail": "missing-required-key",
"oldEmail": "missing-required-key"
}
}
Test results in following error:
Cannot read property newEmail of undefined
I have wrote a similar test in Postman which works, I am trying to convert these tests into something that can be used by node.js.
Postman working example:
var body = JSON.parse(responseBody);
//error messages
var newEmailError = body.error.newEmail;
var oldEmailError = body.error.oldEmail;
tests["New Email Error Message"] = newEmailError === "missing-required-key";
tests["Old Email Error Message"] = oldEmailError === "missing-required-key";
Turns out the endpoint supplied was incorrect, test is valid with the correct endpoint!
you need too do that:
frisby.create('GET JSON data with parameters')
.get('your_json_url.com')
.expectStatus(200)
.expectHeader('Content-Type', 'application/json')
.expectJSON({ 'error': {
'newEmail': 'missing-required-key'
}})
.toss();

Categories

Resources