Using passport-facebook without Mongoose User (No Mongo in the MEAN stack) - angularjs

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.

Related

Linking Twitter account to user account (twitter-passport)

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.

AWS Cognito Authentication in Reactjs

I have built applications using Firebase and React, and the procedure is pretty seamless.
Lately I have been required to use AWS Cognito, and it seems a bit of a pain to set up as the docs are not clear.
Firstly, how can I do user authentication using Cognito? I set up a user pool, with the following app client settings:
Now, I add the authorizer to my API as follows:
Now my question is, how do I use this with my frontend to sign in a user and make authenticated API calls?
There seem to be two different toolkits available:
https://github.com/aws/aws-sdk-js
https://github.com/aws-amplify/amplify-js
It is not clear at all for a beginner what to use, and how to get authentication working. Ideally I would use it like I do for firebase, and just have my frontend make an authentication call using the email and password, and in turn receiving a token of some sort (on success only) that can then be used to then make signed API calls.
Can someone please help with code examples?
sorry for the confusion.
AWS Cognito Userpools act as an Identity Provider. It supports all User management (Sign Up, Sign In, Password reset, User deletion, etc). Cognito also supports Federated Identity (E.g., A User who already has an Google/Facebook account can sign in). In this case, Cognito talks to Google/Facebook using OAuth.
When I was learning about Cognito/JWT tokens, I created a simple JS/HTML to understand how it works. Since you asked for code, you can refer it - https://github.com/ryandam9/Cognito-tokens.
As per your screen shot, you already configured a Userpool - sls-notes-backend. Say, you configured the mandatory attribute as Email.
Step 0 - Initialize
You get both userPoolId and appId when you create the user pool.
poolData = {
UserPoolId: userPoolId,
ClientId: appId
};
userPool = new AWSCognito.CognitoIdentityServiceProvider.CognitoUserPool(poolData);
Step 1 - Signup a User using Email and Password - Say your UI already captured these details from the User and user clicked on 'Sign Up' button.
/**
* Signup a User
* #param e
*/
function addUser(e) {
signupMessage.style.display = 'none';
signupMessage.className = '';
e.preventDefault();
let name = document.getElementById('name').value.trim();
let email = document.getElementById('signup-email').value.trim();
let password = document.getElementById('signup-password').value.trim();
if (name.length === 0 || email === 0 || password === 0) {
return;
}
let attributeList = [
new AWSCognito.CognitoIdentityServiceProvider.CognitoUserAttribute({
Name: 'given_name', Value: name
}),
];
userPool.signUp(email, password, attributeList, null, function (err, result) {
if (err) {
signupMessage.innerText = err;
signupMessage.style.display = 'block';
signupMessage.className = 'alert alert-danger';
return;
}
cognitoUser = result.user;
console.log('user name is ' + cognitoUser.getUsername());
// Show a text box to enter Confirmation code
document.getElementById('signup-btn').style.display = 'none';
document.getElementById('code-block').style.display = 'block';
document.getElementById('confirm-user-btn').style.display = 'inline-block';
});
}
If the signup is successful (It is valid Email and the Email does not yet exist in Userpools, a Confirmation code is sent to the Email provided. Next step is to, allow the user to enter the code and confirm his identity.
Step 3 - Confirm User
/**
* Confirm the user by taking the Confirmation code.
* #param e
*/
function confirmUser(e) {
e.preventDefault();
let verificationCode = document.getElementById('code').value;
cognitoUser.confirmRegistration(verificationCode, true, function (err, result) {
if (err) {
signupMessage.innerText = err;
signupMessage.style.display = 'block';
signupMessage.className = 'alert alert-danger';
return;
}
signupMessage.innerText = result;
signupMessage.style.display = 'block';
signupMessage.className = 'alert alert-success';
});
}
If the User enters correct code, his identity is confirmed. At this point, An entry is made to the Userpool for this user. It looks like this.
Step 4 - Authentication (Sign In)
At this point, User registration is done. Its time to allow him to login. Please ignore the unnecessary code in the code below (the code that fetches and prints credentials, decoding part). If the authentication is successful, Cognito returns two types of Tokens to the application - ID Token and Access Token. These are valid only for this session and for this user only. More details here - https://ryandam.net/aws/19-cognito-userpools/index.html#0
/**
* Signin user with Email and Password
* #param e
*/
function authenticateUser(e) {
e.preventDefault();
let email = document.getElementById('signin-email').value;
let password = document.getElementById('signin-password').value;
if (email.length === 0 || password === 0 || userPool === null || userPool === undefined) {
signinMessage.innerText = 'Fill in all fields!';
signinMessage.style.display = 'block';
signinMessage.className = 'alert alert-danger';
return;
}
let authenticationData = {
Username: email,
Password: password,
};
let authenticationDetails = new AWSCognito.CognitoIdentityServiceProvider.AuthenticationDetails(authenticationData);
let userData = {
Username: email,
Pool: userPool
};
let cognitoUser = new AWSCognito.CognitoIdentityServiceProvider.CognitoUser(userData);
cognitoUser.authenticateUser(authenticationDetails, {
onSuccess: function (result) {
signinMessage.innerText = 'Authentication Success!';
signinMessage.style.display = 'block';
signinMessage.className = 'alert alert-success';
document.getElementById('token-section').style.display = 'block';
document.getElementById('signin-btn').style.display = 'none';
// Decode ID Token
let idToken = result.idToken.jwtToken;
document.getElementById('id-token').innerText = idToken;
document.getElementById('decoded-id-token').appendChild(parseIdToken(idToken));
// Decode Access Token
let accessToken = result.getAccessToken().getJwtToken();
document.getElementById('access-token').innerText = accessToken;
document.getElementById('decoded-access-token').appendChild(parseAccessToken(accessToken));
let cognitoUser = userPool.getCurrentUser();
if (cognitoUser != null) {
cognitoUser.getSession(function (err, result) {
if (result) {
// Set the region where your identity pool exists (us-east-1, eu-west-1)
AWS.config.region = region;
AWS.config.update({region: region});
logins = {};
let key = 'cognito-idp.us-east-2.amazonaws.com/' + userPoolId;
logins[key] = result.getIdToken().getJwtToken();
// Add the User's Id Token to the Cognito credentials login map.
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId: identityPoolId,
Logins: logins,
});
// Make the call to obtain credentials
AWS.config.credentials.get(function () {
// Credentials will be available when this function is called.
var accessKeyId = AWS.config.credentials.accessKeyId;
var secretAccessKey = AWS.config.credentials.secretAccessKey;
var sessionToken = AWS.config.credentials.sessionToken;
});
if (s3BucketName.length > 0)
listS3Bucket(s3BucketName);
}
});
}
},
onFailure: function (err) {
signinMessage.innerText = err;
signinMessage.style.display = 'block';
signinMessage.className = 'alert alert-danger';
}
}
);
}
Step 5 - Invoking the API Endpoint you already created - Since you've already created an Authorizer using the Userpool and you're using Authorization as the header, you can invoke the End point from JS by passing the ID token as Authorization header. What happens is that, the token is validated by the authorizer. Since it is valid the user is able to invoke the API.**
I do not have JS code, you can test your API from CLI/Postman something like this:
Note
AWS Amplify seems to be a wrapper for Cognito and other services. For instance, Amplify sets up User pool for you when you invoke its CLI commands. You can refer this code if you want to see how Amplify works with Flutter - https://github.com/ryandam9/Auth-flutter-aws-amplify.
I am still learning. I tried to be as accurate as possible.

