I can't seem to figure out how to maintain the login state.
I login, the app shows the condition if logged in, but then if I refresh the page, it asks me to login again.
I am using onAuthStateChanged, I just dont know what else to do.
This shows up when user not logged in
after I click login, this shows up
but when i click refresh, it shows you must login again.
here is my firebase config (the relevant bits)
function useAuth() {
const [currentUser, setCurrentUser] = useState();
useEffect(() => {
const unsubsubscribe = onAuthStateChanged(auth, (user) =>
setCurrentUser(user)
);
return unsubsubscribe;
}, []);
return currentUser;
}
export { app, storage, auth, database, useAuth };
I decide to create a function useAuth() inside firebase.config so i dont have to recreate it everywhere i need it.
Here is the login code
const Login = () => {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const currentUser = useAuth();
const [loading, setLoading] = useState(false);
function login(email, password) {
return auth
.setPersistence(browserLocalPersistence)
.then(() => {
signInWithEmailAndPassword(auth, email, password).catch((error) => {
const errorCode = error.code;
const errorMessage = error.message;
console.log(error);
});
})
.catch((error) => {
console.log(error);
});
}
}
And here is the page I want to show if the user is logged in. It shows to correct component if logged in , but when i refresh the page it doesnt remember the logged in user.
here is the code for the page
const Properties = () => {
const currentUser = useAuth();
onAuthStateChanged(auth,(user))
return (
<>
<Head>
<title>Add Property</title>
<meta name="keywords" content="web dev" />
</Head>
<h1>Add Property</h1>
<p>Welcome to the add Property new</p>
{console.log("user logged in? " + currentUser?.email)}
{currentUser ? (
<AddProperty />
) : (
<div>
<p style={{ color: "red" }}>You must be loggedin to add a property</p>{" "}
<Login />
</div>
)}
</>
);
};
export default Properties;
In my application user authentication is done every thing is working fine but I want implement user email verification feature in my app I am done with user authentication and storing user details in Firestore
my signup method code :
const onRegister = async (email, password, username) => {
try {
const authUser = await firebase
.auth()
.createUserWithEmailAndPassword(email, password);
db.collection("users").add({
owner_uid: authUser.user.uid,
displayname: username,
email: authUser.user.email,
photoURL: await getrandompicture(),
});
await firebase.auth().currentUser.updateProfile({
displayName: username,
photoURL: await getrandompicture(),
});
} catch (error) {
Alert.alert(error.message);
}
};
my authnavigation :
const Authnavigation = () => {
const [currentUser, setcurrentUser] = useState(null);
const userHandler = (users) =>
users ? setcurrentUser(users) : setcurrentUser(null);
useEffect(
() => firebase.auth().onAuthStateChanged((user) => userHandler(user)),
[]
);
return (
<>
{currentUser ? (
<Singninstack userID={currentUser.uid} />
) : (
<Signoutstack />
)}
</>
);
};
export default Authnavigation;
You can call sendEmailVerification right after creating the user.
await authUser.user.currentUser.sendEmailVerification()
.then(() => {
// Email verification sent!
// ...
});
I am using a spring boot backend with a react.js frontend for a web application. Once a user is logged in, it directs the user to their Profile where they will have their username, profile picture, and a banner displayed on the screen. I have developed a backend service that returns the necessary information in the body of my GET request on Postman such as the link of the image (Profile or Banner). How can I use React to obtain the necessary link in profile_img_complete and insert it into my image that has a default image in it already if the value is null? My demo user has an image in the database ready to be used, so it should not be displaying the default image, but it is. Any help would be deeply appreciated, here is an image of the information on Postman.
Profile.jsx:
import React, {useState, useEffect} from 'react';
import {Link} from 'react-router-dom';
import {useSelector} from 'react-redux';
import UserProfileService from '../../services/user-profile.service';
import './styles/Profile.css';
const Profile = () => {
const {user: currentUser} = useSelector((state) => state.auth);
const {id: currentId} = useSelector((state) => state.auth);
const [content, setContent] = useState('');
const [photoURL, setPhotoURL] = useState('../../images/user-solid.svg');
//user-solid is the default image I want if the profile image link is null
useEffect(() => {
UserProfileService.getProfile().then(
(response) => {
setContent(response.data);
},
(error) => {
const _content =
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString();
setContent(_content);
}
);
if (currentId && currentId.profile_img_complete) {
setPhotoURL(currentId.profile_img_complete);
}
}, [currentId]);
if (!currentUser) {
return <Link to='/login' />;
}
return (
<div className='page'>
<div className='profile-container'>
<header className='jumbotron'>
<h3>
<strong id='profile-name'>{currentUser.username}</strong> Profile
</h3>
</header>
<p>
<img src={photoURL} alt='Avatar' className='avatar'></img>
<strong>Token:</strong> {currentUser.accessToken.substring(0, 20)} ...{' '}
{currentUser.accessToken.substr(currentUser.accessToken.length - 20)}
</p>
<p>
<strong>Id:</strong> {currentUser.id}
</p>
<p>
<strong>Email:</strong> {currentUser.email}
</p>
<strong>Authorities:</strong>
<ul>
{currentUser.roles &&
currentUser.roles.map((role, index) => <li key={index}>{role}</li>)}
</ul>
<button>
<Link to={'/profile/edit'}>Edit Profile</Link>
</button>
</div>
</div>
);
};
export default Profile;
auth.js:
// We’re gonna import AuthService to make asynchronous HTTP requests with trigger one or more dispatch in the result.
// – register(): calls the AuthService.register(username, email, password) & dispatch setMessage if successful/failed
// – login(): calls the AuthService.login(username, password) & dispatch setMessage if successful/failed
// – logout(): calls the AuthService.logout().
// setMessage is imported from message slice that we’ve created above.
// We also need to use Redux Toolkit createAsyncThunk which provides a thunk that will take care of the action types and dispatching the right actions based on the returned promise.
//There are 3 async Thunks to be exported:
// register
// login
// logout
import {createSlice, createAsyncThunk} from '#reduxjs/toolkit';
import {setMessage} from './messages';
import AuthService from '../services/auth.service';
const user = JSON.parse(localStorage.getItem('user'));
export const register = createAsyncThunk(
'auth/register',
async ({username, email, password}, thunkAPI) => {
try {
const response = await AuthService.register(username, email, password);
thunkAPI.dispatch(setMessage(response.data.message));
return response.data;
} catch (error) {
const message =
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString();
thunkAPI.dispatch(setMessage(message));
return thunkAPI.rejectWithValue();
}
}
);
export const login = createAsyncThunk(
'auth/login',
async ({username, password}, thunkAPI) => {
try {
const data = await AuthService.login(username, password);
return {user: data};
} catch (error) {
const message =
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString();
thunkAPI.dispatch(setMessage(message));
return thunkAPI.rejectWithValue();
}
}
);
export const logout = createAsyncThunk('auth/logout', async () => {
await AuthService.logout();
});
const initialState = user
? {isLoggedIn: true, user}
: {isLoggedIn: false, user: null};
const authSlice = createSlice({
name: 'auth',
initialState,
extraReducers: {
[register.fulfilled]: (state, action) => {
state.isLoggedIn = false;
},
[register.rejected]: (state, action) => {
state.isLoggedIn = false;
},
[login.fulfilled]: (state, action) => {
state.isLoggedIn = true;
state.user = action.payload.user;
},
[login.rejected]: (state, action) => {
state.isLoggedIn = false;
state.user = null;
},
[logout.fulfilled]: (state, action) => {
state.isLoggedIn = false;
state.user = null;
},
},
});
const {reducer} = authSlice;
export default reducer;
user-profile.service.js:
import axios from 'axios';
import authHeader from './auth-header';
const API_URL = 'http://localhost:8080/';
const getProfile = () => {
return axios.get(API_URL + 'profile', {headers: authHeader()});
};
const user_profile = {
getProfile,
};
export default user_profile;
I'm assuming that the data in the image is the value of response.data that is returned by UserProfileService.getProfile(). You need to update the photoURL when the UserProfileService.getProfile() request is fulfilled. Besides, currentId is a string. It does not contain profile_img_complete attribute.
import React, {useState, useEffect} from 'react';
import {Link} from 'react-router-dom';
import {useSelector} from 'react-redux';
import UserProfileService from '../../services/user-profile.service';
import './styles/Profile.css';
const Profile = () => {
const {user: currentUser} = useSelector((state) => state.auth);
const {id: currentId} = useSelector((state) => state.auth);
const [content, setContent] = useState('');
const [photoURL, setPhotoURL] = useState('../../images/user-solid.svg');
//user-solid is the default image I want if the profile image link is null
useEffect(() => {
UserProfileService.getProfile().then(
(response) => {
setContent(response.data);
if (response.data.profile_img_complete)
setPhotoURL(response.data.profile_img_complete);
},
(error) => {
const _content =
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString();
setContent(_content);
}
);
}, [currentId]);
if (!currentUser) {
return <Link to='/login' />;
}
return (
<div className='page'>
<div className='profile-container'>
<header className='jumbotron'>
<h3>
<strong id='profile-name'>{currentUser.username}</strong> Profile
</h3>
</header>
<p>
<img src={photoURL} alt='Avatar' className='avatar'></img>
<strong>Token:</strong> {currentUser.accessToken.substring(0, 20)} ...{' '}
{currentUser.accessToken.substr(currentUser.accessToken.length - 20)}
</p>
<p>
<strong>Id:</strong> {currentUser.id}
</p>
<p>
<strong>Email:</strong> {currentUser.email}
</p>
<strong>Authorities:</strong>
<ul>
{currentUser.roles &&
currentUser.roles.map((role, index) => <li key={index}>{role}</li>)}
</ul>
<button>
<Link to={'/profile/edit'}>Edit Profile</Link>
</button>
</div>
</div>
);
};
export default Profile;
Alternate Solution
state.auth should already hold profile_img_complete. So, you can also do this
import React, {useState, useEffect} from 'react';
import {Link} from 'react-router-dom';
import {useSelector} from 'react-redux';
import UserProfileService from '../../services/user-profile.service';
import './styles/Profile.css';
const Profile = () => {
const {user: currentUser} = useSelector((state) => state.auth);
const auth = useSelector((state) => state.auth);
const {id: currentId} = useSelector((state) => state.auth);
const [content, setContent] = useState('');
const [photoURL, setPhotoURL] = useState(auth.profile_img_complete || '../../images/user-solid.svg');
//user-solid is the default image I want if the profile image link is null
useEffect(() => {
UserProfileService.getProfile().then(
(response) => {
setContent(response.data);
if (response.data.profile_img_complete)
setPhotoURL(response.data.profile_img_complete);
},
(error) => {
const _content =
(error.response &&
error.response.data &&
error.response.data.message) ||
error.message ||
error.toString();
setContent(_content);
}
);
}, [currentId]);
if (!currentUser) {
return <Link to='/login' />;
}
return (
<div className='page'>
<div className='profile-container'>
<header className='jumbotron'>
<h3>
<strong id='profile-name'>{currentUser.username}</strong> Profile
</h3>
</header>
<p>
<img src={photoURL} alt='Avatar' className='avatar'></img>
<strong>Token:</strong> {currentUser.accessToken.substring(0, 20)} ...{' '}
{currentUser.accessToken.substr(currentUser.accessToken.length - 20)}
</p>
<p>
<strong>Id:</strong> {currentUser.id}
</p>
<p>
<strong>Email:</strong> {currentUser.email}
</p>
<strong>Authorities:</strong>
<ul>
{currentUser.roles &&
currentUser.roles.map((role, index) => <li key={index}>{role}</li>)}
</ul>
<button>
<Link to={'/profile/edit'}>Edit Profile</Link>
</button>
</div>
</div>
);
};
export default Profile;
In your .then() of UserServices.getProfile() use the following:
setPhotoURL(response.data.profile_img_complete);
Also remove the following code from your useEffect:
if (currentId && currentId.profile_img_complete) {
setPhotoURL(currentId.profile_img_complete);
}
const image = req.query.profile_img or const {profile_img} = req.query
I am trying to create a project in that login functionality is good and working properly but when I logged in and refreshed the screen the logout button disappears and the login link will come and then again logout button will come.to understand perfectly watch the video https://drive.google.com/file/d/1UvTPXPvHf4EhcrifxDEfPuPN0ojUV_mN/view?usp=sharing, this is because of
const AuthContext = React.createContext()
//useauth will return the AuthContext
export const useAuth = () => {
return useContext(AuthContext)
}
export const Authprovider = ({ children }) => {
var name
auth.onAuthStateChanged((user) => {
name = user
})
const [currentuser, setcurrentuser] = useState(name)
const [load, setload] = useState(true)
function signup(email, password) {
return auth.createUserWithEmailAndPassword(email, password)
}
function login(email, password) {
return auth.signInWithEmailAndPassword(email, password)
}
useEffect(() => {
const unsubscribe = auth.onAuthStateChanged((user) => {
setcurrentuser(user)
setload(false)
})
return unsubscribe
}, [])
const value = {
currentuser,
signup,
login,
load,
}
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>
}
I wrapped the AuthProvider component around the app component so that I can use the values like current user .
in Header component where login link, logout button is there
const { currentuser, load } = useAuth()
const logout = () => {
try {
auth.signOut().then(() => {
console.log('logged out')
})
} catch {
alert('logout is not possible')
}
}
//some code
{currentuser ? (
<button onClick={logout}>Logout</button>
) : (
<Link to='/login'>Login</Link>
)}
if there is a current user then the logout button will appear otherwise login link will appear but when refreshing there is some problem I tried many ways now I am out of ideas. "Even I refresh the page when logged in the logout button should not disappear" can you tell me how to do this?
to understan watch the video in the link
That's because you're not using load state try this:
//some code
{ load ? <div>loading</div>
: currentuser ? (
<button onClick={logout}>Logout</button>
) : (
<Link to='/login'>Login</Link>
)}
I have a function that fires when a user signs into the app.
signin: (email, password, setErrors, setUser, setUserIdToken) => {
firebase
.auth()
.signInWithEmailAndPassword(email, password)
.then(res => {
const user = res.user
const isVerified = user.emailVerified
firebase
.auth()
.currentUser.getIdTokenResult()
.then(idTokenResult => {
setUserIdToken(idTokenResult)
})
.catch(error => console.log(error))
setUser(user)
const db = firebase.firestore()
if (isVerified) {
db.collection('/users')
.doc(user.uid)
.update({ isVerified: true })
}
})
.catch(err => {
setErrors(prev => [...prev, err.message])
})
},
I have another component that uses the user and userIdToken from the signIn method.
const Home = () => {
const { handleSignout, user, userIdToken } = useContext(firebaseAuth)
const { emailVerified, email } = user
const { claims } = userIdToken
return (
<div>
Home page component
<SurveyResults />
{emailVerified && email.endsWith('xxx') && !claims.admin ? (
<button type="button">hi</button>
) : null}
<button type="submit" onClick={handleSignout}>
sign out
</button>
</div>
)
}
export default Home
I don't have access to the properties of userIdToken immediatly when Home renders, as I am still waiting for the promise to resolve..But I also need to check the props of userIdToken to render a button. I'm not sure how to do that?