Express 会话仅在部署时不会持续存在

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

我遇到这个问题,我的项目在本地工作,但部署时,重定向后sessionID不同,前端和后端无法相互通信,我尝试过自定义域,并部署在不同的服务上,我有现在使用 render.com 和 vercel。

这是我的后端index.js 我尝试了不同的方法来解决这个问题,使用 redis、使用 cookie 解析器和更改静态文件。

当我单击“登录”时,它会正确转到 Spotify 并将我登录到那里,然后当我重定向回来时,我认为会话不会持续存在,只有加载屏幕。

const express = require('express');
const SpotifyWebApi = require('spotify-web-api-node');
require('dotenv').config();
const cors = require('cors');
const app = express();
const path = require('path');
const session = require('express-session');
const cookieParser = require('cookie-parser');
// app.use(session({
//   secret: process.env.SESSION_SECRET,
//   resave: false,
//   saveUninitialized: true,
//   cookie: {
//     secure: process.env.NODE_ENV === "production",
//     sameSite: process.env.NODE_ENV === "production" ? 'None' : 'Lax'
//     //domain: '.onrender.com'
//   }
// }));

app.use(cookieParser());

app.use(cors({
  origin: process.env.FRONT_URL, // adjust if your frontend port is different
  credentials: true
}));

//Redis setup
const { createClient } = require('redis');
const redisClient = createClient({
  url: 'rediss://red-cmcp61f109ks73921rng:[email protected]:6379',
  legacyMode: true// Your Redis URL
});
redisClient.connect().catch(console.error);
const RedisStore = require('connect-redis')(session);
// Session setup with Redis


app.use(session({
  store: new RedisStore({ client: redisClient }),
  secret: process.env.SESSION_SECRET,
  resave: false,
  saveUninitialized: false, // You can set this to false to comply with laws that require permission before setting a cookie
  name: 'HaromonyCookie',
  cookie: {
    httpOnly: true,
    sameSite: process.env.NODE_ENV === "production" ? 'None' : 'Lax',
    secure: process.env.NODE_ENV === "production",
    domain: process.env.DOMAIN // Should be true if using HTTPS
  }
  // Add other configurations as needed
}));

redisClient.on('connect', () => console.log('Redis client connected'));
redisClient.on('error', (err) => console.log('Redis Client Error', err));

// Serve static files from the React app
app.use(express.static(path.join(__dirname, '../../client/build')));

const spotifyApi = new SpotifyWebApi({
  clientId: process.env.SPOTIFY_CLIENT_ID,
  clientSecret: process.env.SPOTIFY_CLIENT_SECRET,
  redirectUri: process.env.SPOTIFY_REDIRECT_URI
});

// have to rewrite this to because I am not storing token in spotifyApi object
app.post('/refresh', (req, res) => {
  const refreshToken = req.session.user.refreshToken;
  spotifyApi.setRefreshToken(refreshToken);
  spotifyApi.refreshAccessToken().then(data => {
    res.json({
      accessToken: data.body['access_token'],
      expiresIn: data.body['expires_in']
    });
  }).catch(error => {
    console.error('Error refreshing access token:', error);
    res.status(500).json({ error: 'Error refreshing access token' });
  });
} );

app.get('/api/token', (req, res) => {
  console.log("token endpoint hit")
  console.log('Session ID:', req.sessionID);
  console.log('Cookies sent:', req.cookies);
  if (req.session.user && req.session.user.accessToken) {
    console.log("sending token")
    res.json({ accessToken: req.session.user.accessToken });
  } else {
    res.status(401).json({ error: 'Unauthorized: No active session' });
  }
});

// Redirect users to this endpoint for Spotify login, then redirect them to the /callback endpoint
app.get('/login', (req, res) => {
  console.log("STEP 1")
  console.log("--------------------")
  console.log("login enpoint hit")
  console.log("--------------------")
  const scopes = ['user-read-private', 'user-read-email', 'playlist-read-private', 'playlist-modify-private', 'playlist-modify-public'];
  res.redirect(spotifyApi.createAuthorizeURL(scopes));
  
}); 

