React-facebook-login work only for one account - reactjs

Hello I have implemented react facebook login package in my app. However it works only for one account. I can't get login info data with another account. Here is my code:
const registerFb = async (req, res) => {
console.log(req.body);
const newUser = new UsersSchema({
email: req.body.email,
});
try {
const existEmail = await UsersSchema.findOne({ email: req.body.email });
if (existEmail) {
const token = jwt.sign({ email: req.body.email }, "privKey");
res.send(token);
} else {
const savedUser = await newUser.save();
res.send(savedUser);
}
} catch (err) {
res.json({ message: err });
}
};

Is your app still in development mode? If so, you can only test with specified users -- and the person who set-up the app is one of the specified users by default.
You can add more testing users under Roles in the App Dashboard.

Related

When sign in with google firebase always register user as new

Tech: Firebase, Next.js, Google Sign in, Firebase Stripe exstension
Bug reproduction:
When login with Google
Subscribe on stripe
Stripe saves subscription data for that user in firestore
Logout
Login in with Google and old data are overide with new one, and Subscription is lost
Does anyone had similar problem?
Maybe my implementation of Sign-in is bad, here is the Google Sign in code:
const handleGoogleLogin = () => {
signInWithPopup(auth, googleProvider)
.then(async result => {
if (!result.user) return;
const { displayName, email, uid, providerData, photoURL, phoneNumber } =
result.user;
const name = splitName(displayName as string);
const providerId =
(providerData.length && providerData[0]?.providerId) || '';
const data = {
firstName: name?.firstName || '',
lastName: name?.lastName || '',
email,
photoURL,
phoneNumber,
providerId,
};
await updateUser(uid, data);
})
.catch(error => {
console.log('Google login error: ', error);
});
};
Update user function:
export const updateUser = async (uid: string, data: UpdateUserParams) => {
try {
if (!uid) {
return;
}
await setDoc(doc(firestore, 'users', uid), {
account: {
...data,
initials: `${data.firstName[0]}${data.lastName[0]}`,
},
});
} catch (error) {
console.error('Error updating user: ', error);
}
};
setDoc is overwriting the contents of the document with each sign-in. You should instead use set with merge to prevent overwriting the fields you don't want to lose, or check first if the document exists before creating it.
See also:
https://firebase.google.com/docs/firestore/manage-data/add-data#set_a_document
Difference Between Firestore Set with {merge: true} and Update

react native Firebase onAuthStateChanged does not get current user after closing the app while the user its authenticated using social media providers

