如何使用 google-oauth20 回调来重定向和发送响应数据?

问题描述 投票:0回答:1

我正在使用 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 } 主体的响应,所以我不想开始重构所有内容并保留该模板。

node.js authentication oauth-2.0
1个回答
0
投票

概述:

此设置使用 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 继续

结果

在浏览器中,

在航站楼,

在 Mongodb 中,

© www.soinside.com 2019 - 2024. All rights reserved.