I have an Express app where I'm trying to refresh the Yahoo OAuth 1.0 access token before it expires after an hour so the user doesn't have to re-login. I'm using the https-passport-yahoo-oauth Passport strategy, which works for the initial OAuth.
There's a strategy (passport-oauth2-refresh) for refreshing the OAuth 2.0 token here, which I haven't been able to get to work (obvious reasons, I suppose).
Yahoo docs on refreshing the access token here => https://developer.yahoo.com/oauth/guide/oauth-refreshaccesstoken.html
This is my code for initial OAuth is below. How can I then exchange the expire or expiring token for a new one based off this?
passport.serializeUser(function(user, done) {
done(null, user);
});
passport.deserializeUser(function(obj, done) {
done(null, obj);
});
var strategy = new YahooStrategy({
consumerKey: APP_KEY,
consumerSecret: APP_SECRET,
callbackURL: (process.env.APP_URL || require('./conf.js').APP_URL) + 'auth/yahoo/callback'
},
function(token, tokenSecret, profile, done) {
var data = profile._json;
var userObj = {
id: profile.id,
name: data.profile.nickname,
avatar: data.profile.image.imageUrl,
dateJoined: new Date().getTime(),
lastUpdated: new Date().getTime(),
lastVisit: new Date().getTime(),
accessToken: token,
tokenSecret: tokenSecret,
sessionHandle: profile.oauth_session_handle
};
return done(null, userObj);
}
);
passport.use(strategy);
I was thinking that I might be able to use Request, and roll my own token refresh, though I'm a little unsure with where to start there. Any help? Really appreciate any suggestions.
Yep, you can totally do that using request:
request.post('https://api.login.yahoo.com/oauth/v2/get_token', {
oauth: {
consumer_key:'...',
consumer_secret:'...',
token:'...',
token_secret:'...',
session_handle:'...'
}
}, function (err, res, body) {})
Alternatively you can use Purest as well. Note that you should store the session_handle when the user authorizes your app for the first time.
Related
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);
});
}
));
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');
}
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.
I need to build a token based authentication on my node.js app , that the user can use the his facebook or twitter credential to login my app, and use access token to get to resource. this post is suggesting to once authenticated through facebook or twitter or other, use access token on every request, and Session is NOT needed at all
For example
GET /api/v1/somefunction?token='abcedf'
The client gets the access token from the response.
The client calls some server api with the token argument.
so the following code, is to authorize user through twitter, if my app doesn't find my user information, then store user information into the database.
passport.use(new TwitterStrategy({
consumerKey: config.twitter.clientID,
consumerSecret: config.twitter.clientSecret,
callbackURL: config.twitter.callbackURL
},
function(token, tokenSecret, profile, done) {
console.log('TwitterStrategy /auth/twitter.............',profile.id, profile.displayName, profile.username, profile.emails[0], profile._json.avatar_url);
userModel.findUserByQuery({ 'social.twitter.id': profile.id }, function (err, user) {
if (!user) {
console.log('twitter user not found'.red);
userModel.createNewUser( { username:profile.username,
email:profile.emails[0].value,
img:profile._json.avatar_url,
fullname:profile.displayName,
password:profile._json.avatar_url,
social:{twitter:{id:profile.id,avatar:profile._json.avatar_url, name:profile.username,token:accessToken} }},
function(err,data){
if(err) return done(err);
else if(!data) return done(null, false, { message: 'can not create your profile in database' });
else {
console.log('save the new twitter user into database'.green, data._id);
return done(err, user);
}
})
} else {
console.log('twitter user found'.green);
return done(err, user);
}
})
}
))
However, I have two questions,
1. how to send the access token to the client for the following requests
in the code, after authenticated from twitter, I get the access token and send this token to the client on the browser, since the token is embedded in the url parameter, I tried the code
res.redirect ('/users/profile ? token = blabla '), but in the client browser, the url is still shown as '/users/profile' rather than '/users/profile ? token=blabla'
2. once authenticated from twitter, the following request with token is going through my app locally( which I store the token in database, and compare the following token to verify) or still to twitter API to authenticate?
if in the first situation, so I should store the token into the database, in order to compare the following request in token in the following requests to my app? is that right
I'm also trying hard to get this, and just found this rather relevant answer: Call Bitbucket REST API using 2leg oauth token
I just can't get how to do this with passport? Particularly, how to get ouath instance from passport authenticated session, to do oauth.get(...), as described here: https://github.com/ciaranj/node-oauth
UPDATE: Jared (author of passportjs) has explained this is wrong approach in the google groups thread below, and recommends to use https://github.com/mikeal/request.
This is how it works for me:
var oauth = {
consumer_key: process.env.BB_CONSUMER_KEY,
consumer_secret: process.env.BB_CONSUMER_SECRET
};
passport.use(new BitbucketStrategy({
...
function(token, tokenSecret, profile, done) {
oauth = extend(oauth, {
token: token,
token_secret: tokenSecret
});
}
Note, tokens above may need be persisted per user in clustered environment.
Later, to access api, do:
request.get({url: 'protected end point', oauth: oauth})
Hope it will help someone!
I'm currently looking at implementing a google api, using the nodejs client:
https://github.com/google/google-api-nodejs-client/
I'm trying to use passport in order to authenticate, which seems to be working#
passport.use(new GoogleStrategy({
clientID: GOOGLE_CLIENT_ID,
clientSecret: GOOGLE_CLIENT_SECRET,
callbackURL: "http://localhost:3000/auth/google/callback"
},
function(accessToken, refreshToken, profile, done) {
process.nextTick(function () {
var user = {
id: profile.id,
email: profile.email,
firstName: profile.given_name,
lastName: profile.family_name,
accessToken: accessToken
};
return done(null, user);
});
}
));
In my google auth callback:
app.get('/auth/google/callback',
passport.authenticate('google', { failureRedirect: '/login' }),
function(req, res) {
//do something here with the google api
});
I can get hold of req.user, and this has the accessToken
However, the docs for the Google api nodejs client aren't clear on how to use an accessToken.
The example included shows the OAauth2Client retrieving a token, but I guess that part has already been covered using Passport?
I'm not familiar with google-api-nodejs-client, but this is in their documentation:
oauth2Client.credentials = {
access_token: 'ACCESS TOKEN HERE',
refresh_token: 'REFRESH TOKEN HERE'
};
client
.plus.people.get({ userId: 'me' })
.withAuthClient(oauth2Client)
.execute(callback);
I assume you can just set the credentials to those provided by Passport, and things will work fine.
Honestly, though, I find these API wrappers really contrived, and recommend just using request. That way you can access any API service from any provider using a familiar module.