Firebase handle reset password emails for not verified users

When a new user registers the web application a verification email is sent to him. I prevent new users to log in before verification.
Meanwhile if the verification link expires and the user forgets the password he will click the reset password link and will receive an email.
So I think that I should handle reset password action together with verification at once. Otherwise user will not be able to login even after changing the password.
function handleResetPassword(auth, actionCode) {
auth.verifyPasswordResetCode(actionCode)
.then(function (email) {
// Showing the reset screen and ask the user for
// the new password.
}).catch(function (error) {
//
});
};
When user saves the new password:
function saveNewPassword() {
auth.confirmPasswordReset(actionCode, vm.form.password).then(function (resp) {
// Password reset has been confirmed and new password updated.
// Now auto sign in user
auth.signInWithEmailAndPassword(vm.email, vm.form.password).catch(function (error) {
// Handle Errors here.
});
firebase.auth().onAuthStateChanged(function (user) {
if (user) {
// user signed in.
// check whether the user is verified
// if not set true
user.updateProfile({ emailVerified: true })
}
});
}).catch(function (error) {
//
});
}
But the code below doesn't work as I expected as it has no affect. I can change other user data (e.g. displayName) but not (emailVerified). It only works with firebase email verification.
user.updateProfile({ emailVerified: true })
What is the recommended approach for this type of user scenario ?
You can't update emailVerified from the client, otherwise any unverified user would be able to do that without enforcing actual ownership of the email.
You would need to do it with the Admin SDK using an HTTP endpoint (you can use Firebase Functions for that too). However, you need to ensure that the password reset code succeeded. So in this case you need to run your code on the server. Here is how it would work:
var firebase = require('firebase');
var admin = require('firebase-admin');
// Initialize the client and admin instances.
// firebase.initializeApp(clientConfig);
// admin.initializeApp(adminConfig);
// Send the reset code and the new password to your backend.
var email = null;
// Get email corresponding to code.
firebase.auth().checkActionCode(actionCode)
.then(function(info) {
email = info.email;
// Confirm password reset.
firebase.auth().confirmPasswordReset(actionCode, password)
});
.then(function() {
// Get uid of user with corresponding email.
return admin.auth().getUserByEmail(email);
}).then(function(userRecord) {
// Password reset succeeded. Email can be verified as the user
// must have received the code via their email confirming
// ownership.
return admin.auth().updateUser(userRecord.uid, {emailVerified: true});
});

