I am using PassportJS to handle FB authentication for both browser and mobile clients. For web users I am using the Passport FacebookStrategy and this is working as intended. I would also like to allow mobile clients to access my API. I am trying to use Passport FacebookTokenStrategy to facilitate this. This seems to be working with one small issue. When a mobile client makes a GET request to the server the FacebookTokenStrategy is used and the verify callback function is invoked. In the verify function I can see that the user profile is available and therefore the authentication has succeeded. However an HTTP status of 404 is sent back in the response to the mobile client. I'm not sure how to configure this properly. This is what I'm trying currently:
// Web based auth
passport.use(new FacebookStrategy({
clientID: Config.facebook.clientID,
clientSecret: Config.facebook.clientSecret,
callbackURL: "http://localhost/auth/facebook/callback"
},
function(accessToken, refreshToken, profile, done) {
User.findOrCreate(profile, function(err, user){
done(err, user);
});
}
));
// Mobile client auth
passport.use(new FacebookTokenStrategy({
clientID: Config.facebook.clientID,
clientSecret: Config.facebook.clientID
},
function(accessToken, refreshToken, profile, done) {
console.log(profile);
User.findOrCreate(profile, function(err, user){
done(err, user);
});
}
));
// Redirect the user to Facebook for authentication. When complete,
// Facebook will redirect the user back to the application at
// /auth/facebook/callback
exports.fb_auth = passport.authenticate('facebook',{ scope: 'email' });
// Facebook will redirect the user to this URL after approval. Finish the
// authentication process by attempting to obtain an access token. If
// access was granted, the user will be logged in. Otherwise,
// authentication has failed.
exports.fb_callback = passport.authenticate('facebook', { successRedirect: '/',
failureRedirect: '/login' });
// Mobile Authentication
exports.mobile_fb_auth = passport.authenticate('facebook-token');
Should I be providing passport.authenticate('facebook-token'); with some additional 'onsuccess' callback? That makes sense in the context of a web client but I'm not sure how this should be handled using the facebook-token strategy.
I just had the same issue and was able to resolve it. The 404 is returned because of how middleware works in express. You need to pass in a third function that responds with a success.
The third function isn't called always. It's only called when the previous middleware succeeds.
apiRouter.get('/auth/facebook',
// authenticate with facebook-token.
passport.authenticate('facebook-token'),
// if the user didn't successfully authenticate with the above line,
// the below function won't be called
function(req, res){
res.send(200);
});
`
Related
My goal is to create an authentication backend and I would like to implement google's Oauth2, and for that I decided to follow the passport documentation. My only issue is, how can I test this on my Postman? I'm just developing a backend and I don't know if it's working, I know it sounds a little silly but for a beginner like me it's a lot. Thanks
const dotenv = require('dotenv').config();
const express = require('express');
const passport = require('passport');
var GoogleStrategy = require('passport-google-oauth20').Strategy;
const app = express();
const port = process.env.PORT;
passport.use(new GoogleStrategy({
clientID: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
callbackURL: "http://www.example.com/auth/google/callback"
},
function(accessToken, refreshToken, profile, cb) {
User.findOrCreate({ googleId: profile.id }, function (err, user) {
return cb(err, user);
});
}
));
app.get('/auth/google',
passport.authenticate('google', { scope: ['profile'] }));
app.get('/auth/google/callback',
passport.authenticate('google', { failureRedirect: '/login' }),
function(req, res) {
res.redirect('/');
});
try {
app.listen(port);
console.log(`Server starting on port ${port}`);
} catch (err) {
console.log(err);
}
Whichever OAuth2 service is pretty simply, if you step into it ;)
In first step you send client_id and client_secret to authorization uri.
In second step, the user authenticates with his credentials and you receive the code which you exchange with authorization server for token.
You make request with Bearer token.
Postman can handle some of the things for you. So:
In Google Dev Console create your credentials.
As callback uri fill in:
https://www.getpostman.com/oauth2/callback
https://oauth.pstmn.io/v1/callback
In Postman create new Collection and define following variables:
Define following Authorization:
When you click Get New Access Token you will be asked to login to Google and get back with token.
With this token you can use whichever service you have in scope and your project on Developer Console includes (look for Enabled APIs and services).
Example request:
Create new GET request and save it in your collection, so the variables and tokens are inherited.
URL is: https://www.googleapis.com/oauth2/v1/userinfo?alt=json
Auth in Postman: Inherit auth from parent.
And Send...
Took me months to understand, so hope you'll be swifter :)
I use NodeJs with passportJS in my app to allow users to login/register using Facebook. It seems to work fine, not sure if I understand fully what is happening there though. So the idea is that users can try to login from different pages in my app using Facebook details. After Facebook returns me all the details I log them in or register new user using what Facebook returned and after this is done redirect them to same page but being already logged in. The code is:
var passport = require('passport'),
FacebookStrategy = require('passport-facebook').Strategy;
function setupFBStrategy(req, res, next) {
return function(req, res, next) {
var redirectUrl = req.params.currentPage;
console.log('setupFBStrategy')
passport.use(new FacebookStrategy({
clientID: 'abc',
clientSecret: 'def',
// step 2 ################################
callbackURL: "/auth/facebook/callback?currentPage="+redirectUrl,
passReqToCallback: true,
profileFields: ['first_name', 'last_name', 'photos', 'email']
},
function(req, accessToken, refreshToken, profile, done) {
// step 5 ###########################
// verify callback. Now I have user email inside 'profile' and I authenticate this aginst my database. I use `done(..)` to further instruct my app if the registration/authentication with my system was success or not
}
));
next();
}
}
app.use(passport.initialize());
// step 1 ################################
app.get('/auth/facebook/cp/:currentPage', setupFBStrategy(), passport.authenticate('facebook', { authType: 'rerequest', scope: ['email'] }));
app.get('/auth/facebook/callback',
function(req, res, next) {
console.log(req.query.currentPage)
passport.authenticate('facebook', {
successRedirect: '/'+req.query.currentPage, // redirects when user allowed or logged in with username password
failureRedirect: '/'+req.query.currentPage // takes here when user clicks cancel when asked to give permissions
})(req,res,next);
}
);
passport.serializeUser(function(user, done) {
console.log('serialize='+user)
done(null, {});
});
passport.deserializeUser(function(user, done) {
console.log('deserialize=' + user);
done(null, {});
});
The steps are:
in my app user clicks "login/register with facebook" that makes request to auth/facebook/cp route.
route calls setUpFBStrategy. I append current page user is looking at to callbackUrl.
PassportJS send redirect back to user browser and user browser redirects to facebook for authentication.
when facebook is finished authenticating user it sends redirect to user browser so the browser redirects to callbackURL with URL specified in step 2. It also appends '?code=' querystring to callback URL. Is this querystring hashed version of what Facebook returns so in my case public info and email?
Now my server 'auth/facebook/callback' is executed and verify callback is executed. Depending if I call done(null,profile) etc inside verify callback server returns redirect to browser and browser redirects to successRedirect or failureRedirect 'successRedirect' or 'failureRedirect' routes.
All seems to work so far but is my understanding correct? Is 'code' querystring hashed version of details facebook returns? Why do I even need serializeUser and deserializeUser functions in my code? When would I use refreshToken and accessToken?
yes your code seems to be fine.
passport.serializeUser and passport.deserializeUser and function provided by the passport
passport.serializeUser is called once on login when you call
req.logIn(someValue,function(){})
here someValue is the value you want to store in the passport session
and on every request when you call req.isAuthenticated() function to check that the passport session exist or not the passport.deserializeUser will be called returning true or false.
and in end of the user browsing means on logout you call req.session.destroy to destroy that perticular users session.
I'm trying to implement google oAuth2 for android client. I send empty GET request for google login and receive some token. I can't understand why this happening.
I used my web client id and secret from developer console.
Here is my code:
var GoogleStrategy = require('passport-google-oauth').OAuth2Strategy;
passport.use(new GoogleStrategy({
clientID: 'MY CLIENT ID',
clientSecret: 'MY SECRET',
callbackURL: "http://mysite.io/account/googleLogin/callback"
},
function(accessToken, refreshToken, profile, cb) {
console.log("collection findOrCreate");
console.log("accessToken " + accessToken);
console.log("createIndex" + profile.id);
}
app.get('/googleLogin',
passport.authenticate('google', { scope: ['profile'] }));
app.get('/googleLogin/callback',
passport.authenticate('google', { failureRedirect: '/login' }),
function(req, res) {
// Successful authentication, redirect home.
console.log("Google Login Success!");
res.redirect('/');
});
Logs:
accessToken ya29.tAI7uCiN-JFKWflq4Wm6xbyQjk1S-qdlB6Ks6GTHnNzzr0N_jz8rUVPZLVlvi4aIkF6SGvw
createIndex 102114909694672049994
When I send with the body, effect is the same.
GET http://mysite.io/account/googleLogin/
{ "idToken": "sometoken" }
I've found different approach. First is this part:
router.get('/googleLogin', passport.authenticate('google', { scope : ['profile', 'email'] }));
redirects user to the google authorization page, after success of which called callback method. It's not what I need, because I already have a token on Android side.
For Android client one of the ways is manually check if the google tokenId is valid or not, sending request to the gooogle API. Implementing this part from official source solved my problem:
https://developers.google.com/identity/sign-in/android/backend-auth#verify-the-integrity-of-the-id-token
Using passport-google-oauth: "0.2.0" in my MEAN Stack application (found here: https://github.com/jaredhanson/passport-google-oauth). When I run the application and attempt to sign in with a Google API this error is returned
That’s an error.
Error: invalid_request
Missing required parameter: redirect_uri
Request Details
scope=https://www.googleapis.com/auth/plus.login
response_type=code
redirect_uri=
client_id=xxxx-xxxx.apps.googleusercontent.com
The redirect param is here
passport-init.js
var GoogleStrategy = require('passport-google-oauth').OAuth2Strategy;
var GOOGLE_CLIENT_ID = "xxx-xxx.apps.googleusercontent.com";
var GOOGLE_CLIENT_SECRET = "xxxx";
passport.use(new GoogleStrategy({
clientID: GOOGLE_CLIENT_ID,
clientSecret: GOOGLE_CLIENT_SECRET,
callbackUrl: "http://127.0.0.1:3000/auth/google/oauth2callback" },
function(accessToken, refreshToken, profile, done){
done(null,profile); } ));
The routes are here authenticate.js
router.get('/google', passport.authenticate('google',
{ scope: ['https://www.googleapis.com/auth/plus.login']}),
function (req, res){ });
router.get('/google/oauth2callback', passport.authenticate('google', {
successRedirect: '/auth/success', failureRedirect: '/auth/failure' })
, function (req, res) {res.redirect('/');} );
I am sure I am missing something simple, but I don't know what to add in this question that will give you the best information. Please ask and I will do my best to answer you. This is what feels like the pertinent data.
Funny thing is if I add the callbackUrl manually then everything works great. I can reach the Google API fine. Then I am given the choice to "allow" or "deny" the request.
When defining the GoogleStrategy, the JSON key should be callbackURL instead of callbackUrl (i.e., capital URL). Had this 'issue' as well ;-)
I use something like that-
1. Make sure that's URL and not uri.
2. Make sure the callback URL you registered is same as the one you are requesting for.
var passport = require('passport');
var GoogleStrategy = require('passport-google-oauth').OAuth2Strategy;
// Use the GoogleStrategy within Passport.
// Strategies in Passport require a `verify` function, which accept
// credentials (in this case, an accessToken, refreshToken, and Google
// profile), and invoke a callback with a user object.
passport.use(new GoogleStrategy({
clientID: GOOGLE_CLIENT_ID,
clientSecret: GOOGLE_CLIENT_SECRET,
callbackURL: "http://www.example.com/auth/google/callback"
//should be same as the one you registered in credentials.
},
function(accessToken, refreshToken, profile, done) {
User.findOrCreate({ googleId: profile.id }, function (err, user) {
return done(err, user);
});
}
));
P.S:my first one on stackoverflow. please ignore mistakes and help me in improving.
I have a working example using the same strategy. Since I don't get this error, can't pinpoint what the problem is, but I wanted to suggest you check the following:
add to your google strategy creation (new GoogleStrategy({...})) the scope:
scope: 'https://www.googleapis.com/auth/userinfo.email',
Make sure your google api is configured properly in the dev api console. Specifically:
Under APIs and auth | Credentials | OAuth consent screen - all the required url.
Under APIs and auth | Credentials - look for your web api client and see you authorized all the relevant URIs. The auth will not work if the call is from or the redirection is to a page that was not listed in this section.
My issue occuors in authenticate with WSL. This error bellow is presented:
error issue
To solved , utilize the command gcloud init --console-only in your terminal.
I had this issue to. in my nestJs Application. I solved it by changing the CallbackURL from "callbackUrl" to "callbackURL" URL all caps
I want to authenticate a user via passport's twitter strategy, but not sign the user in. All I want to do is store their credentials so I can tweet to their account at a later date, but I'm not seeing how this is possible.
It looks like you have to call the done callback which then stores the users id in the session. My user is already authenticated with my application, but wants to attach one or more twitter account that they can choose to tweet to at a later date.
Here's my Twitter strategy
passport.use(new TwitterStrategy({
consumerKey: '...',
consumerSecret: '...',
callbackURL: '/add/twitter/callback',
passReqToCallback: true
},
function(req, token, tokenSecret, profile, done) {
process.nextTick(function() {
//save the users twitter credentials for use later on and call
//my custom callback here ...
});
});
}));
I'm also using express.js so here is are my routes
router
.get('/auth/twitter', passport.authenticate('twitter'))
.get('/auth/twitter/callback',
passport.authenticate('twitter'), function(req, res) {
console.log('made it here');
// Successful authentication
res.render('manager/add-twitter', {});
});
Is it possible to have a custom callback that gets fired no matter what?
As it turns out, you can do this using authorize instead of authenticate. Here's the docs for anyone who's interested: http://passportjs.org/guide/authorize/