The sms code has expired. Please re-send the verification code to try again - firebase-authentication

Whenever I tried to login with phone number using react-native-firebase sdk, I recieve OTP code through sms and when I submit the recieved code, an error is there saying:"The sms code has expired. Please re-send the verification code to try again." And here point to be noted that an entry for respective phone number is writing in Users section of firebase even there is an error.
I am using following:
NodeJS: v8.11.1,
NPM: v5.6.0,
react-native: "^0.59.9",
react-native-firebase: "^5.5.3"
Some links I have already tried are:
1. https://github.com/invertase/react-native-firebase-
docs/blob/master/docs/auth/phone-auth.md
2. https://stackoverflow.com/questions/46522726/firebase-phone-
authentication-error-the-sms-code-has-expired
3. https://www.bountysource.com/issues/45308170-firebase-phone-
number-auth-integration
4. https://medium.com/#chrisbianca/getting-started-with-firebase-
authentication-on-react-native-a1ed3d2d6d91
5. https://invertase.io/oss/react-native-firebase/v6/auth/phone-
auth
In MobileRegistration.js:
navigateToOtpScreen() {
console.log("Phone number: ", "+91" + this.state.phoneNumber)
firebase.auth().signInWithPhoneNumber("+91" +
this.state.phoneNumber)
.then(confirmResult => {
this.setState({ confirmResult, message: 'Code has been sent!'})
this.props.navigation.navigate('EnterOtp', { 'confirmResult':
confirmResult})
})
.catch(error => {
alert(error.message);
this.setState({ message: `Sign In With Phone Number Error:
${error.message}` })
});
};
In EnterOtp.js:
componentDidMount() {
this.unsubscribe = firebase.auth().onAuthStateChanged((user) => {
alert(JSON.stringify(user));
if (user) {
this.setState({
user: user.toJSON()
});
} else {
// User has been signed out, reset the state
this.setState({
user: null,
message: '',
otp: '',
otp1: '',
otp2: '',
otp3: '',
otp4: '',
otp5: '',
otp6: ''
});
}
});
}
componentWillUnmount() {
if (this.unsubscribe) this.unsubscribe();
}
verifyOTP = async () => {
const {
confirmResult,
} = this.props.navigation.state.params;
let otp = this.state.otp + "" + this.state.otp1 + "" + this.state.otp2 + "" + this.state.otp3 + "" + this.state.otp4 + "" + this.state.otp5 + "" + this.state.otp6
if (confirmResult && otp.length) {
alert(otp);
confirmResult.confirm(otp).then((user) => {
AsyncStorage.setItem('accessToken', confirmResult._verificationId);
this.props.navigation.navigate('SetupCoverVideo');
this.setState({
message: 'Code Confirmed!'
});
})
.catch(error => {
alert(error.message) && this.setState({
message: `Code Confirm Error: ${error.message}`
})
});
}
}
Expected Result: Code should be verified and an entry should be in Users section of firebase and navigate to SetupCoverVideo.
Actual Result: Facing an error saying: "The sms code has expired. Please re-send the verification code to try again." And here point to be noted that an entry for respective phone number is writing in Users section of firebase even there is an error.
I am wondering for the solution. Anyone please assist me.

Apparently, some recent versions of Android are smart enough to receive the SMS verification code and use it to authenticate the user. This authentication happens in the background while the user still receives the verification code in an SMS. When the user tries to enter the verification code, he/she gets a message that the verification code expired, because Android has already used it (in the background) and has already logged in the user! To double-check that, check the Firebase Console. You should find that this new user has been added to the list of users.
To avoid receiving the verification code expiry message, we need to set up a listener for "authentication changes." As soon as Android logs in the user in the background, this listener should navigate the user away from the login screen, in which he/she was supposed to enter the verification code. The following demonstrates how this can be implemented. I would add the following code to the login screen.
Example code for use with functional components:
useEffect( () => {
firebase.auth().onAuthStateChanged( (user) => {
if (user) {
// Obviously, you can add more statements here,
// e.g. call an action creator if you use Redux.
// navigate the user away from the login screens:
props.navigation.navigate("PermissionsScreen");
}
else
{
// reset state if you need to
dispatch({ type: "reset_user" });
}
});
}, []);
Example code for use with class components:
// I did NOT test this version, because I use functional components.
componentDidMount() {
firebase.auth().onAuthStateChanged( (user) => {
if (user) {
// Obviously, you can add more statements here,
// e.g. call an action creator if you use Redux.
// navigate the user away from the login screens:
props.navigation.navigate("PermissionsScreen");
}
else
{
// reset state if you need to
this.setState({
user: null,
messageText: '',
codeInput: '',
phoneNo: '',
confirmResult: null,
});
}
});
};

