After calling "createUserWithEmailAndPassword" or "signInWithEmailAndPassword", a property "currentUser" becomes filled in "auth()". It remains full after restarting an application, even if I remove this user from the Firebase console.
How can I check user's authorization when the application starts?
To force the client to "check in" with the server, you would use the User#getIdToken() method by calling firebase.auth().currentUser.getIdToken(true). If the user has been deleted, this should reject with the error code 'auth/user-token-expired'.
From the React Native Firebase Quick Start documentation, I'll use this as the base of the MWE:
import React, { useState, useEffect } from 'react';
import { View, Text } from 'react-native';
import auth from '#react-native-firebase/auth';
function App() {
// Set an initializing state whilst Firebase connects
const [initializing, setInitializing] = useState(true);
const [user, setUser] = useState();
// Handle user state changes
function onAuthStateChanged(user) {
setUser(user);
if (initializing) setInitializing(false);
}
useEffect(() => {
const subscriber = auth().onAuthStateChanged(onAuthStateChanged);
return subscriber; // unsubscribe on unmount
}, []);
if (initializing) return null;
if (!user) {
return (
<View>
<Text>Login</Text>
</View>
);
}
return (
<View>
<Text>Welcome {user.email}</Text>
</View>
);
}
Once the user has logged in or their cached access has been restored, onAuthStateChanged will receive an event with the current user object. Here we add the ID token request.
function onAuthStateChanged(user) {
if (!user) {
// not logged in
setUser(user);
if (initializing) setInitializing(false);
return;
}
user.getIdToken(/* forceRefresh */ true)
.then(token => {
// if here, this user is still authorised.
setUser(user);
if (initializing) setInitializing(false);
}, error => {
if (error.code === 'auth/user-token-expired') {
// token invalidated. No action required as onAuthStateChanged will be fired again with null
} else {
console.error('Unexpected error: ' + error.code);
}
});
}
Related
After a user signs in, I use router.push() to redirect the user to their profile page. I am using getServerSideProps() for authentication right now. When the redirect happens, the props don't seem to be fetched and I have to refresh the browser myself to call gSSR. Is this behavior normal or is there a way to fix it?
Demonstration - Updated
login.js
import {useRouter} from 'next/router';
export default function Login({user}) {
const router = useRouter();
// invoked on submission
async function submitLoginForm(email, password) {
const user = await signIn(email, password)
const username = await getUsernameFromDB(user);
router.push("/" + username);
}
return ( ... );
}
export async function getServerSideProps({req}) {
const user = await getUserFromCookie(req);
if(user === null) {
return {
props: {}
}
}
return {
redirect: {
destination: `/${user.username}`,
permanent: false
}
}
}
[username].js
export default function Profile({user, isUser}) {
// Use isUser to render different interface.
return ( ... );
}
export async function getServerSideProps({params, req}) {
// The username of the path.
const profileUsername = params.username
// Current user.
const user = await getUserFromCookie(req);
...
if(user !== null) {
return {
props: {
user: user,
isUser: user !== null && profileUsername === user.username
}
}
}
return {
notFound: true
}
}
The cookie is set in the _app.js using the Supabase auth sdk.
function MyApp({Component, pageProps}) {
supabase.auth.onAuthStateChange( ( event, session ) => {
fetch( "/api/auth", {
method: "POST",
headers: new Headers( { "Content-Type": "application/json" } ),
credentials: "same-origin",
body: JSON.stringify( { event, session } ),
} );
} );
return (
<Component {...pageProps} />
);
}
I would recommend that you update your _app.js like that:
import { useEffect } from 'react';
function MyApp({ Component, pageProps }) {
// make sure to run this code only once per application lifetime
useEffect(() => {
// might return an unsubscribe handler
return supabase.auth.onAuthStateChange(( event, session ) => {
fetch( "/api/auth", {
method: "POST",
headers: new Headers( { "Content-Type": "application/json" } ),
credentials: "same-origin",
body: JSON.stringify( { event, session } ),
});
});
}, []);
return <Component {...pageProps} />;
}
Also, please make clear what is happening. E.g. my current expectation:
Not authenticated user opens the "/login" page
He does some login against a backend, that sets a cookie value with user information
Then router.push("/" + username); is called
But the problem now: On page "/foo" he sees now the Not-Found page instead of the user profile
Only after page reload, you see the profile page correctly
If the above is correct, then it is possible the following line is not correctly awaiting the cookie to be persisted before the navigation happens:
const user = await signIn(email, password)
It could be that some internal promise is not correctly chained/awaited.
As an recommendation, I would log to the console the current cookie value before calling the router.push to see if the cookie was already saved.
I have been looking on several ways to keep the user logged in my react native application. One of those are:
auth.onAuthStateChanged((user) => {
if (user) {
setIsAuthenticated(true);
} else {
setIsAuthenticated(false);
});
Which isn't working for me, whenever I press r on the application to hot refresh, it losses the user.
I'm currently storing the user email, uid on the local storage, and only loggin it out whenever he presses the logout button. Is this the right way to do so? I have encountered some problems, for example. I can't use auth().currentUser since the data is stored in async storage.
This is how i'm managing it:
export default function App() {
// Autentication
const [isAuthenticated, setIsAuthenticated] = useState(false);
// Dark mode
const [darkMode, setDarkMode] = useState(false);
// Setting location
let [locale, setLocale] = useState(Localization.locale);
i18n.fallbacks = true;
i18n.translations = { en, pt };
i18n.locale = locale;
// Settings fonts
const [fontsLoaded] = useFonts({
Roboto_400Regular,
Roboto_500Medium,
Roboto_700Bold,
});
useEffect(() => {
setLocale('pt');
getData('#rememberUser')
.then((data) => data && setIsAuthenticated(true))
.catch(() => console.log('No user on async'));
// auth.onAuthStateChanged((user) => {
// if (user) {
// setIsAuthenticated(true);
// } else {
// setIsAuthenticated(false);
// }
// console.log(user);
// });
}, []);
if (!fontsLoaded) return <></>;
return (
<DataProvider
setIsAuthenticated={setIsAuthenticated}
setDarkMode={setDarkMode}
darkMode={darkMode}
>
<ThemeProvider theme={darkMode ? darkTheme : lightTheme}>
{!isAuthenticated ? (
<NavigationContainer>
<AuthRoutes />
</NavigationContainer>
) : (
<Provider store={store}>
<Main />
</Provider>
)}
<ToastMessage />
</ThemeProvider>
</DataProvider>
);
}
storing:
async function storeData(value: any) {
try {
await clearAll();
const jsonValue = JSON.stringify(value);
await AsyncStorage.setItem('#rememberUser', jsonValue);
} catch (error) {
console.log('Error on async storage');
}
}
Is this the right way to keep track of the user?
i'm trying to send notification with firebase. i wanna get devices token (senderID) but when i run my code the app shut down immediately
please look at my code and tell me where is my mistake
import React,{useEffect} from 'react'
import { View, Text } from 'react-native'
import messaging from '#react-native-firebase/messaging'
const checkToken = async () => {
try {
const fcmToken = await messaging().getToken();
if (fcmToken) {
console.log(fcmToken);
}
} catch (error) {
console.error(error);
}
};
const App = () => {
useEffect(() => {
checkToken()
}, [])
return (
<View>
<Text>sadasdasfdasdafsfdasfdasdafsdf</Text>
</View>
)
}
export default App
I'm writing a project in React-Native for both iOS and Android, I want to send notification automatically when users just install app but don't login or sign-up after 3 days. I'm using firebase cloud data for the project. Is it possible to do that over coding or firebase?
Using Firebase, you can get the FCM token for the device even without them being logged in. You will needs to get their permission to receive notifications though.
import React, { Component } from "react";
import { Text, View } from "react-native";
import firebase from "react-native-firebase";
export default class componentName extends Component {
async componentDidMount() {
this.checkPermission();
}
//1
async checkPermission() {
firebase
.messaging()
.hasPermission()
.then((enabled) => {
if (enabled) {
this.getToken();
} else {
this.requestPermission();
}
});
}
//2
async requestPermission() {
firebase
.messaging()
.requestPermission()
.then(() => {
this.getToken();
})
.catch((error) => {});
}
//3
async getToken() {
fcmToken = await firebase.messaging().getToken();
if (fcmToken) {
//
//
//
//
//
// Call your API here
//
//
//
//
//
//
}
}
render() {
return (
<View>
<Text> Your APP </Text>
</View>
);
}
}
I'd like to activate Facebook login by react native firebase. My purpose is to move to Signin screen to Main Screen.
I use,
react-native: 0.52.2
react-native-firebase: ^3.2.4
react-navigation: ^1.0.0-beta.28
LoginScreen.js
const facebookLogin = () => {
return LoginManager
.logInWithReadPermissions(['public_profile', 'email'])
.then((result) => {
if (!result.isCancelled) {
console.log(`Login success with permissions: ${result.grantedPermissions.toString()}`)
// get the access token
return AccessToken.getCurrentAccessToken()
}
})
.then(data => {
if (data) {
// create a new firebase credential with the token
const credential = firebase.auth.FacebookAuthProvider.credential(data.accessToken)
// login with credential
return firebase.auth().signInAndRetrieveDataWithCredential(credential)
}
})
.then((currentUser) => {
if (currentUser) {
console.log(currentUser);
return this.props.navigation.navigate('Main');
}
})
.catch((error) => {
console.log(`Login fail with error: ${error}`)
})
}
After (currentUser), I added following that.
this.props.navigation.navigate('Main');
But that doesn't work!
I have already set up Router, and that did work at out of Auth Component.
I checked at console.log and that shows me:
Login fail with error: TypeError: Cannot read property 'navigate' of undefined"
So I tried to move const { navigation } = this.props; from Auth Component to other SigninScreen Component. But I can't see successful login.
Thanks for your answer. And I have defined already in Parent component.
I defined navigation here.
class SignInScreen extends React.Component {
render() {
const { navigate } = this.props.navigation;
return (
<View style={styles.containerStyle}>
<TouchableOpacity onPress={facebookLogin}>
<Text style={styles.textStyle}>Facebook</Text>
</TouchableOpacity>
</View>
);
}
}
It seems that the navigation object is undefined. Check the parent component to see if navigation is defined before it is passed as props to this component
It could also be due to the way javascript handles the this keyword. see this answer You could try binding the navigate method to the Class by adding
this.navigate = this.props.navigation.navigate.bind(this) inside the constructor,
and change
.then((currentUser) => {
if (currentUser) {
console.log(currentUser);
return this.props.navigation.navigate('Main');
}
to
.then((currentUser) => {
if (currentUser) {
console.log(currentUser);
return this.navigate('Main');
}