POST JSON with iron-ajax to SQL Server with Node - sql-server

I am trying to return user data from a login with Polymer. I have it working with Postman, but am having trouble translating it into Polymer.
In Postman this returns a JSON object, but in Polymer it is returning undefined.
Polymer Client Code [Connecting to node.js server]
<iron-ajax id="ajaxUser"
url="http://localhost:8080/login"
method="post"
handle-as="json"
content-type="application/json"
headers='{"Access-Control-Allow-Origin": "*"}'
params="[[params]]"
on-response="saveUserCredentials"
last-response="{{user}}"></iron-ajax>
...
<paper-input id="username"></paper-input>
<paper-input id="password"></paper-input>
<paper-button on-tap="loginUser"></paper-button>
...
loginUser() {
this.params = {"username": this.$.username.value, "password": this.$.password.value};
console.log(this.params); // logs this.params as populated JSON
let request = this.$.ajaxUser.generateRequest();
request.completes.then(req => {
console.log(req); // logs <iron-request></iron-request>
console.log(this.user); // logs []
})
.catch(rejected => {
console.log(rejected.request); // not returned
console.log(rejected.error); // not returned
})
}
saveUserCredentials() {
console.log(this.user);
}
Node, Express, mssql Server Code [Connecting to SQL Server database]
app.post("/login", (req, res) => {
session.login(req, res)
})
...
exports.login = (req, res) => {
sql.connect(config.properties)
.then(pool => {
pool.request()
.input('user', sql.VarChar(50), req.body.username)
.input('password', sql.VarChar(50), req.body.password)
.query("SELECT role FROM Login WHERE username = #user AND password = #password")
.then(response => res.send(response))
.catch(err => res.send(err))
})
}
Error
SyntaxError: Unexpected token # in JSON at position 0 .
at JSON.parse ()
at createStrictSyntaxError (C:\node_modules\body-parser\lib\types\json.js:157:10)
at parse (C:\node_modules\body-parser\lib\types\json.js:83:15)
at C:\node_modules\body-parser\lib\read.js:121:18 .
at invokeCallback (C:\node_modules\raw-body\index.js:224:16)
at done (C:\node_modules\raw-body\index.js:213:7)
at IncomingMessage.onEnd (C:\node_modules\raw-body\index.js:273:7)
at IncomingMessage.emit (events.js:159:13)
at endReadableNT (_stream_readable.js:1062:12)
at process._tickCallback (internal/process/next_tick.js:152:19)

