passport-google-oauth2: InternalOAuthError: Failed to obtain access token 0

After choosing the google account from the google page, the page redirect to my app but the process keep hanging then an error of " InternalOAuthError: Failed to obtain access token 0" was thrown, thus authentication process can not proceed.

1.launch the app 2.go to http://localhost:3000/login or http://localhost:3000/register route , both have the link to /auth/google/ route 3. the route trigger the passport.authenticate(“google”, { scope: [“https://www.googleapis.com/auth/userinfo.profile”] })); 4.redirect to the google page, then I choose my google account to login 5.the google page seems to try wait for response from local host, 6 an error of “InternalOAuthError: Failed to obtain access token 0” was thrown

I am just trying to login with google oauth2

Expected behavior

After choosing the google account from the google authentication page, I should be redirected to my app and an Access Token is retrieved from google

Actual behavior

After choosing the google account from google page, the process hanged then the error was thrown, it seems the Authoriazation Code is retrieved but the passport-google-oauth2 package faild to exchange the Access Token with the Authorization Code in hand, the package should exchange them automatically, but why is it not done?

Steps to reproduce

11.launch the app 2.go to http://localhost:3000/login or http://localhost:3000/register route , both have the link to /auth/google/ route 3. the route trigger the passport.authenticate(“google”, { scope: [“https://www.googleapis.com/auth/userinfo.profile”] })); 4.redirect to the google page, then I choose my google account to login 5.the google page seems to try wait for response from local host, 6 an error of “InternalOAuthError: Failed to obtain access token 0” was thrown

//jshint esversion:6
require("dotenv").config();
const express = require("express");
const ejs = require("ejs");
const mongoose = require("mongoose");
const session = require("express-session");
const passport = require("passport");
const passportLocalMongoose = require("passport-local-mongoose");
const GoogleStrategy = require("passport-google-oauth20").Strategy;
const findOrCreate = require("mongoose-findorcreate");


const app = express();

app.use(express.static("public"));
app.set("view engine", "ejs");
app.use(
  express.urlencoded({
    extended: true
  })
);

app.use(
  session({
    secret: "Our little secret.",
    resave: false,
    saveUninitialized: false
  })
);
app.use(passport.initialize());
app.use(passport.session());

const mongoDB = "...";
const mongoDBLocal = "mongodb://localhost:27017/secretsDB";
const enviornment = process.env.NODE_ENV || "developement";
const url = enviornment === "developement" ? mongoDBLocal : mongoDB;
mongoose.set("useCreateIndex", true);
mongoose.set("useUnifiedTopology", true);
mongoose.connect(url, { useNewUrlParser: true });

const userSchema = new mongoose.Schema({
  email: String,
  password: String
  googleId: String,
  secret: String
});

userSchema.plugin(passportLocalMongoose);
userSchema.plugin(findOrCreate);

const User = new mongoose.model("User", userSchema);

passport.use(User.createStrategy());
passport.serializeUser(User.serializeUser());
passport.deserializeUser(User.deserializeUser());

passport.use(
  new GoogleStrategy(
    {
      clientID: process.env.CLIENT_ID,
      clientSecret: process.env.CLIENT_SECRET,
      callbackURL: "http://localhost:3000/auth/google/secrets",
      userProfileURL: "https://www.googleapis.com/oauth2/v3/userinfo"
    },
    function(accessToken, refreshToken, profile, cb) {
      console.log(profile);
      
      User.findOrCreate(
        {
          googleId: profile.id
        },
        function(err, user) {
          return cb(err, user);
        }
      );
    }
  )
);

app
  .route("/")
  .get(function(req, res) {
    res.render("home");
  })
  .post(function(req, res) {});

app.route("/auth/google").get(
 passport.authenticate("google", { scope: ["https://www.googleapis.com/auth/userinfo.profile"] }));

app
  .route("/auth/google/secrets")
  .get(passport.authenticate("google", { failureRedirect: "/login" }), function(
    req,
    res
  ) {
    res.redirect("/secrets");
  });

app
  .route("/login")
  .get(function(req, res) {
    res.render("login");
  })
  .post(function(req, res) {
    const user = new User({
      username: req.body.username,
      password: req.body.password
    });
    req.login(user, function(err) {
      if (err) {
        console.log(err);
      } else {
        passport.authenticate("local")(req, res, function() {
          res.redirect("/secrets");
        });
      }
    });

    app.route("/logout").get(function(req, res) {
      req.logout();
      res.redirect("/");
    });
    const username = req.body.username;
    const password = req.body.password;
    User.findOne({ email: username }, function(err, foundUser) {
      if (err) {
        console.log(err);
      } else {
        if (foundUser) {
          bcrypt.compare(password, foundUser.password, function(err, result) {
            if (result === true) {
              res.render("secrets");
            } else {
              res.send("user not exsist or password incoreect.");
            }
          });
        }
      }
    });
  });

