Random Prelight / OPTIONS requests hanging - ios

Around November 17th we noticed in our production application that some of our IOS users we're experiencing HTTP requests that were hanging for sometimes 10-20 minutes before they would fail.
Example request:
curl 'https://api.example.com/api/' -H 'Accept: application/json, text/plain, */*' -H 'X-Custom-Header: accesstoken' -H 'timezone: America/Toronto' -H 'authorization: auth' --compressed
Example Response:
Headers: None
status: [0]
Current environment
React: 17.0.2
Axios: 1.2.0
Node: 16.3.0
react-query: 4.2.1
aws-amplify: 5.0.2
API:
ExpressJS: 4.18.2
Node: 14.20.1
Cognito-express: 2.0.19
Cors: 2.8.5
Our React application build is hosted on an AWS S3 bucket with Cloudfront. Our DNS provider is Cloudflare. The API is hosted on AWS through elastic beanstalk. The API is served on NginX.
As far as we can tell, nothing significant has changed in the last several weeks that would cause this issue. (configuration as well as code)
Our API headers:
res.setHeader(
'Access-Control-Allow-Methods',
'GET, POST, OPTIONS, PUT, PATCH, DELETE'
);
res.setHeader(
'Access-Control-Allow-Headers',
'origin,X-Requested-With,content-type'
);
res.setHeader('Access-Control-Allow-Credentials', true);
Our cors settings:
app.use(
cors({
origin: [
'https://examplewebapp1.com',
'https://examplewebapp2.com',
'https://examplewebapp3.com',
],
})
);
React Axios instance:
const instance = axios.create({
baseURL: apiURL,
timeout: 30000,
headers: { 'X-Custom-Header': 'accesstoken', 'timezone': timezone},
});
React Axios request interceptor:
instance.interceptors.request.use(
async (config) => {
try {
const session = await Auth.currentSession();
const accessToken = session.accessToken.jwtToken;
config.headers.authorization = accessToken;
return config;
} catch (err) {
if (err === "No current user")
{
console.log("There is no longer a user.")
handleInactiveUser()
}
captureException(err);
throw err;
}
},
(error) => {
return Promise.reject(error);
},
);
React Axios response interceptor:
instance.interceptors.response.use(
async (resp) => {
// irrelevant code
return resp;
},
(error) => {
// check if the preflight failed
if (typeof error.response === 'undefined') {
// preflight failed
}
if (error.response && error.response.data && error.response.data === 'No Active Account') {
// irrelevant code
}
return Promise.reject(error);
},
);
The actual endpoints that fail are seemingly random. There are quite a few endpoints with failing preflights - we don't see a pattern. (Mostly GET requests).
During debugging on safari with the MacOS debugging tools we've seen this error a few times when we were able to replicate the issue.
[Error] XMLHttpRequest cannot load https://api.example.com due to access control checks.
Here's what we know so far:
The users having these problems are users on IOS (versions 15.3.0 to 16.1.2)
Users are on LTE connection (doesn't occur on Wifi)
Chrome browser and Safari are both having the issue
Occurring on both PWA application and regular browsers
Areas we've looked into:
Removed DDOS protection on Cloudflare (thought Cloudflare was blocking requests)
Verified SSL certificates
Updated Axios from 0.27.2 to 1.2.0
Verified CORS settings
Added origin to the access-control-allow-headers
We've recently added react-query (2 months ago)
Updated our aws-amplify to 5.0.2
Verified our cognito user pool on aws is not rate limiting our authentications

Related

CORS error: The Same Origin Policy disallows reading the remote resource at http://api.com (Reason: CORS request did not succeed). Status code: (null)

Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://bayut-api-v1:4000/properties/list?purpose=for-sale&locationExternalIDs=5002&sort=city-level-score&location=dubai&page=1. (Reason: CORS request did not succeed). Status code: (null).
I am using docker I built "API" and "FrontEnd with nextjs" dockerized all of that in a custom network
named "bayut"
and exposed port 80 with Nginx *using Nginx as a reverse proxy
the traffic comes on port 80 -> port 3000 which is the nextjs app
iam getting a cors error when I use the app from localhost
but when I try to use the firefox docker image inside my "bayut" network
everything works perfectly fine
API: http://bayut-api-v1:4000
FrontEnd http://client-bayut:3000 ->no cors error when I use this domain inside the bayut network
but when I access from localhost outside the bayut network iam getting a cors error
one possible solution is to use https://nextjs.org/docs/api-reference/next.config.js/rewrites
but that solution will expose my API and I want my API to privat
I will add my index page on the server side
import express from "express";
import { Request, Response, NextFunction, Application } from "express";
import { Server } from "http";
import createHttpError from "http-errors";
const Redis = require("ioredis");
const cors = require("cors");
const client = new Redis({
port: 6379,
host: "redis-bayut",
});
// const client = new Redis(6379, "172.17.0.3");
require("dotenv").config();
const app: Application = express();
const allowedOrigins = ["http://bayut-client:3000", "http://localhost:3000" ,"127.0.0.1:3000"];
app.use(cors({
origin: allowedOrigins,
methods: ["GET"],
}));
app.get("/", async (req: Request, res: Response) => {
res.send("Hello World3!🌏");
});
// Routes
app.use("/auto-complete", require("./routes/auto-complete"));
app.use("/properties", require("./routes/properties"));
app.use("/agencies", require("./routes/agencies"));
const acceptOnlyGetRequsets = (
req: Request,
_res: Response,
next: NextFunction
) => {
if (req.method !== "GET") {
return next(createHttpError(405, "Method Not Allowed"));
}
};
// accept Only Get Requsets
app.use(acceptOnlyGetRequsets);
app.use((req: Request, res: Response, next: NextFunction) => {
next(new createHttpError.NotFound());
});
const errorHandler = (
err: any,
req: Request,
res: Response,
next: NextFunction
) => {
res.status(err.status || 500);
res.send({
message: err.message,
status: err.status,
});
};
app.use(errorHandler);
const PORT = process.env.PORT || 4000;
const server: Server = app.listen(PORT, () =>
console.log(`=> http://localhost:${PORT}/
⌛ ${new Date().toLocaleTimeString("en-us", { timeStyle: "medium" })}
`)
);
export const redisClient = client;
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://bayut-api-v1:4000/properties/list?purpose=for-sale&locationExternalIDs=5002&sort=city-level-score&location=dubai&page=1. (Reason: CORS request did not succeed). Status code: (null).
This error means that:
You are making a cross-origin request
The request failed entirely
Because it was cross-origin and it failed entirely there is no response so there are no response headers that could include CORS permission to give your JS access to that data.
(I think other things could cause the null status, but its pretty clear what it means given the context of the rest of your question).
In short: The browser can't access the URL you gave it.
but that solution will expose my API and I want my API to privat
If you want client-side JavaScript to access the API then you must expose the API to the browser.

401 Unauthorized - Chrome App Oauth to invoke Google Cloud Functions

I'm following this: https://developer.chrome.com/apps/tut_oauth
But it doesn't work. When I invoke Cloud Function, I get 401 error. The Authorization: Bearer "access-token" is added in the request header. Although another question here[1] states that ID_TOKEN should be used. Which I tried via curl but have the same 401 error.
chrome.identity.getAuthToken({interactive: true}, function(token) {
var dat = {
"user_email":email_id,
"user_id":user_id
};
$.ajax({
type: "POST",
data:dat,
dataType: 'json',
url:str,
contentType: "application/json",
error: function (xhr, status, error) {
console.log(xhr)
}, success: function (data, status, xhr) {
console.log('Success!' +data + status);
},
headers:{
'x-goog-project-id': 'xxxxxxxxxxxxx',
'Authorization': 'Bearer ' + token,
'Content-Type':'application/json',
'Accept': 'application/json'
}
});
});
[1] Why doesn't granting 'allAuthenticatedUsers' member the 'Cloud Functions Invoker' role work for google cloud functions?
The tutorial that you mentioned used "access-token" to accesses a user's Google contacts using the Google People API and the Chrome Identity API.
If you want to access a Google Cloud Function which does not Allow unauthenticated invocations you have to use an ID_TOKEN.
For testing you can create a service account with --role="roles/cloudfunctions.invoker", then create a key.json file and export the GOOGLE_APPLICATION_CREDENTIALS env variable link
Finaly you can use:
curl "https://us-central1-your-project.cloudfunctions.net/yourfunction"
# Error 403 (Forbidden)
curl "https://us-central1-your-project.cloudfunctions.net/yourfunction" -H "Authorization: bearer $(gcloud auth print-identity-token)"
#Success
I gave up on this as there is no direct solution to invoke Cloud function using oauth in Chrome Apps. The alternative solution that worked is to authenticate via API key. That is using Cloud Function with Cloud Endpoints.
I followed the logic here: https://medium.com/#akash.mahale/triggering-google-cloud-functions-with-cloud-endpoints-and-api-key-857e94a8a3aa
But just need to take note that rotation of API keys should be done regularly and automatically..

Cannot POST with ESP8266 (espruino)

I cannot make post request (get works fine) with espruino.
I've already checked the documentation and it seems pretty equal
here is my code:
let json = JSON.stringify({v:"1"});
let options = {
host: 'https://******,
protocol: 'https',
path: '/api/post/*****',
method: 'POST',
headers:{
"Content-Type":"application/json",
"Content-Length":json.length
}
};
let post = require("http").request(options, function(res){
res.on('data', function(data){
console.log('data: ' + data);
});
res.on('close', function(data){
console.log('Connection closed');
});
});
post.end(json);
The espruino console only return the 'connection closed' console.log.
The node.js server console (hosted on heroku and tested with postman) dont return anything.
Obv the esp8266 is connected to the network
What you're doing looks fine (an HTTP Post example is here), however Espruino doesn't support HTTPS on ESP8266 at the moment (there isn't enough memory on the chips for JS and HTTPS).
So Espruino will be ignoring the https in the URL and going via HTTP. It's possible that your server supports HTTP GET requests, but POST requests have to be made via HTTPS which is why it's not working?
If you did need to use HTTPS with Espruino then there's always the official Espruino WiFi boards, or I believe ESP32 supports it fine too.
you're using a package called "http" and then trying to send a request over https. You should also log out 'data' in the res.close so you can get some errors to work with.

devise token auth doesn't return access-token with React

I am moving an application from using devise to devise token auth. I have added the gem, set up everything, etc. My issue lies in the fact that when I make a request from React (using axios) to the backend it returns the user (as it should), but doesn't return the access-token headers. All, I get is:
cache-control: "max-age=0, private, must-revalidate"
content-type:"application/json; charset=utf-8"
I do get the user object from the backend though so I know it is a valid sign in. I also see tokens being added to the database:
UPDATE users SET tokens = '
Here is my call via axios:
export const userLogin = (options) => ((dispatch) => {
return axios.post(`${process.env.DOMAIN}/auth/sign_in`, {email:
options.username, password: options.password})
.then((response) => {
console.log("response is: ", response);
dispatch(setUser(response))
dispatch(loginError('', false))
})
.catch((response) => {
dispatch(loginError('Username or Password is incorrect, please try again',
true))
})
})
I have done quite a bit of research on this and tried reverting to an older version of active_model_serializers for example and this hasn't helped. The console.log above shows quite a bit of info:
config
data
headers
request
status
statusText
The response is a 200 OK.

Apollo and GraphQL CORS

Running into a frustrating issue with Apollo getting content from a Rails backend. The issue seems to be resolving around the use of CORS in my Apollo project.
Tech
apollo-client: 1.9.3
graphql: 0.11.7
react: 15.6.1
react-apollo: 1.4.16
cors.rb
Rails.application.config.middleware.insert_before 0, Rack::Cors do
allow do
origins `*`
resource '*',
headers: :any,
methods: [:get, :post, :put, :patch, :delete, :options, :head]
end
end
rails is running on port 3001 rails s -p 3001
With this backend you can make curl requests and everything works as expected
Working Curl
curl -X POST -H "Content-Type: application/json" -d '{"query": "{users{first_name}}"}' http://localhost:3001/graphql
This returns back expected data
So this is all pointing to an issue with Apollo and the frontend of the application.
index.jsx
import React from 'react';
import ReactDOM from 'react-dom';
import ApolloClient from 'apollo-client';
import { ApolloProvider, createNetworkInterface } from 'react-apollo';
import App from './containers/App.jsx';
const client = new ApolloClient({
networkInterface: createNetworkInterface({
uri: 'http://localhost:3001/graphql', <<<<< There is a different endpoint then the standard 'graphql' which is why this is declared
})
});
ReactDOM.render(
<ApolloProvider client={client}>
<App />
</ApolloProvider>,
document.getElementById('root')
);
App.jsx
import React, { Component } from 'react';
import gql from 'graphql-tag';
import { graphql } from 'react-apollo';
class App extends Component {
render() {
console.log(this.props);
return (
<div>Application</div>
);
}
}
const query = gql`
{
users {
first_name
}
}
`;
export default graphql(query)(App);
This returns the error
Failed to load http://localhost:3001/graphql: Response to preflight
request doesn't pass access control check: No
'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'http://localhost:8080' is therefore not allowed
access. If an opaque response serves your needs, set the request's
mode to 'no-cors' to fetch the resource with CORS disabled.
app.jsx (change mode)
const client = new ApolloClient({
networkInterface: createNetworkInterface({
uri: 'http://localhost:3001/graphql',
opts: {
mode: 'no-cors'
}
})
});
This returns the error
Unhandled (in react-apollo) Error: Network error: Network request
failed with status 0 - ""`
Looking at the request:
GENERAL
Request URL:http://localhost:3001/graphql
Request Method:POST
Status Code:200 OK
Remote Address:[::1]:3001
Referrer Policy:no-referrer-when-downgrade
RESPONSE HEADERS
Cache-Control:max-age=0, private, must-revalidate
Content-Type:application/json; charset=utf-8
Transfer-Encoding:chunked
Vary:Origin
REQUEST HEADERS
Accept:*/*
Connection:keep-alive
Content-Length:87
Content-Type:text/plain;charset=UTF-8 <<< I'm wondering if this needs to be application/json?
Host:localhost:3001
Origin:http://localhost:8080
Referer:http://localhost:8080/
User-Agent:Chrome/61
REQUEST PAYLOAD
{query: "{↵ users {↵ first_name↵ __typename↵ }↵}↵", operationName: null}
operationName
:
null
query
:
"{↵ users {↵ first_name↵ __typename↵ }↵}↵"
So what I did to get some sort of response was to install the Chrome Extension Allow-Control-Allow-Origin: *
If mode: 'no-cors' is removed and this extension is active, data can be retrieved.
In looking through the Apollo docs I'm unable to find much on this topic. I tried implementing the Apollo Auth Header but this simply produced the same errors as above.
What in my Apollo code could be causing these errors? And what steps are there to fix the problem?
Searching over GitHub issues and other Google searches are either for much-older versions of Apollo in which issues "have been addressed" or do not work when implemented.
Edit
Adding Ruby on Rails tag just in case there is more configuration needed in Rails. Upon researching the Apollo Client issues found Network error: Network request failed with status 0 - "" This issued was resolved by the OP because of an issue on the backend.
Issue was in the cors.rb file
Rails.application.config.middleware.insert_before 0, Rack::Cors do
allow do
origins `*`
resource '*',
headers: :any,
methods: [:get, :post, :put, :patch, :delete, :options, :head]
end
end
The issue was origins '*' had backticks instead of quotes
Was also able to remove the opts object in the index.jsx and everything is working as expected

Resources