How do I store more information in Firebase for a user than the Auth module allows?

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.

Getting Username from Firebase after email/password login [React Native]

I have created a simple login app using react native that let's users signup, login, and logout. my signup function takes a username that is then used in the createUser callback to generate a db entry with the uid as the key, and the username entered as a value. The answer in this post is the structure I followed - How do you include a username when storing email and password using Firebase (BaaS) in an Android app?
After the user is logged in, I'd like to get the username and display it but I'm having trouble figuring this out.
This is the code I currently have to attempt and do it:
var ref = new Firebase("https://myreactapp.firebaseio.com");
module.exports = React.createClass({
getInitialState: function() {
var authData = ref.getAuth();
var user = ref.child("users/" + authData.uid + "/username");
return {
username: user
};
},
This is how the code looks when I signup and the structure of my db.
var self = this;
let ref = new Firebase("https://myreactapp.firebaseio.com");
ref.createUser({
email : this.state.email,
password : this.state.password
}, function(error, authData) {
if (error) {
return this.setState({errorMessage: 'Error creating user'});
} else {
ref.child("users").child(authData.uid).set({
username: self.state.username
});
console.log("Successfully created user account with uid:", authData.uid);
ToastAndroid.show('Account Created', ToastAndroid.SHORT)
return self.props.navigator.pop();
}
});
----------------Not actual code-------------------------------------------------
DB
+users
--<uid>
-username -> value
--<uid>
-username -> value
I try to login and get an error of maximum call stack exceeded, but I have a feeling I'm going at this the wrong way. I've looked online, but everything I found was for retrieving data that is either being added, changed or deleted. All I want is to get this username once.

Resources