我遇到这个问题,我的项目在本地工作,但部署时,重定向后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,只使用默认会话,它不起作用,我已经尝试了诸如信任代理设置等一切。
尝试更改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
}
}))