The first issue appears to be that your server is expecting a JSON object in the request, but the server sees the request as a string due to a missing Content-Type header on your request. To set the Content-Type for JSON requests, set <iron-ajax>.contentType to application/json:
<iron-ajax content-type="application/json" ...>
OK, I was able to get a server response by setting content-type=application/json as an iron-ajax property. Am now getting Unexpected token # in JSON at position 0 as a server side error...
That sounds like the request is not actually valid JSON (since it contains a # as the first character). Use the Chrome DevTools Network Panel to inspect the actual contents of the payload. I wouldn't rely solely on console.log in your code.
Also, it is now making two requests when I submit. One with the content-type set, which resolves to 200, and one with that resolves to 400 with parsing error.
The first message is likely the preflight request, which is sent (as part of CORS) to the server to check whether content-type="application/json" is an allowed header. The second message is the intended data request, but that fails with the following error.
And a client side error of No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:4001' is therefore not allowed access. The response had HTTP status code 400.
Your server needs to enable CORS requests. There are various ways to accomplish this, but the simplest Node solution might be to use the cors package:
const cors = require('cors');
const app = express();
app.use(cors());

Related

csurf with React: Invalid token after changing user

I've had csrf protection with the csurf module working for a while now on my React SPA. I am also using passport for authentication. I do not do any server-side rendering, so the server sends a csrf token in the response body to the client when it hits the /users/current endpoint, which is protected with csrfProtection, something like this:
import csrf from 'csurf';
const csrfProtection = csrf();
router.get("users/current", csrfProtection, async function(req, res)
{
.....
res.write(JSON.stringify({ ..., csrfToken: req.csrfToken() }));
res.end();
}
On the client side I then add the token to all subsequent request headers, a bit like this:
axiosInstance.get("/users/current")
.then(resJson =>
{
axiosInstance.interceptors.request.use(config =>
{
config.headers["x-csrf-token"] = resJson.data.csrfToken;
return config;
});
}
My first question is how the first request even manages to pass the csrfProtection without a token in its header. Yet since the token can only be accessed on the server to send to the client if the route is csrf protected, I don't see a way around this, and it does work somehow.
However, recently I have been getting "ForbiddenError: invalid csrf token" when a user logs in or deletes their account. This has only started happening after I upgraded all my node packages to the latest versions. First the client makes a request to /users/login to submit the username & password, and then makes a request to /users/current to get the new csrf token:
axiosInstance.post("/users/login", {
"username": login.username,
"password": login.password
})
.then(async resJson =>
{
// *code to update user details in redux store*
// ......
axiosInstance.interceptors.request.use(config =>
{
config.headers["x-csrf-token"] = undefined;
return config;
});
return resJson;
})
.then(async resJson =>
{
const { csrfToken } = await axiosInstance.get("/users/current")
.then(resJson => resJson.data);
axiosInstance.interceptors.request.use(config =>
{
config.headers["x-csrf-token"] = csrfToken;
return config;
});
return resJson.data;
}
I suspect it's something to do with subsequent requests coming from a different userId (which I obtain from req.user[0].userId), with which csurf will not accept the previously issued token. But I have no idea how to issue the new token csurf does expect, to the client. And it still doesn't explain why what I had before has suddenly stopped working since none of my logic has changed. This isn't the kind of error I'd typically expect after package updates.
Here someone mentions you can just set any header on the client and have the server check for that. atm I am adding the csrf token to all the client's request headers and using the csurf module's request handler function to check it, but there is nothing stopping me from writing my own. If this is true, the value of the header doesn't even matter, just that it exists. I am holding off on this option though because I feel there is something basic I'm not understanding about my current setup, which once rectified will mean this can be easily fixed.
Would appreciate any help or explanation! Thanks 🤍

Getting CORS error rather than expected 429 response when rate limit is reached

In my backend I implemented an IpRateLimit middleware with the AspNetCoreRateLimit package in my .net core entity framework backend. When an IP Address x makes y calls in a specific time, it gets blocked for a certain time and the backend should return an 429 error and this works fine when testing with postman. But when the I make an request with axios, that is supposed to get blocked because of the ip rate limiter, I receive an axios error:
"Access to XMLHttpRequest at 'https://localhost:44372/api/Users/Login/' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource."
"POST https://localhost:44372/api/Users/Login/ net::ERR_FAILED"
After receiving this error, I have added the needed header, but it hasn't changed the result. Other axios requests to my backend (also post, put and delete) work fine, but when the ip rate limiter hits, I just get the cors error.
I implemented the limiter in my application as in the following tutorial:
https://edi.wang/post/2019/6/16/ip-rate-limit-for-aspnet-core
React axios request:
async function buildPostAndFetch(url, param, header) {
const finalurl = `${BASE_URL}${url}`;
return axios.post(finalurl, param, {headers:{"Access-Control-Allow-Origin": "*"}})
.then(res => {
response(res);
return res.data ? res.data : true;
})
.catch(err => {
handleError(err);
return false;
})
}
handleError() {
const handleError = err => {
setError(true);
if(err.request?.status === 0) {
console.log("*********************************************")
console.log(err.request);
console.log("*********************************************")
// throw new Error("API is currently offline or you are not connected to the internet:(");
} else if(err.response.status === 429) {
console.log("*********************************************")
console.log(err.response);
console.log("*********************************************")
}
}
}
When requestion and limiter hits I always get in the err.request.status === 0 path.
Most server systems/runtimes by default don’t add application-set headers to 4xx and 5xx responses but instead only add them to 2xx success responses and maybe to 3xx redirects.
So you may need to do explicit config to force headers to get added to 4xx responses, in order for that 429 response end up with the Access-Control-Allow-Origin header.
In Apache and nginx for example, that’s done by adding the always keyword to the header-setting directive. Maybe your server system has some similar thing.
You get a CORS error because that 429 error has no Access-Control-Allow-Origin header.
Firstly, ensure that you have Install-Package Microsoft.AspNetCore.Cors from NuGet.
Then, please add the following to your Startup.cs
public void ConfigureServices(IServiceCollection services)
{
// Put it before AddMvc() if there's any
services.AddCors();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
// Put it before UseMvc() if there's any
app.UseCors(
options => options
.WithOrigins("http://localhost:3000")
.AllowAnyHeader()
.AllowAnyMethod()
);
app.UseMvc();
}
After finish the above configuration, by inspecting on your network tab (Browser's developer tools) you may see the one of the returned response header is access-control-allow-origin : http://localhost:3000.
Try:
app.UseCors(opt => opt
.WithOrigins("http://localhost:3000"")
.AllowAnyHeader()
.AllowAnyMethod());
The protocol (http/https) can not be omitted. Also, the cors middleware should be placed after app.UseRouting but before UseAuthorization.
You can see Middleware Order .

Blocked by CORS policy "...does not have HTTP ok status" (Amplify and ReactJS, AWS Gateway and Lambda)

I'm almost embarassed to be asking this question due to CORS support out there on SO but I can't get by:
Access to XMLHttpRequest at 'https://a93xxxxx.execute-api.eu-west-1.amazonaws.com/dev[object%20Object]' from origin 'https://www.example.com' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: It does not have HTTP ok status.
I've even published my React project with Amplify and attempted it from the real domain name to even eliminate anything to do with the development environment (Cloud 9 running npm version 6.14.8)
I've also made a test running Chrome with the --disable-web-security flag.
My Lambda function contains the following (out of the box stub)
exports.handler = async (event) => {
// TODO implement
const response = {
statusCode: 200,
// Uncomment below to enable CORS requests
headers: {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Headers" : "Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With",
"Access-Control-Allow-Methods" : "OPTIONS,POST,GET,PUT"
}
,
body: JSON.stringify("Hello from Lambda!")
};
return response;
};
Note that I've uncommented the CORS request part and the response statusCode is set to 200.
The code in my application that execute when a submission form is sent from the client:
uploadcontactusdata = async data => {
try {
console.log("Contact Us pressed")
const settings = {
method: 'POST',
body: JSON.stringify(data),
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
}
}
const fetchResponse = await API.post('econtactus', settings);
Notification({
title: 'Success',
message: 'Notification has been sent',
type: 'success'
});
}
catch (err) {
console.log("unable to send");
console.error(err)
}
}
I created the API Gateway + Lambda using Amplify (version 4.41.2). Not sure where else to look now. Any clues will be appreciated. Thanks
You can completely get past the need for api gateway by using appsync.
amplify add api
Choose graphql (I have not tried using rest but you shouldn't need it) choose the basic schema, edit it if you'd like, and publish. Once it's published you can create your own method. You can view this inside the AppSync UI under Schema.
type Mutation {
yourMethod(input: Input!): TableName <-- add your method to the list
}
Now inside Appsync choose Data Sources and add datasource. Give it a name, choose lambda as the type, then find your lambda in the list. Once it's added go back to your schema and find the method you created above. On the right side bar locate your method and click the attach link. Find the data source you just added. Fill out the region and lambda ARN. MAKE SURE you choose new role and not an existing one.
You might need to configure the request and response templates.
For request:
{
"version" : "2017-02-28",
"operation": "Invoke",
"payload": $util.toJson($context.args)
}
For response:
$util.toJson($context.result)
Now you can call your lambda directly from the UI and return your result without worrying about CORS or managing API Gateway.

Microsoft Azure - OAuth2 - "invalid_request"

I would like to connect my app with Microsoft Graph. I created my web-app in Azure (I have my client_id and client_secret). I am able to send a request to get the authorization code from https://login.microsoftonline.com/common/oauth2/v2.0/authorize.
The problem is that when I send a POST request in order to get the acess token from https://login.microsoftonline.com/common/oauth2/v2.0/token (exactly like said here in the "Using permissions" section) using Postman (with form-data option), I get an "AADSTS9000410: Malformed JSON" error:
{
"error": "invalid_request",
"error_description": "AADSTS9000410: Malformed JSON.\r\nTrace ID: f5c1dd4b-ad43-4265-91cb-1b7392360301\r\nCorrelation ID: 1dea54ed-bb43-4951-bc9e-001877fe427b\r\nTimestamp: 2019-01-14 21:38:42Z",
"error_codes": [9000410],
"timestamp": "2019-01-14 21:38:42Z",
"trace_id": "f5c1dd4b-ad43-4265-91cb-1b7392360401",
"correlation_id": "1dea54ed-bb43-4951-bc9e-001878fe427b"
}
Moreover, when I send the same request with a raw option in Postman, I get "AADSTS900144: The request body must contain the following parameter: 'grant_type'":
{
"error": "invalid_request",
"error_description": "AADSTS900144: The request body must contain the following parameter: 'grant_type'.\r\nTrace ID:a7c2f8f4-1510-42e6-b15e-b0df0865ff00\r\nCorrelation ID:e863cfa9-0bce-473c-bdf6-e48cfe2356e4\r\nTimestamp: 2019-01-1421:51:29Z",
"error_codes": [900144],
"timestamp": "2019-01-14 21:51:29Z",
"trace_id": "a7c2f8f4-1510-42e6-b15e-b0df0865ff10",
"correlation_id": "e863cfa9-0bce-473c-bdf6-e48cfe2356e3"
}
However, when I remove application/json in my header in Postman, and I put x-www-form-urlencoded option, everything looks fine.
I can only send POST requests with a JSON format in my application.
Does Microsoft Graph support JSON format for POST requests?
Is it a Postman issue?
I ran into a similar issue, but realized that there was a mismatch between the Content-Type: application/x-www-form-urlencoded header and the JSON-formatted request body. If you refer to this documentation, you'll see that the request body needs to be URL encoded (concatenated with ampersands, encoded entities, etc.), which ultimately resolved my issue. So, I don't believe this is an issue with Postman or MS APIs but, rather, just incorrect formatting of your request body.
I'm not sure what language your app uses, but here's an example using Node and Express that works for me:
const fetch = require('node-fetch')
const { URLSearchParams } = require('url')
async function getAccessToken(req, res, next) {
try {
const response = await fetch('https://login.microsoftonline.com/common/oauth2/v2.0/token', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
// Previously I was doing `body: JSON.stringify({...})`, but
// JSON !== URL encoded. Using `URLSearchParams` (or whatever
// the equivalent is in your language) is the key to success.
body: new URLSearchParams({
client_id: YOUR_CLIENT_ID_HERE,
scope: 'User.Read Calendars.Read',
redirect_uri: YOUR_REDIRECT_URL_HERE,
grant_type: 'authorization_code',
client_secret: YOUR_CLIENT_SECRET_HERE,
code: req.query.code
}
})
const json = await response.json()
// `json` will have `access_token` and other properties
} catch (err) {
throw err
}
}
Hope that helps!
It is neither a Microsoft nor a Postman issue, it is simply how OAuth defines the token workflow. This is defined in RFC 6749 - Section 4.1.3:
The client makes a request to the token endpoint by sending the following parameters using the application/x-www-form-urlencoded format per Appendix B with a character encoding of UTF-8 in the HTTP request entity-body

React admin-on-rest adding X-Total-Count

I'm using admin-on-rest but getting an error when trying to connect to github api
Error:
The X-Total-Count header is missing in the HTTP Response. The jsonServer REST client expects responses for lists of resources to contain this header with the total number of results to build the pagination. If you are using CORS, did you declare X-Total-Count in the Access-Control-Expose-Headers header?
and
Warning: Missing translation for key: "The X-Total-Count header is missing in the HTTP Response. The jsonServer REST client expects responses for lists of resources to contain this header with the total number of results to build the pagination. If you are using CORS, did you declare X-Total-Count in the Access-Control-Expose-Headers header?"
I'm trying to add the X-Total-Count header but then got a new error
render() {
const httpClient = (url, options = {}) => {
if (!options.headers) {
options.headers = new Headers({Accept: 'application/json'});
}
// add your own headers here
options.headers.set('X-Total-Count', '32');
return fetchUtils.fetchJson(url, options);
}
const restClient = jsonServerRestClient('https://api.github.com', httpClient);
return (
<Admin restClient={restClient}>
<Resource name="users" list={PostList}/>
</Admin>
);
}
Failed to load https://api.github.com/users?_end=10&_order=DESC&_sort=id&_start=0: Request header field x-total-count is not allowed by Access-Control-Allow-Headers in preflight response.
At your backend API function need to get X-Total-Count and set it to Response Header
Example:
exports.findAll = (req, res) => {
var total = Data.countAll()// your count all function
Data.findAll({ where: condition })
.then(data => {
res.set('Access-Control-Expose-Headers', 'X-Total-Count')
res.set('X-Total-Count', total)
res.send(data);
})
.catch(err => {
res.status(500).send({
message:
err.message || "Some error occurred while retrieving data."
});
});
};
As kunal pareek said, this header must be part of the response, not the request for this jsonRestClient.
You'll have to create a custom restClient specific to the github api.
Please read https://marmelab.com/admin-on-rest/RestClients.html#writing-your-own-rest-client.

Resources