Capacitor Android fails to fetch from api - android-studio

I'm trying to port my webapp to android by using capacitor.
I've got all but one part figured out.
Whenever I wanna get something from the databaser/server, i'm using fetch. And that works great in the browser - both on desktop and mobile.
But when I run the app through Android Studio, the fetch just fails with this error message
E/Capacitor/Console: File: http://localhost/ - Line 0 - Msg: Uncaught (in promise) TypeError: Failed to fetch
On the same device, but in the browser it works just fine.
I've tried adding a network-security-config.xml with the following
<network-security-config>
<network-security-config>
<domain-config cleartextTrafficPermitted="true">
<domain includeSubdomains="true">http://5e4ce8849526.ngrok.io</domain>
</domain-config>
</network-security-config>
</network-security-config>
I've seen other people converting to HTTPS, but I couldn't get that to work either. This is for at school project due in a few days, so a timeconsuming process is not what I need.
Can it be caused by ngrok?
This is the fetch function running on load
let url = ' http://5e4ce8849526.ngrok.io'
function autoLogin(cb) {
let key = localStorage.getItem('userToken');
if(key == null || key == '') {
return 'No key stored';
} else {
fetch(url + '/informatik/readingapp/restapi/api/users/login.php', {
method: 'post',
headers: {
'Authorization': 'Bearer ' + key
},
})
.then(res => res.json())
.then((data) => {
console.log(data)
switch(data.message) {
case 'Password not verified':
presentAlert('Forkert kodeord', 'Har du skrevet dit kodeord rigtigt?')
break;
case 'Password verified':
nav.push('nav-home')
let tk = data.jwt.token
let usnm = data.username
let usid = data.id
cb(tk, usnm, usid);
break;
case 'No login data':
presentToastHome('Intet gemt login, desværre :(')
break;
}
})
}
}
I would appreciate any help :)

try to add
<Application android:usesCleartextTraffic="true">
in the AndroidManifest.xml of your app.
Since Api29 this is needed to do http request on android.

Related

Wrong URI google OAuth authentication

