I'm trying to use this library to authenticate using Linkedin:
https://github.com/auth0/passport-linkedin-oauth2
I have configured my Passport Linkedin Strategy like so:
var passport = require('passport');
var LinkedInStrategy = require('passport-linkedin-oauth2').Strategy;
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.findById(id, function (err, user) {
done(err, user);
});
});
passport.use(new LinkedInStrategy({
clientID: 'LINKEDIN_API_KEY',
clientSecret: 'LINKEDIN_API_SECRET',
callbackURL: 'http://localhost:1337/auth/linkedin/callback',
scope: ['r_emailaddress', 'r_basicprofile'],
state: true
}, function(accessToken, refreshToken, profile, done) {
// asynchronous verification, for effect...
process.nextTick(function () {
// To keep the example simple, the user's LinkedIn profile is returned to
// represent the logged-in user. In a typical application, you would want
// to associate the LinkedIn account with a user record in your database,
// and return that user instead.
return done(null, profile);
});
}));
My AuthController.js
looks like this:
var passport = require('passport');
module.exports = {
login: function(req, res) {
passport.authenticate('linkedin', function(err, user, info) {
// The request will be redirected to LinkedIn for authentication, so this
// function will not be called.
});
},
callback: function(req, res) {
// ------------------------------------------------------------------------
// after user authenticated, we get the user's email from
// Linkedin's JSON response and save it against the matching
// email address in the User model
// ------------------------------------------------------------------------
console.log(res);
},
logout: function(req, res) {
req.logout();
res.send('logout successful');
}
};
From the linkedin oauth library, I expect the call to:
passport.authenticate('linkedin', function...);
In my AuthController's login action, to redirect the user to Linkedin's login prompt page but what I am actually seeing is my browser just keeps on loading, loading, loading and never stops.
Am I doing something wrong ?
Some questions I am not sure of:
OK, so continuing on, my next problem appears to be here:
return done(null, profile);
^
TypeError: object is not a function
My code is following the npm module instruction here: https://www.npmjs.com/package/passport-linkedin-oauth2
Maybe SailsJS has another way of writing it yet again....
After fixing the callback error as mentioned in my solution below, I decided to keep moving on and see how it goes despite the Linkedin documentation isn't quite matching 100% to what I expect from the NPM library.
My next problem is my authenticated.js
policy appears to always fail.
My code is below:
// We use passport to determine if we're authenticated
module.exports = function (req, res, next) {
if(req.authenticated) { // <---- this is the error line
return next();
}
else
{
res.send(401, {
error: 'Nice try buddy. Try logging in with Linkedin first :]'
});
}
};
sigh
I think I'm beginning to grasp some of the difference between SailsJS and pure ExpressJS codes.
The problem appears that I was missing this piece of code at the end of my passport.authenticate() method:
(req, res)
I picked it up after looking this tutorial again: http://iliketomatoes.com/implement-passport-js-authentication-with-sails-js-0-10-2/
So now, the final authenticate method should look like:
passport.authenticate('linkedin', function(err, user, info) {
// The request will be redirected to LinkedIn for authentication, so this
// function will not be called.
})(req, res); // <--- notice this extra (req, res) code here
Which matches the Passportjs documentation:
passport.authenticate('local'),
function(req, res) {
// If this function gets called, authentication was successful.
// `req.user` contains the authenticated user.
res.redirect('/users/' + req.user.username);
});
In a way....if you know what I mean... :D
Now I got my Linkedin login prompt as expected.
Finally!
OK.....I'm not sure if this is completes the login process...but....
I noticed I had an extra line:
passReqToCallback: true
Taken from this page here:
https://github.com/auth0/passport-linkedin-oauth2/issues/29
I removed that and I got a different error message.
I've also changed my callback code to look like:
passport.authenticate('linkedin', function(err, user, info) {
res.json(200, {
user: user
});
})(req, res);
and I got my user JSON which appears to be my Linkedin user profile info:
{
user: {
provider: "linkedin",
...
}
}
But that's...contradicting the Linkedin documentation...I don't see any access_token
or expire_in
properties which I was expecting to see in step 3 of the Linkedin OAuth 2.0 documentation (https://developer.linkedin.com/docs/oauth2)...
So...supposedly...I should take this user object and create/update against an existing user object ?
OK, so few more days, I added extra code to generate a User entity if one isn't found in my database, otherwise just return the found user.
The was one last problem, in my policies
folder, I have a authenticated.js
and it looked like this:
// We use passport to determine if we're authenticated
module.exports = function (req, res, next) {
if(req.authenticated) { // <---- this is the error line
return next();
}
else
{
res.send(401, {
error: 'Nice try buddy. Try logging in with Linkedin first :]'
});
}
};
Being new to all this web development stuff, I thought:
req.authenticated; // should call match name of the file ?
was correct but I was following this tutorial:
http://iliketomatoes.com/implement-passport-js-authentication-with-sails-js-0-10-2/
and he named his file: isAuthenticated.js
I figured it's just a name....but I was wrong :D
Turns out, the correct code was:
req.isAuthenticated()
So in full, the correct code becomes:
// We use passport to determine if we're authenticated
module.exports = function (req, res, next) {
if(req.isAuthenticated()) { // alright, that's more like it!
return next();
}
else
{
res.send(401, {
error: 'Nice try buddy. Try logging in with Linkedin first :]'
});
}
};
Perhaps isAuthenticated
is a Passportjs function and not just a name like I initially thought.
My further research shows this page which suggests so to me:
Problems getting Passport.js to authenticate user
Maybe req.authenticated
can only be used for HTML email-password login form as suggested in above Stackoverflow post and req.isAuthenticated()
is for OAuth stuff.
Anyhow, I still don't know if this is the right path but so far, I got authentication in my application now and I can access protected resources. Not sure how long I'll be logged in for, maybe I still need to build the refresh token thingo every 15 minute like the Linkedin documentation stated ?
Hope this helps other fellow Sailsjs users who are facing the same problem :)