passport-google-oauth2
passport-google-oauth2 copied to clipboard
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: [email protected]
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.
I have the same problem, have you fixed it?
Hi, If you can post the exact error for more insight.
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.
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.
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
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
Internet Explorer proxy settings:
- Open Windows Explorer.
- Select Tools > Internet Options from the bar menu.
- Select Connections tab.
- Navigate to LAN settings.
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.
- 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.
- 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.
- Those parameters are what you need to request google for real access token and refresh token. please check https://developers.google.com/oauthplayground
- You could use the real google oauth 2 library to request the real access tokens inside the redirect url controller.
- 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.
@wjj28 thanks for the solution, worked well for me!
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
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?
@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.
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 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
reproduced with
- chrome - Incognito mode, version: 113.0.5672.126
- firefox - privacy mode, version: 113.0.1
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.
中国宝宝特供,哎。亲测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
中国墙受害者特供,使用的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