How can I get the user profile data in passport google auth callback function? - node.js

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)
}

Related

Access req and res inside Passport JS strategy

I'm working on an app and I want my users to be able to connect using their Spotify accounts. I've managed to do this using the Passport JS Spotify strategy. The problem now is that I also want to provide a functionality for users to be able to register and log in via email. After they are logged in, they should be able to link their Spotify account to the brand new email-based account.
I have this code to use the Spotify strategy.
passport.use(
new SpotifyStrategy(
{
clientID: process.env.SPOTIFY_CLIENTID,
clientSecret: process.env.SPOTIFY_SECRET,
callbackURL: '/auth/spotify/callback',
scope: ['user-read-email']
},
async function (_accessToken, _refreshToken, _expires_in, profile, cb) {
return await passportSocialLogin(profile, cb)
}
)
)
Here I have that function call (passportSocialLogin). Here I add the data from Spotify to my database. It would be really nice to be able to pass req as an argument of passportSocialLogin. This would help me to find the logged in user via the session object and then to link his account with the Spotify one.
Do you have any ideas for this? Thank you! Have a nice day!
I've found a solution here: https://www.passportjs.org/concepts/delegated-authorization/
As you can see, you can use passReqToCallback inside the strategy, and the req object will be available as the first argument of the verify callback.
In my case the solution was:
passport.use(
new SpotifyStrategy(
{
clientID: process.env.SPOTIFY_CLIENTID,
clientSecret: process.env.SPOTIFY_SECRET,
callbackURL: '/auth/spotify/callback',
scope: ['user-read-email'],
passReqToCallback: true
},
async function (req, _accessToken, _refreshToken, _expires_in, profile, cb) {
return await passportSocialLogin(profile, req, cb)
}
)
)

Unable to get profile information in callback, But able to found that information in serializeUser function

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

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);
});
}
));

Passport & JWT & Google Strategy - Disable session & res.send() after google callback

Using: passport-google-oauth2.
I want to use JWT with Google login - for that I need to disable session and somehow pass the user model back to client.
All the examples are using google callback that magically redirect to '/'.
How do I:
1. Disable session while using passport-google-oauth2.
2. res.send() user to client after google authentication.
Feel free to suggest alternatives if I'm not on the right direction.
Manage to overcome this with some insights:
1. disable session in express - just remove the middleware of the session
// app.use(session({secret: config.secret}))
2. when using Google authentication what actually happens is that there is a redirection to google login page and if login is successful it redirect you back with the url have you provided.
This actually mean that once google call your callback you cannot do res.send(token, user) - its simply does not work (anyone can elaborate why?). So you are force to do a redirect to the client by doing res.redirect("/").
But the whole purpose is to pass the token so you can also do res.redirect("/?token=" + token).
app.get( '/auth/google/callback',
passport.authenticate('google', {
//successRedirect: '/',
failureRedirect: '/'
, session: false
}),
function(req, res) {
var token = AuthService.encode(req.user);
res.redirect("/home?token=" + token);
});
But how the client will get the user entity?
So you can also pass the user in the same way but it didn't felt right for me (passing the whole user entity in the parameter list...).
So what I did is make the client use the token and retrieve the user.
function handleNewToken(token) {
if (!token)
return;
localStorageService.set('token', token);
// Fetch activeUser
$http.get("/api/authenticate/" + token)
.then(function (result) {
setActiveUser(result.data);
});
}
Which mean another http request - This make me think that maybe I didnt get right the token concept.
Feel free to enlighten me.
Initialize passport in index.js:
app.use(passport.initialize());
In your passport.js file:
passport.use(
new GoogleStrategy(
{
clientID: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
callbackURL:
'http://localhost:3000/auth/google/redirect',
},
async (accessToken, refreshToken, profile,
callback) => {
// Extract email from profile
const email = profile.emails![0].value;
if (!email) {
throw new BadRequestError('Login failed');
}
// Check if user already exist in database
const existingUser = await User.findOne({ email
});
if (existingUser) {
// Generate JWT
const jwt = jwt.sign(
{ id: existingUser.id },
process.env.JWT_KEY,
{ expiresIn: '10m' }
);
// Update existing user
existingUser.token = jwt
await existingUser.save();
return callback(null, existingUser);
} else {
// Build a new User
const user = User.build({
email,
googleId: profile.id,
token?: undefined
});
// Generate JWT for new user
const jwt = jwt.sign(
{ id: user.id },
process.env.JWT_KEY,
{ expiresIn: '10m' }
);
// Update new user
user.token = jwt;
await auth.save();
return callback(null, auth);
}
}));
Receive this JWT in route via req.user
app.get('/google/redirect', passport.authenticate('google',
{failureRedirect: '/api/relogin', session: false}), (req, res) => {
// Fetch JWT from req.user
const jwt = req.user.token;
req.session = {jwt}
// Successful authentication, redirect home
res.status(200).redirect('/home');
}

understanding passportjs authenticate method

I am having an hard time understanding how passportjs authentication method works, in particular with the http-bearer strategy.
So I have two routes, one for registration and one for accessing user's profile, which goes through passportjs middleware. Have a look at the following code:
exports.register = function(req, res){
User.schema.statics.generateUserToken(function(t){
var user = new User({
token: t,
name: 'john doe',
});
user.save(function(e){
res.json(user)
});
});
};
My authentication strategy is as follow:
var mongoose = require('mongoose'),
passport = require('passport'),
BearerStrategy = require('passport-http-bearer').Strategy;
passport.use(new BearerStrategy(
function(token, done) {
User.findOne({ token: token }, function (err, user) {
if (err) { return done(err); }
if (!user) { return done(null, false); }
return done(null, user, { scope: 'read' });
});
}
));
as you can see, when a user requests the registration, my server returns him his object, with its token that should be locally saved.
Then, in a protected route, I added the passportjs middleware, like this:
app.get('/me', passport.authenticate('bearer', { session: false }), routes.me);
where I obviously get an unauthorized error. Why is this' where does passport.authenticate get the token from my client?! This is really confusing for me and is driving me mad. Any help?
Also, is this the right way of doing token authorization? Or do I also need some more details like timestamp, expires, etc.?
could you please refer http-bearer's sample code: https://github.com/jaredhanson/passport-http-bearer/blob/master/examples/bearer/app.js to refactor your codebase. I think here is very clearly definition.

Resources