You need to check for background authentication using:
conponentDidMount() {
firebase.auth().onAuthStateChanged((user) => {
if (user) {
// alert(JSON.stringify(user))
// Obviously, you can add more statements here,
// e.g. call an action creator if you use Redux.
// navigate the user away from the login screens:
}
});
}
Then you need to log out your user once you are done with it (I faced this issue of old user session already present while new user was coming to authenticate)
So, at the time of logging out use:
if (firebase.auth().currentUser)
firebase.auth().currentUser.delete();
And you are all set!

Related

Avoiding Wix members confirmation emails or changing the sending address

I am working with a client who runs a BtoB. We have set up Member Accounts and some member roles to secure sensible information to which they would have access once approved. However, we are running with an issue that is driving me completely crazy: as Wix’s default registration process rules, once someone signs up and we approve Member Access, an email is sent to their account with a token to confirm their email.
Even though we have an email integrated with Wix with our domain, both the confirmation emails and password reset (sent after a user clicks on “forgot my password”), are not being sent from that address, but from some stupid #site-members.com address.
sender address
My client’s clients all use business emails, and most of their servers are completely blocking these emails. Therefore, they are not being able to complete the registration process and never get to login.
After trying to talk to Wix’s support - which was a complete waste of time - I started scouting the internet and found a tutorial for sending Triggered Emails as confirmation emails. I tried it and it seems to work, however, even though the Triggered Email is set to be sent from my account, it adds a via something:
via ascendbywix
Aaaand.. Guess what! After doing some testing, these get blocked as well in many servers.
So, I don’t know what else to do! Can somebody please help me to either avoid sending confirmation emails at all, or to see if there is any other way to set the email address sending those using Corvid or the Wix dashboard?
Just in case you want to see the code, there are three parts to it:
REGISTRATION LIGHTBOX
import wixWindow from 'wix-window';
import wixData from 'wix-data';
import { doRegistration } from 'backend/register';
let registration;
$w.onReady(function () {
$w("#register").onClick((event) => {
console.log("Button was clicked");
$w("#errorMessage").collapse();
$w("#emailExists").collapse();
if ($w("#email").valid && $w("#password").valid && $w("#company").valid && $w("#name").valid) {
registerPerson();
console.log("Trying to register");
} else {
$w("#errorMessage").expand();
console.log("Missing Information");
}
})
});
function registerPerson () {
let email = $w("#email").value;
let password = $w("#password").value;
let name = $w("#name").value;
let company = $w("#company").value;
let toInsert = {
"name": name,
"company": company,
"email": email
};
wixData.insert("Members", toInsert)
.then( (results) => {
let item = results;
} )
.catch( (err) => {
let errorMsg = err;
} );
doRegistration(email, password, name)
.then((result) => {
wixWindow.openLightbox("confirmation");
let userId = result.user.id
.catch((err) => {
let errMsg = err;
console.log(err);
$w("#emailExists").expand();
} );
});
}
BACKEND REGISTER.JSW
export function doRegistration(email, password, name, company) {
// register the user
return wixUsers.register(email, password, {
"contactInfo": {
"name": name,
"company": company
}
})
.then((results) => {
wixUsers.emailUser('verifyRegistration', results.user.id, {
variables: {
approvalToken: results.approvalToken
}
});
return results
});
}
export function doApproval(token) {
// approve the user
return wixUsers.approveByToken(token)
// user is now active, but not logged in
// return the session token to log in the user client-side
.then((sessionToken) => {
return { sessionToken, "approved": true };
})
.catch((error) => {
return { "approved": false, "reason": error };
});
}
CONFIRMATION PAGE (where users will be redirected after confirming their email)
import wixLocation from 'wix-location';
import wixUsers from 'wix-users';
import {doApproval} from 'backend/register';
$w.onReady( () => {
// get the token from the URL
let token = wixLocation.query.token;
doApproval(token)
.then( (result) => {
if (result.approved){
// log the user in
wixUsers.applySessionToken(result.sessionToken);
console.log("Approved");
}
else {
console.log("Not approved!");
}
} );
} );
just create the SPF record in the DNS and include everything to it along with the WIX servers (ascendbywix.com ~all).

