Unable to get profile information in callback, But able to found that information in serializeUser function - node.js

I am trying to authenticate a user using passport-github strategy. It seems like everything working fine except I am unable to fetch profile information inside call back. But Able to get that information in serializeUser function.
var configurePassport = {
configureGithubStrategy : (app) => {
// STEP 1 : [Initialize passport into the app]
app.use(passport.initialize())
app.use(passport.session());
// STEP 2 : [Implement serialize and deserialize objects of passport]
passport.serializeUser(function(profile,done){
console.log(`+++ Inside Serialize +++\n${profile.username}\n+++ Note : Able The Get Profile Information +++`)
done(null,profile);
})
passport.deserializeUser(function(profile,done){
done(null,profile);
})
// STEP 3 : [Configure GitHub Strategy]
passport.use(new GitHubStrategy({
clientID: config.gitHub.clientID,
clientSecret: config.gitHub.clientSecret,
callbackURL: config.gitHub.callbackURL
},
function(accessToken, refreshToken, profile, done) {
console.log(`+++ Inside Configuration +++\n${profile}`)
done(null,profile)
}
));
}
}
const passport = require('passport')
const configurePassport = require('./config/passportConfig')
configurePassport.configureGithubStrategy(app)
app.get('/auth/github',passport.authenticate('github'));
app.get('/auth/github/callback',passport.authenticate('github',{failureRedirect : '/auth/failed'}),function(request,response){
console.log(`Unable To Get Profile Information -> ${request.profile}`)
response.redirect('/profile')
})
app.get('/profile',function(request,response){
response.status(200).json(request.profile)
})
Inside callback function request.profile gets undefiend

Correct me If I am wrong, But I found that the done() function attaches the profile info on the request object as user so it's available on the callback url as request.user

Related

How can I get the user profile data in passport google auth callback function?

I'm using Passport Google auth for authentication. I am just confused about how I can pass the user profile data to the callback URL passed to passport google strategy.
In reference to the above picture, I have passed a callback URL to passport google strategy. I want the below user data object on this callback URL. How can I achieve this?
This is my callback path:
And the handler for this route:
I am getting only the code, scope params over there:
In Short, I want to return the custom jwt token to the frontend when the OAuth successfully completes. Can someone please help me achieve this?
Thanks in Advance.
After having a lot of search over the internet, finally I found the solution for this. To achieve this, you have to use the req argument in the callback and attach the user data with it as shown in the screenshot below:
and then in the callback, you can get the user from req._user
Passport google strategy Code:
passport.use(new GoogleStrategy({
clientID: config.googleOAuth.clientId,
clientSecret: config.googleOAuth.clientSecret,
callbackURL: `${config.endpoints.apiUrl}/user/gmail/oauth`,
passReqToCallback: true
},
async function (req, accessToken, refreshToken, profile, done) {
const profileJson = profile._json;
const userData = {
username: profileJson.email,
firstname: profileJson.given_name,
lastname: profileJson.family_name,
picture: profileJson.picture,
authType: 'oauth'
}
const user = await userCtrl.createOrUpdateUser(userData);
req._user = userData;
return done(null, user);
}))
Callback Code:
const OAuthCallback = async (req, res, next) => {
// function called after successfull oauth
console.log(req._user)
}

Account link using passport without session

I read this interesting article about how to link accounts using passport:
https://codeburst.io/account-linking-with-passportjs-in-3-minutes-2cb1b09d4a76
passport.use(
new GitHubStrategy(
{
clientID: process.env.GithubClientID,
clientSecret: process.env.GithubClientSecret,
callbackURL: process.env.GithubCallbackURL,
passReqToCallback: true
},
(req, accessToken, refreshToken, profile, cb) => {
const { email, name, id } = profile._json
// Check if user is auth'd
if (req.user) {
// Link account
} else {
// Create new account
}
}
)
)
The interesting thing is that he check if user is logged or not. If user logged in then link user account
if (req.user) {
// Link account
} else {
// Create new account
}
I dont know why req object has user? Maybe the user comes from session but in my NodeJS application, I use token instead of session. My question is how to attach user object to req without using session (I use jwt)?
I do like this but the req.user is undefined
router
.route('/google')
.get(
checkToken(true),
passport.authenticate('google', {scope: 'profile email'}),
)
checkToken is a function that if we have valid token, then it will use userId (found in token) to get the user object and then attach it to req. Something like this
if (validToken) {
const id = fromToken(token)
const user = await User.findById(id)
req.user = user
next()
}

Handling authentication in Nodejs with passport-facebook-token, request coming from frontend Facebook SDK