app.get("/secrets", function(req, res) {
  if (req.isAuthenticated()) {
    res.render("secrets");
  } else {
    res.redirect("/login");
  }
});

app
  .route("/register")
  .get(function(req, res) {
    res.render("register");
  })
  .post(function(req, res) {
    User.register({ username: req.body.username }, req.body.password, function(
      err,
      user
    ) {
      if (err) {
        console.log(err);
        res.redirect("/register");
      } else {
        passport.authenticate("local")(req, res, function() {
          res.redirect("/secrets");
        });
      }
    });
   

let port = process.env.PORT;
if (port == null || port == "") {
  port = 3000;
}
app.listen(port, function() {
  console.log(`Server online, now listening on port ${port}`);
});
// Format code using Markdown code blocks

Environment

  • Operating System: windows 7 64 x86
  • Node version: v10.16.0
  • passport version: passport 0.4.1
  • passport-google-oauth2 version: passport-google-oauth20@2.0.0

About this issue

Most upvoted comments

For anyone still struggling with this issue, there’s a problem with the node-oauth package mentioned in issue #87 that causes this behaviour: Here’s a possible fix:

diff --git a/node_modules/oauth/lib/oauth2.js b/node_modules/oauth/lib/oauth2.js
index 77241c4..42dd372 100644
--- a/node_modules/oauth/lib/oauth2.js
+++ b/node_modules/oauth/lib/oauth2.js
@@ -158,6 +158,7 @@ exports.OAuth2.prototype._executeRequest= function( http_library, options, post_
     });
   });
   request.on('error', function(e) {
+    if (callbackCalled) { return }
     callbackCalled= true;
     callback(e);
   });

@VoyageinStyle I had a similar issue. Found this https://github.com/nodejs/node/issues/42116 issue in the node repo, downgraded to 16.3.0 and it worked like a charm without any errors. But I guess it’ll help if you’re using ESM like I do and experiencing the issue. Of course, downgrading node is not the best option, I’ll investigate further when I have time.

I have this problem on the production:

authentication-ms | [Nest] 18   - 01/18/2021, 7:01:46 AM   [ExceptionsHandler] Failed to obtain access token +258ms
authentication-ms | InternalOAuthError: Failed to obtain access token
authentication-ms |     at GoogleStrategy.OAuth2Strategy._createOAuthError (/usr/src/app/node_modules/passport-oauth2/lib/strategy.js:408:17)
authentication-ms |     at /usr/src/app/node_modules/passport-oauth2/lib/strategy.js:175:45
authentication-ms |     at /usr/src/app/node_modules/oauth/lib/oauth2.js:191:18
authentication-ms |     at ClientRequest.<anonymous> (/usr/src/app/node_modules/oauth/lib/oauth2.js:162:5)
authentication-ms |     at ClientRequest.emit (events.js:210:5)
authentication-ms |     at TLSSocket.socketErrorListener (_http_client.js:406:9)
authentication-ms |     at TLSSocket.emit (events.js:210:5)
authentication-ms |     at emitErrorNT (internal/streams/destroy.js:92:8)
authentication-ms |     at emitErrorAndCloseNT (internal/streams/destroy.js:60:3)
authentication-ms |     at processTicksAndRejections (internal/process/task_queues.js:80:21)

No problem with localhost. Also added my domain to authorized origins and redirect urls.

Hi @XiaohanZhong ,

Check this solution posted by @danyxudong.

"I searched google a while, found a solution, and my test is ok, please check it in the below.

1st, please find your proxy on the internet explorer. My sample is http://192.168.23.4:999;

2nd, install “https-proxy-agent” node package in your project folder;

$ npm install https-proxy-agent

3rd, write the code as below;

const HttpsProxyAgent = require(‘https-proxy-agent’);

const gStrategy = new GoogleStrategy({ clientID: process.env.CLIENT_ID, clientSecret: process.env.CLIENT_SECRET, callbackURL: “http://localhost:3000/auth/google/secrets”, userProfileURL: “https://www.googleapis.com/oauth2/v3/userinfo” }, function(accessToken, refreshToken, profile, cb) { console.log(profile); User.findOrCreate({ googleId: profile.id }, function (err, user) { return cb(err, user); }); } ); const agent = new HttpsProxyAgent(process.env.HTTP_PROXY || “http://192.168.23.4:999”); gStrategy._oauth2.setAgent(agent);

passport.use(gStrategy); Wish my solution works for you."

Reference:

Github: #59

NPM: https://www.npmjs.com/package/https-proxy-agent

@VoyageinStyle I had a similar issue. Found this nodejs/node#42116 issue in the node repo, downgraded to 16.3.0 and it worked like a charm without any errors. But I guess it’ll help if you’re using ESM like I do and experiencing the issue. Of course, downgrading node is not the best option, I’ll investigate further when I have time.

Many thanks, this solved the issue for me. I had to downgrade to node version 16.3.0.

I’m on a Mac and using google chrome.

Same problem but we don’t have a node-oauth dependency

@wjj28 thanks for the solution, worked well for me!