hello we have the following problem using firebase with react native and social login providers.
The login is not persistent after a successful login using the three major social login providers (google, apple, facebook). The function onAuthStateChanged does not return the user after closing the app while the user logged in using the former providers, therefore the user has to login again every time the app is opened. This problem is not present while using auth with email and password credentials when the app persist user after login and closing the app.
we are using the following technogologies and versions
react-native: 0.63.3
firebase: 8.10.1 (previously we were using 7.8.2 )
react-native-firebase/app": "^14.2.2",
"#react-native-firebase/auth": "^14.2.2"
for creating the credentials we are using the following packages:
#react-native-google-signin/google-signin": "^7.0.3"
#invertase/react-native-apple-authentication": "^2.1.5"
react-native-fbsdk-next": "^6.2.0 -> facebook login
we don't know if it's a problem with firebase because the listener function does not get the user after closing the app with a successful login.
I hope someone can helps us out.
Edit: here is example code how we are using the firebabse and the libraries to login using apple:
on App.js, we have the auth listener
auth.onAuthStateChanged(async (user) => {
try {
if (user) {
const userInfo = await getUserById(user.uid);
const userFetched = userInfo.val();
setUser(userFetched);
}
} catch (error) {
if (error.message !== 'user is null') {
console.log('ERROR: ' + error.message);
}
}
});
login the user and creating the credentials fo
const onAppleButtonPress = async() => {
if (Platform.OS === 'android'){
return onAppleButtonPressAndroid;
}
try {
const appleAuthRequestResponse = await appleAuth.performRequest({
requestedOperation: appleAuth.Operation.LOGIN,
requestedScopes: [appleAuth.Scope.EMAIL, appleAuth.Scope.FULL_NAME],
});
const {
user: newUser,
email,
nonce,
identityToken,
realUserStatus /* etc */,
} = appleAuthRequestResponse;
if (!identityToken){
Toast.show({
type: 'error',
text1:'error al ingresar con las credenciales',
});
return;
}
// loguear con firebase here
const response = await SignInWithCredentialApple(identityToken,nonce);
const usermail = response.user.email;
const userFound = await searchUserByEmail(usermail);
if (!userFound.exists()){
const newUser = {
uid: response.user.uid,
email: usermail,
role: 'user',
userName: usermail.split('#')[0],
profileFilled: false,
created: new Date(),
authMethod: 'apple',
};
setUser(newUser);
//setUser(newUser);
await addUser(newUser.uid, newUser);
} else {
setUser(Object.values(userFound.val())[0]);
}
} catch (error){
setShowError(true);
if (error.code === appleAuth.Error.CANCELED) {
console.warn('User canceled Apple Sign in.');
setError('Operacion cancelada por el usuario');
} else {
setError(`Error: ${error.toString()}`);
}
}
};
login and creating the credentials
import authRNFirebase from '#react-native-firebase/auth';
export const SignInWithCredentialApple = async (identityToken, nounce) => {
//return auth.signInWithCredential(credential);
const credential = authRNFirebase.AppleAuthProvider.credential(
identityToken,
nounce,
);
return authRNFirebase().signInWithCredential(credential);
};

Email verification MERN stack app fails to handle wrong or expired token

I'm trying to implement email verification in a MERN stack app, and while a big deal of it works, I'm struggling to handle the case when the token is either invalid or expired.
To try and be brief, this is more or less what happens:
User registers
Towards the end of the registration function (backend) I have:
// Create user
const user = new User({
name,
email,
password: hashedPassword,
});
// Email verification
jwt.sign(
{
user: user._id,
},
process.env.JWT_SECRET,
{
expiresIn: 3600,
},
(err, emailToken) => {
mailTransport().sendMail({
from: process.env.USER_EMAIL,
to: user.email,
subject: 'Verify your email address',
html: generateEmail(emailToken),
});
}
);
await user.save();
The function generateEmail() creates and emails a verification link such as:
${process.env.FRONTEND}/verify-email/${token}
The React component in the frontend, VerifyEmail.jsx is roughly like this (deleted redundant code):
const API_URL = '/api/users/verify-email';
const VerifyEmail = () => {
const { verificationToken } = useParams();
const navigate = useNavigate();
const onVerify = async () => {
try {
await axios.get(`${API_URL}/${verificationToken}`);
toast.success('Email verified. You can now log in');
navigate('/login');
} catch (error) {
toast.error(error);
}
};
return (
<section>
<h1 className="text-6xl border-b-2 pb-2">Verify your email</h1>
<p className="my-5">Please click below to verify your email</p>
<button type="button" className="btn btn-info" onClick={onVerify}>
Verify Email
</button>
</section>
);
};
The backend route for that request is:
router.get('/verify-email/:verificationToken', verifyToken);
And the function verifyToken is:
const verifyToken = asyncHandler(async (req, res) => {
try {
const { user } = jwt.verify(
req.params.verificationToken,
process.env.JWT_SECRET
);
await User.findByIdAndUpdate(user, { verified: true }, { new: true });
} catch (error) {
res.status(400);
throw new Error(error.message);
}
return res.redirect(process.env.FRONTEND);
});
All this effectively verifies the email and updates the user in the database with verified: true BUT it doesn't handle when the token is invalid or expired.
What am I missing?
Need to add that the only error I get is, in the console, a 400 saying I'm attempting a GET request on the API on the frontend (port 3000) rather than the backend (5001):
GET http://localhost:3000/api/users/verify-email/eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiNjFmZDUyNzFhNzIyMjNjZWE4MGM0MDcxIiwiaWF0IjoxNjQzOTkxNjY1LCJleHAiOjE2NDM5OTUyNjV9.zyWhGtfV2coNZKYVW6yUOUbHo6gBnZZ4aRU79OE0Nbw
(Only when the JWT is expired or invalid)