React-Native/Expo Location status if it user don't want to allow location after giving permission to app

After allowing permission to access GPS in react native App. If the user rejected to turn on the gps. It will show errors like
Unhandled promise rejection: Error: Location request failed due to unsatisfied device settings."
I want to avoid if the user rejects the Gps turn on option It will return something. so I need If condition for the location whether it is On or Off. (I'm using expo-location)
You're seeing this error because the Location.getCurrentPositionAsync is async method and it returns a promise and if it fails it throws an error (the error you're seeing).
You could wrap your code inside a try and catch block to catch the error and do something about it.
Example:
_getLocationAsync = async () => {
let { status } = await Permissions.askAsync(Permissions.LOCATION);
if (status !== 'granted') {
alert('The request was denied');
}else{
try{
let location = await Location.getCurrentPositionAsync({});
// do something with location
}catch(e){
alert('We could not find your position. Please make sure your location service provider is on');
console.log('Error while trying to get location: ', e);
}
}
}
// call this._getLocationAsync();
you will need to check the status from expo-location and redirect user to settings to allow the permission for that you can use android intents for android and for ios you can use Linking to redirect the user to device settings and give permissions
requestLocationPermission = async () => {
const { status } = await Permissions.askAsync(Permissions.LOCATION);
if (status === 'granted) {
navigation.navigate('screen_name');
} else {
// Alert Message if user does not allow permissions
Alert.alert("alert Message", "Instructions based on OS", [
{
text: 'Open Settings',
onPress: () => goToSettings(),
style: 'cancel',
},
{ text: Languages.DENY, onPress: () => navigation.goback()},
]);
}
};
go to settings
goToSettings = () => {
if (Platform.OS == 'ios') {
// Linking for iOS
Linking.openURL('app-settings:');
} else {
// IntentLauncher for Android
IntentLauncher.startActivityAsync(
IntentLauncher.ACTION_MANAGE_ALL_APPLICATIONS_SETTINGS
);
}
};
NOTE Intent launcher is a separate package for android
use this to turn gps on:
Location.enableNetworkProviderAsync()
It's a simple popup. (without redirection to a setting)

Set up listeners only after permission is verified

I am using react-native-notifications - Version 2.1.7 library to receive notifications in a react-native mobile app. I don't want to set up the notification-related listeners until the user has provided permission to receive notifications.
Q1. The documentation says that it is highly recommended to keep listeners registration at global scope rather than at screen scope. What problems should I expect if I set up listeners at a screen, at which the user is asked to provide permission?
Q2. The device token listener NotificationsAndroid.setRegistrationTokenUpdateListener() does NOT seem to work if it is inside a promise. What am I missing here? Please, see my code below.
// This function is called when the user clicks on the button "Provide permission to receive notifications."
const _requestPermissionNotification = async () => {
let hasPermission = false;
try {
hasPermission = await NotificationsAndroid.isRegisteredForRemoteNotifications();
}
catch (error) {
Alert.alert(
"Notification",
"To utilise the full functionality of this app, Permission to receive notifications is required.",
[{ text: "Ok." }]
);
} // end of: try/catch
if (hasPermission) {
// A. Register Token
// THIS LISTENER DOES NOT SEEM TO WORK UNLESS IT IS SET UP OUTSIDE THE COMPONENT!
NotificationsAndroid.setRegistrationTokenUpdateListener((deviceToken) => {
console.log("PermissionsScreen - setRegistrationTokenUpdateListener - deviceToken:", deviceToken);
});
// B. Pending Notifications
PendingNotifications.getInitialNotification()
.then((notification) => {
console.log("PermissionsScreen - getInitialNotification - notification:", notification);
})
.catch((err) => console.error("getInitialNotifiation failed", err));
// C. Notification Opened
NotificationsAndroid.setNotificationOpenedListener((notification) => {
console.log("PermissionsScreen - setNotificationOpenedListener - :data", notification.getData());
});
// D.a Notification Received
NotificationsAndroid.setNotificationReceivedListener((notification) => {
console.log("PermissionsScreen - setNotificationReceivedListener - data:", notification.getData());
});
// D.b Notification Received "IN FOREGROUND"
NotificationsAndroid.setNotificationReceivedInForegroundListener((notification) => {
console.log("PermissionsScreen - setNotificationReceivedInForegroundListener (foreground)", notification.getData());
});
} // end of: if()
}; // end of: _requestPermissionNotification()
It seems that version 3.1.1 of React-Native-Notifications does not have these limitations anymore.
The following code, which uses the new commands, can be used inside a promise and inside a component.
// Step A.1: Register this app to receive notifications.
Notifications.registerRemoteNotifications();
// Step A.2: Get the device token
Notifications.events().registerRemoteNotificationsRegistered( (event) => {
console.log("deviceToken:", event.deviceToken);
});

