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

react-router-dom v6+OIDC:signInRedirectCallback持续调用无法跳转首页

OIDC回调循环问题排查与解决

问题描述

OIDC初始化完成,signInRedirectCallback执行成功且sessionStorage已正确设置,但该方法出现持续调用的情况(见控制台截图)。页面始终显示Callback组件的“Callback Loading...”,设置完sessionStorage后无法返回首页。

控制台截图:
控制台重复调用signInRedirectCallback

错误原因分析

  1. Callback组件未处理路由跳转:调用signInRedirectCallback后未触发路由跳转,页面一直停留在/signin-oidc路由,组件反复渲染导致方法重复调用。
  2. signInRedirectCallback逻辑错误:调用userManager.signinRedirectCallback()后未重新获取用户信息,直接使用空值user更新状态;且异步状态更新后,sessionStorage存入的是旧的isAuthenticated值。
  3. AuthProvider的useEffect依赖问题:依赖了isAuthenticated和user,状态变化时重复执行loadUserFromStorage,引发不必要的重渲染循环。
  4. PrivateRoute的props接收错误:未正确解构props,导致children无法正常获取。

修复方案

1. 修正Callback组件,添加路由跳转

修改Callback.js,回调完成后跳转到首页或之前存储的页面:

import * as React from 'react';
import { AuthConsumer } from "./AuthProvider";
import { useNavigate } from 'react-router-dom';

export const Callback = () => {
  const navigate = useNavigate();
  
  return (
    <AuthConsumer>
      {({ signInRedirectCallback }) => {
        (async () => {
          await signInRedirectCallback();
          // 跳转到存储的页面,默认首页
          const redirectUri = sessionStorage.getItem('redirectUri') || '/';
          sessionStorage.removeItem('redirectUri');
          navigate(redirectUri);
        })();
        
        return <span>Callback loading</span>;
      }}
    </AuthConsumer>
  );
};

2. 修复signInRedirectCallback逻辑

修改AuthProvider.js中的方法,确保正确获取用户信息并更新状态:

const signInRedirectCallback = async () => {
  console.log('signInRedirectCallback flow!');
  let user = await userManager.getUser();
  if(!user) {
    // 调用回调后重新获取用户
    user = await userManager.signinRedirectCallback()
      .then((newUser) => {
        console.log('signInRedirectCallback Successful!');
        return newUser;
      })
      .catch((error) => {
        console.error('signInRedirectCallback Error : ', error);
        throw error;
      });

    const isAuth = !!user;
    setIsAuthenticated(isAuth);
    setUser(user);
    sessionStorage.setItem('access_token', user.access_token);
    sessionStorage.setItem('id_token', user.id_token);
    sessionStorage.setItem('isAuthenticated', String(isAuth));
  }
};

3. 调整AuthProvider的useEffect依赖

移除不必要的依赖,避免重渲染循环:

useEffect(() => {
  const loadUserFromStorage = async () => {
    let user = await userManager.getUser();
    if (user) {
      console.log("User flow")
      const isAuth = !!user;
      setIsAuthenticated(isAuth);
      setUser(user);
      sessionStorage.setItem('access_token', user.access_token);
      sessionStorage.setItem('id_token', user.id_token);
      sessionStorage.setItem('isAuthenticated', String(isAuth));
    }
  };

  loadUserFromStorage();
}, []); // 仅在组件挂载时执行一次

4. 修正PrivateRoute的props接收

修改PrivateRoute.js的参数结构:

import * as React from 'react';
import { AuthConsumer } from "./AuthProvider";

export const PrivateRoute = ({ children }) => (
  <AuthConsumer>
    {({ isAuthenticated, signInRedirect }) => {
      console.log(isAuthenticated)
      console.log(sessionStorage.isAuthenticated)
      
      if(isAuthenticated) {
        return children
      }
      else if(sessionStorage.getItem('isAuthenticated') === 'true') {
        return children
      }
      else {
        signInRedirect();
        return <span>PrivateRoute loading</span>;
      }
    }}
  </AuthConsumer>
);

5. 修正App.js中的路由语法错误

移除多余的闭合括号:

<Route exact path="/" element={<PrivateRoute><HomePage/></PrivateRoute>} />
<Route exact path="/profile" element={<PrivateRoute><Profile/></PrivateRoute>} />

关键说明

  • 回调完成后必须手动触发路由跳转,否则会持续停留在回调页面导致循环调用。
  • 异步操作中要确保获取最新用户信息后再更新状态和sessionStorage。
  • useEffect依赖需谨慎设置,避免不必要的重渲染循环。

内容的提问来源于stack exchange,提问作者Prakash Raj

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 05:25:40