Hi I am using express for backend authentication and these are my sign in functions/controllers on the front end.
export const signInUser = async credentials => {
console.log('this is for the signInUser', credentials)
try {
const resp = await api.post('/sign-in', credentials)
localStorage.setItem('token', resp.data.token)
return resp.data
} catch (error) {
throw error
}
}
onSignIn = event => {
event.preventDefault()
const { history, setUser } = this.props
signInUser(this.state)
.then(res => setUser(res.user))
.then(() => history.push('/Home'))
.catch(error => {
console.error(error)
this.setState({
loginUsername: '',
loginPassword: '',
})
})
}
setUser = user => this.setState({ user })
and this is my sign in controller on the backend
const signIn = async (req, res) => {
try {
console.log('hello' ,req.body);
const { loginUsername, username, loginPassword } = req.body;
const user = await User.findOne({
where: {
username: loginUsername
}
});
console.log('this is the user', user)
if (await bcrypt.compare(loginPassword, user.dataValues.password_digest)) {
const payload = {
id: user.id,
username: user.username,
password: user.password
};
const token = jwt.sign(payload, TOKEN_KEY);
return res.status(201).json({ user, token });
} else {
res.status(401).send("Username or Password is invalid- try again.");
}
} catch (error) {
return res.status(500).json({ error: error.message });
}
};
The issue is the state of the user doesn't persist on refresh but I still have the json webtoken in my local storage and this is an issue when I make post requests and even signing up since I am redirecting to the home page and losing the user state. Any help would be appreciated!
From your tags, I noticed that you are using React, so the solution is simple!
you can have an GlobalAuthManager context for your application that would wrap all the components at the most higher level! after <React.strictMode> like below:
<React.StrictMode>
<GlobalAuthManager.Provider value={{authData}}>
<App />
</GlobalAuthManager.Provider>
</React.StrictMode>
As you might guess, this would be a context! that would provide you your user data to all your components!
The Pattern:
1. Store token:
when your user logins to your app, you would receive a token ( in your response or in response header ), you need to store the token value in localstorage, or more better in cookie storage (there are a lot of articles about it why), one is here.
2. have a /getUserData endpoint in backend:
you need to have a /getUserData endpoint in backend to retrive your user data based on token
3. call /getUserData in app mount:
before every thing in your app, you need to call this endpoint if you find token in localstorage or cookie storage. so if you run this in your componnetDidMount or useEffect(() => { ... }, []), that would work!
4. store your user data and state in context:
after you've called the /getUserData and if you had a valid token(i mean not expired token or not interrupted and edited token) , you will get you user data and what you need to do is that you need to store this in your GlobalAuthManager and provide that in to your Global App component!
after that you have your user data available to you that you can decide to show login or sign up button in your Navbar or disable/enable comment section for example based on your user data!
Wrap up:
So the key is that you have to have a GlobalAuthManager for only one purpose, that before every thing it runs in the top level in your app and gets you your user data based on provided token from localstorage or cookie storage!
after that you can manage your app state based on that your user is logged in or not!
Related
On logging on via my signin page and navigating to my dashboard, I am trying to query a collection calls 'hotels' based upon the uid of the logged in user.
The issue I have is the uid I am using to query the collection, appears blank or as per the previous user logged in.
I am calling the data service after signin at my dashboard:
Dashboard.ts
this.getAllStudents();
}
// register() {
// this.auth.logout();
// }
getAllStudents() {
this.data.getAllStudents().subscribe(res => {
this.studentsList = res.map((e: any) => {
const data = e.payload.doc.data();
data.id = e.payload.doc.id;
return data;
})
}, err => {
alert('Error while fetching student data');
})
The dataserivce (data.ts) has the below code, which works, but the uid is either blank on first login or the previous user:
getAllStudents() {
return this.afs.collection('/hotels',ref=>ref.where('userid','==',this.auth.userState.uid)).snapshotChanges();
//return this.afs.collection('/hotels',ref=>ref.where('userid','==',)).snapshotChanges().subscribe(res =>
}
Help! I assume login is not set at the point of querying the database. Cheers
I am following this procedure:
- Hitting to Login API
- Getting Success response containing token
- Setting the token to local storage
- Trying to redirect to protected routes
- Protected component check the token from local storage
- For the first time, it is not getting token from local storage as local storage take some time to store token on local storage
- As the token not set yet to local storage, it assumes the use as not authenticated, and then is now redirecting to the login page for the first time.
Moreover, if I keep my token to the state, and then try to check the authentication taking the token from state, then If I logout from one tab or window, then rest of tabs are not logged out automatically. And need to logout from all tabs manually.
ProtectedRouter.jsx
import { Navigate, Route } from "react-router-dom";
import useAuth from "../hooks/useAuth";
const ProtectedRouter = ({ component }) => {
const auth = JSON.parse(localStorage.getItem("token")) ?? false;
if(auth) {
return component;
} else {
return <Navigate replace to="/login"/>
}
};
export default ProtectedRouter;
Router.jsx
<Route element={<Layout/>}>
<Route path="/" element={<ProtectedRouter component={<Dashboard/>}/>}/>
</Route>
Login.jsx
const login = ({email, password}) => {
axios.post(`http://localhost:7000/api/users/login`, { email, password }).then(response => {
localStorage.setItem("loggedInUser", JSON.stringify(loggedInUser));
localStorage.setItem("token", JSON.stringify(token));
}).catch(err => {
console.error(err.message().toString());
});
};
It has been 3 months since the question was asked, but maybe this can help someone else. I find using navigate('/route-to', { replace: true }); very useful as it refreshes the page making it possible for the token to be available on local storage.
I can't see where you are redirecting after a successful login. You should redirect like this navigate('/', { replace: true });
Also, where are you getting the token and loggedInUser from? After a successful login, the API is returning token, so you should get loggedInUser and token from the response.
Here is what you can do.
const login = ({email, password, navigate}) => {
axios.post(`http://localhost:7000/api/users/login`, { email, password }).then(response => {
localStorage.setItem("loggedInUser", JSON.stringify(response.data.loggedInUser));
localStorage.setItem("token", JSON.stringify(response.data.token));
navigate('/', { replace: true });
}).catch(err => {
console.error(err.message().toString());
});
};
Dont forget to pass navigate (useNavigate) when using login().
when create user with Firebase createUserWithEmailAndPassword. Function runs twice. That's why, it gives error that email is already in use. App in vue js 2 and firebase 9.
async registerForm(){
const auth = getAuth()
const user = await createUserWithEmailAndPassword(auth, this.email, this.password)
.then(userCredential => {
this.userId = userCredential.user.uid
console.log(this.userId);
this.x++
})
.catch(error => {
console.log(error);
})
console.log('user signed up', this.x);
this.$router.replace({name: 'home'})**
}
The problem maybe in the routing redirect, if you have located the auth function on the rendering side, when you change the route or move to another one you are rendering again the component which could be a reason to call the function twice
I am developing an app in React Native and I want to implement logging in with Facebook.
I have an API in Node.js where I handle the logic for users to log in, etc.
I use passport.js to let users log in with either Facebook or traditional Email.
I am opening an URL in my API with SafariView which is just a regular "WebView" directly in my app.
I have tried using the following code:
class FacebookButton extends Component {
componentDidMount() {
// Add event listener to handle OAuthLogin:// URLs
Linking.addEventListener('url', this.handleOpenURL);
// Launched from an external URL
Linking.getInitialURL().then((url) => {
if (url) {
this.handleOpenURL({ url });
}
});
}
componentWillUnmount() {
Linking.removeEventListener('url', this.handleOpenURL);
}
handleOpenURL({ url }) {
// Extract stringified user string out of the URL
const [, user_string] = url.match(/user=([^#]+)/);
this.setState({
// Decode the user string and parse it into JSON
user: JSON.parse(decodeURI(user_string))
});
if (Platform.OS === 'ios') {
SafariView.dismiss();
}
}
openURL(url) {
if (Platform.OS === 'ios') {
SafariView.show({
url: url,
fromBottom: true,
});
} else {
Linking.openURL(url);
}
}
render() {
return (
<Button
onPress={() => this.openURL('https://mywebsite.com/api/auth/facebook')}
title='Continue with Facebook'
...
so I guess I will have to do the authentication on URL https://mywebsite.com/api/auth/facebook and then send the user to an url that looks something like OAuthLogin://..., but I am not entirely sure how to use it.
Can anyone help me move in the right direction?
import { LoginManager, AccessToken } from 'react-native-fbsdk'; // add this file using npm i react-native-fbsdk
Create function
const onFacebookButtonPress = async () => {
// Attempt login with permissions
const result = await LoginManager.logInWithPermissions(['public_profile', 'email']);
if (result.isCancelled) {
throw 'User cancelled the login process';
}
// Once signed in, get the users AccesToken
const userInfo = await AccessToken.getCurrentAccessToken();
if (!userInfo) {
throw 'Something went wrong obtaining access token';
}
console.log('user info login', userInfo)
// Create a Firebase credential with the AccessToken
const facebookCredential = auth.FacebookAuthProvider.credential(userInfo.accessToken);
setGoogleToken(userInfo.accessToken)
// Sign-in the user with the credential
return auth().signInWithCredential(facebookCredential)
.then(() => {
//Once the user creation has happened successfully, we can add the currentUser into firestore
//with the appropriate details.
console.log('current User ####', auth().currentUser);
var name = auth().currentUser.displayName
var mSplit = name.split(' ');
console.log("mSplit ",mSplit);
let mUserDataFacebook = {
user_registration_email: auth().currentUser.email,
user_registration_first_name: mSplit[0],
user_registration_last_name: mSplit[1],
registration_type: 'facebook',
user_registration_role: "Transporter",
token: userInfo.accessToken,
user_image : auth().currentUser.photoURL,
};
console.log('mUserDataFacebook',mUserDataFacebook)
LoginWithGoogleFacebook(mUserDataFacebook) /// Call here your API
firestore().collection('users').doc(auth().currentUser.uid) //// here you can add facebook login details to your firebase authentication.
.set({
fname: mSplit[0],
lname: mSplit[1],
email: auth().currentUser.email,
createdAt: firestore.Timestamp.fromDate(new Date()),
userImg: auth().currentUser.photoURL,
})
//ensure we catch any errors at this stage to advise us if something does go wrong
.catch(error => {
console.log('Something went wrong with added user to firestore: ', error);
})
})
}
Call this function on button press onFacebookButtonPress()
For android need to setup and add facebook id in
android/app/src/main/res/values/strings.xml file
add these two lines.
YOUR_FACEBOOK_ID
fbYOUR_FACEBOOK_ID //Don't remove fb in this string value
/////////////add this code in AndroidMainfest.xml file
//////////This code add in MainApplication.java file
import com.facebook.FacebookSdk;
import com.facebook.appevents.AppEventsLogger;
/////////add code build.gradle file
implementation 'com.facebook.android:facebook-android-sdk:[5,6)'
So I am working on an angular2 application and am trying to generate a JWT after logging in so that a user's profile information can be obtained. I can successfully login and that's when I generate the token. After logging in I route the user to the profile page and that's where I call my api to get the user information. All of this only works after I login and refresh to page.
this.auth_service
.Login(this.email, this.password)
.subscribe(
data => {
this.global_events.change_nav_bar.emit(true)
console.log('logged in successfully: ' + data)
this.router.navigate(['/profile'])
})
// auth_service above calls this method
Login(email, password): Observable<boolean>
{
return this.http
.post('/api/login', JSON.stringify({email: email, password: password}), this.Get_Headers('no_auth'))
.map(Handle_Response)
function Handle_Response(response: Response)
{
let token = response.json() && response.json().token
if(token)
{
this.token = token
localStorage.setItem('current_user', JSON.stringify({ email: email, token: token }))
return true
}
else
return false
}
}
// Then in my profile component I do this. I have experimented with different timeout times.
ngOnInit(): void
{
this.global_events.change_nav_bar.emit(true)
setTimeout(() => this.Get_User_Data(), 10000)
}
I solved this (not really solved but found another solution) by just pulling the token directly from localStorage instead of setting in the authenticationService