Currently, a user is able to login in and sign up for my application no problem. I've then added a "Link your twitter user to account" button which when clicked takes the user to '/auth/twitter'. This then kicks off passport-twitter and the oAuth process begins.
Right now, I'm using passport-twitter as the package for twitter oAuth. This process works. I'm able to get the user successfully authenticated. Here is the code.
However two problems: I don't see a way to 1) keep the user signed into Twitter so they don't have to keep doing this flow of reconnecting their twitter every time they want to push content to it from my app. and 2) associate the Twitter user and the signed in user to my application. Long term, I plan to add other social media accounts, so the user will have multiple social media linked. Twitter will be just one.
Problem #2: I wasn't able to do an axios.get call from my redux store or from the front end to '/auth/twitter/' otherwise I could then just get the information back from the call and then post it to the user's table (right?). So, instead I'm accessing '/auth/twitter' from an tag in the front end to kick off the flow.
passport.use(
new TwitterStrategy(
{
consumerKey: "XXX",
consumerSecret: "XXX",
callbackURL: "http://localhost:8080/auth/twitter/callback",
// callbackURL: "http://www.localhost:8080/home",
includeEmail: true,
},
async(accessToken, refreshToken, profile, cb) => {
console.log('got the prodile')
const twitterIDforOAuth = profile.id
const { id, username } = profile;
let theuser = await User.findOne({
where: {
twitterID: id
}
})
if(theuser){
console.log('FOUND USER', '\n', theuser)
} else {
try {
console.log('NO USER FOUND')
var passwordUser = (Math.random() + 1).toString(36).substring(7);
console.log('CREATING USER')
theuser = await Promise.all([
User.create({
twitterID: id,
username : username,
password: passwordUser
})
])
console.log('USER CREATED');
} catch (error) {
console.log(error);
}
}
//this callback calls the auth/callback url to kick off the redirect process
// need to send username and password to /auth/signup
return cb(null, {username: username, password: passwordUser})
//Line below sends too much data that is irrelevant for the user... lets review it?
// return cb(null, {username: twitterIDforOAuth})
}
)
);
app.get('/auth/twitter', passport.authenticate("twitter"));
app.get(
"/auth/twitter/callback",
passport.authenticate("twitter", {
failureRedirect: "/login",
failureMessage: true,
session: false
}),
async (req, res) => {
var user = req.user;
console.log(user.username, user.password);
//GET USERNAME AND PASSWORD
var username = user.username;
var password = user.password;
///they need to login the app
//auth/login
res.redirect('/AccountSettings')
}
);
The user is being redirected to /AccountSettings while they go through this flow, so I know that the user is 100% authenticated and signed in with Twitter (otherwise they'd be pushed to /login, which isn't happen).
Most people in this flow create a user in their database using the information returned from Twitter.
However, I'm trying to link this information to the signed in user, and keep them signed into Twitter so the user doesn't need to keep reconnecting their Twitter account (at least not often). (With access to their Twitter account, my plan is to allow them to push content to it)
Currently I'm hitting the '/auth/twitter' route with an tag which's href takes it to '/auth/twitter'. Is this the right way about it or is this approach causing my linkage issue?
What are people's recommendation for this issue? Whats the right way to approach linking social media accounts to a signed in user's account?
I'm using Express, Redux, React, Postgres, and passport-twitter
SOLUTION: How to passing data in TwitterStrategy, PassportJS?
had to create a state object outside the /auth/twitter route and then added a id param to the /auth/twitter route so the full route was /auth/twitter/:id
once I got the id I saved it to a state route outside the route in the server file that was accessible to the callback function later in the proces.
Related
I used #react-native-google-signin/google-signin": "^8.0.0" to create a google sign in button into my app.
When I used it, google recognised it as not trusted app, so send my an email to advice of a violation, and now every time I use a password saved on my google account to login on any site or application, he gives me a message telling me to change all of my passwords. I solved to remove the message, by ignoring for every passwords, almost 200 :/. But it's just temporary solution, cause if I do login again to my app it will happen again. How can I say to google that it is an app in developing, is there any mode to activate? Here's my code:
GoogleSignin.configure({
scopes: ['https://www.googleapis.com/auth/drive.readonly'], // what API you want to access on behalf of the user, default is email and profile
webClientId: '15299853035-njb79hdij6h1svo22drigurca1qb4djb.apps.googleusercontent.com', // client ID of type WEB for your server (needed to verify user ID and offline access)
offlineAccess: true, // if you want to access Google API on behalf of the user FROM YOUR SERVER
// hostedDomain: '', // specifies a hosted domain restriction
// forceCodeForRefreshToken: true, // [Android] related to `serverAuthCode`, read the docs link below *.
// accountName: '', // [Android] specifies an account name on the device that should be used
iosClientId: '15299853035-siujgcjtol0lfja83n7p6fk55cq6jinn.apps.googleusercontent.com', // [iOS] if you want to specify the client ID of type iOS (otherwise, it is taken from GoogleService-Info.plist)
// googleServicePlistPath: '', // [iOS] if you renamed your GoogleService-Info file, new name here, e.g. GoogleService-Info-Staging
// openIdRealm: '', // [iOS] The OpenID2 realm of the home web server. This allows Google to include the user's OpenID Identifier in the OpenID Connect ID token.
// profileImageSize: 120, // [iOS] The desired height (and width) of the profile image. Defaults to 120px
});
try {
await GoogleSignin.hasPlayServices();
const { idToken } = await (await GoogleSignin.signIn());
const googleCredential = await GoogleAuthProvider.credential(idToken);
await signInWithCredential(authApp, googleCredential)
.then(async(userCredential) => {
if(!authApp.currentUser.emailVerified)
{
sendEmailVerification(authApp.currentUser)
.then(() => {
// Email verification sent!
// ...
})
.catch((error)=>{
setLoading(false)
console.log(error)
})
}
......
I'm currently doing forgot password functionality for the first time and here's the code so far.
sends the email for the user that has the URL with the JWT token
router.post('/change-password', verifyAuth, resetPassword);
receives and confirms JWT then changes password
router.post('/change-password/:token/:password', confirmResetPassword);
the process I'm currently thinking about is in the email I send the user to
http://localhost:3000/change-passowrd?token=TOKEN_VALUE
but I'm not sure if this is a smart idea or not? I can also use cookies if it's better, any idea?
It's okay to store the JWT token store in the URL for reset password functionality. You have to send this link using Email or any other secure communication service.
I implemented this feature
https://yourapp.com/home/reset/${token}
const data = {
from: "yourcompanymail#outlook.com",
to: user.email,
subject: "Please reset your password",
text: `Hello ${user.name},\n\nI heard that you lost your Teeny password. You can use the following link to reset your password: https://yourapp.com/home/reset/${token}
};
transporter.sendMail(data, function (error, info) {
if (error) {
console.log(error);
} else {
console.log("Email sent: " + info.response);
}
});
Now if the user hits this URL, validate the token and redirect or render the change password page. But don't send the password through the URL.
i need to get cookies(it's a token) which has been defined in a node js Route file to my front, because i need to check infos of this token to show data if it's a user or admin.
THis is some code of the cookies :
// auth with google+
router.get('/auth/google', passport.authenticate('google', {
scope: [
'https://www.googleapis.com/auth/userinfo.profile',
'https://www.googleapis.com/auth/userinfo.email'
]
}));
// callback route for google to redirect to
// hand control to passport to use code to grab profile info
router.get('/auth/google/callback*', passport.authenticate('google'), (req, res) => {
if(req.user){
console.log(req.user);
res.cookie('token', req.user);
return res.redirect(config.clientURL);
}
else{
console.log('error');
return res.redirect(config.clientURL);
}
});
// auth with faceboook
router.get('/auth/facebook', passport.authenticate('facebook'));
// callback route for facebook to redirect to
// hand control to passport to use code to grab profile info
router.get('/auth/facebook/callback*', passport.authenticate('facebook'), (req, res) => {
console.log("je suis dans la route callback");
if(req.user){
console.log(req.user);
res.cookie('token', req.user);
return res.redirect(config.clientURL);
}
else{
console.log('error');
return res.redirect(config.clientURL);
}
});
Edit :
i did this :
const auth_head = document.cookie.split('.')[0];
const auth_payload = document.cookie.split('.')[1];
const auth_signature = document.cookie.split('.')[2];
var auth_token = auth_head + "." + auth_payload + "." + auth_signature;
console.log(JSON.parse( auth_head));
console.log(JSON.parse( auth_payload));
console.log(JSON.parse( auth_signature));
but i got this error :
Uncaught (in promise) SyntaxError: Unexpected token o in JSON at position 1
Thank you
As I mentioned in the comments, it's good advice to use httpOnly flag when setting cookies; this means that you need another strategy to return the user data.
Option 1: One easier to implement way could be: After your server redirects the client to let's say /logged-in, you can fetch the user data from let's say /api/userinfo; the response should a json containing the user info; you should use that json to store the information in your client using localStorate.setItem(...). This is the classic and more used way to store your user data in the client.
Example Server (Create an endpoint that returns the logged-in user info):
// Server endpoint that returns user info
router.get('/api/userinfo',
passport.authenticate(your_strategy_here),
(req, res) => {
res.json({ name: req.user.name, role: req.user.role }); // Return just what you need
})
Example Client (Create a component that requests the user info from the new server endpoint):
componentDidMount(){
fetch('/api/userinfo')
.then( res => res.json() )
.then( user => localStorate.setItem('user', user);
}
Option 2: Give Google a URL which is resolved by the client, and then have the client send the request to /auth/facebook/callback; then have the server do res.json(user), instead of the doing the redirect.
Google -> /your-client-app/auth/callback
Client -> /auth/facebook/callback
Option 2 is my advice, however, Option 1 may be more straight forward for your current setup.
Option 3: Disable httpOnly when setting the cookie, there are security concerns when doing this and it's not meant to be done like that in production apps.
res.cookie('token', req.user, { httpOnly: false });
And then on your client, you can use the following data to check the cookies.
const cookieData = document.cookie;
console.log(cookieData)
The Authorization module in Firebase only allows me to store a user's email and password; but I want to store more information, like: name, phone number, list of games they own, etc. How do I do that in firebase?
Choosing Firebase for data storage is good choice in my view. Because, it is easy to use and less expensive.
Coming to the problem, you can't set additional data to the authentication table in Firebase console.
It just shows email and unique user id and doesn't show even password used for registration.
One of the easy way of storing user information in Firebase is as follows.
After success of login or signup of user, you will get user's unique id.
function(error, userData) {
if (error) {
console.log("Error creating user:", error);
} else {
console.log("Successfully created user account with uid:", userData.uid);
}
With that user id, you can create an object in Firebase database.
function(error, userData) {
if (error) {
console.log("Error creating user:", error);
} else {
var userId = userData.uid;
var ref = new Firebase('https://docs-examples.firebaseio.com/web/data');
var userRef = ref.child('users/' + userId);
userRef.set({
email: "userEmail",
name: "userName",
phoneNumber: "userPhoneNumber",
password: "userPassword",
interestedGames: {
"game1": true,
"game2": true,
"game3": true
}
});
}
You can retrieve the data of the user using childRef as I shown above,
that you can get when user logs in.
I'm very new to the MEAN stack, and this might seem to be very naive or wrong approach, but I want to ask that when we authenticate using passport-facebook strategy, using the following code:
var FacebookStrategy = require('passport-facebook').Strategy;
var User = require('../models/user');
var fbConfig = require('../fb.js');
module.exports = function(passport) {
passport.use('facebook', new FacebookStrategy({
clientID : fbConfig.appID,
clientSecret : fbConfig.appSecret,
callbackURL : fbConfig.callbackUrl
},
// facebook will send back the tokens and profile
function(access_token, refresh_token, profile, done) {
console.log('profile', profile);
// asynchronous
process.nextTick(function() {
// find the user in the database based on their facebook id
User.findOne({ 'id' : profile.id }, function(err, user) {
// if there is an error, stop everything and return that
// ie an error connecting to the database
if (err)
return done(err);
// if the user is found, then log them in
if (user) {
return done(null, user); // user found, return that user
} else {
// if there is no user found with that facebook id, create them
var newUser = new User();
// set all of the facebook information in our user model
newUser.fb.id = profile.id; // set the users facebook id
newUser.fb.access_token = access_token; // we will save the token that facebook provides to the user
newUser.fb.firstName = profile.name.givenName;
newUser.fb.lastName = profile.name.familyName; // look at the passport user profile to see how names are returned
//newUser.fb.email = profile.emails[0].value; // facebook can return multiple emails so we'll take the first
// save our user to the database
newUser.save(function(err) {
if (err)
throw err;
// if successful, return the new user
return done(null, newUser);
});
}
});
});
}));
};
I don't need to store the user information in any data store. I want to store the token only for the time the user is logged into my web application, basically I don't have the need to use Mongo, because all the data that will be displayed in the web application will come from Facebook api, for example the posts for a profile, the number of likes on a particular posts etc. I don't need to have a backend as such, because if I store the data in any data store such as Mongo, the next time the user login then the data will be stale (in a way the Facebook api is kind of my backend), and I also want that the updates for information on any posts done on Facebook should be updated realtime on my web application for e.g. if someone likes a post on the actual Facebook page the number of likes on my web application should also be updated in realtime, so it seems unnecessary to first bring the data from the Facebook SDK and then store it in Mongo, why not just give it to the controller and from there the view can present the data. If my approach is wrong please do correct me.
So basically every time the user logs in an access token is created and used for that session, when the user logs out the access token is destroyed and so completely eliminates the need for storing the token and any data that is brought in using the Facebook SDK.
Replace the function call
User.findOne({ 'id' : profile.id }, function(err, user) {
With facebook sdk authentication call and return the user object when it's validated.
return done(null, user);
Please refer...
https://github.com/jaredhanson/passport-facebook
you need to create a new user template in the model folder. I have created the following: user.js
var facebook = module.exports.facebook = {
id : String,
token : String,
email : String,
name : String
}
and then change the passport.serializeUser and passport.deserializeUser functions.
passport.serializeUser(function(user, done) {
done(null, user.facebook.id);
});
// used to deserialize the user
//passport.deserializeUser(function(id, done) {
passport.deserializeUser(function(id, done) {
done(null, { id: User.facebook.id, token: User.facebook.token, name: User.facebook.name, email: User.facebook.email})
});
then the function: process.nextTick(function() {} replace the content by this code :
var newUser = User;
// set all of the facebook information in our user model
newUser.facebook.id = profile.id; // set the users facebook id
newUser.facebook.token = token; // we will save the token that facebook provides to the user
newUser.facebook.name = profile.name.givenName + ' ' + profile.name.familyName; // look at the passport user profile to see how names are returned
newUser.facebook.email = profile.emails[0].value; // facebook can return multiple emails so we'll take the first
return done(null, newUser);
add the line profileFields: ['id', 'displayName', 'photos', 'emails', 'name'] in function passport.use(new FacebookStrategy({}
change the profile.ejs file by removing the local information div and changing the properties <% = user.facebook.id%> to <% = user.id%> and so on in the others.