我正在使用 Passport.js 使用 google 进行身份验证。我有本地策略(电子邮件+密码)和谷歌策略(谷歌身份验证):
router.post('/login', authenticateRequest, authController.login);
router.get('/google', passport.authenticate('google', {
scope: ['profile', 'email'] })
);
router.get('/google/callback', passport.authenticate('google', {
failureRedirect: '/', session: false,}),
authController.login
);
登录函数和 google 回调函数在验证后都会激活 authController.login 方法,该方法创建令牌并发送响应:
const login = (req: Request, res: Response) => {
const accessToken = req.user.generateJWT(); // req.user is set by passport
const refreshToken = req.user.generateRefreshToken();
const userInfo = req.user.toJSON();
res.json({ accessToken, refreshToken, userInfo });
};
对于本地登录和谷歌登录,响应都会按预期发送回来。问题是,当使用回调时,我没有被重定向回我的网站,因此无法实际使用数据。
这是我的谷歌策略:
import passport from 'passport';
import { Strategy as GoogleStrategy } from 'passport-google-oauth20';
import User from '../models/User';
const googleLogin = new GoogleStrategy(
{
clientID: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
callbackURL: `${process.env.SERVER_URL_DEV}${process.env.GOOGLE_CALLBACK_URL}`,
proxy: true,
},
async (accessToken, refreshToken, profile, done) => {
try {
// try to find user by googleId
const user = await User.findOne({ googleId: profile.id });
if (user) {
return done(null, user);
}
} catch (err) {
console.log(err);
}
// if here then no user found, create new user, then return it
try {
const newUser = await new User({
provider: 'google',
googleId: profile.id,
username: `user${profile.id}`, // saves googleId as username: user1234567890
email: profile.emails[0].value,
name: profile.displayName,
}).save();
done(null, newUser);
} catch (err) {
console.log(err);
}
},
);
passport.use(googleLogin);
这是来自 google 回调的响应 url,以及该 url 上的响应(如果此数据有帮助):
http://localhost:8000/auth/google/callback?code=4%2F0AfJohXkqG-Hv4H27yDa9NRxTv3SCyytau9ZbwwpEvNBp1ssh5x2O7JmzG0Apla9MpAr-lQ&scope=email+profile+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile+https% 3A% 2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email+openid&authuser=0&prompt=同意
{
"accessToken": "accesTokenHere",
"refreshToken": "refreshTokenHere",
"userInfo": {
"id": "65b2e6c7af1334087e7049fc",
"provider": "google",
"email": "[email protected]",
"username": "user102015033027514482051",
"name": "John Doe",
"avatar": "images/default/default-avatar.png",
"posts": [],
"comments": [],
"createdAt": "2024-01-25T22:55:03.616Z",
"updatedAt": "2024-01-25T22:55:03.616Z"
}
}
我没有使用会话。相反,我只有定时令牌。
我尝试创建一个单独的登录方法,专门供谷歌回调使用:
const loginWithGoogle = async (req: Request, res: Response) => {
const accessToken = req.user.generateJWT(); // req.user is set by passport
const refreshToken = req.user.generateRefreshToken();
const userInfo = req.user.toJSON();
res.json({ accessToken, refreshToken, userInfo }).redirect(process.env.CLIENT_URL_DEV);
};
但这也不起作用,因为在 .json 中设置标头后我无法同时使用 .json 和 .redirect 。
我还尝试过将令牌和用户信息作为查询参数发送,但这似乎是完全错误的方法,而且做法极其糟糕且不安全。
我有很多前端依赖于具有 { accessToken,refreshToken,userInfo } 主体的响应,所以我不想开始重构所有内容并保留该模板。
此设置使用 Express 创建 Node.js 应用程序,集成 Google OAuth 进行用户身份验证。用户数据使用 MongoDB 进行管理,Passport.js 处理用户身份验证和会话管理。该应用程序包括 Google 身份验证和回调的路由,将经过身份验证的用户详细信息存储在 MongoDB 中。
我在本地使用Mongodb,需要设置Google Project
https://console.cloud.google.com/
#1 创建谷歌项目
#2 创建 OAuth 客户端 ID
#3 从 Google Developer 保存 client_secret.json 它将在 python 演示代码中使用凭证
#4 安装 Mongodb 并创建“users”集合和“users”数据库
#5 安装node.js依赖项
npm install express express-session passport passport-google-oauth20
此代码可以工作
另存为
google.js
const express = require('express');
const session = require('express-session');
const passport = require('passport');
const GoogleStrategy = require('passport-google-oauth20').Strategy;
const fs = require('fs');
const path = require('path');
const { promisify } = require('util');
const CLIENT_SECRETS_FILE = path.join(__dirname, 'client_secret.json');
const clientSecrets = JSON.parse(fs.readFileSync(CLIENT_SECRETS_FILE, 'utf8'));
const CLIENT_ID = clientSecrets.web.client_id;
const CLIENT_SECRET = clientSecrets.web.client_secret;
const REDIRECT_URI = clientSecrets.web.redirect_uris[0];
const { MongoClient } = require('mongodb');
const mongoUri = 'mongodb://127.0.0.1:27017/users';
const client = new MongoClient(mongoUri);
async function connectMongo() {
try {
await client.connect();
console.log("Connected successfully to MongoDB");
} catch (e) {
console.error(e);
}
}
connectMongo();
// Configure Passport to use Google OAuth 2.0
passport.use(new GoogleStrategy({
clientID: CLIENT_ID,
clientSecret: CLIENT_SECRET,
callbackURL: REDIRECT_URI,
proxy: true
},
async (accessToken, refreshToken, profile, done) => {
try {
const usersCollection = client.db("users").collection("users");
const email = profile.emails && profile.emails.length > 0 ? profile.emails[0].value : null;
let user = await usersCollection.findOne({ googleId: profile.id });
if (!user) {
user = {
googleId: profile.id,
username: `user${profile.id}`,
email: email,
name: profile.displayName,
accessToken: accessToken,
refreshToken: refreshToken,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString()
};
const result = await usersCollection.insertOne(user);
console.log("Inserted new user:", result);
} else {
// Update the existing user (if needed)
// For example, you might want to update the access token
}
done(null, user);
} catch (err) {
console.error("Error in GoogleStrategy:", err);
done(err, null);
}
}));
// Passport session setup
passport.serializeUser((user, done) => {
done(null, user.googleId); // Storing user's Google ID in the session
});
passport.deserializeUser(async (id, done) => {
try {
const usersCollection = client.db("users").collection("users");
const user = await usersCollection.findOne({ googleId: id });
done(null, user); // The user object is added to the request as req.user
} catch (err) {
done(err, null);
}
});
const app = express();
// Configure Express session
app.use(session({
secret: 'YourSessionSecret',
resave: false,
saveUninitialized: false
}));
// Initialize Passport and restore authentication state, if any, from the session
app.use(passport.initialize());
app.use(passport.session());
// Define routes
app.get('/', (req, res) => {
res.send('Home Page. Login with <a href="/auth/google">Google</a>');
});
app.get('/auth/google',
passport.authenticate('google', {
scope: ['profile', 'email'],
accessType: 'offline', // Request offline access
prompt: 'consent' // Force consent screen to reissue refresh token
})
);
app.get('/callback',
passport.authenticate('google', { failureRedirect: '/' }),
(req, res) => {
console.log(req.user); // Log the user data
const userProfile = req.user;
const response = {
accessToken: userProfile.accessToken,
refreshToken: userProfile.refreshToken,
userInfo: {
id: userProfile.googleId,
provider: 'google',
email: userProfile.email || 'No email provided',
username: userProfile.username || 'user undefined',
name: userProfile.name,
avatar: userProfile.avatar || 'default_avatar_url_here',
posts: [], // Populate as required
comments: [], // Populate as required
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString()
}
};
res.json(response);
});
// Using you redirect port #
app.listen(3000, () => {
console.log('Server started on http://localhost:3000');
});
node google.js
http://locahost:3000
#1 单击此按钮
#2 点击你的好ID
#3 继续