Reputation: 138
I realize that this has come up several times, but I have tried every solution I could find, and my refreshToken is still coming up as undefined
.
accessType: 'offline'
prompt: 'consent'
, and approvalPrompt: 'force'
passport.authorize
instead of passport.authenticate
.http://localhost:4000/auth/google/callback?access_type=offline
Some clarification; my app is in testing in the Google Developer Console. It is not published. When I am asked for what scopes I will authorize in the oauth flow, I am not asked to authorize offline_access
.
What I am trying to do is create a MERN stack app that allows users to sign into their Google ("connect" their account), and then use a feature in the app that will let them use added metadata to schedule a livestream video to YouTube. Right now, my focus is on the Node.js backend.
Here is oauth/passportConfig.js
:
require('dotenv').config();
const passport = require("passport");
const axios = require("axios");
const GoogleStrategy = require("passport-google-oauth20").Strategy;
const GoogleUser = require("../models/usergoogleModel");
// Initialize Passport.js
passport.initialize();
// Configure Passport.js with Google OAuth 2.0 Strategy
passport.use(
new GoogleStrategy(
{
clientID: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
callbackURL: process.env.GOOGLE_CALLBACK_URL,
scope: [
"https://www.googleapis.com/auth/youtube",
"https://www.googleapis.com/auth/youtube.force-ssl",
"https://www.googleapis.com/auth/youtube.upload",
],
skipUserProfile: true,
accessType: 'offline'
},
async (accessToken, refreshToken, _profile, done) => {
try {
console.log("Refresh Token:", refreshToken);
// Make a request to the YouTube API to get the user's profile information
const response = await axios.get(
"https://www.googleapis.com/youtube/v3/channels",
{
params: {
part: "snippet,contentDetails,statistics",
mine: "true",
},
headers: {
Authorization: `Bearer ${accessToken}`,
},
}
);
const youtubeProfile = response.data.items[0];
const googleId = youtubeProfile.id;
// Find or create the user based on googleId
let user = await GoogleUser.findOne({ googleId });
if (!user) {
user = new GoogleUser({
googleId,
accessToken,
refreshToken,
});
} else {
// Update user's access token and refresh token
user.accessToken = accessToken;
user.refreshToken = refreshToken;
}
await user.save();
done(null, user);
} catch (err) {
console.error(
"Error in OAuth callback:",
err.response ? err.response.data : err.message
);
done(err);
}
}
)
);
// Implement Passport.js Serialization and Deserialization
passport.serializeUser((user, done) => {
done(null, user);
});
passport.deserializeUser(async (id, done) => {
try {
const user = await GoogleUser.findById(id);
if (!user) {
// If user is not found, pass null as the user
return done(null, null);
}
done(null, user);
} catch (err) {
done(err);
}
});
module.exports = passport;
Here is controllers/authgoogleController.js
:
const passport = require("passport");
// Controller function to initiate Google OAuth authentication
exports.googleAuth = passport.authenticate("google", {
});
// Callback function for Google OAuth authentication
exports.googleAuthCallback = (req, res, next) => {
passport.authenticate("google", (err, user, info) => {
if (err) {
return next(err);
}
if (!user) {
// Handle authentication failure
// return res.redirect("/login"); // Redirect to the login page or handle appropriately
return res.send("Login failed.");
}
// Handle authentication success
res.send("Google OAuth authentication successful!"); // Send a response indicating successful authentication
})(req, res, next);
};
// Controller function for user logout
exports.logout = (req, res) => {
req.logout(() => {
// res.redirect("/"); // Redirect to the home page after logout
res.send("Logout successful!");
});
};
Here is routes/authgoogle.js
:
const express = require("express");
const router = express.Router();
const authgoogleController = require("../controllers/authgoogleController");
// Route for initiating Google OAuth authentication
router.get("/", authgoogleController.googleAuth);
// Route for handling the OAuth callback
router.get("/callback", authgoogleController.googleAuthCallback);
// Route for user logout
router.get("/logout", authgoogleController.logout);
module.exports = router;
And here is models/usergoogleModel.js
:
const mongoose = require("mongoose");
const usergoogleSchema = new mongoose.Schema({
googleId: String,
accessToken: String,
refreshToken: String,
});
module.exports = mongoose.model("GoogleUser", usergoogleSchema);
And in server.js
:
require("dotenv").config();
const express = require("express");
const session = require("express-session");
const mongoose = require("mongoose");
const passport = require("./oauth/passportConfig")
const authgoogleRoutes = require("./routes/authgoogle");
// Express app
const app = express();
// Middleware to initialize Passport and configure session
app.use(
session({
secret: process.env.SESSION_SECRET,
resave: false,
saveUninitialized: true,
})
);
app.use(passport.session());
// Routes
app.use("/auth/google", authgoogleRoutes);
// Connect to db
mongoose
.connect(process.env.MONGO_URI)
.then(() => {
// Listen for requests
app.listen(process.env.PORT, () => {
console.log("Connected to the db & listening on port", process.env.PORT);
});
})
.catch((error) => {
console.log(error);
});
Upvotes: 0
Views: 209
Reputation: 1
Make sure your app is published, probably production. It worked for me
Upvotes: 0