How can I test my Backend API against google Oauth2? - node.js

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

Related

Trying to use Google Oauth2 with Passportjs in Graphql-Yoga Server

I thought I followed all of the docs correctly to implement this, however, I am getting a TokenError: Bad Request that has the error message invalid_grant.
My server is super simple:
require('dotenv').config();
import createServer from './createServer';
const passport = require('passport');
const GoogleStrategy = require('passport-google-oauth20').Strategy;
const server = createServer();
passport.use(
new GoogleStrategy(
{
clientID: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
callbackURL: 'http://localhost:4000/auth/callback',
},
(accessToken, refreshToken, profile, cb) => {
console.log(accessToken);
console.log(refreshToken);
console.log(profile);
cb(null, profile);
},
),
);
server.express.use(
'/auth',
passport.authenticate('google', {
scope: ['email', 'profile'],
session: false,
}),
);
server.express.use(
'/auth/callback',
passport.authenticate('google', {
successRedirect: 'http://localhost:3000/authenticate',
failureRedirect: 'http://localhost:3000/authenticate',
}),
);
server.start(
{
cors: {
credentials: true,
origin: 'http://localhost:3000',
},
},
() => console.log('Server is running on http://localhost:4000'),
);
Is this a problem with the way that I have setup Google in the cloud platform? I can't figure out where I went wrong. My callback is correctly setup. I'm not sure where else to look for a mistake?
Another thing that is confusing is that the GoogleStategy is console logging the user profile and the access token that is returned. I am guessing that the error comes when the callback route tries to verify the code from the URL. Can anyone point me in a direction to look to better troubleshoot this? Thanks in advance.
I found a solution that works for me, but I am still unclear if it is "best-practice" or not. I would love someone with more GraphQL experience to chime in.
I followed the directions in the docs to authenticate in the front-end: https://developers.google.com/identity/sign-in/web/backend-auth
Then I wrote a query called isAuthenticated on the back-end that I can use to verify the token.
async isAuthenticated(_: any, { token }) {
if(!token) {
return null;
}
const ticket = await client.verifyIdToken({
idToken: token,
audience: process.env.GOOGLE_CLIENT_ID,
});
return payload;
},
I use a React component to check the token in localStorage before rendering any protected routes. This is working for me.

OAuth2 using passport-google-oauth: there is no body in GET request but I still recieve some token

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

FacebookTokenError: This authorization code has been used

