I am trying to set up a simple websocket connection in my React Native app. It is returning the following error in Android: {"isTrusted": false, "message": "Expected HTTP 101 response but was '401 Unauthorized'"}. The websocket is opening fine in iOS, and using a websocket connection that does not require my user to be authenticated works (my user is authenticated for this server but it still say I'm not authorized).
Here is the relevant code in my app:
useEffect(() => {
const ws = new WebSocket(`wss://${DOMAIN}/api/stream/all`)
ws.onopen = () => {
console.log('websocket open') // --> iOS
}
ws.onerror = e => {
console.log('error', e) // --> Android
}
}, [])
Changing it to this works on Android:
useEffect(() => {
const ws = new WebSocket(`wss://echo.websocket.events/.ws`)
ws.onopen = () => {
console.log('websocket open') // --> Android & iOS
}
ws.onerror = e => {
console.log('error', e)
}
}, [])
Any idea why authentication is not working? Is there some Android config that needs to be changed so the authentication that already happened for this domain on https gets to the websocket connection somehow?
I ended up changing it to this (added session cookie to headers for Android):
if (Platform.OS === 'ios') {
const ws = new WebSocket(`wss://${DOMAIN}/api/stream/all`)
ws.onopen = () => {
console.log('websocket open') // --> it works!
}
} else {
const ws = new WebSocket(`wss://${DOMAIN}/api/stream/all`, '', { headers: {Cookie: sessionCookie } })
ws.onopen = () => {
console.log('websocket open') // --> it works!
}
}
I got the session cookie from the response after the user logged in and kept that in state to be used in the websocket connection: const sessionCookie = res.headers.get('set-cookie'). This solution may not work for everyone if login doesn't work the same way for you, but it may help someone.
Related
I make several requests from a React Native app to an API. Every request works fine both on iOS and Android except the DELETE method that does not work on Android. The call is correctly made, it goes through the API and the objects are deleted. But instead of getting the response, the call falls under the catch statement with [TypeError: Network request failed]. This does not happen in iOS.
Some people with the same problem were missing 'Content-Type': 'application/json' on the request headers which is not my case.
This is happening both locally, in testing and production stages (using an ip instead of localhost will do nothing).
The request is also successfully performed in Postman.
What can it be?
React Native 0.63.5
export const deleteApi = async (api: string, body?: any) => {
const userResponse = await getUserCredentials();
const authState = await getAuthState();
let response = await fetch(api, {
method: 'DELETE',
headers: await getHeaders(userResponse, authState),
body: JSON.stringify(body)
});
if (response.status === UNAUTHENTICATED_CODE)
response = await interceptor(response, userResponse, {
api: api,
method: 'DELETE',
body: body
});
return response;
};
leaveClass = async (
uuid: string,
onSuccess: () => void,
onFailure: (error: string) => void,
) => {
this.setLoading(true);
try {
const api = LEAVE_CLASS_API_PREFIX + uuid + LEAVE_CLASS_API_SUFFIX;
const response = await deleteApi(api);
if (response.status === SUCCESS_STATUS_CODE) {
onSuccess();
}
else {
const jsonResponse = await response.json();
if (jsonResponse.detail) onFailure(jsonResponse.detail);
else onFailure(translations.SOMETHING_WENT_WRONG);
}
} catch (error) {
console.log('leaveClass error: ', error);
}
this.setLoading(false);
};
You can use a network plugin for Flipper (https://fbflipper.com/docs/setup/plugins/network/), copy your request from it as a curl, and try to perform it from your terminal or postman. If it has the same error, the problem is not in React Native.
I hope you are doing well.
I have a problem when I connect with google on the emulator with android.
If I go through Expo Go on either Android or Ios, it works fine. But when I build my apk, and I install it on the emulator it sends me back to the same login page without redirecting me to the application.
Do you have an idea of the origin of the problem?
My google login function :
try {
const result = await promptAsync();
if (result.type === "success") {
/* `accessToken` is now valid and can be used to get data from the Google API with HTTP requests */
const { id_token } = result.params;
const provider = new firebase.auth.GoogleAuthProvider();
const credential =
firebase.auth.GoogleAuthProvider.credential(id_token);
auth.signInWithCredential(credential)
.then((res) => {
const user = res.additionalUserInfo.profile;
let action = addUserOnFirestore(
res.user?.uid,
user.email,
user.given_name,
user.family_name,
user.picture,
res
);
setIsLoading(true);
try {
dispatch(action);
} catch (err) {
setError(err.message);
}
setIsLoading(false);
})
.catch((error) => {
console.log("firebase cred err:", error);
});
} else {
console.log("cancelled");
}
} catch (e) {
console.log("general error : ", e);
return { error: true };
}
}
And the properties define :
const [request, response, promptAsync] = Google.useIdTokenAuthRequest({
clientId: "XXXX",
iosClientId: "XXX",
androidClientId: "XXX",
androidStandaloneAppClientId: "XXX",
redirectUri: Platform.select({
// iOS handles redirectUri perfectly fine on it's own
ios: undefined,
// Due to Expo's bug, we need to manually encode the redirectUri
// https://github.com/expo/expo/issues/12044
android: makeRedirectUri({
// intent filter set up in app.config.js
// must be the same as "package name" in Google Cloud Console
native: 'packagename://oauthredirect',
}),
})
});
Thanks in advance for your responses.
i'm using react-native
When I run the emulator virtually the api is well requested to the backend router.
However, when I actually test it on my Android device, the api request does not reach the backend.
My front port number is 8081 and my back end is 3065.
this is my code
for example my ip address: 49.174.243.53
(front.js)
const Kakao = ({navigation}) => {
const hello = async () => {
const {data} = await axios.post('http://43.164.243.53:3065/kakao/test', {
hi: 'hi2',
});
};
return (
<LoginButton style={{marginTop: 30}} onPress={hello}>
<Label>hello</Label>
</LoginButton>
);
};
export default Kakao;
(back/app.js)
const express = require('express');
const dotenv = require('dotenv');
const morgan = require('morgan');
const path = require('path');
const kakaoRouter = require('./routes/kakao');
const db = require('./models');
dotenv.config();
const app = express();
db.sequelize
.sync()
.then(() => {
console.log('db 연결 성공');
})
.catch(console.error);
passportConfig();
app.use(morgan('dev'));
app.use('/', express.static(path.join(__dirname, 'uploads')));
app.use(express.json());
app.use(express.urlencoded({extended: true}));
app.get('/', (req, res) => {
res.send('hello express');
});
app.use('/kakao', kakaoRouter);
app.listen(3065, () => {
console.log('서버 실행 중!');
});
(back/router)
router.post('/test', async (req, res, next) => {
//whic is kakao/test
try {
console.log('req.body:::::::::', req.body);
return res.status(200).json({
posts: 'h!!!!!!!!!!!!!!!!!',
});
} catch (error) {
next(error); // status 500
}
});
In order to view remote documents from an HTTP URL, cleartext network traffic support is required. On Android 9.0 (API level 28) or higher, cleartext support is disabled by default and apps targeting Android 9.0 or higher will need to add the android:usesClearTextTraffic="true" flag in the AndroidManifest.xml file.
If you are only working with HTTPS files, this flag is not required
Note: Total Ionic newbie here.
I have the following:
Ionic 5 (Capacitor) app with Angular 11.
Express backend (localhost:3000)
I can fetch data from an API call and display in the browser, but not on the emulated Android device. I don't know how to check for console errors in Android Studio.
This image can explain the situation better.
I think this is due to CORS. I tried to follow the Ionic page on this but no resolution.
Here is my Express code:
const express = require("express");
const cors = require("cors");
const app = express();
const port = 3000;
const allowedOrigins = [
"capacitor://localhost",
"ionic://localhost",
"http://localhost",
"http://localhost:8080",
"http://localhost:8100",
"http://192.168.2.25:8100",
];
// For parsing JSON in request body
app.use(express.json());
// MySQL connection details - for POC sake.
// In PROD, these are typically saved in .env variables
// Ref: https://www.linkedin.com/pulse/storing-database-credentials-securely-siddhesh-jog
var mysql = require("mysql");
var connection = mysql.createConnection({
host: "____________________________.us-east-2.rds.amazonaws.com",
user: "admin",
password: "*****************",
database: "poc",
});
const corsOptions = {
origin: (origin, callback) => {
if (allowedOrigins.includes(origin) || !origin) {
callback(null, true);
} else {
console.log(origin);
callback(new Error("Origin not allowed by CORS"));
}
},
};
// Enable preflight requests for all routes
app.options("*", cors(corsOptions));
// Connect to MySQL
connection.connect(function (err) {
if (err) throw err;
console.log("Connected!");
});
// Dashboard - GET
app.get("/dashboard", cors(corsOptions), (req, res) => {
rows = [];
connection.query(
"select label_id, value from poc_fct",
function (err, result) {
if (err) throw err;
res.json(result);
}
);
});
app.listen(port, () => {
console.log(`CORS-enabled web server listening at http://localhost:${port}`);
});
Any help will be greatly appreciated.
What finally worked for me was changing the API endpoint from http://localhost:3000/data to http://192.168.2.25:3000/data, where 192.168.2.25 is the local IP address of the host where the Express server is running.
Few notes for anyone else who might have this issue in the future:
This isn't a CORS issue. I commented out app.use(cors)
This isn't a HTTP/HTTPS issue
Changing the emulator's proxy to 10.0.2.2:3000 did not work
I'm using this plugin inside a ionic + stencil app
https://github.com/don/cordova-plugin-ble-central
I'm send a write command to a characteristic that support WRITE.
If I call ble.write on Android, the callback returns me OK
If I call ble.write on iOS, the callback returns me NULL.
in both cases, I can see on my peripheral device that the command has been sent correctly.
I connect to the device like this:
connectToDevice(device){
var toast = new utils();
let tclass;
let tmessage;
console.log('----connectToDevice----');
BLE.connect(device.id).subscribe(
peripheralData => {
this.device = peripheralData;
let connectBtn = document.querySelector('#connectBtn') as any;
console.log('device connected: ', this.device);
},
error => {
console.log('connect error: ', error);
}
);
}
And then, send the command with this code:
async sendCommandNew(id){
let noteOk;
let noteError;
let data;
data = this.stringToBytes(this.commandToSend);
let timerId = setInterval(() => {
BLE.write(id, "49535343-fe7d-4ae5-8fa9-9fafd205e455", "49535343-1e4d-4bd9-ba61-23c647249616", data).then((res) => {
console.log('res: ', res);
}).catch(() => {
console.log('res no');
}
)
}, 1000);
setTimeout(() => { clearInterval(timerId); console.log('stopped'); }, 5000);
}
This is the characteristic
Inside the plugin issue many people had this problem but no solution provided.