Firebase phone number authentication, code is expired every time - android

I have added firebase authentication in my application. It works fine if i add phone number of other user in my phone. But if i add mine phone number it sends OTP but when i enter that OTP every time it shows error "SMS code has expired".
Is there any auto authentication? Is there something that i missing?
I have used below code to send OTP
firebase.auth().signInWithPhoneNumber(this.state.countryCode + this.state.phoneNumber)
.then(confirmResult => {
this.setState({
progressVisible: false
})
console.log("confirmResult is " + confirmResult)
AsyncStorage.setItem('CountryCode', this.state.countryCode);
AsyncStorage.setItem('PhoneNumber', this.state.phoneNumber);
this.props.navigation.navigate("OtpScreen", {
confirmResult, isConnected: this.state.isConnected
})
})
I have used below code to verify OTP
this.state.confirmResult.confirm(this.state.otpText)
.then(user => {
// this.setState({
// progressVisible: false
// })
console.log("Debug starts, 2");
this.hitAuthApi()
})
.catch(error => {
this.setState({
progressVisible: false
})
setTimeout(() => {
Alert.alert("Error" + error);
}, 100)
});

There is a firebase in-build listener for auto login, which need to initialise in component. It will automatically detect SMS in android.
this.unsubscribe = firebase.auth().onAuthStateChanged((user) => {
// alert(JSON.stringify(user))
if (user) {
//hit Api
} else {
// User has been signed out, reset the state
}
});

Related

firebase notifications stops working after a while (one day or a few days)