Ok, so this is a common error with many causes. I am trying to modify an existing Node-Passport-Facebook module to have local images from the desktop uploaded to a users Facebook account after they log in. That is my goal. This is the code module I am extending
https://github.com/passport/express-4.x-local-example
which in turn is based on
https://github.com/jaredhanson/passport-facebook
I never get past console.log('ERROR HERE... with an error of "This authorization code has been used."
What's confusing is that the auth code returned is ALWAYS DIFFERENT! so how could it already have been used when I try and exchange it for an access token?
Can anyone offer some suggestions, and or next steps I might try? My hunch is that there is something about Passport.js that is not implemented properly.
So my question is, how would I modify the code below (based on this passport facebook example) https://github.com/passport/express-4.x-facebook-example/blob/master/server.jsto upload an image after logging in?
var express = require('express');
var passport = require('passport');
var Strategy = require('passport-facebook').Strategy;
var CLIENTSECRET ='<client secret>';
var APPID ='<app id>';
// Configure the Facebook strategy for use by Passport.
//
// OAuth 2.0-based strategies require a `verify` function which receives the
// credential (`accessToken`) for accessing the Facebook API on the user's
// behalf, along with the user's profile. The function must invoke `cb`
// with a user object, which will be set at `req.user` in route handlers after
// authentication.
passport.use(new Strategy({
clientID: APPID,
clientSecret: CLIENTSECRET,
callbackURL: 'http://localhost:3000/login/facebook/return',
enableProof: true
//callbackURL: 'http://localhost:3000/login/facebook/return'
},
function(accessToken, refreshToken, profile, cb) {
// In this example, the user's Facebook profile is supplied as the user
// record. In a production-quality application, the Facebook profile should
// be associated with a user record in the application's database, which
// allows for account linking and authentication with other identity
// providers.
cb(null, profile);
}));
// Configure Passport authenticated session persistence.
//
// In order to restore authentication state across HTTP requests, Passport needs
// to serialize users into and deserialize users out of the session. In a
// production-quality application, this would typically be as simple as
// supplying the user ID when serializing, and querying the user record by ID
// from the database when deserializing. However, due to the fact that this
// example does not have a database, the complete Twitter profile is serialized
// and deserialized.
passport.serializeUser(function(user, cb) {
cb(null, user);
});
passport.deserializeUser(function(obj, cb) {
console.log(" ");
console.log("ASSERT passport.deserializeUser being called");
console.log(" ");
cb(null, obj);
});
// Create a new Express application.
var app = express();
// Configure view engine to render EJS templates.
app.set('views', __dirname + '/views');
app.set('view engine', 'ejs');
// Use application-level middleware for common functionality, including
// logging, parsing, and session handling.
app.use(require('morgan')('combined'));
app.use(require('cookie-parser')());
app.use(require('body-parser').urlencoded({ extended: true }));
app.use(require('express-session')({ secret: 'keyboard cat', resave: true, saveUninitialized: true }));
// Initialize Passport and restore authentication state, if any, from the
// session.
app.use(passport.initialize());
//app.use(passport.session());
// Define routes.
app.get('/',
function(req, res) {
res.render('home', { user: req.user });
});
app.get('/login',
function(req, res){
res.render('login');
});
app.get('/login/facebook',
passport.authenticate('facebook'));
app.get('/login/facebook/return',
passport.authenticate('facebook', { failureRedirect: '/login' }),
function(req, res) {
//my code changes start here!!
var code = req.query.code;
console.log("1 ASSERT after successful login! code="+code);
if(req.query.error) {
// user might have disallowed the app
return res.send('login-error ' + req.query.error_description);
} else if(!code) {
return res.redirect('/');
}
var options={
host:'graph.facebook.com',
path:'/oauth/access_token?client_id='+APPID+'&code='+code +'&client_secret='+CLIENTSECRET+'&redirect_uri=http://localhost:3000/login/faceboo k/return'
}
var https=require('https');
https.get(options,function(res){
res.setEncoding('utf8');
res.on('data', function (chunk) {
console.log('ERROR HERE'+chunk);
});
});
console.log("2 ASSERT after successful login!")
//my code changes end here!!
});
app.get('/profile',
require('connect-ensure-login').ensureLoggedIn(),
function(req, res){
res.render('profile', { user: req.user });
});
app.listen(3000);
You don't need to make a request to /oauth/access_token at all (well you do, but passport has already handled it for you). That endpoint is for getting an access token when you don't have one, but you already have an access token here:
passport.use(new Strategy({
clientID: APPID,
clientSecret: CLIENTSECRET,
callbackURL: 'http://localhost:3000/login/facebook/return',
enableProof: true
//callbackURL: 'http://localhost:3000/login/facebook/return'
},
function(accessToken, refreshToken, profile, cb) {
// You have the access token here!
cb(null, profile);
}));
You'll need to save that accessToken some way, so that you can use it later when you make requests to the Graph API. You'll probably want to save it to the user's session, but you can also use a strategy like this: https://stackoverflow.com/a/24474900/772035
If you want the user to grant permission to publish (which you will need them to do, to be able to post to their feeds) you also need to replace every call to passport.authenticate with:
passport.authenticate('facebook', { scope: ['publish_actions'] } );
So that the posting permission is requested when the user first adds your app. Then you'll be able to use the /user/photos endpoint to upload a photo, passing the accessToken that you saved earlier in the query string.
You need to encode your query params.
var qs = {
client_id: APPID,
redirect_uri: 'http://localhost:3000/login/facebook/return',
client_secret: CLIENTSECRET,
code: code,
};
options = {
host:'graph.facebook.com',
path:'/oauth/access_token?' + require('querystring').stringify(qs),
};
I think that's your problem. The code itself looks fine apart from that. You'll want the querystring module to parse the results too by the way.
I solved this error. Follow the progress.
Facebook Log in -> Settings -> Apps -> Logged in with Facebook -> Delete Your apps.
After deleting your apps, Try to login with Facebook button.
you need to define profileFields instead of enableProof: true
passport.use(new FacebookStrategy({
clientID:keys.FacebookAppID,
clientSecret:keys.FacebookAppSecret,
callbackURL:'http://localhost:3000/auth/facebook/callback',
profileFields:['email','name','displayName','photos']
},(accessToken,refreshToken,profile,done)=>{
console.log(profile);
}));

Missing required parameter: redirect_uri with passport-google-oauth

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

nodejs backbone and passportjs

i am using nodejs (with express) and backbone. I would like to integrate passport.js for facebook authentication.
I have the following route:
app.get('/auth/facebook', passport.authenticate('facebook', { scope: [ 'email', 'user_about_me'], failureRedirect: '/login' }), users.signin);
What should I do in case the user logged in successfully? How can I access to the user data?
What should I do in case the user DID NOT log in successfully? How can I open the facebook dialog?
Are there any good examples of using passport.js with single page applications?
I know I am answering late but in case you haven't been able to figure out how to use facebook strategy of passport yet, you can go through example provided here.
I hope this is helpful for you :)
Here is the solution recently I have done the facebook connect with passport
I assume you install the passport modules
app.js
var passport = require('passport');
require('./lib/connect')(passport); // pass passport for configuration
routes.js
// send to facebook to do the authentication
app.get('/auth/facebook',
passport.authenticate('facebook', { scope : 'email' })
);
I have created a lib file
connect.js
var FacebookStrategy = require('passport-facebook').Strategy;
module.exports = function(passport) {
passport.use(new FacebookStrategy({
clientID : facebookAuth.clientID,
clientSecret : facebookAuth.clientSecret,
callbackURL : facebookAuth.callbackURL,
passReqToCallback : true // allows us to pass in the req from our route
},
function(req, token, refreshToken, profile, done) {
// here you can get the user profile info
console.log("profile : "+JSON.stringify(profile));
// asynchronous
process.nextTick(function() {
//your logic
});
});
};

Resources