passport-google-oauth2 icon indicating copy to clipboard operation
passport-google-oauth2 copied to clipboard

InternalOAuthError: Failed to obtain access token 0

Open ThomasLai1991 opened this issue 5 years ago • 21 comments

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: [email protected]

ThomasLai1991 avatar May 23 '20 13:05 ThomasLai1991

I tried this out. I don't get any errors related to the whole authentication flow (InternalOAuthError in this case). I do get the user profile information back, and see it logged to the console (via console.log(profile);). Wondering why it fails for you. Can you paste the error here probably? So one can get more insight.

A few catch though, I had to fix a couple of non-trivial syntax errors, before being able to run successfully. And there was a slight issue serialising the user details here --> return cb(err, user);, but that's after the details have been fetched successfully by passport anyways...nothing to do with the whole auth flow.

toritsejuFO avatar Jun 28 '20 00:06 toritsejuFO

I have the same problem, have you fixed it?

XiaohanZhong avatar Jul 05 '20 02:07 XiaohanZhong

Hi, If you can post the exact error for more insight.

toritsejuFO avatar Jul 06 '20 19:07 toritsejuFO

Hi, If you can post the exact error for more insight.

InternalOAuthError: Failed to obtain access token at Strategy.OAuth2Strategy._createOAuthError (C:\Users\xhzho\Desktop\Web Development\Secretes-completed\Secrets\node_modules\passport-oauth2\lib\strategy.js:408:17) at C:\Users\xhzho\Desktop\Web Development\Secretes-completed\Secrets\node_modules\passport-oauth2\lib\strategy.js:175:45 at C:\Users\xhzho\Desktop\Web Development\Secretes-completed\Secrets\node_modules\oauth\lib\oauth2.js:191:18 at ClientRequest. (C:\Users\xhzho\Desktop\Web Development\Secretes-completed\Secrets\node_modules\oauth\lib\oauth2.js:162:5) at ClientRequest.emit (events.js:311:20) at TLSSocket.socketErrorListener (_http_client.js:426:9) at TLSSocket.emit (events.js:311:20) at emitErrorNT (internal/streams/destroy.js:92:8) at emitErrorAndCloseNT (internal/streams/destroy.js:60:3) at processTicksAndRejections (internal/process/task_queues.js:84:21)

XiaohanZhong avatar Jul 07 '20 09:07 XiaohanZhong

Sorry for the late reply, really busy.

Ok, so I'm unable to replicate this, I really can't seem to figure out what the issue is. Everything goes smoothly.

Also, for my earlier issue where I said the user details was unable to be serialised,

And there was a slight issue serialising the user details here --> return cb(err, user)

I needed to pass the usernameField as an option for passport-local-mongoose plugin like so userSchema.plugin(passportLocalMongoose, { usernameField: 'googleId' }) ...because that's what's used by findOrCreate.

Continuing, this might be a little bit too much, but if you're up for it, you can debug the function where this fails. I was able to trace and pinpoint the exact function called for getting the accessToken, it's here, the oauth library in node_modules...that's where the actual request to google api is called over the network...

