Login status in React - reactjs

I created authorization in javascript. Then if success login I redirect to React project with url parameter http://localhost:3000/?phoneNum=%2B77072050399
Then in React I get userId by using the passed url parameter => phoneNumber using axios.
I realized it in App.js. Code below:
let url = window.location.href;
let params = (new URL(url)).searchParams;
const userPhoneNum = encodeURIComponent(params.get('phoneNum'));
const [userToken, setUserToken] = useState(null);
const getUserToken = async() => {
try {
const data = await axios
.get(`https://stormy-escarpment-89406.herokuapp.com/users/getToken?phone_number=${userPhoneNum}`)
.then(response => {
setUserToken(response.data);
})
.catch(function(error) {
console.log('No such user! Error in getting token!');
});
} catch (e) {
console.log(e);
}
}
useEffect(() => {
getUserToken();
console.log(userToken);
}, userToken);
So, when I go to next page localhost:3000/places, it is requesting for userToken again with parameter null, because there is no param phoneNum.
How to make it to request only one time and save the userId after it is taken in main page. So, then only when I click LogOut button reset the variable where userID is saved.

If you want to do that without using any third party libraries you can use browser's in built storage api
So, when you receive the token, you can store that in the local storage of the browser using localstorage.setItem and later when you wan to see if the token is there or not just read from there using localStorage.getItem
const getUserToken = async() => {
try {
const data = await axios
.get(`https://stormy-escarpment-89406.herokuapp.com/users/getToken?phone_number=${userPhoneNum}`)
.then(response => {
setUserToken(response.data);
Storage.setItem('token',JSON.stringify(response.data))
})
.catch(function(error) {
console.log('No such user! Error in getting token!');
});
} catch (e) {
console.log(e);
}
}
For Logout you can simply remove the token using localStorage.removeItem

You can easily achieve this by using the react-cookie library
npm i react-cookie
Can be easily implemented in your code by
cookies.set('key', value, { path: '/' });
cookies.get('key')
After getting the userNumber form the param
const userPhoneNum = encodeURIComponent(params.get('phoneNum'));
cookies.set('userphoneNum', userPhoneNum);
View the documentation for more information
https://www.npmjs.com/package/react-cookie

Related

onAuthStateChanged and getRedirectResult returning null user after successful google login redirect

