Frontend: React App, развернутое на Vercel
Backend: Express.js REST API, развернутый на рендеринге
Database: Mongodb Atlas
Authentication: Google OAuth 2.0
Идеально работает в локальных разработках, но не сбои в производстве с помощью оборотов. После того, как Google Oauth завершится, он перенаправляет обратно в мое приложение, но сеанс cookie не поддерживается в перенаправлении.
Сообщения об ошибках
в консоли браузера: < /p>
Код: Выделить всё
xhr.js:139 Refused to set unsafe header "Origin"
xhr.js:139 Refused to set unsafe header "Origin"
Access to XMLHttpRequest at 'https://full-stack-crm-platform.onrender.com/api/customers?_t=1748716416363' from origin 'https://full-stack-crm-platform.vercel.app' has been blocked by CORS policy: Request header field pragma is not allowed by Access-Control-Allow-Headers in preflight response.
Access to XMLHttpRequest at 'https://full-stack-crm-platform.onrender.com/api/auth/current_user' from origin 'https://full-stack-crm-platform.vercel.app' has been blocked by CORS policy: Request header field pragma is not allowed by Access-Control-Allow-Headers in preflight response.
AuthContext.js:32 Authentication check failed
< /code>
Что произойдет < /p>
[list]
[*] I Нажмите «Войти с Google» < /li>
Google OAuth Process завершает < /li>
Перенаправление обратно в мое приложение < /li>
. LOOP
[/list]
Что я попробовал
[list]
[*] Обновленные URL -адреса OAUTH
[/list]
// Исправлены в паспорте.js
const callbackURL = process.env.NODE_ENV === 'production'
? `${process.env.BACKEND_URL}/api/auth/google/callback`
: 'http://localhost:5000/api/auth/google/callback';
- Настроенные файлы cookie-cross-domain
Код: Выделить всё
app.use(session({
name: 'xeno.session',
secret: process.env.SESSION_SECRET || 'dev-secret-key',
resave: false,
saveUninitialized: false,
store: process.env.NODE_ENV === 'production'
? new MongoStore({ mongoUrl: process.env.MONGO_URI })
: new session.MemoryStore(),
cookie: {
maxAge: 30 * 24 * 60 * 60 * 1000, // 30 days
secure: process.env.NODE_ENV === 'production', // true in production
httpOnly: true,
sameSite: process.env.NODE_ENV === 'production' ? 'none' : 'lax'
}
}));
- Настроенные Cors с учетными данными
Код: Выделить всё
app.use(cors({
origin: function(origin, callback) {
// Allow requests with no origin or from allowed origins
if (!origin || allowedOrigins.indexOf(origin) !== -1) {
callback(null, true);
} else {
console.log('Unknown origin allowed:', origin);
callback(null, true); // Currently allowing any origin for debugging
}
},
credentials: true,
exposedHeaders: ['Set-Cookie'],
allowedHeaders: [
'Content-Type', 'Authorization', 'X-Requested-With',
'Origin', 'Accept', 'Cache-Control', 'X-PINGOTHER',
'pragma', 'access-control-request-headers'
],
methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS', 'PATCH'],
maxAge: 86400
}));
- Конфигурация фронта Axios
Код: Выделить всё
const apiService = axios.create({
baseURL: process.env.REACT_APP_API_URL || 'http://localhost:5000',
withCredentials: true,
timeout: 15000
});
// Added request interceptor
apiService.interceptors.request.use(
config => {
// Set explicit origin header
config.headers['Origin'] = window.location.origin;
console.log('Making request to:', config.url, 'with headers:', config.headers);
return config;
},
error => {
return Promise.reject(error);
}
);
- Увеличенная обработка обратного вызова OAuth
Код: Выделить всё
app.get('/api/auth/google/callback',
passport.authenticate('google', { failureRedirect: '/login' }),
(req, res) => {
// Force session save before redirect
req.session.save(err => {
if (err) {
console.error('Session save error:', err);
}
console.log('Session saved, redirecting to frontend with logged in user:',
req.user ? req.user.email : 'unknown');
// Add CORS headers to redirect response
res.header('Access-Control-Allow-Origin', process.env.FRONTEND_URL);
res.header('Access-Control-Allow-Credentials', 'true');
res.redirect(`${process.env.FRONTEND_URL}?auth=success&user=${req.user?.email}`);
});
}
);
- Добавлена логика учреждения сеанса фронта
Код: Выделить всё
const location = useLocation();
const [authProcessing, setAuthProcessing] = useState(false);
// Handle OAuth redirect with query parameters
useEffect(() => {
const query = new URLSearchParams(location.search);
const authSuccess = query.get('auth') === 'success';
if (authSuccess && !authProcessing) {
setAuthProcessing(true);
console.log('OAuth redirect detected, establishing session...');
// Call backend to establish session cookie
apiService.get('/api/auth/current_user')
.then(response => {
console.log('Session established:', response.data);
// Clear URL parameters
window.history.replaceState({}, document.title, window.location.pathname);
setAuthProcessing(false);
})
.catch(error => {
console.error('Failed to establish session:', error);
setAuthProcessing(false);
});
}
}, [location, authProcessing]);
- Добавлен прокси CORS для внешних вызовов API
app.get('/proxy', async (req, res) => {
try {
const targetUrl = req.query.url;
if (!targetUrl) {
return res.status(400).json({ error: 'Missing URL parameter' });
}
// Forward request to target URL with CORS headers
// ... rest of proxy implementation
} catch (error) {
console.error('CORS Proxy error:', error);
res.status(500).json({ error: 'CORS Proxy error' });
}
});
< /code>
переменные среды (Backend - render) < /p>
NODE_ENV=production
BACKEND_URL=https://full-stack-crm-platform.onrender.com
FRONTEND_URL=https://full-stack-crm-platform.vercel.app
MONGO_URI=mongodb+srv://...
SESSION_SECRET=...
GOOGLE_CLIENT_ID=...
GOOGLE_CLIENT_SECRET=...
< /code>
Вопросы: < /p>
Почему куки сессии не сохраняется после перенаправления Google OAuth в производстве? Отсутствует?>
Подробнее здесь: https://stackoverflow.com/questions/796 ... ontend-ver