React-native : how to check if it's the user's first connection?

I 'm new to react-native and I would like to set up my first page of the app with
1- The user never logged in the app, I 'll present the app with some slides + go to signup page
2 -The user is already in => directly go to the user page
3 -The user is already in but the id is not correct
Can you guide me through it ?
For the first point I really don't know how to check if the user exist in my db
For the second and third points I thought I'd try :
onPressLogin(){
fetch('linktomyAPI',{
method: 'POST',
headers:{
'Content-Type' : 'application/json',
'Accept':'application/json'
},
body: JSON.stringify({
username:this.state.username,
password: this.state.password,
})
})
.then(response => response.json())
.then((responseData) =>{
if(responseData.error !== 1){ // verify the success case, as you didn't provide the success case i am using the error code
this.setState({ // its recommended you verify the json before setting it to state.
userdetail: responseData,
})
setTimeout(() => {
Actions.Authentication();
}, 2300);
AsyncStorage.setItem('username', this.state.username); // its setItem not saveitem.
} else {
console.log(responseData);
Alert.alert(JSON.stringify(responseData)); // Alerts doesn't allow arrays or JSONs, so stringify them to view in Alerts
}
}).catch((error) => {
// handle catch
console.log("error:"+JSON.stringify(error));
});
}
Do you think I can do this way, is that relevant?
Thanks for taking the time to answer me. I'm really new so I need help and explanations. Thanks !!! :)
you can use the UserInfo to do. firstly, you have saved the user info in memory use Session js and in the device local file use AsyncStore. you already do it use the AsyncStore.
firstly save the user info into Session,the Session structure is:{id:"dd333",name:"john"},
...
setTimeout(() => {
Actions.Authentication();
}, 2300);
Session.id = res.id;
Session.name = res.name;
AsyncStorage.setItem('userinfo', Json.stringfy(res));
then you can show diffent page and condition.
// in the component which you want compoentDidMount
componentDidMount() {
// here you can try to get it from AsyncStore if the Session is null.
// then hanle it as the followng
if(Session.id == ""){
//default it is "", it means the user does not login
} else if(Session.id != "222"){
// the user id is not correct
} else {
// here is the user login and id is correct
}
}

Skype for web sdk signin issue with on prem use case

I am trying your samples and some very simple code for signing in. Status changes to Signing In. But it stays there and doesn't go to Signed In.
Following is my code :
var config = {
apiKey: 'a42fcebd-5b43-4b89-a065-74450fb91255', // SDK
apiKeyCC: '9c967f6b-a846-4df2-b43d-5167e47d81e1' // SDK+UI
};
var client;
$(function () {
'use strict';
Skype.initialize({ apiKey: config.apiKey }, function (api) {
client = new api.application();
// whenever client.state changes, display its value
client.signInManager.state.changed(function (state) {
$('p').text("Status : "+state);
});
}, function (err) {
console.log(err);
alert('Cannot load the SDK.');
});
$('#LogIn').click(function () {
// start signing in
client.signInManager.signIn({
username: $('#username').val(),
password: $('#password').val()
}).then(function () {
//log in worked!
$('p').text("Status : "+"Logged In");
}, function (error) {
//Something went wrong.
$('p').text("Status : "+error);
});
});
I installed Skype for Business desktop client and used same username and password and could sign in successfully. But with the above code, I am unable to sign in. Its staying at "signing In". I am not getting errors also. I also used your samples as per this article : https://msdn.microsoft.com/en-us/skype/websdk/docs/downloadrunsamples
But I saw the same issue. The spinning wheel is forever spinning. It looks like it is looking at the same status as my code and staying at signing in.
Please let me know what wrong I might be doing. Just starting with this SDK. Any suggestions for debugging also can help.