MERN stack Ec2 deployed website does not log in

Seeking some help, trying to deploy a mern stack application on aws using ec2 instances. Everythings going well I can register new users and I can log in, problem is it doesn't stay logged in, the user instantly becomes undefined. Has anyone ran into this problem before? Think it might have something to do with the cookies. As Postman works good, its able to log in and then retrieve user.
Featured is the middleware code using Passport.
const LocalStrategy = passportLocal.Strategy;
mongoose.connect(...);
// Middleware
const app = express();
app.use(express.json());
//before deployment
//app.use(cors({origin:"http://localhost:3000",credentials:true}))
app.use(cookieParser());
app.use(cors({origin:"http://ec2-18-191-200-43.us-east-2.compute.amazonaws.com:3000",credentials:true}))
app.use(
session({
secret:"secretcode",
resave:true,
saveUninitialized:true,
cookie:{
sameSite:false
}
})
);
app.use(passport.initialize());
app.use(passport.session());
//Passport
passport.use(new LocalStrategy((username:string, password:string, done) => {
User.findOne({ username: username }, (err:any, user: DatabaseUserInterface) => {
if (err) throw err;
if (!user) {
console.log("not user");
return done(null, false);
}
bcrypt.compare(password, user.password, (err, result:boolean) => {
if (err) throw err;
if (result === true) {
console.log("good should be logged in");
return done(null, user);
} else {
//unuathorised
console.log("unathorised");
return done(null, false);
}
});
});
})
);
passport.serializeUser((user: DatabaseUserInterface, cb) => {
cb(null, user._id);
});
passport.deserializeUser((id: string, cb) => {
User.findOne({ _id: id }, (err: any, user: DatabaseUserInterface) => {
const userInformation :UserInterface = {
username: user.username,
isAdmin: user.isAdmin,
id: user._id,
wins:user.wins,
losses:user.losses
};
cb(err, userInformation);
});
});
//log in
app.post("/login", passport.authenticate("local"), (req, res) => {
res.send("success");
});
Follow up: Im an aws noob, and thought you had to run 2 different clusters with 2 different instances to run 2 tasks. You in fact only need 1 cluster with 1 instance that can run as many tasks as you want. This way cookies can actually be transferred between the ports used by the different tasks i.e. front and backend.

How to store my JWT token correctly and return data in MERN app

