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

Remix项目登录页异常:修改Loader后自动跳转至微软登录页

问题分析:登录页面自动跳转至微软登录界面的原因及修复

问题场景

原本登录页面包含「Sign in」按钮,点击后才会跳转至微软登录界面;修改Loader逻辑后,打开登录页直接自动跳转,完全看不到登录页面内容。

问题代码

export const loader = async ({ request }: LoaderArgs) => {
  const user = await authenticator.authenticate("microsoft", request, {
    failureRedirect: "/",
    successRedirect: "/stewardship",
  });

  // 检查访问令牌是否过期
  if (new Date((user as User).expiresAt) < new Date()) {
    const tenantId = process.env.TENANT_ID!;
    // 调用令牌端点获取新的访问令牌
    const data = new FormData();
    data.append("client_id", process.env.CLIENT_ID!);
    data.append("client_secret", process.env.CLIENT_SECRET!);
    data.append("scope", "openid email profile");
    data.append("refresh_token", (user as User).refreshToken);
    data.append("grant_type", "refresh_token");
    const response = await fetch(
      `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`,
      { method: "POST", body: data },
    );

    const responseData = await response.json();

    // 更新会话中的新访问令牌、刷新令牌和过期时间
    const session = await getSession(request.headers.get("Cookie"));
    session.set("user", {
      ...(user as User),
      accessToken: responseData.access_token,
      refreshToken: responseData.refresh_token,
      expiresAt: new Date(
        Date.now() + responseData.expires_in * 1000,
      ).toISOString(),
    });

    const cookie = await commitSession(session);
    // 返回用户信息并携带Set-Cookie头
    const headers = { "Set-Cookie": cookie };
    return json(user, { headers });
  }

  return user;
};

export default function Index() {
  return (
    <Form action="/auth/microsoft" method="post">
      <Grid
        container
        alignItems="center"
        justifyContent="center"
      >
        <Grid item xs="auto">
          <Card sx={{ maxWidth: 345 }}>
            <CardContent>
              <Box textAlign="center">
                <Box component="img" src={Logo} />
                <Box my={2}>
                  <Typography align="center">
                    This site uses your organizational username and password.
                    Sign in with your credentials.
                  </Typography>
                </Box>
                <Box>
                  <Button
                    variant="contained"
                    endIcon={<ArrowForward />}
                    sx={{ borderRadius: "30px" }}
                    type="submit"
                  >
                    Sign In
                  </Button>
                </Box>
              </Box>
            </CardContent>
          </Card>
        </Grid>
      </Grid>
    </Form>
  );
}

核心原因

问题出在登录页Loader中的authenticator.authenticate调用:

  • 该方法的默认行为是:如果检测到无有效会话,会自动触发跳转至微软登录页面,而非允许页面继续渲染。
  • 你将这个认证逻辑直接放在了登录页的Loader中,导致每次访问登录页时,只要没有有效会话,就会立即触发跳转,完全没有机会渲染登录页面的「Sign in」按钮。

修复方案

1. 拆分路由逻辑

将微软认证的核心逻辑移至专门的路由(如/auth/microsoft),登录页仅负责渲染登录按钮和检查已登录状态。

2. 修改登录页Loader

登录页Loader仅检查用户是否已登录,已登录则跳转至目标页面,未登录则返回空值让页面渲染:

export const loader = async ({ request }: LoaderArgs) => {
  // 仅检查会话状态,已登录则跳转,否则渲染登录页
  try {
    await authenticator.isAuthenticated(request, {
      successRedirect: "/stewardship",
    });
  } catch {
    // 无有效会话,返回空以渲染登录页面
    return null;
  }
};

3. 保留认证与Token刷新逻辑到专门路由

在/auth/microsoft路由中保留原有的认证和Token过期刷新逻辑:

export const loader = async ({ request }: LoaderArgs) => {
  const user = await authenticator.authenticate("microsoft", request, {
    failureRedirect: "/",
    successRedirect: "/stewardship",
  });

  // 检查并刷新过期的Access Token
  if (new Date((user as User).expiresAt) < new Date()) {
    const tenantId = process.env.TENANT_ID!;
    const data = new FormData();
    data.append("client_id", process.env.CLIENT_ID!);
    data.append("client_secret", process.env.CLIENT_SECRET!);
    data.append("scope", "openid email profile");
    data.append("refresh_token", (user as User).refreshToken);
    data.append("grant_type", "refresh_token");
    
    const response = await fetch(
      `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`,
      { method: "POST", body: data },
    );

    const responseData = await response.json();

    const session = await getSession(request.headers.get("Cookie"));
    session.set("user", {
      ...(user as User),
      accessToken: responseData.access_token,
      refreshToken: responseData.refresh_token,
      expiresAt: new Date(Date.now() + responseData.expires_in * 1000).toISOString(),
    });

    const cookie = await commitSession(session);
    return json(user, { headers: { "Set-Cookie": cookie } });
  }

  return user;
};

4. 登录页组件保持不变

原登录页的表单已经指向/auth/microsoft,点击「Sign in」按钮会提交至该路由,触发微软认证流程,符合预期逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 21:40:27