I am working on a Unity App. For login, there are two methods, one using Email and another using Facebook. In case of login separately, I do not have any problem. Registration and Login with Email works perfectly. And Login with Facebook works perfectly as well. Here's the workflow, I created just to make you clear.
tl;dr [read update]
There's another schema for account, which is used for login.
var Account = new Schema({
email: String,
password: String,
facebookId: String
});
Things to know about the backend API.
Passport is used for Authentication
Successful login returns email and token to the client through API.
On client, token is most to play game and use the overall features.
As I said, I have already covered the part when if a client registers and login using email, then client can use the app. But my confusion is handling the logins with Facebook. Facebook SDK is already integrated with the Unity App, and Login is success.
Now, how can I use the Facebook login information that is generated by the Facebook SDK onto my back end, so that I can authorize the user throughout the system, as done in email login.
Going through other questions in SO and Google, I came across passport-facebook-token, I also tried using the plugin but could not came up with the logic and flow for handling the data from SDK into the Nodejs API. Can someone me help understand how it is done?
Update 1: Using passport-facebook-token
Strategy on index.js
passport.use(new FacebookTokenStrategy({
clientID: FACEBOOK_APP_ID,
clientSecret: FACEBOOK_APP_SECRET
}, function(accessToken, refreshToken, profile, done) {
Account.findOrCreate({facebookId: profile.id}, function (error, user) {
return done(error, user);
});
}
));
Controller API
api.post('/auth/facebook/token',
passport.authenticate('facebook-token'),
function (req, res) {
console.log(req.user);
// do something with req.user
res.sendStatus(req.user? 200 : 401);
}
);
Now, there is no error shown, but the data is not inserted into Account Schema, I have this findOrCreate() function in Model.
Account.statics.findOrCreate = function findOrCreate(profile, cb){
var userObj = new this();
this.findOne({facebookId : profile.id},function(err,result){
if(!result){
userObj.facebookId = profile.id;
//....
userObj.save(cb);
}else{
cb(err,result);
}
});
};
you can use facebook-passport for that, you can check the documentation here: https://github.com/jaredhanson/passport-facebook but basically, after you have already set up your developer account and got your keys from the developer site of facebook you can implement a FacebookStrategy object like following where you have to specify your credential and also a callback that in the documentation example is an http request to another resource of an express server where you can then save the data to mongo
passport.use(new FacebookStrategy({
clientID: FACEBOOK_APP_ID,
clientSecret: FACEBOOK_APP_SECRET,
callbackURL: "http://localhost:3000/auth/facebook/callback"
},
function(accessToken, refreshToken, profile, cb) {
User.findOrCreate({ facebookId: profile.id }, function (err, user) {
return cb(err, user);
});
}
));

Access User Profile in node.js after SSO authentication (BlueMix)

