I am trying to conditionally render a React Native screen dependent on whether 1) the screen is loading whilst the app checks for authentication status 2) the user is already authenticated thereby displaying the home screen 3) the user is not authenticated thereby displaying the login/signup screen.
I've been largely success implementing this, however, in the case of (2), the Signup screen is very briefly displayed until the correct Homepage screen is shown.. which is annoying. Here is my code:
const App = () => {
const [isReady, setIsReady] = useState(false)
const [user, setUser] = useState({})
useEffect(() => {
const checkAuth = async () => {
try {
await Auth.getCurrentAuthenticatedUser()
.then(user => setUser(user))
} catch (e) {
console.log('An error occurred.. ', e)
}
}
checkAuth()
setIsReady(true)
}, [])
const showAppLoading = (!isReady && !user)
if (showAppLoading) {
// App is loading
view = <LoadingComponent />
} else if (!user) {
// User not authenticated - go to Authenticator
view = <AuthNavigator />
} else {
// User is authenticated - go to App
view = <App signOut={signOut} />
}
return (
<View>{view}</View>
)
}
It appears that <AuthNavigator /> loads briefly because user state hasn't loaded in time, however, I don't know why this is because setUser() has been called straight after I get the user credentials.
Hope you can help
Try this way.
useEffect(() => {
try {
Auth.getCurrentAuthenticatedUser()
.then(user => {
setUser(user);
setIsReady(true);
});
} catch (e) {
console.log('An error occurred.. ', e)
}
}, [])
Related
I'm developing a React Native project, I have a problem. On the example home page, I am listening to the firebase table in useEffect. When I log out and go to the login page, the firebase process on the home page still works and gives an error because there is no user id. How can I stop Firebase processes on the home page when I'm logged out?
useEffect(() => {
const userRef = database().ref('messages')
.child(session.userId)
.child(session.fbid);
const onChildChanged = userRef.on(
'child_changed',
snapshot => {
console.log(snapshot)
},
function (error) {
console.log(error);
},
);
return () => {
userRef.off('child_changed', onChildChanged);
};
}, [session.fbid, session.userId, navigation]);
[Error: [database/permission-denied] Client doesn't have permission to access the desired data.]
It's required to check user authentication status before executing Firebase authentication-based features.
useEffect(() => {
// Get current authenticated user
const user = firebase.auth().currentUser;
// Listen to data changes when user is authenticated only.
if (user) {
const userRef = database()
.ref("messages")
.child(session.userId)
.child(session.fbid);
const onChildChanged = userRef.on(
"child_changed",
(snapshot) => {
console.log(snapshot);
},
function (error) {
console.log(error);
}
);
}
return () => {
user && userRef.off("child_changed", onChildChanged);
};
}, [session.fbid, session.userId, navigation, user]);
Hey guys I am trying to use Firebase Authentication in my React App and I am doing this by setting up an context. I have checked a lot of questions regarding this and I wasn't able to get a satisfactory answer and so I am asking this here. Everything works except for the signInWithEmailAndPassword .My logic is that when the user signs in and the user has verified its email then take him to another page.
Here in the context whenever the user object changes the onAuthStateChanged gets trigger and there I am updating the user object.But then to make this sign in work I have to click on the sign in button twice and then the page gets redirected.
When the sign in button is triggered the first time , the setUser() doesnt update the user object quickly.
Here is the snippet of code for the context I have set up
const userAuthContext = createContext();
//Use the created context
export function useUserAuth() {
return useContext(userAuthContext);
}
export function UserAuthContextProvider({ children }) {
const [user, setUser] = useState({});
const [loading, setLoading] = useState(true);
// Sign in a user with email and password
function logIn(email, password) {
return signInWithEmailAndPassword(auth, email, password);
}
useEffect(() => {
const unsubscribe = onAuthStateChanged(auth, (currentUser) => {
console.log(currentUser);
setUser(currentUser);
setLoading(false);
console.log("User is " , user);
});
const value = {
user,
logIn,
}
return (
<userAuthContext.Provider
value={value}
>
{!loading && children}
</userAuthContext.Provider>
);
}
And this is the login.js where I check that if the user has verified its email then I redirect to another page
export default function LoginForm() {
const { register, setError ,formState, handleSubmit } = useForm();
const { user, logIn } = useUserAuth();
const onSubmit = async(data) => {
console.log(data);
try{
await logIn(data.email,data.password);
}catch (error) {
setError("email", {
type: "manual",
message: error.message,
});
}
console.log(user);
if(user && user.emailVerified===true)
{
navigate('/studDash');
}else if(user){
setError("email", {
type: "manual",
message: "Email Not Verified !! Check your mail for verification mail !!",
});
}
}
return (
<div>
//Login Page Div is here
</div>
);
}
I have tried to do console.log(user) at onAuthStateChanged in the context and in the onSubmit function here is the screenshot of it. This is the console when I click the sign in button once , if i click signin button again then the user gets directed to next page successfully.
Tried what i could but i failed ,tried with useState and all but nope.
Im using firebase auth ,if the user is logged in I show main page component ,if not i show the log in page ,but sometimes it just flashes the login page and goes to the main page in a weird/bad way ,any suggestions to fix it?
EDIT : I tried without useEffect but still the same
function App() {
const [mycurrentUser,SetCurrentUser] = useState(null)
useEffect(() => {
onAuthStateChanged(auth, (user) => {
if (user) {
SetCurrentUser(user)
} else {
SetCurrentUser(null)
}
});
}, [auth.currentUser])
return (
<section>
{ mycurrentUser
? <FeedPage/>
: <AuthPage/>}
</section>
);
}
One thing i noticed is that you don't have to keep calling the onAuthStateChange, just once on mount is good enough.
useEffect(() => {
onAuthStateChanged(auth, (user) => {
if (user) {
SetCurrentUser(user)
} else {
SetCurrentUser(null)
}
});
}, [])
Also how does the Auth page know the current user ? Either you have to pass it in via prop or context.
I think I should ask this question, cause I face some very weird problem.
I have a protected route, /app, if user is not login reached this page, redirect to /auth/login, so I have this, common Context hooks:
const { user, isLoading } = useAuth()
useEffect(() => {
if (!(user || isLoading)) {
// HERE REDIRECT TO /auth/login
}
}, [user, isLoading])
//here make sense
Now in my useAuth(), I have this:
const useAuth = () => {
const [user, setUser] = useState(null)
useEffect(() => {
firebase.auth().onAuthStateChanged((currentUser) => {
setIsLoading(false)
if (currentUser) {
console.log(currentUser)
//here
getUserFromMyServer(currentUser.uid).then(res => {
let userDetails = res.detail
setUser(userDetails) // here will get User data from server
}).catch(error => setUser(null))
} else {
setUser(null)
}
})
}, [])
return {
user,
isLoading,
loginWithTwitter, // a function will mention later
getTwitterRedirectResult, //a function will mention later
logout // a function
}
}
This is what I call to server
// here is the getUserFromMyServer function in another files, it look like this:
export const getUserFromMyServer = async () => {
const path = "/user"
const firebaseToken = await firebase.auth().currentUser.getIdToken()
console.log(firebaseToken)
const myInit = {
headers: {
Authorization: `Bearer ${firebaseToken}`,
}
}
return API.get(apiName, path, myInit) // Here return a promise
}
OK, so far is working well. When done login, it will setUser(), then will redirect from /auth/login to /app according to the user, this is what I want.
Now when I implement Sign in with Twitter, it comes in a problem.
The behavior: When done Sign in with Twitter, it redirect to /app. Then when /app is loaded for 20s like that then it go back to /auth/login again. This is keep repeating, non-stop until the browser hang.
Here is my code for Sign in with Twitter.
useAuth.js (same file with code above with loginWithTwitter)
const [operation, setOperation] = useState('login')
const loginWithTwitter = () => {
setOperation(type)
let provider = getTwitterProvider()
return firebase.auth().signInWithRedirect(provider)
}
const getTwitterRedirectResult = () => {
return firebase
.auth()
.getRedirectResult()
.then((result) => {
console.log(result)
var credential = result.credential;
var token = credential.accessToken;
var secret = credential.secret;
var twitterHandle = result.additionalUserInfo.username
if (operation === 'sign-up') {
// if sign up, here save to my db
createUserInDb(twitterHandle, token, secret).then(res=> {
setUser(res.data)
}).catch(error => setUser(null))
}
if (operation === 'login') {
// here get back the user details same as above
getUser()
}
}).catch((error) => {
console.log(error.message)
setErrorMessage(error.message)
setUser(null)
});
}
Then in my /auth/login and /auth/sign-up I have this code:
const Login = () => {
const { user, getTwitterRedirectResult } = useAuth() //
useEffect(() => {
getTwitterRedirectResult() // here call to twitter redirect result
}, [])
useEffect(() => {
if (user) {
// code redirect to app
router.push('/app')
}
})
}
So now, when login/sign up with password, is no problem. Once login, go to /app. then stay there.
But when sign in with Twitter, it go to /app. Stay there for 20s, then it redirect back to /auth/login. Basically is infinite loop.
So now my question:
What is the correct way to call getTwitterRedirect in react? Is it call at useEffect() when the page load?
How to solve the infinite loop? Done login go to /app, then it go back to /auth/login again.
How to save user data to my own db when getting the result back from getTwitterRedirect. So when Sign up I need to create the record, but when login with Twitter, I just need to get back the same record. But right now, it only have 1 function getTwitterRedirect, login and sign up also use this. So how can I solve this?
Please someone tell me what I doing wrong, I have totally no clue.
Thanks in advance.
I am working on the authentication of my app and I have managed to add login to my page. Users are able to login and their session is stored however as soon as I refresh the page their session is gone.
ReactJs + NextJS
I know there is getTokenSilently but it will return this when I call it!
error: "login_required"
error_description: "Login required"
state: "N3B+aWt4T1dBeGlibWsua2ZkdX5LTzR6T19ndTdXfkJ2Tm5kUzJIY3lXTQ=="
What am I doing wrong here?
My profile page!
useEffect(() => {
if (typeof window !== `undefined`) {
if (!loading && !isAuthenticated) {
loginWithRedirect({})
}
}
});
Home page which shows an icon if user is logged in!
<Button
className="account-button"
variant="textButton"
icon={<i className="flaticon-user" />}
aria-label="login"
title={loading ? 'loading' : isAuthenticated ? 'Hi' : 'login'}
/>
Auth service
// src/react-auth0-spa.js
import React, { useState, useEffect, useContext } from "react";
import createAuth0Client from "#auth0/auth0-spa-js";
const DEFAULT_REDIRECT_CALLBACK = () =>
window.history.replaceState({}, document.title, window.location.pathname);
export const Auth0Context = React.createContext();
export const useAuth0 = () => useContext(Auth0Context);
export const Auth0Provider = ({
children,
onRedirectCallback = DEFAULT_REDIRECT_CALLBACK,
...initOptions
}) => {
const [isAuthenticated, setIsAuthenticated] = useState();
const [user, setUser] = useState();
const [auth0Client, setAuth0] = useState();
const [loading, setLoading] = useState(true);
const [popupOpen, setPopupOpen] = useState(false);
useEffect(() => {
const initAuth0 = async () => {
const auth0FromHook = await createAuth0Client(initOptions);
setAuth0(auth0FromHook);
if (window.location.search.includes("code=") &&
window.location.search.includes("state=")) {
const { appState } = await auth0FromHook.handleRedirectCallback();
onRedirectCallback(appState);
}
const isAuthenticated = await auth0FromHook.isAuthenticated();
setIsAuthenticated(isAuthenticated);
if (isAuthenticated) {
const user = await auth0FromHook.getUser();
setUser(user);
}
setLoading(false);
};
initAuth0();
// eslint-disable-next-line
}, []);
const loginWithPopup = async (params = {}) => {
setPopupOpen(true);
try {
await auth0Client.loginWithPopup(params);
} catch (error) {
console.error(error);
} finally {
setPopupOpen(false);
}
const user = await auth0Client.getUser();
setUser(user);
setIsAuthenticated(true);
};
const handleRedirectCallback = async () => {
setLoading(true);
await auth0Client.handleRedirectCallback();
const user = await auth0Client.getUser();
setLoading(false);
setIsAuthenticated(true);
setUser(user);
};
return (
<Auth0Context.Provider
value={{
isAuthenticated,
user,
loading,
popupOpen,
loginWithPopup,
handleRedirectCallback,
getIdTokenClaims: (...p) => auth0Client.getIdTokenClaims(...p),
loginWithRedirect: (...p) => auth0Client.loginWithRedirect(...p),
getTokenSilently: (...p) => auth0Client.getTokenSilently(...p),
getTokenWithPopup: (...p) => auth0Client.getTokenWithPopup(...p),
logout: (...p) => auth0Client.logout(...p)
}}
>
{children}
</Auth0Context.Provider>
);
};
The problem was using Brave Browser!!!!!! Detailed description here:
Right. So the silent authentication issue, the “login required” error, is what you get when your browser does not, or cannot, send the “auth0” cookie. This is the cookie that Auth0 leaves on the browser client once the user has a session with Auth0, i.e. the user has logged in through an interactive flow. You should be able to confirm this by looking at the network logs or analyzing the HAR output. The scenarios that work will have the cookie attached, whereas the ones that fail will not. If that’s the case, this is neither a sample nor SDK issue as they are not involved in the setting of that cookie; it is issued by the authorization server.
If the browser cannot sent this cookie, it’s most likely because of some software or browser extension or something which is blocking third-party tracking cookies. Safari does this by default thanks to its Intelligent Tracking Prevention (ITP2) 1 software that is built-in. This can explain why silent auth works in Chrome’s incognito mode but not in normal mode. If you’re running some extensions, you might want to disable some of them to narrow down which one is preventing the cookie from being sent.
What I can’t readily explain is how it works in Safari’s Private mode, as I thought ITP2 would block such cookies regardless. Let me get some clarity on that.
https://community.auth0.com/t/failed-silent-auth-login-required/33165/24