app.post('/logout', (req, res) => {
  req.session.destroy((err) => {
    if (err) {
      res.status(500).send('Could not log out, please try again');
    } else {
      res.clearCookie('connect.sid', { path: '/' });
      res.status(200).send('Logged out');
    }
  });
});

// Spotify will redirect users to this endpoint after login
app.get('/callback', (req, res) => {
  console.log("STEP 2")
  console.log("--------------------")
  console.log("callback endpoint hit")
  console.log("--------------------")
  res.set('Cache-Control', 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0');
  res.set('Pragma', 'no-cache'); // HTTP 1.0.
  res.set('Expires', '0'); 
  const error = req.query.error;
  const code = req.query.code;

  if (error) {
    console.error('Callback Error:', error);
    res.send(`Callback Error: ${error}`);
    return;
  }

  spotifyApi.authorizationCodeGrant(code).then(data => {
    const accessToken = data.body['access_token'];
    const refreshToken = data.body['refresh_token'];
    const expiresIn = data.body['expires_in'];
    const expiryTime = new Date().getTime() + expiresIn * 1000;
    console.log('Session ID:', req.sessionID);
    console.log('Cookies received:', req.cookies);

    req.session.user = {
      accessToken: accessToken,
      refreshToken: refreshToken,
      expiresIn: expiresIn,
      expiryTime: expiryTime
    };

    req.session.save(err => {
      if (err) {
        console.error('Error saving session:', err);
        res.send(`Error saving session: ${err}`);
      } else {        
        console.log("saved")
        console.log(req.session)
        // In your Express route after successful authentication
        setTimeout(() => {
          console.log('Redirecting to front URL');
          res.redirect(`${process.env.FRONT_URL}/auth/callback`);
        }, 1000);
      }
    });
  }).catch(error => {
    console.error('Error getting Tokens:', error);
    res.send(`Error getting Tokens: ${error}`);
  });
});

app.get('*', (req, res) => {
  res.sendFile(path.join(__dirname, '../client/build', 'index.html'));
});

const port = process.env.PORT || 3001;
app.listen(port, () => {
  console.log(`Listening at http://localhost:${port}`);
});
import React, { useEffect, useContext } from 'react';
import { AuthContext } from '../contexts/authContext';

const AuthCallback = () => {
    const { fetchTokens } = useContext(AuthContext);
    useEffect(() => {
        const fetchAndSetTokens = async () => {
            console.log("AuthCallback reached and fetching tokens...")
            const tokens = await fetchTokens();
            if (tokens) {
                localStorage.setItem('accessToken', tokens);
                localStorage.setItem('isLoggedIn', true);
                // Redirect to home or other page after successful login
                window.location.href = `${process.env.REACT_APP_FRONT}/home` || "localhost:3000/home";
            }
        };
        fetchAndSetTokens();
    }, [fetchTokens]);

    return <div>Loading...</div>; // Or some loading indicator
};

export default AuthCallback;

在后端显示我已成功登录,保存会话并重定向到前端。日志中显示会话 ID 不同。

我尝试过不使用redis,只使用默认会话,它不起作用,我已经尝试了诸如信任代理设置等一切。

javascript reactjs express express-session nodejs-express-server
1个回答
0
投票

尝试更改redis会话配置中间件

//Configure session middleware
app.use(session({
    store: new RedisStore({ client: redisClient }),
    secret: 'yoursecret',
    resave: false,
    saveUninitialized: false,
    cookie: {
        secure: false, // if true only transmit cookie over https
        httpOnly: false, // if true prevent client side JS from reading the cookie 
        maxAge: 1000 * 60 * 10 // session max age in miliseconds
    }
}))

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