Somewhat randomly my login flow stops working for some users when using google as a login provider (possibly facebook too, unsure) on iOS. They are able to log in on desktop using google login with the same app bundle. I think this is happening for ~1/10 users.
After a user selects which google account to use, the google redirects back to my app. The onAuthStateChange triggers, but the user is null. This is after successfully "logging in" with google. I think this may have to do with users trying to use multiple auth providers and getting stuck in a weird state. I'm not receiving any error or console logs when this happens, it's as if it's a fresh page load instead of a redirect.
I haven't been able to reproduce the issue using my own account until recently. I tried reverting back to earlier builds when the issue wasn't present for my account, with no luck. I'm now unable to log into my own app 😂
in UserProvider.tsx:
firebaseConfig.apiKey = process.env.REACT_APP_AUTH_API_KEY;
firebaseConfig.authDomain = process.env.REACT_APP_AUTH_DOMAIN;
firebaseConfig.projectId = "redacted";
firebaseConfig.storageBucket = "redacted.appspot.com";
firebaseConfig.messagingSenderId = "redacted";
firebaseConfig.appId = "redacted";
// Initialize Firebase
const app = initializeApp(firebaseConfig);
// Initialize Firebase Authentication and get a reference to the service
const auth = initializeAuth(app, {
popupRedirectResolver: browserPopupRedirectResolver,
persistence: [indexedDBLocalPersistence, browserLocalPersistence, inMemoryPersistence],
errorMap: debugErrorMap
});
const UserContextProvider = (props) => {
const signInWithGoogle = () => {
signInWithRedirect(auth, googleAuthProvider);
}
const handleUserCredential = async (result: UserCredential) => {
if (result.user) {
const additionalInfo = getAdditionalUserInfo(result)
dispatch({
type: 'SET_ADDITIONAL_USER_INFO', data: {
additionalInfo: additionalInfo,
}
});
}
}
useEffect(() => {
dispatch({ type: 'LOGIN_INITIATED' });
const handleRedirectResult = async () => {
alert("handling redirect result");
const result = await getRedirectResult(auth);
alert(`redirect result: ${result}`);
if (result) {
handleUserCredential(result)
}
}
onAuthStateChanged(auth, async (user: User | null) => {
alert(`on auth change fired: ${user}`);
if (user) {
// set the token now
const token = await getIdToken(user);
const refreshToken = async () => {
return await getIdToken(user, true);
}
onLoginSuccess(user, token, refreshToken);
} else {
onLogoutSuccess();
}
}, (error) => {
onError(error)
});
handleRedirectResult();
}, [])
onLogoutSuccess() is triggering because user is null.
PS: I spammed a bunch of alerts in there because I'm struggling to debug on my device, those aren't in my production build.

ID is gone when I refresh a NextJS Dynamic Route page

I am trying to get dynamic routing working in order to display and retrieve the right information using the id from the url. This works when I visit the page, but when I reload the page the id is blank. Is this fixable? I couldn't find a answer for it on the internet.
Any help would be appreciated.
Code for reference
//Initializing router
const router = useRouter();
//Getting id from url
const { id } = router.query;
//Fetching postdata
const getPostData = async () => {
setLoading(true);
await db
.collection("Posts")
.doc(id)
.get()
.then(function (doc) {
if (doc.exists) {
setPostData(doc.data());
setLoading(false);
} else {
//router.push("/404");
}
})
.catch(function (error) {
//router.push("/404");
});
};
This is a known caveat about Next.js routing, you can read about it here.
Anyway, add this on your page that uses router.query:
export async function getServerSideProps(context) {
return {
props: {},
};
}
You can save to localstorage the id the first time, and get that item if query.id is undefined, look at this approach:
query.id && localStorage.setItem('lastId', query.id)
const id = query.id || localStorage.getItem('lastId')

Token is Invalid in Reactjs application

I try to get a list from the backend in Reactjs component with JWT token but I get an error message {"status":"Token is Invalid"}, please guide me.
My backend API is working fine and my token is saved in the localstore after login.
my frontend used API code
import {API} from "../config";
/**
* to get about pages
* get a single about page
* update a about page
* delete about page
*/
export const getAboutContent = (token) =>{
return fetch(`${API}/about/?token=${token}`, {
method: "GET",
})
.then(response =>{
return response.json();
})
.catch(err =>{
console.log(err);
});
};
about/index.js
const [allAboutContent, setAllAboutContent] = useState([]);
const loadAllAboutContent = () => {
getAboutContent().then(data => {
if(data.error){
console.log(data.error)
} else{
setAllAboutContent(data.data)
}
});
};
useEffect(() =>{
loadAllAboutContent();
}, [])
Please help.
You are invoking getAboutContent in about/index.js file without JWT and hence it not defined. Just update your code to read JWT from localStorage like below
const loadAllAboutContent = () => {
// Read token from local storage
const token = localStorage.getItem('jwt');
// Pass to getAboutContent
getAboutContent(token).then(data => {
if(data.error){
console.log(data.error)
} else{
setAllAboutContent(data.data)
}
});
};
Also, I see you have stored your token as {token: ''}. Maybe, you can directly save it. Otherwise you have to read it like this JSON.parse(localStorage.getItem('jwt')).token

React Linking.addEventListener runs a Firebase sign in link twice in a useEffect

I have the following function to authenticate a user with React Native (Expo) and Firebase:
export default function AuthenticateUser() {
const user = useStore((state) => state.user); // Gets the user from state
const setUser = useStore((state) => state.setUser);
const setLoadingUser = useStore((state) => state.setLoadingUser);
const [GQL_getOrCreateUser] = useMutation(getOrCreateUser); // GraphQL function
useEffect(() => {
let unsubscribe: any;
let urlHandler: any;
function handleUrl(event: any) {
const { url }: { url: string } = event;
if (url.includes('/account')) {
const isSignInWithEmailLink = firebase.auth().isSignInWithEmailLink(url);
if (isSignInWithEmailLink) {
AsyncStorage.getItem('unverifiedEmail').then((email) => {
if (email) {
firebase
.auth()
.signInWithEmailLink(email, url)
.then(() => {
// We are signed in
})
.catch((error) => {
// Failed to sign in
});
} else {
// Missing pending email from AsyncStorage
}
});
}
};
}
function handleLinking(userDetails: User) {
urlHandler = ({ url }: { url: string }) => {
handleUrl({ url, userDetails });
};
// Listen to incoming deep link when app first opens
Linking.getInitialURL().then((url) => {
if (url) {
handleUrl({ url, userDetails });
}
});
// Listen to incoming deep link while app is open
Linking.addEventListener('url', urlHandler);
}
if (!user) {
unsubscribe = firebase.auth().onAuthStateChanged((authenticatedUser) => {
setLoadingUser(false);
if (authenticatedUser) {
const uid = authenticatedUser.uid;
const phoneNumber = authenticatedUser.phoneNumber;
let email: string;
if (authenticatedUser.email) {
email = authenticatedUser.email;
} else {
// retreiving email from AsyncStorage. We add it there when requesting a passwordless sign in link email, as recommended by Firebase.
AsyncStorage.getItem('unverifiedEmail').then((unverifiedEmail) => {
email = unverifiedEmail ? unverifiedEmail : '';
});
}
const emailVerified = authenticatedUser.emailVerified;
// Updating user record with GraphQL
GQL_getOrCreateUser({ variables: { uid, phoneNumber } })
.then(async (document) => {
const data = document.data.getOrCreateUser;
const userDetails = { ...data, phoneNumber, email, emailVerified }
setUser(userDetails); // Setting the stateful user record
handleLinking(userDetails); // Handle deeplink
})
.catch(() => {
// GraphQL failed
});
}
});
}
return () => {
unsubscribe?.();
// Removing event listener;
Linking.removeEventListener('url', urlHandler);
};
}, [GQL_getOrCreateUser, setLoadingUser, setUser, user]);
}
My problem is that the sign in method runs too often resulting in unexpected behavior.
I suspect it is caused by re-rendering triggered by the user auth state and the GraphQL running (GraphQL call to get or create a user causes three renders, which seems to be how it should behave).
I use deeplinking to handle passwordless email sign-in (firebase.auth().isSignInWithEmailLink(url))
The URL is detected with either Linking.getInitialURL (when the deeplink opens the app) or Linking.addEventListener('url', handler) when the app is already running.
As example, let's take scenario 1: Linking.getInitialUrl
I click the link. It asks to open the app.
The app opens
The user is not logged in (user is not in the app state) so the code inside the if (!user) is triggered.
The user email is in AsyncStorage because we just requested the login link email and I save it when the user asks for it.
GraphQL fetches the user and causes two more renders
I set the user in state with setUser and run handleLinking.
Because the app was closed, getInitialURL for the URL is triggered and it goes correctly through the steps and signs me in.
HOWEVER, handleLinking runs a second time (possibly the extra two renders caused by GraphQL trigger a Linking.addEventListener event to fire?) and returns an error because the sign in link cannot be used a second time.
I think there is a fundamental flow in my logic. What is it? How can this be improved and done correctly?
Thanks for helping!
If any of the objects in your useEffect dependency changes, then the useEffect function will re-run. So if your useEffect callback runs too often, the dependency array is where you should look at.
It is important to know that a "change" can be a simple as an object being reinstantiated or a function being re-created, even though the underlying value remains the same.

Internal Error when creating new user with Firebase Auth in React

I am working with Firebase for authentication in my application, and below is the function I am calling on my form. Before I had added the .then() with the updateProfile inside, it was working fine. However, now when I create a new user, the user's email and password is stored, but I get the error Internal error: Too much recursion and the displayName is never set.
const handleSignUp = useCallback(
async (event) => {
event.preventDefault();
const { fullName, email, password } = event.target.elements;
try {
await app
.auth()
.createUserWithEmailAndPassword(email.value, password.value)
.then((result) => {
const user = app.auth().currentUser;
return user.updateProfile({
displayName: fullName,
});
});
} catch (error) {
alert(error);
}
history.push("/dashboard");
},
[history]
);
I've tried different ways of calling updateProfile (using the function argument & the user variable as in the example below. I am still getting this error.
Is there a way to write this code to avoid that recursion error?
First, I suggest you to choose between using async/await or then to handle promises.
Second, you can use createUserWithEmailAndPassword return value, as such:
const userCredentials = await app.auth().createUserWithEmailAndPassword(email.value, password.value);
const user = userCredentials.user;
await user.updateProfile({ displayName: fullName });
The relevant documentation: https://firebase.google.com/docs/reference/js/firebase.auth.Auth#createuserwithemailandpassword

Resources