I'm having a lot problems with OAuth in Google. I have 2 apps deployed in Azure (front in react, and backend in Play (scala)).
Let's suppose we can find:
backend at: "backend.azure.net",
frontend at: "frontend.azure.net".
I use Silhouette in my backend app.
My SocialAuthController :
class SocialAuthController #Inject()(scc: DefaultSilhouetteControllerComponents, addToken: CSRFAddToken)(implicit ex: ExecutionContext) extends SilhouetteController(scc) {
def authenticate(provider: String): Action[AnyContent] = addToken(Action.async { implicit request: Request[AnyContent] =>
(socialProviderRegistry.get[SocialProvider](provider) match {
case Some(p: SocialProvider with CommonSocialProfileBuilder) =>
p.authenticate().flatMap {
case Left(result) => Future.successful(result)
case Right(authInfo) => for {
profile <- p.retrieveProfile(authInfo)
res <- userRepository.getByEmail(profile.email.getOrElse(""))
user <- if (res == null) userRepository.create(profile.loginInfo.providerID, profile.loginInfo.providerKey, profile.email.getOrElse(""))
else userRepository.getByEmail(profile.email.getOrElse(""))
_ <- authInfoRepository.save(profile.loginInfo, authInfo)
authenticator <- authenticatorService.create(profile.loginInfo)
value <- authenticatorService.init(authenticator)
result <- authenticatorService.embed(value, Redirect(s"https://backend.azure.net?user-id=${user}"))
} yield {
val Token(name, value) = CSRF.getToken.get
result.withCookies(Cookie(name, value, httpOnly = false))
}
}
case _ => Future.failed(new ProviderException(s"Cannot authenticate with unexpected social provider $provider"))
}).recover {
case _: ProviderException =>
Forbidden("Forbidden")
}
})
}
My routes:
# Authentication
POST /signUp controllers.SignUpController.signUp
POST /signIn controllers.SignInController.signIn
POST /signOut controllers.SignInController.signOut
GET /authenticate/:provider controllers.SocialAuthController.authenticate(provider: String)
silhouette.conf (google part):
# Google provider
google.authorizationURL="https://accounts.google.com/o/oauth2/auth"
google.accessTokenURL="https://accounts.google.com/o/oauth2/token"
google.redirectURL="https://backend.azure.net/authenticate/google"
google.clientID="my_id"
google.clientSecret="my_secret"
google.scope="profile email"
So as you see i paste token to the link in Redirect(s"http://backend.azure.net?user-id=${user}")).
My js function in frontend app:
export function signInGoogle() {
const host = "https://backend.azure.net/"
const route = "authenticate/google";
const requestOptions = {
method: 'GET',
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
mode: 'cors',
};
return fetch(host + route, requestOptions)
}
And function with redirect
export default function GoogleSignIn() {
const responseGoogle = () => {
window.location.href = "https://backend.azure.net/authenticate/google";
}
return (
<div>
<button onClick={responseGoogle}
className="add-to-cart-button register-bttn ingsoc ggl-bttn"> Login With Google </button>
</div>
)
}
If you click login with google the second function is called.
On my google dev account I have some links. My URI Identificators:
https://backend.azure.net and http://front.azure.net
and authorized URI Redirect:
http://backend.azure.net/authenticate/google
After I click "login with google" I get 403 forbidden on site and 3 exceptions in console :
1 GET https://uj-ebiznes-back.azurewebsites.net/authenticate/google?state=very_long_link 403 (Forbidden)
Unchecked "long link on back " runtime.lastError: Could not establish connection. Receiving end does not exist.
Error handling response: "very long link to backend/authenticate/google"
TypeError: Cannot destructure property 'yt' of 'undefined' as it is undefined.
at chrome-extension://cmedhionkhpnakcndndgjdbohmhepckk/scripts/contentscript.js:535:6
Could someone explain what is wrong here? I have no idea and I tried everything.

Cant`t insert Cyrillic symbols in POST fetch URL - NodeJS

Working on Telegram bot. A user sends requests from the bot to an external website API to get information. The very usual stuff.
I'm trying to make a POST request from NodeJS (Express) backend which contains cyrillic symbols
https://somewebsite.ru/api/v1/order.json?orderId=**МУЗ**008134
it says: TypeError [ERR_UNESCAPED_CHARACTERS]: Request path contains unescaped characters
Then I was trying to use ${encodeURIComponent(orderNumber)} instead of ${orderNumber}
Error checnged to
FetchError: invalid json response body at https://somewebsite.ru/api/v1/order.json?orderId=%D0%9C%D0%A3%D0%97008058 reason: Unexpected end of JSON input
When I use Postman there is no problem. I just put the whole URL https://somewebsite.ru/api/v1/order.json?orderId=МУЗ008134 and it works fine.
the server has utf8 encoding.
I'm using WebStorm and VS Code - both are set with UTF8
Here is the code:
oneOrder: async (orderNumber) => {
try {
let url = `https://somewebsite.ru/api/v1/order.json?orderId=${orderNumber}`
return fetch(url, {
method: 'post',
headers: { 'Content-Type': 'text/plain; charset=utf-8' }
})
.then(res => res.json())
.then(answer => {
if (answer.error) {
return answer.message
} else if (answer.orderId) {
return `Номер заказа: ${answer['orderId']}\nСоздан ${answer['createdAt']}\nОбщая стоимость товаров в заказе: ${answer['totalCost']}\nСтатус оплаты: ${answer['status']['payment']}\nСтатус доставки: ${answer['status']['delivey']}`
}
return 'Нет информации по заказу'
})
} catch (e) {
console.log('ERROR with oneOrder function:', e)
}
},
...and, by the way, I have no idea why the "МУЗ008134" is not showed as a part of URL, but as a ppendix to the URL.
Thanks a lot and sorry if it seems to be too obvious.

