React native + Axios not persisting cookies across requests - android

I have an react native app that talks to my Node server running on my laptop(http://192.168.x.x:3000). Even after performing an successful login, the consequent calls to my server fails with an 401 status. I see that no cookies are passed on the server.
Some observations:
1) My React Native app works just fine when i deploy my code to an actual server and use a proper domain(http://example.com) while making the API calls.
2) Using postman, I am able to authenticate myself and make consequent successful API calls to my local server.
This is a really strange issue and am sure I have missed out something small, but have been stuck with it for quite some time now.
Any hints/suggestions on what can be done?
Thanks,

I recognize this is a little old, but if anyone is still facing issues, I found that the following steps resolved it for me
Include withCredentials on all requests from the react app
Used the cors middleware server side. Be sure to specify the domain (in my case, my VM's IP address with port) and set credentials to true
Increase the maxAge property -- I think the default was like 10 seconds so it kept seeming like my session wasn't persisting when in reality it was just expiring too fast.
An example for steps 2 and 3 might look something like:
const express = require('express');
const path = require('path');
const session = require('express-session');
const cors = require('cors')
const app = express();
// STEP 2
app.use(cors({
credentials: true,
origin: '192.168.0.100:3000',
methods:['GET', 'POST', 'PUT', 'DELETE']
}));
// STEP 3
app.use(session({
cookie: {
maxAge: 86400000 // one day
}
}));
EDIT:
Also worth mentioning that if you're running your server on a virtual machine (like me) to be sure your virtual machine's date is consistent with the date of the device running your react app. I discovered the hard way that this also manifests as Axios appearing to lose sessions.

Related

Google Cloud socket.io server, client not keeping persistent connection

I have a Node.js server using socket.io to connect Android apps and I've been hosting it locally by just running it in my IDE and connecting to my local IPv4 address but I want it to work without me having to keep my PC running constantly so I've tried using Google Cloud and managed to get it mostly working but the client doesn't keep the connection and disconnects consistently.
I followed this tutorial up to step 4 after that I ran gcloud app deploy.
My Node.js server is in one file, it has these declarations at the top.
const express = require("express");
const app = express();
const http = require("http");
const server = http.createServer(app);
const { Server } = require("socket.io");
const io = new Server(server);
I then have this for the initial client connection.
io.on("connection", (socket) => {
console.log("User connected.");
Everything inside of this is just listeners for what gets emitted by the client so I don't think they're the problem.
And then outside of that I have
server.listen(8080, () => {
console.log("Server is listening");
});
I don't know if anything from the package.json file is relevant but I can provide it if need be.
After deploying to Google Cloud using the tutorial there are a few things in the logs that may be the reason behind the problem.
Waiting for network connection open. Subject:"app/invalid" Address:127.0.0.1:8080
Waiting for network connection open. Subject:"app/invalid" Address:127.0.0.1:8081
Wait successful. Subject:"app/invalid" Address:127.0.0.1:8080 Attempts:97 Elapsed:485.916418ms
App is listening on port 8080. We recommend your app listen on the port defined by the PORT environment variable to take advantage of an NGINX layer on port 8080.
These might have simple solutions but I have very little experience with server hosting.
Once my Android client connects to the server, the log outputs "User connected." as it should, then around 10 seconds later it does it again and this repeats. There's no error I can see between the connections just a few socket.io POST/GET requests.
I tried adding session affinity to the app.yaml but hasn't solved it either.
This is my app.yaml if any changes need to be made here
runtime: nodejs16
env: standard
instance_class: F1
automatic_scaling:
min_idle_instances: automatic
max_idle_instances: automatic
min_pending_latency: automatic
max_pending_latency: automatic
network:
session_affinity: true
Thanks for any help, if I need to provide any other files I can do so.
Socket communication requires persistent network connections. So, App Engine Flexible provides an option to keep the network connection alive.
I think you are deploying your app in App Engine Standard , which does not support this option.
So, you can deploy your app in App Engine Flexible and you need to include the following configuration in your app.yaml
network:
session_affinity: true
For refence on session affinity, please refer to this page https://cloud.google.com/appengine/docs/flexible/nodejs/using-websockets-and-session-affinity#session_affinity
So, your updated app.yaml can look like this
runtime: nodejs
env: flex
network:
session_affinity: true
Please refer to this page for supported yaml configuration for flexible environment - https://cloud.google.com/appengine/docs/flexible/nodejs/reference/app-yaml
Messages you have pasted are most likely not indicating a problem. Your application is running in a sandbox environment for which a container instance and a web server must be initialized the first time that your app engine service receives a request. This is also called Loading request and the messages you see indicate the startup of your container instance and your webserver.
You can take a look at Google's own documentation regarding handling requests in node.js or follow a quickstart guide.
If there are no other logs during the disconnection, I would suggest checking the quotas to see if you're not exceeding any.

Why can't I 'fetch' some URLs from my Capacitor app on Android?

I'm trying to write a mobile app via Capacitor that makes use of PouchDB. When I run the app in the emulator via Android Studio the connection to the remote CouchDB instance fails. I've tracked this down to a failure in the fetch API for certain URLs when running on Android.
To debug I made a minimal web application and wrapped it using Capacitor to run on Android. The app includes the following code
const testFetch = (url) => {
console.log("Testing fetch", url)
fetch(url)
.then((response) => response.text())
.then((t) => {
console.log("Respose from fetch:", url)
console.log(t)
console.log("that was it")
})
.catch((reason) => {
console.log("FETCH FAILED", url, reason)
})
}
I then have three tests:
testFetch("https://jsonplaceholder.typicode.com/todos/1"); // just some JSON
testFetch("http://10.0.2.2:5984/simple"); // local pouchdb instance
testFetch("http://10.0.2.2:8080/sample.json"); // local http server + CORS
The second two use the IP address that is an alias for the development machine when running in the Android emulator. I confirmed that I can access all of these URLs from the browser on the emulator but the app succeeds on the first and fails on the second two (error: TypeError: Failed to fetch). When running the base web app in the browser, all succeed (with localhost instead of 10.0.2.2).
CORS headers are in place on all URLs. As far as I can see the app doesn't even try to access the two servers that fail - no HEAD requests for example. I've also tried various other URLs and can't see a pattern to the failures -- eg. it's not the port number != 80.
Any clues as to what is going on would be appreciated.
So the thing I didn't notice that the failing URLs had in common was http rather than https. It turns out that fetch silently fails to work for any http URL, just giving the error 'Failed to fetch'.
I'm not sure whether this is a feature of the Android web view or of Capacitor itself. The Capacitor docs suggest that using https is a good idea but not that http won't work.
This policy doesn't get altered by setting a Content Security Policy in the main page header.
The original goal was to connect a local PouchDB database to a remote CouchDB instance. This now works as long as the CouchDB instance is served via https. Without that you just get silent failure to sync.

How do I get http content from a https website?

I get "Http failure response for https://www.google.com: 0 Unknown Error" when I request from Secured url.
I am trying to test my ionic/angular mobile app. When I tried with http requests I had problem with Android 9, but with Android 7 was working fine. Anyhow I need to set my backend to public https server. So now I'm testing with https request and none of 7 and 9 Android versions works.
I am using Angular 7 ,
"#ionic/angular": "^4.6.1",
"#ionic-native/core": "^5.0.0",
"rxjs": "~6.5.1"
I made these small functions in order to make my problem simpler.
inside my html file i have this code:
myFile.html
<ion-button
(click)="onStartTest()"
>Click me</ion-button>
<p id="testme"></p>
myFile.page.ts
onStartTest() {
this.taskService.onTest().subscribe(result => {
document.getElementById('testme').innerText = 'result ' + result;
console.log(result);
}, error => {
document.getElementById('testme').innerText = error.message;
console.log('Problem ', error.message);
});
}
myTask.service.ts
onTest() {
return this.http.get('https://www.google.com').pipe(
catchError(err => {
return throwError(err);
})
);
}
At first I tried my server's URL but I changed it to "https://www.google.com" just to verify that the backend is correct.
Also I have an interceptors.ts file that I am using it for authentication, but I am not logged in when I execute the onStartTest() function, but im gonna share it anw.
interceptors.ts
import {Injectable} from '#angular/core';
import {HttpEvent, HttpHandler, HttpInterceptor, HttpRequest} from '#angular/common/http';
import {Observable} from 'rxjs';
#Injectable()
export class TokenInterceptor implements HttpInterceptor {
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
const token = localStorage.getItem('auth_token');
let newHeaders = req.headers;
if (token) {
console.log(token);
newHeaders = newHeaders.set('Authorization', 'Token ' + token);
const modified = req.clone({
headers: newHeaders
});
return next.handle(modified);
} else {
newHeaders = newHeaders.set('Content-Type', 'application/json');
const modified = req.clone({
headers: newHeaders
});
return next.handle(modified);
}
}
}
I think these are the necessary files to share for this problem.
I also tested the url of google with Postman just to be sure that I should get a status 200
I am also aware that there is an "add_header" directive (nginx) that adds 'Allow-access-control-origin' when the response code is 20x or 30x. According to my screenshot with Postman, google is responding with 200 status, but my app still gets status 0 error.
Ignore the first Error. It's a function I use with http when the app begins. Right now im testing https.
I tried superficially to use ionic-native library HTTP but my app totally crashed.
I also execute the command ionic serve --ssl but still nothing.
I read somewhere that for secured connection I need a certificate, but I understood that this is a server's work.
I tried to request from Dark Sky from Vanilla JavaScript and it works fine. So there is something wrong with angular/ionic side and not server's.
What am I missing? I really need to fix this problem soon!
I want to send a secured request to an https url and get the appropriate response.
Your main problem is that you are trying make an API to an unsecure call (http) location (http://192....../mobile/tasks) from a secure origin (https://localhost:8100).
This is clearly indicated in your error message and this is not allowed, and has been answered before
Your second problem is that, for testing purposes, you are trying to call a 3rd party https ressource from your website. This only works if the 3rd party ressource implement CORS, which is not the case for Google and api.darksky.net. Sending a GET request with Postman is useless, as Postman will not check for CORS headers before displaying the response. If you want to use Postman to check CORS, send an OPTIONS request to these ressources and you'll see that there are no CORS headers
So the answer is in MDN - CORS
For security reasons, browsers restrict cross-origin HTTP requests
initiated from scripts. For example, XMLHttpRequest and the Fetch API
follow the same-origin policy. This means that a web application using
those APIs can only request resources from the same origin the
application was loaded from, unless the response from other origins
includes the right CORS headers.
This means the back-end I was using needed some more configuration since I was using 'same-origin' policy script. I thought we had it because when we tried from the browser's console to fetch the request it was working fine, but on mobile it wasn't. We had a custom CORS configuration but we changed it to the django-cors-headers. Since we switched to django-cors-headers I could get correctly the response from HTTP and HTTPs requests.
The other answer and comments were really useful to focus to the right direction.

Ionic Cordova web app cookies not being stored on Android or iOS

My company has built a medium size Ionic application and now we're trying to make available natively for Android and iOS and I'm having an issue with persisting cookies.
When running the application Safari there are no issues at all, cookies persist just fine, but when building for devices, the cookies are not being persisted.
The way things work now is when a user logs in, a cookie is returned from the server. After this, the cookie should be sent with every subsequent request, ensuring that the user is still logged in. When I look at the initial response from the server after login, the 'set-cookie' header is there. However, all requests afterward don't contain the cookie, so the server returns a response of not logged in. This only happens when running natively on iOS or Android.
When I debug to see if the cookies are being persisted, I see that there are no cookies.
I know there was a similar issue with iOS 13, but I'm not using iOS 13, I'm using iOS 12. And my problem is on both Android and iOS. When doing Google searches, all the workarounds I see are for problems that are not the same as the one I'm having, and I've tried those workarounds anyways, and they don't work.
Has anyone experienced this issue before? Any ideas on what this might be and how to fix it would be greatly appreciated, I haven't gotten anywhere and I've been searching for an answer for days now.
Also too, because the cookie is HttpOnly I'm not able to access using TypeScript, so I can't persist the cookie manually without sending a non-HttpOnly cookie. Which isn't really an option at the moment.
i had some troubles with cookies and cordova too .
In my case, the plugin cordova-plugin-cartegraph-cookie-master helped me to get and set cookies.
Source Plugin:
https://www.npmjs.com/package/cordova-plugin-cartegraph-cookie-master
Install :
cordova plugin add cordova-plugin-cartegraph-cookie-master
Usage :
Get cookie value :
cookieMaster.getCookieValue('http://<some host>:<some port>', '<cookie name>', function(data) {
console.log(data.cookieValue);
}, function(error) {
if (error) {
console.log('error: ' + error);
}
});
Set cookie value :
cookieMaster.setCookieValue('http://<some host>:<some port>', '<cookie name>', '<cookie value>',
function() {
console.log('A cookie has been set');
},
function(error) {
console.log('Error setting cookie: '+error);
});

Is there a way to make axios more verbose?

We're using axios to make requests from our react-native app.
Today we bumped onto an issue where an old Samsung device couldn't reach our servers, and it was because our servers no longer support old TLS. The customer tried reaching our servers through the an old Samsung browser the device had, and couldn't. After they installed Chrome they were able to reach our servers without issues.
If axios returned a more specific error, we'd have probably figured out what went wrong, but instead axios returned a generic 'Network Error'.
All I'm now asking is this, is there a way to make axios more verbose when it comes to errors, or more specific?
p.s I'm still not sure how to resolve the tsl issue itself either.
As far as I know axios on background uses two types of request structure. HTTP and XHR. Since react-native does not run on node engine it is probably using XHR and the error you are seeing is related to this line.
// Handle low level network errors
request.onerror = function handleError() {
// Real errors are hidden from us by the browser
// onerror should only fire if it's a network error
reject(createError('Network Error', config, null, request));
// Clean up request
request = null;
};
Although this is not exactly an answer to your question, might give you a start point.

Categories

Resources