How can i navigate to homescreen after facebook authentication using firebase + expo - react-native

Hello I'm new to react native and I'm trying to navigate to Home Screen with a successful authentication, but this generates an error, can someone help me? please
loginWithFacebook = async() => {
await Facebook.initializeAsync(
'235487284376992',
);
const { type, token } = await Facebook.logInWithReadPermissionsAsync(
{ permissions: ['public_profile'] }
);
if (type === 'success') {
const credential = firebase.auth.FacebookAuthProvider.credential(token);
this.props.navigation.navigate('Home')
firebase
.auth().signInWithCredential(credential).catch(error => {
console.log(error);
});
}
Firebase is working, however an error is generated during navigation
The error when try login
this error only happens after the first authentication, then the navigation normally occurs

Are you using react-native-firebase?
Which version are you using?
If you are using react-native-firebase v6+ , you need to install #react-native-firebase/auth module.
React Native Firebase version 6 has been re-created from the ground up.
And also, you must import the firebase from auth module on header
import {firebase} from "#react-native-firebase/auth";
This is my code.
import {
createTypes,
createAction,
transformNetworkError,
socialLoginError,
} from '../../utils/actions';
import {getWithData} from '../../utils/request';
import {OneSignalUtils} from '../../utils/oneSignalUtils';
import {FirebaseUtils} from '../../utils/firebaseServices';
import { LoginManager, AccessToken } from 'react-native-fbsdk';
import { GoogleSignin } from '#react-native-community/google-signin';
import {firebase} from "#react-native-firebase/auth";
const facebookLogin = () => async dispatch => {
const loginAction = {
do: () => createAction(LOGIN.DO, {}),
success: (response, authData) => createAction(LOGIN.SUCCESS, {userData: response, authData: authData}),
failed: (error) => createAction(LOGIN.FAILED, error),
};
try {
dispatch(loginAction.do());
const result = await LoginManager.logInWithPermissions(['public_profile', 'email']);
if (result.isCancelled) {
dispatch(loginAction.failed(socialLoginError({status: true, message: 'Facebook login canceled.'})));
}
const logindata = await AccessToken.getCurrentAccessToken();
if (!logindata) {
dispatch(loginAction.failed(socialLoginError({status: true, message: 'Something went wrong obtaining access token.'})));
return null;
} else {
const credential = firebase.auth.FacebookAuthProvider.credential(logindata.accessToken);
const firebaseUserCredential = await firebase.auth().signInWithCredential(credential);
const data = firebaseUserCredential.user.toJSON();
await FirebaseUtils.userRef.doc(data.uid).set(data);
dispatch(loginAction.success());
return data;
}
} catch (e) {
console.log('facebook login error', e);
dispatch(loginAction.failed(socialLoginError({status: true, message: 'Something went wrong.'})));
return null;
}
};

Related

I only get an exception and not the error message from the server

I am currently programming a small login api and have implemented it with my react native project using axios request. But i have a problem now my server send an error status code with a message but in the react antive app it only comes to an exception with the status code and not with the message. How can I best solve this do I have to take the status code out of the server and just send text back or is there another solution?
My React Native api.js:
import axios from "axios";
import AsyncStorage from "#react-native-community/async-storage";
const instance = axios.create({
baseURL: "http://example.com",
});
instance.interceptors.request.use(
async (config) => {
const token = await AsyncStorage.getItem("token");
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
},
(err) => {
return Promise.reject(err);
}
);
export default instance;
My React Native api trigger:
const Signup = async ({email, password}) => {
try{
const response = await myApi.post("/api/signup", { email, password });
if (response.data.token){
navigation.navigate('setup');
}else{
setError(response.data);
}
}catch (err){
console.log(err);
setError('Internet Error');
}
}
and my server response
return res.status(400).send({
message: "Email or password incorrect!",
});

React native error: [Unhandled promise rejection: TypeError: _app.default.storage is not a function.]

I'm using react native, expo and firebase to develop my app. I'm having trouble trying to upload an image to firebase storage.
I will show some of my imports and code here. I have my firebase configuration file set up like so:
import firebase from 'firebase/compat/app';
import 'firebase/compat/auth';
import 'firebase/compat/firestore';
const firebaseConfig = {
apiKey: "**",
authDomain: "**",
projectId: ""**"",
storageBucket: "**",
messagingSenderId: "**",
appId: "**",
measurementId: "**"
};
let app;
if (firebase.apps.length === 0) {
app = firebase.initializeApp(firebaseConfig)
} else {
app = firebase.app();
}
const db = app.firestore();
const auth = firebase.auth();
export { db, auth, firebaseConfig};
Inside my register file I have the following imports:
import * as ImagePicker from 'expo-image-picker';
// firebase
import firebase from 'firebase/compat/app';
import { auth, db, firebaseConfig } from '../config/Firebase';
// image storage
import storage from 'firebase/storage';
Then, I try and upload the image to firestore using the following functions:
let openImagePickerAsync = async () => {
let permissionResult = await ImagePicker.requestMediaLibraryPermissionsAsync();
if (permissionResult.granted === false) {
alert('Permission to access camera roll is required!');
return;
}
let pickerResult = await ImagePicker.launchImageLibraryAsync();
if (pickerResult.cancelled === true) {
return;
}
setImage(pickerResult.uri);
}
const uploadImage = async () => {
const imageUri = image;
let filename = imageUri.substring(imageUri.lastIndexOf('/') + 1);
try {
await storage().ref(filename).putFile(imageUri);
console.log('Image uploaded!');
} catch (error) {
console.log(error);
}
}
The error I'm receiving is: "0, _storage.default) is not a function." Can anyone help?

Is User already have Permissions FB SDK react native

I have a simple Question i just want to know that is user first time trying to logged in or else.
I am using react native fb SDK and Im enable to find any way.
I already try but not getting success.
AccessToken.getCurrentAccessToken()
Thanks
This is my login facebook function:
Step is:
Get permission
User login
Get token
My code:
import { LoginManager, AccessToken } from 'react-native-fbsdk';
const loginFacebook = () => {
setTypeLogin(TYPE_LOGIN.FACEBOOK)
if (Platform.OS === "android") {
LoginManager.setLoginBehavior("web_only")
}
LoginManager.logInWithPermissions(['public_profile', 'email']).then(
function (result) {
if (result.isCancelled) {
} else {
AccessToken.getCurrentAccessToken()
.then((data) => {
setAccessToken(data.accessToken)
prepareCallApi()
})
.catch(error => {
console.log(error)
})
}
},
function (error) {
console.log('Login fail with error: ' + error);
},
);
};

How to call useNavigation inside axios file

I try to implement axios interceptor, for my request.
So when the error response is forbidden (401), I want to clear token and navigate to login screen.
But when I want to call useNavigation from react navigation v5 inside my axios file, it always shows error like this.
React Hook "useNavigation" is called in function "refreshAccessToken" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter.
This is my axios file code,
import axios from 'axios'
import AsyncStorage from '#react-native-community/async-storage'
import {useNavigation} from '#react-navigation/native'
import {generateRandomString, getDeviceInfo} from '../helper/helper'
const base = axios.create({
baseURL: '------',
timeout: 500000,
headers: {
'Content-Type': 'application/json'
}
})
async function refreshAccessToken() {
const navigation = useNavigation()
try {
const refreshToken = await AsyncStorage.getItem('refreshToken')
const response = await base.get('------', {
refreshToken
})
await AsyncStorage.setItem('accessToken', response.data.accessToken)
console.log('Response from refresh access token ', response)
return Promise.resolve(response.data)
} catch (error) {
console.log('Error from refresh access token', error.response)
if (error.response.data.errors.flag === 'INVALID_REFRESH_TOKEN') {
await AsyncStorage.removeItem('refreshToken')
await AsyncStorage.removeItem('accessToken')
}
return Promise.reject(error)
}
}
base.interceptors.request.use(
async function (config) {
const accessToken = await AsyncStorage.getItem('accessToken')
config.headers.authorization = accessToken
const deviceInfo = await getDeviceInfo()
const latitude = await AsyncStorage.getItem('latitude')
const longitude = await AsyncStorage.getItem('longitude')
config.headers['accept-language'] = 'id-ID'
config.headers['version'] = '1.0.0'
config.headers['date'] = new Date().toUTCString()
config.headers['x-coordinate'] = `${latitude};${longitude}`
config.headers['x-trace-id'] = generateRandomString()
config.headers['x-device'] = `${deviceInfo.deviceType}/${deviceInfo.deviceName}/${deviceInfo.deviceVersion}/${deviceInfo.deviceUid}`
return config
},
function (error) {
return Promise.reject(error)
}
)
base.interceptors.response.use(
(response) => response,
async (error) => {
const originalRequest = error.config
if (error.response.status === 403 && !originalRequest._retry) {
originalRequest._retry = true
const accessToken = await refreshAccessToken()
originalRequest.headers.Authorization = accessToken
return axios(originalRequest)
}
return Promise.reject(error)
}
)
export default base
You can't, hooks are only meant to be called inside components. There is a guide on how to navigate without the navigation prop you could use for this situation though.
You could also send an event (via a custom event emitter) and set up a listener anywhere else in your app where the navigation prop is available.

How to get FB Access Token with Expo

I'm building app where i need to make Facebook Graph API requests in many places. But i dont know how to retrieve access token and then make Graph API request.
I'm using Expo, React Native and Firebase. I would like to do it without installing Xcode and/or Android Studio.
Login is working fine. My code is below:
async loginWithFacebook() {
try {
const {
type,
token,
expires,
permissions,
declinedPermissions,
} = await Expo.Facebook.logInWithReadPermissionsAsync('<APP_ID', {
permissions: ['email', 'public_profile'],
});
if (type === 'success') {
const credential = f.auth.FacebookAuthProvider.credential(token)
f.auth().signInAndRetrieveDataWithCredential(credential).catch((error) => {
console.log(error)
})
var that = this;
const response = await fetch(`https://graph.facebook.com/me/?fields=id,name&access_token=${token}`);
const userInfo = await response.json();
this.setState({userInfo});
this.setState({
dataSource: userInfo.data,
isLoading: false,
});
} else {
// type === 'cancel'
}
} catch ({ message }) {
alert(`Facebook Login Error: ${message}`);
}
}
Can someone help me and give me some tips how i can use access token everywhere in my app?
Thank you in advance
Getting the token and saving it into AsyncStorage
Well the code that you have written is basically correct. You have successfully got the access token. It comes back to you when you make the Expo.Facebook.logInWithReadPermissionsAsync request. Once you have it you could then store it in Redux or AsyncStorage to be used at a later date.
logIn = async () => {
let appID = '123456789012345' // <- you'll need to add your own appID here
try {
const {
type,
token, // <- this is your access token
expires,
permissions,
declinedPermissions,
} = await Expo.Facebook.logInWithReadPermissionsAsync(appID, { permissions: ['public_profile', 'email'], });
if (type === 'success') {
// Get the user's name using Facebook's Graph API
const response = await fetch(`https://graph.facebook.com/me/?fields=id,name&access_token=${token}`); //<- use the token you got in your request
const userInfo = await response.json();
alert(userInfo.name);
// you could now save the token in AsyncStorage, Redux or leave it in state
await AsyncStorage.setItem('token', token); // <- save the token in AsyncStorage for later use
} else {
// type === 'cancel'
}
} catch ({ message }) {
alert(`Facebook Login Error: ${message}`);
}
}
app.json
Also remember to add the following to your app.json, obviously replacing the values with your own. You get these by registering your app with Facebook, you can see more about that here https://docs.expo.io/versions/latest/sdk/facebook/#registering-your-app-with-facebook
{
"expo": {
"facebookScheme": "fb123456789012345",
"facebookAppId": "123456789012345", // <- this is the same appID that you require above when making your initial request.
"facebookDisplayName": "you_re_facebook_app_name",
...
}
}
Getting token from AsyncStorage
Then if you wanted to make another request at a later time you could have a function similar to this where you get the token out of AsyncStorage and then use it to make your request.
makeGraphRequest = async () => {
try {
let token = await AsyncStorage.getItem('token'); // <- get the token from AsyncStorage
const response = await fetch(`https://graph.facebook.com/me/?fields=id,name&access_token=${token}`); // <- use the token for making the graphQL request
const userInfo = await response.json();
alert(userInfo.name)
} catch (err) {
alert(err.message)
}
}
Snack
I would make a snack to show you this working however, snacks do not allow editing of the app.json file (as far as I can tell). So here is something that you could replace your App.js with and then if you added your appIDs etc to the app.json it should work.
import React from 'react';
import { AsyncStorage, Text, View, StyleSheet, SafeAreaView, Button } from 'react-native';
export default class App extends React.Component {
logIn = async () => {
let appID = '123456789012345' // <- you'll need to add your own appID here
try {
const {
type,
token, // <- this is your access token
expires,
permissions,
declinedPermissions,
} = await Expo.Facebook.logInWithReadPermissionsAsync(appID, {
permissions: ['public_profile', 'email'],
});
if (type === 'success') {
// Get the user's name using Facebook's Graph API
const response = await fetch(`https://graph.facebook.com/me/?fields=id,name&access_token=${token}`); //<- use the token you got in your request
const userInfo = await response.json();
console.log(userInfo);
alert(userInfo.name);
// you could now save the token in AsyncStorage, Redux or leave it in state
await AsyncStorage.setItem('token', token); // <- save the token in AsyncStorage for later use
} else {
// type === 'cancel'
}
} catch ({ message }) {
alert(`Facebook Login Error: ${message}`);
}
}
makeGraphRequest = async () => {
try {
let token = await AsyncStorage.getItem('token');
// Get the user's name using Facebook's Graph API
const response = await fetch(`https://graph.facebook.com/me/?fields=id,name&access_token=${token}`);
const userInfo = await response.json();
alert(userInfo.name)
} catch (err) {
alert(err.message)
}
}
render() {
return (
<View style={styles.container}>
<Button title={'Sign in to Facebook'} onPress={this.logIn} />
<Button title={'Make GraphQL Request'} onPress={this.makeGraphRequest} />
</View>
)
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'white'
}
});