I have searched and I have searched a lot, finally came here.
Have:
I have deployed a simple node.js app on Bluemix that does nothing but display Hello! after authenticating the user through SSO SAML service bound to the app on Bluemix.
Need:
What I need to do is get the user profile (firstName, lastName, displayName, emailId etc) so that I can store the user on to a database of my choice (say Cloudant) and further develop the app.
Issue:
I couldn't find a way (example code) to retrieve the user profile. I have read that there is a token returned by the server which needs to be claimed/consumed, but nowhere is an example to be found on how to do that.
Existing thread:
There is a thread on stackoverflow which is similar to my issue, but the solution didn't work. My code below explains that.
My code:
Strategy = new OpenIDConnectStrategy({
authorizationURL : authorization_url,
tokenURL : token_url,
clientID : client_id,
scope: 'openid',
response_type: 'code',
clientSecret : client_secret,
callbackURL : callback_url,
skipUserProfile: false,
issuer: issuer_id},
function(iss, sub, profile, accessToken, refreshToken, params, done) {
process.nextTick(function() {
profile.accessToken = accessToken;
profile.refreshToken = refreshToken;
done(null, profile);
})
});
passport.use(Strategy);
}
app.get('/',ensureAuthenticated, function(req, res){});
app.get('/login', passport.authenticate('openidconnect', {successRedirect: '/hello',failureRedirect: '/failure'}));
app.get('/hello', function(req, res) {
console.log("Pooshon1: ",JSON.stringify(req.user));
console.log("Pooshon3: ",JSON.stringify(req.profile));
res.send('Hello, ');
//res.send('Hello, '+ req.user.displayName + '!');
//res.send('Hello, '+ req.user['id'] + '!');
});
app.get('/failure', function(req, res) {
res.send('login failed');
});
I did not put the entire code, just what was relevant. So, passport returns done(null, profile), and what I read on the internet is that this profile object is returned by the server and can be found in the request object. In my code above, under the app.get("/hello".... the two console.log statements print "Pooshon: undefined", which means there is nothing like req.user or req.profile hence the last two lines are commented, because it throws Internal Server Error (500).
If anyone has done something like this, please help.
app.get('/', function(req,res,next){
if (!req.isAuthenticated()) {
req.session.originalUrl = req.originalUrl;
res.redirect('/login');
} else {
//If authenticated, continue to next middleware
res.send("You are authenticated : "+JSON.stringify(req.session.passport.user.cn));
res.redirect('/welcome');
}});
I found the answer thanks to fellow IBMer Sasaki Rei. The answer is, to retrieve the user profile you need to access the session object contained in the request object. The session object contains an instance of passport object which in turn contains a 'user' object and that my friend contains all the information you may need about the user. I am not mentioning the properties inside the user object because it depends on the SSO token that is returned. In this example I have used the 'cn' property. You can print the user object to see what else is in there that you may need.
To print:
res.send("You are authenticated : "+JSON.stringify(req.session.passport.user));

Authenticate user with passport through LinkedIn login

I have built a login system in Passport and works quite well. Now, I want to integrate LinkedIn login in my system. I already have clientID, clientSecret etc. needed to login. This is the code that is called when the LinkedIn login button is pressed.
passport.use('linkedin', new OAuth2Strategy({
authorizationURL: 'https://www.linkedin.com/uas/oauth2/authorization',
tokenURL: 'https://www.linkedin.com/uas/oauth2/accessToken',
clientID: clientid,
clientSecret: clientsecret,
callbackURL: '/linkedinLogin/linkedinCallbackUrlLogin',
passReqToCallback: true
},
function(req,accessToken, refreshToken, profile, done) {
console.log('authenticated');
console.log(accessToken);
req.session.code = accessToken;
process.nextTick(function () {
done(null, {
code : req.code
});
});
}));
Both the console.log() calls in the callback function are successfully fired, this means I am successfully logged in through LinkedIn and I receive my access token. The part where I connect with LinkedIn is thus correct, what I am missing is the part where I actually log in the user. As you can see, the callbackURL points to /linkedinLogin/linkedinCallbackUrlLogin. This is what I do in that route:
app.get('/linkedinLogin/linkedinCallbackUrlLogin', passport.authenticate('linkedin', {
session: false,
successRedirect:'/linkedinLogin/success',
failureRedirect:'/linkedinLogin/fail'
}));
I just specify a successRedirect and a failureRedirect. Note that if I put session : true I receive as an error Failed to serialize user into session, so for now I keep it to false.
The successRedirect is successfully called. In that route I call a GET request to LinkedIn to access some data about the user. I want to store this data in my DB and remember the user that logged in. This is how I do it:
https.get(
{
host: 'api.linkedin.com' ,
path: '/v1/people/~?format=json' ,
port:443 ,
headers : {'Authorization': ' Bearer ' + req.session.code}
},
function(myres) {
myres.on("data", function(chunk) {
var linkedinJsonResult = JSON.parse(chunk);
User.findOne({linkedinLogin : linkedinJsonResult.id}, function(err, userSearchResult){
if(err) {
throw err;
}
//user found, login
if(userSearchResult){
console.log(userSearchResult);
}
else {
//create user
var newUser = new User(
{
url : linkedinJsonResult.siteStandardProfileRequest.url,
name : linkedinJsonResult.firstName + " " + linkedinJsonResult.lastName,
linkedinLogin : linkedinJsonResult.id,
regDate : new Date()
}
);
//save user
newUser.save(function(err, user){
if(err){
throw err;
}
//login
console.log(user);
});
}
});
});
}
);
Let me explain the code there. After getting the data of the user I check the field "id" that is received. If this id matches one of my users' linkedinLogin field stored into the DB, I consider it already registered (the user has been found in the DB), thus I have to log him/her in. Otherwise I just create a new user using the data received from the GET request.
My question is, in both the cases - the user is found in my DB, or the user has to be created - how can I set req.user to be my user whenever it interacts with my website? Is it sufficient to just do req.user = userSearchResult (if the user is found, inside the if statement) or req.user = user (if the user has been created, inside the newUser.save() callback), or should I call some passport functions that will set it for me?
All the other passport functions related to the registration and login of users without using LinkedIn login are working fine. I am just worried about making this LinkedIn login work with passport.
Thank you.
passport.js will automatically set the req.user object to the object you will pass as the second argument to the done function of the strategy callback.
This means that you should do something like this:
function(req,accessToken, refreshToken, profile, done) {
console.log('authenticated');
console.log(accessToken);
req.session.code = accessToken;
process.nextTick(function () {
// retrieve your user here
getOrCreateUser(profile, function(err, user){
if(err) return done(err);
done(null, user);
})
});
}));
I hope this helps.

Resources