You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React+Express+Node中Strava OAuth2认证:替代window.open方案问询

解决Strava OAuth2认证与前端Context私有路由同步问题

核心思路

OAuth2授权码流程本身就是基于浏览器重定向设计的,Strava API出于安全限制不支持AJAX跨域请求,所以没必要强行用axios替代页面跳转。正确的做法是利用授权完成后的页面加载时机,从Cookie读取认证状态并更新前端Context,以此实现私有路由的控制。


具体实现步骤

1. 简化前端跳转逻辑

把window.open替换为直接页面跳转(或用<a>标签),本质上两者效果一致,但代码更简洁:

function HomePage() {
  const stravaAuth = () => {
    window.location.href = "http://localhost:8800/api/auth/strava/callback";
  };

  return (
    <div className="App">
      <button onClick={stravaAuth}>Connect to strava!</button>
    </div>
  );
}

export default HomePage;

2. 修正后端路由逻辑

修复授权跳转URL的错误

原stravaCallback路由里的Strava授权URL多了无效的/exchange_token参数,需要删除:

export const stravaCallback = async (req, res) => {
  const client_id = process.env.CLIENT_ID;
  const scope = process.env.SCOPE;
  const redirect_uri = process.env.REDIRECT_URI;
  
  res.redirect(
    `https://www.strava.com/oauth/authorize?client_id=${client_id}&response_type=code&approval_prompt=force&scope=${scope}&redirect_uri=${redirect_uri}`
  );
};
完善Token获取与重定向逻辑

原stravaRedirect路由存在模板字符串错误、全局变量存Token不安全等问题,修正后:

export const stravaRedirect = async (req, res) => {
  const accessCode = req.query.code;
  if (!accessCode) return res.redirect("http://localhost:3000");

  try {
    // 用POST参数传递密钥,比URL拼接更安全
    const response = await axios.post("https://www.strava.com/api/v3/oauth/token", {
      client_id: process.env.CLIENT_ID,
      client_secret: process.env.CLIENT_SECRET,
      code: accessCode,
      grant_type: "authorization_code"
    });

    const accessToken = response.data.access_token;
    // 设置Cookie,生产环境建议开启httpOnly、secure、sameSite属性
    res.cookie("Token", accessToken, {
      sameSite: "lax",
      maxAge: 3600000 * 24 * 7 // 7天有效期
    });

    // 授权完成后重定向回前端私有页面
    return res.redirect("http://localhost:3000/Activities");
  } catch (error) {
    console.error("Strava Token获取失败:", error);
    return res.redirect("http://localhost:3000");
  }
};

3. 实现前端Context与私有路由

创建AuthContext管理认证状态

在页面加载时自动检查Cookie中的Token,同步认证状态:

import { createContext, useContext, useState, useEffect } from 'react';

const AuthContext = createContext();

export function AuthProvider({ children }) {
  const [isAuthenticated, setIsAuthenticated] = useState(false);

  useEffect(() => {
    checkAuthStatus();
  }, []);

  // 读取Cookie中的Token判断认证状态
  const checkAuthStatus = () => {
    const token = document.cookie.split('; ')
      .find(row => row.startsWith('Token='))
      ?.split('=')[1];
    
    setIsAuthenticated(!!token);
  };

  const logout = () => {
    // 清除Cookie并更新状态
    document.cookie = 'Token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;';
    setIsAuthenticated(false);
  };

  return (
    <AuthContext.Provider value={{ isAuthenticated, logout }}>
      {children}
    </AuthContext.Provider>
  );
}

export function useAuth() {
  return useContext(AuthContext);
}
私有路由组件

基于Context状态控制页面访问权限:

import { Navigate } from 'react-router-dom';
import { useAuth } from './AuthContext';

export function PrivateRoute({ children }) {
  const { isAuthenticated } = useAuth();
  return isAuthenticated ? children : <Navigate to="/" />;
}
配置路由

在App中包裹AuthProvider并使用私有路由:

import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import { AuthProvider } from './AuthContext';
import HomePage from './HomePage';
import Activities from './Activities';
import { PrivateRoute } from './PrivateRoute';

function App() {
  return (
    <AuthProvider>
      <Router>
        <Routes>
          <Route path="/" element={<HomePage />} />
          <Route path="/Activities" element={
            <PrivateRoute>
              <Activities />
            </PrivateRoute>
          } />
        </Routes>
      </Router>
    </AuthProvider>
  );
}

export default App;

关键注意事项

  1. 全局变量存Token的问题:原代码中用全局变量accessToken存储用户Token是不安全的,多用户访问会互相覆盖,建议存在用户Session或数据库中。
  2. 生产环境Cookie配置:生产环境需开启httpOnly: true和secure: true防止XSS攻击,此时前端无法直接读取Cookie,需新增后端接口/api/auth/check验证Token有效性。
  3. Token刷新逻辑:Strava的access_token有过期时间,需保存返回的refresh_token,在Token过期时自动刷新。

内容的提问来源于stack exchange,提问作者program.exe

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 03:30:56