I am very frustrated with this problem:(
I am developing an app for android and ios (using capacitor 3) and I am sending notifications to the app via firebase notifications. (capacitor packages: #capacitor-community/fcm and #capacitor/push-notifications).
It works for a while and after one day or a few days that the app is running in background or foreground (and not killed) it stops from working and the app doesn't get notifications(This has happened to me in android device.).
I am sending notifications using topics and i also tried to send the notification through firebase console, but it didn't work.
I am not sure if this means that the registration token has expired because I would think that the capacitor packages are suppose to handle it since they are not talking about this problem.
I did everything from the documentation of capacitor push notifications.
When I watch the logs I can see the next error: Failed to sync topics. Won't retry sync. INVALID_PARAMETERS.
My code in javascript:
import '#capacitor/core';
import { ActionPerformed, PushNotificationSchema, PushNotifications } from '#capacitor/push-notifications'
import { FCM } from '#capacitor-community/fcm';
import { getMessaging, getToken as firebaseGetToken, onMessage, deleteToken, isSupported } from "firebase/messaging";
import { myAxios } from './generic-functions/my-axios';
const platform = window.Capacitor && window.Capacitor.platform;
const topicIos = `${process.env.REACT_APP_TOPIC}_ios`;
const topicAnd = `${process.env.REACT_APP_TOPIC}_and`;
function isCapacitor(): boolean {
//check if we are in a capacitor platform
return window.Capacitor && (window.Capacitor.platform === "android" || window.Capacitor.platform === "ios")
}
export async function InitFCM(destination: string) {
if (!isCapacitor()) {
const isNtfSupported = await isSupported()
if (!isNtfSupported) return
// web notifications
Notification.requestPermission().then(function (permission) {
if (permission === 'granted') {
subscribeTo(destination);
} else {
// Show some error
}
});
const messaging = getMessaging();
onMessage(messaging, (payload) => {
let notification = payload.data;
const notificationOptions: NotificationOptions = {
badge: notification?.largeIco,
body: notification?.body,
icon: notification?.largeIcon
};
const title = notification?.title || "";
// show notification
navigator.serviceWorker
.getRegistrations()
.then((registration) => {
if (notification?.sound) {
const audio = new Audio(`/notifications/${notification?.sound}`)
audio.play()
}
registration[0].showNotification(title, notificationOptions);
});
})
return
}
try {
console.log('Initializing Push Notifications');
// Request permission to use push notifications
// iOS will prompt user and return if they granted permission or not
// Android will just grant without prompting
PushNotifications.requestPermissions().then(result => {
if (result.receive === 'granted') {
// Register with Apple / Google to receive push via APNS/FCM
// PushNotifications.register();
subscribeTo(destination);
} else {
// Show some error
}
});
// Some issue with our setup and push will not work
PushNotifications.addListener('registrationError',
(error: any) => {
console.log('Error on registration: ' + JSON.stringify(error));
}
);
// Show us the notification payload if the app is open on our device
PushNotifications.addListener('pushNotificationReceived',
(notification: PushNotificationSchema) => {
console.log('Push received: ' + JSON.stringify(notification));
}
);
// Method called when tapping on a notification
PushNotifications.addListener('pushNotificationActionPerformed',
(notification: ActionPerformed) => {
console.log('Push action performed: ' + JSON.stringify(notification));
}
);
} catch (e) {
console.log('err in push notifications: ', e);
}
}
async function subscribeTo(destination: string) {
if (!isCapacitor()) {
//subscribe to web topic
const messaging = getMessaging();
firebaseGetToken(messaging, { vapidKey: process.env.REACT_APP_FIREBASE_VAPID_KEY }).then(
async (token) => {
if (token) {
await myAxios.post("/api/notifications/subscribe-to-topic", { token, destination });
}
}).catch((err) => {
console.log('An error occurred while retrieving token. ', err);
});
return
}
try {
await PushNotifications.register();
if (platform === "ios") {
//subscribe to ios topic
const resIos = await FCM.subscribeTo({ topic: `${topicIos}_${destination}` });
console.log(`subscribed to ios Topic ${JSON.stringify(resIos)}`);
}
if (platform === "android") {
//subscribe to android topic
const resAnd = await FCM.subscribeTo({ topic: `${topicAnd}_${destination}` });
console.log(`subscribed to android Topic ${JSON.stringify(resAnd)}`);
}
} catch (error) {
console.log(JSON.stringify(error));
}
}
export async function getToken() {
try {
/* const result = */ await FCM.getToken();
// console.log("TOKEN", result.token);
} catch (error) {
console.log(error);
}
}
export async function unsubscribeFrom(destination?: string) {
if (!isCapacitor()) {
const isNtfSupported = await isSupported()
if (!isNtfSupported || !destination) return
const messaging = getMessaging();
//unsubscribe from web topic
firebaseGetToken(messaging, { vapidKey: process.env.REACT_APP_FIREBASE_VAPID_KEY }).then(
async (token) => {
if (token) {
await myAxios.post("/api/notifications/unsubscribe-from-topic", { token, destination });
}
}).catch((err) => {
console.log('An error occurred while retrieving token. ', err);
});
return
}
try {
await PushNotifications.removeAllListeners();
if (destination) {
if (platform === "ios") {
//unsubscribe from ios topic
const resIos = await FCM.unsubscribeFrom({ topic: `${topicIos}_${destination}` });
console.log(`unsubscribed from ios topic ${resIos}`);
}
if (platform === "android") {
//unsubscribe from android topic
const resAndroid = await FCM.unsubscribeFrom({ topic: `${topicAnd}_${destination}` });
console.log(`unsubscribed from android topic ${topicAnd}_${destination}: ${resAndroid.message}`);
}
}
} catch (error) {
console.log(error)
}
if (platform === 'android') {
await FCM.deleteInstance();
}
}
Thank you all in advanced!
This is a common issue since Android 7.0. The problem occurs because you make use of data messages. This part of your code onMessage(messaging, (payload) => { tells me that you rely on that. This means that when a message is received, your apps code will handle the delivery even when in the background. It will create a notification to show it on the device and play a sound for example.
Power Management taken too far
Several device manufacturers have improved their power management too far. This results in the following problem: After a few days of inactivity, an app is completely killed by the Android OS. This means that the app is not able to handle incoming messages in the background anymore. Vendors have gone too far. But you can't do anything about that.
What to do?
To solve the problem, you should rely on notification messages. These are messages that are directly delivered to the Android OS, instead of your app. This means that messages do not need background handling of your app. On the server (sending) side it means you have to modify your current message and add notification info to the message that is sent.
The drawback
The drawback of notification messages is that you can't lay your hands on the data that is part of the notification. If you previously filled your app with data from each notification, with notification messages, you get the data only when your app is in the foreground or the notification is clicked. To get all data within your app, you need a server API solution or something else.
To overcome this you can add a NotificationListener to your app. I am not sure how to do this in Capacitor. A native example can be found here: https://github.com/Chagall/notification-listener-service-example. The NotificationListener can listen for notifications delivered to the Android device also in the background. With this solution you can be sure notifications are always delivered and the data is delivered in the background. But maybe, I don't know, this listener is killed too by power management. When you use the NotificationListener, you need a special permission, that must be set via device settings (see the mentioned example).
Conclusion
Change from data messages to notification messages. Provide a different way to get the data of your messages in your app. You can use the NotificationListener but I don't know if that is reliable. The most obvious solution is to introduce a server side API that provides the data to your app. In the new situation the notifications are reliable delivered to the app.

When React native RNIap.purchaseErrorListener is called?

I have integrated react-native-iap for in app purchase.
Android Payment flow works properly(payment success, payment failed and user cancel payment).
But facing issue when purchase card is shown and I click outside the card, card is dismissed but not getting event inside RNIap.purchaseErrorListener .
Hence my state variable did not updated.
here is sample:
Listener code :
useEffect(() => {
initPurchase()
purchaseErrorSubscription = RNIap.purchaseErrorListener(
(error) => {
console.log('purchaseErrorListener INAPP>>>>', error);
if(error.code == "E_USER_CANCELLED") {
//for cancelled or refund sku
setSelectedPackage(null)
RNToasty.Show({
title: error.message,
});
} else if(error.code == "E_ITEM_UNAVAILABLE") {
//item not found
setSelectedPackage(null)
} else {
setSelectedPackage(null)
}
},
);
}, [])
Listener not called when I perform above action.
Can anyone help me to get this event?
Thanks in advance!!
you need to put this in a function, and in useEffect call this function
const checkCurrentPurchaseError = async () => {
purchaseErrorListener(async currentPurchaseError => {
if (currentPurchaseError) {
Here is your code...
}
});
};
useEffect(() => {
checkCurrentPurchaseError(currentPurchaseError);
}, []);

React Native Google Signin with multiple accounts

I have a react native application which uses google sign in. currently, I can sign in with one of my google accounts which my mobile signed in, but I need to do an OAuth for another google account in my google accounts stack, this is to get the API access I need the serverAuthToken for the secondary accounts
The library I'm using for google login - https://github.com/react-native-community/google-signin
What I have done till now:
const firebaseGoogleLogin = async () => {
try {
await GoogleSignin.hasPlayServices();
const userInfo = await GoogleSignin.signIn();
if (userInfo) {
await storeToken(userInfo);
// eslint-disable-next-line no-console
console.log('User info retrieved during login', userInfo);
}
// create a new firebase credential with the token
// eslint-disable-next-line max-len
const credential = firebase.auth.GoogleAuthProvider.credential(userInfo.idToken, userInfo.accessToken);
// login with credential
// eslint-disable-next-line no-unused-vars
const firebaseUserCredential = await firebase.auth().signInWithCredential(credential);
console.log('Logged in')
} catch (error) {
if (error.code === statusCodes.SIGN_IN_CANCELLED) {
console.log('sign in cancelled', error);
} else if (error.code === statusCodes.IN_PROGRESS) {
console.log('signin in progress error', error);
} else if (error.code === statusCodes.PLAY_SERVICES_NOT_AVAILABLE) {
console.log('play services are not available or outdated', error);
} else {
console.log('some other error occurred', error);
}
}
};
Result for the above code:
This is the code I used to login with my primary Google account, after logging in I'm triggering the play services modal to sign in with other accounts using the above, but the modal closes with loading a sec and gets closed, shows me Logged in in the log, since I have already logged, in the play services automatically bypass the option to select an account
The above problem occurs only in android, ios seems fine even after logging in with an account
Was trying to do a hack if there isn't a way for multiple account authorization since an account already signed in it is bypassing the play service modal, I'm thinking to logout the user once he signed into my application (process level) and inside the main activity, I can have the play services still? is this the right way to do!
Probably you have fixed the issue. I think the method below will solve your problem. Call this after logout.
signOut = async () => {
try {
await GoogleSignin.revokeAccess();
await GoogleSignin.signOut();
this.setState({ user: null }); // Remember to remove the user from your app's state as well
} catch (error) {
console.error(error);
}
};
First import { GoogleSignin } from "#react-native-google-signin/google-signin"; in your logout file
If you're using firbase authentication after
auth().signOut().then(() => {
GoogleSignin.revokeAccess();
});

The expired sms code immediately after receiving the sms

I have an application that you can log in to by phone number
After entering the phone number I receive an SMS code
A new screen opens where I can enter this code
When I enter the code, I get information that the code is expired
Sign: First screen
onSignIn() {
const {code, phoneNumber} = this.state;
const newNumber = '+' + code + phoneNumber;
if (newNumber.length > 10) {
firebase
.auth()
.signInWithPhoneNumber(newNumber)
.then(confirmResult => {
this.setState({result: confirmResult});
const navigateAction = NavigationActions.navigate({
routeName: 'SecurityCode',
params: {phoneAuthResponse: confirmResult},
});
this.props.navigation.dispatch(navigateAction);
})
.catch(error => {
if (error.message === 'TOO SHORT') {
alert('Please enter a valid phone number');
} else {
alert(error.message);
}
});
} else {
alert('Please Enter Your Number');
}
}
Confirm: Second screen
onConfirmCode() {
const {securityCode} = this.state;
if (securityCode.length > 5) {
this.props.navigation.state.params.phoneAuthResponse
.confirm(securityCode)
.then(async user => {
const ref = firebase.database().ref(`users/${user.uid}`);
ref.once('value', async snapshot => {
let data = snapshot.val();
if (!data) {
this.props.navigation.navigate('CreateProfile', {
user: {uid: user.uid, phone_number: user.phoneNumber},
});
} else {
this.props.reduxLoginUser(data);
this.props.navigation.navigate('InviteContacts');
}
});
})
.catch(error => console.warn(error.message));
} else {
alert('Please enter the 6 digit code');
}
}
What is done wrong?
Check if the user has been created (You can do this on the Firebase project page)
If it is created then there is another problem to be solved
You must catch that the user is created and go to the screen after logging in

Automatic logging in - Nodejs and Android system

I am working on Registration and Login system in Nodejs and Android based on this tutorial:
https://www.learn2crack.com/2016/09/android-user-registration-login-node-server.html
https://www.learn2crack.com/2016/09/android-user-registration-login-node-client.html
It works fine, however I want to create a system in which user stays logged in- automatic login everytime he turns on the app. At the moment the logging in is based on token created with 'jsonwebtoken' and it works only if user gives email and password:
router.post('/authenticate', (req, res) => {
const credentials = auth(req);
if (!credentials) {
res.status(400).json({ message: 'Invalid Request !' });
} else {
login.loginUser(credentials.name, credentials.pass)
.then(result => {
const token = jwt.sign(result, config.secret, { expiresIn: 1440 });
res.status(result.status).json({ message: result.message, token: token });
})
.catch(err => res.status(err.status).json({ message: err.message }));
}
});
I couldn't find anything specific how to solve it. Could anyone tell me what is the best way to do it and support me with a link or tutorial?

Categories

Resources