I'm creating a MERN application as I'm learning how to use React to create a blog/social media like website. I already have routes and jsx setup in order to display posts via axios requests and pulling the data from my MongoDB cluster.
This all works perfectly but I am now struggling on finding a proper way to handle authentication for users and display data back into my MERN application.
My current code that registers the user takes their name, email and password; using bcrpyt to hash/salt the password for security.
Register Code
exports.register = (req, res, next) => {
let {
name,
email,
password,
password_confirmation
} = req.body;
User.findOne({
email: email
})
.then(user => {
if (user) {
return res.status(422).json({
errors: [{
user: "email already exists"
}]
});
} else {
const user = new User({
name: name,
email: email,
password: password,
});
bcrypt.genSalt(10, function (err, salt) {
bcrypt.hash(password, salt, function (err, hash) {
if (err) throw err;
user.password = hash;
user.save()
.then(response => {
res.status(200).json({
success: true,
result: response
})
})
.catch(err => {
res.status(500).json({
errors: [{
error: err
}]
});
});
});
});
}
}).catch(err => {
res.status(500).json({
errors: [{
error: 'Something went wrong'
}]
});
})
}
my login function then uses bcrypt.compare in order to make sure the password types in matches with the hashed password stored in the database. If succesful, my code will call a function I created called createJWT which has a payload containing the email, userId and duration of the token before expiring.
const jwt = require("jsonwebtoken");
exports.createJWT = (email, userId, duration) => {
const payload = {
email,
userId,
duration
};
return jwt.sign(payload, process.env.TOKEN_SECRET, {
expiresIn: duration,
});
};
Login code
exports.login = (req, res) => {
let {
email,
password
} = req.body;
User.findOne({
email: email
}).then(user => {
if (!user) {
return res.status(404).json({
errors: [{
user: "not found"
}],
});
} else {
bcrypt.compare(password, user.password).then(isMatch => {
if (!isMatch) {
return res.status(400).json({
errors: [{
password: "incorrect"
}]
});
}
let access_token = createJWT(
user.email,
user._id,
3600
);
jwt.verify(access_token, process.env.TOKEN_SECRET, (err,
decoded) => {
if (err) {
res.status(500).json({
errors: err
});
}
if (decoded) {
return res.status(200).json({
success: true,
token: access_token,
message: user
});
}
});
}).catch(err => {
res.status(500).json({
errors: err
});
});
}
}).catch(err => {
res.status(500).json({
errors: err
});
});
}
If the login function is succesful, the code will decode the JWT code if the token matches my TOKEN_SECRET stored in my env file.
This all works perfectly fine but I am stuck at this point on where to go next. I have created a piece of middleware that I can succesfully implement into my routes in order to stop them loading if the user is not authorized.
//Auth middleware
const jwt = require('jsonwebtoken');
module.exports = (req, res, next) => {
try {
const token = req.header('x-auth-token');
jwt.verify(token, process.env.TOKEN_SECRET);
next();
} catch (error) {
res.status(401).json({
message: "Token not authorized!"
})
}
};
const express = require('express');
const router = express.Router();
const multer = require("multer");
const authorize = require('../middleware/auth');
const {
getPosts,
createPost,
findPostById,
updatePost,
deletePost
} = require('../controllers/posts');
const storage = multer.diskStorage({
destination: (req, file, callback) => {
callback(null, './client/public/uploads');
},
filename: (req, file, callback) => {
callback(null, file.originalname);
}
});
const upload = multer({
storage: storage
});
router.get('/', authorize, getPosts);
router.post('/add', upload.single("postImage"), createPost);
router.get('/:id', findPostById);
router.put('/update/:id', upload.single("postImage"), updatePost);
router.delete('/:id', deletePost);
module.exports = router;
From this point, what would be the best way to have the user login and gain access to the posts (containing my authorization middleware) if they have logged in succesfully and have the JWT token. I was also wondering how i would achieve something like pulling the users name from the decoded token and have it shown (for example) in the navbar for as long as they logged in / the session persists.
Any help here would be greatly appreciated, thanks everyone!
let access_token = createJWT(
user.email,
user._id,
3600
);
jwt.verify(access_token, process.env.TOKEN_SECRET, (err,
decoded) => {
if (err) {
res.status(500).json({
errors: err
});
}
if (decoded) {
return res.status(200).json({
success: true,
token: access_token,
message: user
});
}
});
There's no point to verify the JWT here as you just generated it on the server-side. This is the process:
Register: The user provides a username and a password. You save the user in the database and for the password, you save a hash.
When logging in, the user is providing the username and the password. If they are correct you create a JWT and return that to the client application. In the JWT you will encode the username/user_id.
The client needs to store that JWT somewhere and then send it in the following requests(usually as a header).
On the server you have a middleware that parses that header, decodes the JWT, and identify the user based on username/user_id or whatever you encoded there.
The client should not decode the JWT and pull information like the username from there. It should make an API request to the backend asking for the user information.
There are lots of things to take into consideration here like. Just to name a few:
how do you store the JWT on the client in a secure way?
on the client app, how do you prevent an unauthenticated user to access some private routes?
what do you do after the JWT expires?
how do you handle CORS?
are there any vulnerabilities?
I highly recommend this free course which will teach you a lot about how to implement these things.

Resources