basically /path/to/project/node_modules/oauth/lib/oauth2.js [line 177] code on that line -> exports.OAuth2.prototype.getOAuthAccessToken= function(code, params, callback) {

In there, you can check that you're actually getting the code passed in, and pinpoint where exactly it fails. The whole trace starts from the passport-oauth2, but the main call happens in the oauth library. Basically place debuggers or console logs everywhere and play around.

toritsejuFO avatar Jul 14 '20 21:07 toritsejuFO

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.

matinzd avatar Jan 18 '21 12:01 matinzd

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

wjj28 avatar Jan 19 '21 10:01 wjj28

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

@wjj28 How does one "Find" their proxy on IE? Struggling with this also. Thanks in advance!

abanobmikaeel avatar Feb 05 '21 16:02 abanobmikaeel

@abanobmikaeel

Internet Explorer proxy settings:

  • Open Windows Explorer.
  • Select Tools > Internet Options from the bar menu.
  • Select Connections tab.
  • Navigate to LAN settings.

wjj28 avatar Feb 08 '21 04:02 wjj28

I'm not sure if I'm wrong but as far as i understand, passport-google-oauth2 is not meant to deliver neither access token nor refresh token, and it has a very simple explanation.

  1. The access token returned is not the token you are looking for if you are planning to grant some api access later to the user.
  2. We need not the "access token" returned by passport, instead use the parameters returned in the redirect url configured that are in the query parameters in the final url, you will get a code parameter and a client id.
  3. Those parameters are what you need to request google for real access token and refresh token. please check https://developers.google.com/oauthplayground
  4. You could use the real google oauth 2 library to request the real access tokens inside the redirect url controller.
  5. inside the controller you should request the new tokens and return the Bearer token and the refresh token to give it to the user so he/she/xx can store it and start consuming the restful api.

So, finally the issue is not getting resolved adding openid, or email in the strategy scope, no matter what we put there apparently doesn't work because it only returns the "access token" and the user information. So this is it, I hope this helps more users stuck in the same.

hackerunet avatar May 15 '21 03:05 hackerunet

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

HeyMengxu avatar May 27 '21 08:05 HeyMengxu

For me the problem was that the clientSecret was having a space in the end (copy past error). Very hard to spot since the redirects work well until the callback

juliandm avatar Jan 10 '22 23:01 juliandm

I have the same issue, same errors, no extra space at the end of clientSecret... I've been stuck with this for a while now.. I'm on mac using Chrome, so I can't setup a proxy as @wjj28 suggests... Any idea how to fix this?

hamilton-seguin avatar Feb 16 '22 18:02 hamilton-seguin

@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.

iohansson avatar Feb 27 '22 20:02 iohansson

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);
   });

MithileshHinge avatar Jul 17 '22 23:07 MithileshHinge

@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.

RantiBaba avatar Sep 14 '22 20:09 RantiBaba

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

patrykk21 avatar Nov 30 '22 18:11 patrykk21

reproduced with

  • chrome - Incognito mode, version: 113.0.5672.126
  • firefox - privacy mode, version: 113.0.1

51yu avatar May 25 '23 15:05 51yu

I also experienced this exact error. In my case, the problem lied in the inability of the library to access profile data. It might be related to your internet connection, or your proxy settings.

ariadng avatar Nov 26 '23 14:11 ariadng

中国宝宝特供,哎。亲测wjj28分享的方法有效。 Make some complements based on newest development. Thank you! @wjj28

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; Add HTTP_PROXY=http://192.168.23.4:999 into the .env file in your project folder ( If there is no .env file, then create one).

2nd, install "https-proxy-agent"、“dotenv” node packages in your project folder;

$ npm install https-proxy-agent dotenv

3rd, write the code as below;

import { HttpsProxyAgent } from 'https-proxy-agent'; import dotenv from "dotenv";**

dotenv.config();

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

Chris-1912 avatar Jan 10 '24 00:01 Chris-1912

中国墙受害者特供,使用的VPN使用的不是http代理协议而是Sockets代理协议的方法,wjj28分享的方法有效。 Make some complements based on newest development. Thank you! @wjj28

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 socks5://127.0.0.1:40008; Add HTTP_PROXY=socks5://127.0.0.1:40008 into the .env file in your project folder ( If there is no .env file, then create one).

2nd, install "socks-proxy-agent"、“dotenv” node packages in your project folder;

$ npm install socks-proxy-agent dotenv

3rd, write the code as below;

import { SocksProxyAgent } from 'socks-proxy-agent'; import env from "dotenv";

env.config();

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 SocksProxyAgent(process.env.SOCKS5_PROXY||"socks5://127.0.0.1:40008");

gStrategy._oauth2.setAgent(Agent);

passport.use('google',gStrategy);

Wish my solution works for you."

Reference:

Github: https://github.com/jaredhanson/passport-oauth2/issues/59

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

kightbal avatar Apr 16 '24 05:04 kightbal