How to download an excel (xlsx) file using Angular 5 HttpClient get method with Node/Express backend?

I have an excel file in a directory on my nodejs server - Path to the file is - ./api/uploads/appsecuritydesign/output/appsecdesign.xlsx
On click of a button in my Angular 5 component I am just trying to download the file using FileSaver.
Below is my Angular component.
Here the template code for the button in Angular that will call the saveFile() function once clicked.
<a class="btn btn-primary" (click) = "saveFile()">Download</a>
Here is the saveFile() function.
saveFile(){
console.log("In ToolInput Component: ", this.token); //Okta token
console.log("In ToolInput Component: ", this.whatamidoing); //this variable has the server FilePath
this.fileService.getappsecdesignfile(this.token, this.whatamidoing).subscribe(res => {
let blobtool5 = new Blob([res], { type: 'application/vnd.ms-excel;charset=utf-8' });
FileSaver.saveAs(blobtool5, 'Application_Security_Design.xlsx');
},
(err: HttpErrorResponse) => {
if (err.error instanceof Error) {
console.log('An error occurred:', err.error.message);
console.log('Status', err.status);
} else {
console.log(`Backend returned code ${err.status}, body was: ${err.error}`);
console.log('Status', err.status);
}
});
}
At this point I checked the console.log in the browser. They are exactly what they are supposed to be. So I am passing the correct filepath and token to getappsecdesignfile method in my fileService.
Now Lets take a look at the getappsecdesignfile method in my fileService.
getappsecdesignfile ( token, tool5filepath ) : Observable<any>{
console.log("In Service tool5filepath: ", tool5filepath);
console.log("In Service token", token);
console.log("In Service GET url: ", this.getappsecdesignfileurl);
//Since the tool5filepath has / (slashes etc) I am encoding it below.
let encodedtool5filepath = encodeURIComponent(tool5filepath);
console.log('Encoded File Path: ', encodedtool5filepath);
let req = new HttpRequest('GET', this.getappsecdesignfileurl,{params: new HttpParams().set('path', encodedtool5filepath)},{headers: new HttpHeaders().set('Accept', 'application/vnd.ms-excel').set('Authorization', token)});
console.log(req);
return this.http.request(req);
}
That's all there is to the fileService method. Lets look at the console.logs from this method from the browser to ensure all the correct values are being set.
Now Lets take a look at the request itself before we go to the server part.
As far as I am concerned the headers are set correctly, params are set correctly. Two issues I see is that Angular's interceptors probably sets the responseType: json and adds a param op:s to my request.
Node/Express Server code.
app.get('/getappsecdesignfile', function(req, res){
console.log("In get method app security design");
accessTokenString = req.headers.authorization;
console.log("Okta Auth Token:", accessTokenString);
console.log("Tool5 File Path from received from Angular: ", req.query.path); //this is where the server console logs shows Tool5 File Path after decoding: ./undefined
oktaJwtVerifier.verifyAccessToken(accessTokenString)
.then(jwt => {
// the token is valid
console.log(jwt.claims);
res.setHeader('Content-Disposition', 'attachment; filename= + Application_Security_Design.xlsx');
res.setHeader('Content-Type', 'application/vnd.ms-excel');
let tool5filepath = './' + decodeURIComponent(req.query.path);
console.log("Tool5 File Path after decoding: ", tool5filepath);
res.download(tool5filepath);
}).catch(err => {
// a validation failed, inspect the error
res.json({success : false, message : 'Authorization error.'});
});
});
If I use Postman the api works fine. However somewhere between Angular to Node communication something happens that I don't understand.
Below is what the server logs. (Big question how does this become undefined)?
Tool5 File Path from received from Angular: undefined
Tool5 File Path after decoding: ./undefined
Error: ENOENT: no such file or directory, stat '<dirpath>/undefined'
Here is what I see in the browser log:
zone.js:2933 GET http://localhost:3000/getappsecdesignfile 404 (Not Found)
toolinput.component.ts:137 Backend returned code 404, body was: <!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Error</title>
</head>
<body>
<pre>Error: ENOENT: no such file or directory, stat '<dirpath>/undefined'</pre>
</body>
</html>
Then the browser downloads a xlsx file that is corrupt and cannot be opened.
I have checked the file resides in the directory and is ready to be downloaded.
Thanks for any tips that can help me resolve this issue.
Finally figured it out.
2 specific changes made this work.
Change # 1 - Setting responseType : 'blob' and defining the params and headers first and then using them in http.get. (http is nothing but an object of type HttpClient from angular/common/http that has been injected into the service class.
getappsecdesignfile ( token, tool5filepath ) : Observable<any>{
console.log("In Service tool5filepath: ", tool5filepath);
console.log("In Service token", token);
console.log("In Service GET url: ", this.getappsecdesignfileurl);
let encodedtool5filepath = encodeURIComponent(tool5filepath);
console.log('Encoded File Path: ', encodedtool5filepath);
let getfileparams = new HttpParams().set('filepath', encodedtool5filepath);
let getfileheaders = new HttpHeaders().set('Accept', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet').set('Authorization', token);
return this.http.get(this.getappsecdesignfileurl, {responseType: 'blob', params: getfileparams, headers: getfileheaders});
}
Change # 2 - Component code - FileSaver. For some reason type: 'application/vnd.ms-excel' did not work in FileSaver. Here the res is nothing but the response from the http.get call.
let blobtool5 = new Blob([res], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
FileSaver.saveAs(blobtool5, 'Application_Security_Design.xlsx');

HttpClientModule http.get not working

I picked up this previous working app (Angular2) and find that it is not working (Angular4) as expected now.
Module was used (it may not matter):
import { HttpModule, JsonpModule } from '#angular/http';
Module being used now:
import { HttpClientModule} from '#angular/common/http';
Trying to get a list of records from the backend (Node.js, Express, and MongoDB) as below.
listResource(resType: string, parameters: string[]) {
console.log("***listResource");
let headers = new HttpHeaders().set("X-CustomHeader", "custom header value");
headers.append('Content-Type', 'application/fhir+json');
headers.append("'Access-Control-Allow-Origin", "*");
headers.append("Accept", "application/fhir+json");
headers.append("USER_KEY", "QIF83Fjoe4sYxdQsah3h"); //TOUCHSTONE KEY
let urlString = this.baseUrl + resType + "/list"; // + queryString;
console.log("List resource URL string: [" + urlString + "]");
return (this.httpClient.get(urlString, { headers })
.map((res: Response) => res.json()))
.catch((error: any) => Observable.throw(error.json().error || 'Server error from Observable http.get call')); //...errors if any
}
when my component is loaded, the above listResource will be called as below.
ngOnInit() {
//Get the initial 25 latest patient
//this.progressBar = true;
this.currentPage = 0;
this.patientList = [];
this.globalSvc.gPatient = [];
console.log("***OnInit");
this.restSvc.listResource("Patient", ["identifier=*", "family=*", "given=*"]).subscribe(
data => {
console.log("Response data: " + JSON.stringify(data));
},
(err: HttpErrorResponse) => {
if (err.error instanceof Error) {
// A client-side or network error occurred. Handle it accordingly.
console.log('An error occurred:', err.error.message);
} else {
// The backend returned an unsuccessful response code.
// The response body may contain clues as to what went wrong,
console.log(`Backend returned code ${err.status}, body was: ${err.error}`);
}
});
}
Below is the output from Chrome console. Of course, I don't get any good response. It seems to me the Chrome browser sends CORS option and the server responds correctly, then the browser doesn't send the actual GET.
If I send the REST API request from PostMan directly which doesn't have CORS, I get the expected good response from the server. Hence, it seems to me the server is ok.
Questions:
Any idea how to debug or fix it?
Will this relate to CORS on both Angular client and Node.js server?
The ${err.status} and ${err.error} are "undefined" in Chrome console. How can I find the actual error?
console.log(Backend returned code ${err.status}, body was: ${err.error});
Update 1 based on Grey's suggestion on the immutable header and const.
The GET is returning data now.
headers.append() does not alter the headers, it returns a new Headers (because Headers is immutable).
So, instead of
let headers = new HttpHeaders().set("X-CustomHeader", "custom header value");
headers.append('Content-Type', 'application/fhir+json');
headers.append("'Access-Control-Allow-Origin", "*");
headers.append("Accept", "application/fhir+json");
headers.append("USER_KEY", "QIF83Fjoe4sYxdQsah3h"); //TOUCHSTONE KEY
you need to do something like:
let headers = new HttpHeaders().set("X-CustomHeader", "custom header value")
.append('Content-Type', 'application/fhir+json')
.append("'Access-Control-Allow-Origin", "*")
.append("Accept", "application/fhir+json")
.append("USER_KEY", "QIF83Fjoe4sYxdQsah3h");
Oh, and that should actually be const headers =, rather than let headers =

Ajax call working on localhost but does not on azure-webites.net

<script type="text/javascript">
$(document).ready(function () {
function validemail(isemail) {
var emailReg = /^([a-zA-Z0-9_.+-])+\#(([a-zA-Z0-9-])+\.)+([a-zA-Z0-9]{2,4})+$/;
return emailReg.test(isemail);
}
$("#<%=txtEmail.ClientID %>").blur(function () {
if ($("#<%=txtEmail.ClientID %>").siblings().size() > 0) {
$("div").remove(".tooltips");
}
});
$("#btnSubmit").click(function () {
var name = $("#<%=txtName.ClientID %>").val();
var email = $("#<%=txtEmail.ClientID %>").val();
var message = $("#<%=txtMessage.ClientID %>").val();
if (name != '' && email != '' && message != '') {
if (validemail(email)) {
$.ajax({
type: "POST",
contentType: "application/json; charset=utf-8",
url: "http://abcname.azurewebsites.net/Contact.aspx/InsertData",
data: "{'customername':'" + name + "','customeremail':'" + email + "','customermessage':'" + message + "'}",
dataType: "json",
success: function (data) {
var obj = data.d;
if (obj == 'true') {
$("#<%=txtName.ClientID %>").val('');
$("#<%=txtEmail.ClientID %>").val('');
$("#<%=txtMessage.ClientID %>").val('');
alert('Details submitted successfully');
}
},
error: function (result) {
alert("An error occur while submitting details.");
}
});
}
else {
$("#<%=txtEmail.ClientID %>").focus();
$("<div class='tooltips'><span>Invalid Email Address</span></div>").insertAfter("#<%=txtEmail.ClientID %>");
}
}
else {
alert('Please fill all the fields');
}
});
});
</script>
Above code perfectly working on local host but it doesn't on server side. If there would any error on the .cs file then it will show alert box, but it even doesn't showing alert box that "An error occur while submitting details"
url: "http://abcname.azurewebsites.net/Contact.aspx/InsertData",
Based on the URL, I suspected that you were using WebMethod to handle AJAX request in WebForms application. Since there were .aspx suffix in your URL, please make sure you have commented out the following code which default exist in RouteConfig.cs.
//settings.AutoRedirectMode = RedirectMode.Permanent;
but it even doesn't showing alert box that "An error occur while submitting details"
var obj = data.d;
The reason for this may be that the response is came back but it doesn’t contain a property named d or its value doesn’t equals ‘true’. I suggest use a tool to view the detail response message. Fiddler is common tool which could help you do it.
In addition, did you send the AJAX request from different domain as 'http://abcname.azurewebsites.net'? For example, the code which you posted is in the website named 'http://defname.azurewebsites.net'. If it is true, you need to configure CORS in abcname web app. Steps below are for your reference.
In Azure portal, open web app abcname.
On menus bar, click CORS.
Input the domain name in the ‘Allowed Regions’ textboxes.
Click [Save] button to save all your operations.

Resources