Cannot connect mobile app into browser REST api - android

I have a working react-native app installed in my mobile device. Also, I have an REST server running in my machine.
GOAL
I want that the app should communicate to the REST api server. I am trying to use REST method to fetch data from the site.
PROBLEM/MOTIVATION
I was able to use my router's IP address as connection for the app and api.
Below are the URLs that can be accessed in both mobile browser and my machine/laptop browser.
Api browser debugger-ui runs in
http://192.168.254.106:8081/debugger-ui
REST server runs in
http://192.168.254.106/open_social
Things I've done so far:
I modified the app using jsonapi(waterwheel) code to fetch or update data from the site.
const Waterwheel = require('waterwheel');
const waterwheel = new Waterwheel({
base: 'http://192.168.254.106/open_social',
timeout: 10000,
oauth: {
grant_type: 'password',
client_id: '2249cb82-98d8-49ea-a32b-a7cff7912a7e',
client_secret: 'admin',
username: 'admin',
password: 'n8hBtuFY8R',
scope: 'administrator'
}
})
waterwheel.jsonapi.get('node/test', {}, "1cc692b8-c37c-4ee7-942f-b2e6b8276f35")
.then(res => {
console.log(res)
console.log('Success..!')
})
.catch(err => {
console.log(err)
})
Waterwheel is a module added in the node_modules folder in my app when running npm install waterwheel
CORS are also enabled in my api:
cors.config:
enabled: true
allowedHeaders: ['Content-Type', 'Access-Cntrol-Allow-Headers', 'Authorization']
allowedMethods: ['POST', 'GET', 'OPTIONS', 'PATCH', 'DELETE']
allowedOrigins: ['*']
exposedHeaders: true
maxAge: false
supportsCredentials: true
Manifest permission is also enabled
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
Expected result: I should be able to get response from the REST api server when this code is added in the app.
I think there is no connection made from the app and api that I need to resolve and need help from this.
TIA

Are you testing on IOS device? check this!
PS: I don't have enough reputation to make a comment
By default, iOS will block any request that's not encrypted using SSL.
If you need to fetch from a cleartext URL (one that begins with http)
you will first need to add an App Transport Security exception. If you
know ahead of time what domains you will need access to, it is more
secure to add exceptions just for those domains; if the domains are
not known until runtime you can disable ATS completely. Note however
that from January 2017, Apple's App Store review will require
reasonable justification for disabling ATS. See Apple's documentation
for more information.

Related

Vue-cli on android - cannot call any endpoint from my local node server

I'm developing a Vue-cli + Node (express) application, Vue-cli is running on port 8080 and my node server in port 3001, both on localhost.
When I test my app in my desktop it works just as expected, I can navigate the entire app without issues, the problem appears when I test it on android, for all request the frontend does to the backend I get this error whether using fetch or axios:
<whatever rest verb> https://localhost:3001/<whatever endpoint> net::ERR_CONNECTION_REFUSED
My axios instance:
const http = axios.create({
baseURL: 'https://localhost:3001',
withCredentials: true
});
And this is the relevant node server configuration:
const corsOptions = {
origin: 'https://<my local ip>:8080',
credentials: true,
};
app.use(cors(corsOptions)); // <-- this is located before any other express middleware
I'm setting credentials in both part because I'm using passportjs and cookie-session to handle login, but this issue is present even in a simple GET request, also I already tried without setting credentials but the issue persists.
Axios version 1.2.1
cors middleware version 2.8.5
The solution was to use the local ip (as oppose to localhost) also in the baseUrl for the axios instance:
const options = {
baseURL: 'https://<your local ip>:<your api's server port>',
withCredentials: true
}

Why does preflight fail on newer Android devices but not older devices and desktop?

I am using the axios package to send API requests from my app to my backend
The backend is built with Laravel and is a simple rest API
If I run the Ionic app on desktop using ionic serve it works fine
If I build an APK and install the app:
on an Android device with version 8 or below it works fine
on an Android device with version 9 or above the preflight checks keep failing
(This is using the remote devices feature by Chrome to inspect requests from the app)
Initially, the requests wouldn't even send and it would error out before then and so I setup my network security configuration for Android and that removed the initial error but now it keeps failing on preflight
My suspcion is that it might be related to the fact that the requests on the app are being sent from http://localhost but I'm not sure how to resolve this. Can you force it to use an SSL? If so, how?
My CORS setup for Laravel is more leniant than the default CORS config that Laravel ships with:
return [
'paths' => ['*'],
'allowed_methods' => ['*'],
'allowed_origins' => ['*'],
'allowed_origins_patterns' => [],
'allowed_headers' => ['*'],
'exposed_headers' => [],
'max_age' => 0,
'supports_credentials' => false,
];
The issue was related to SSL/TLS pinning which axios doesn't deal with as per this comment.
The Ionic Native HTTP plugin handles the pinning natively and that worked for but of course it makes use of Cordova which isn't available on your non-mobile devices.
There were two potential solutions:
Implement SSL/TLS pinning natively
Create a service/factory which determines whether we want to use axios or the native plugin
I opted for #2 - if you want information regarding #1, see this answer.
The following method for me determines which HTTP wrapper to use:
static makeRequest() {
return isPlatform('cordova') ? cordovaHttpService : axiosHttpService;
}
Of course, that can be amended with each specific case.

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.

One Specific API call fails only on Android ( react-native )

I have a react-native app that logs in and grabs user information based on the following code:
TestCall(){
console.log(`${myLocation}/login`);
return fetch(`${myLocation}/login`, {
method:'POST',
headers: {Accept: 'application/json',
'Content-Type': 'application/json',
},
credentials: "include",
body: JSON.stringify({
email:this.state.email,
password:this.state.password,
})
})
.then(response => response.json())
.then(json => console.log(json))
}
I have different options for myLocation because I'm using this to grab account info from multiple sites that all allow login based on email and password. All start with https. This works perfectly for IOS no matter what I'm using for myLocation. This works perfectly on Android for all except for one option for myLocation.
For this specific combination of this specific myLocation and Android I get 'Network Request Failed' no matter which user account I try. This includes user account/myLocation combos that work on IOS. It's not giving me a 400, or 404, or 500 or anything like that because it's not even getting that far. When I use catch to see what's happening it just goes straight to the catch and always gives me the 'Network Request Failed' as the error that's being logged.
Also, I am using Android emulator and IOS simulator on the same computer so IP gateway and IP address are the same for both. Only user agent is different (okhttp for android and CFNetwork Darwin for IOS). I'm stumped. Does anyone know why this would be happening? Thanks!
This problem went away after the certificate was changed for the site that I was having a problem with.

Categories

Resources