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

如何在React Native Expo中实现IdentityServer4的完全登出

解决Expo React Native + IdentityServer4登出后无需重新输入密码的问题

你的问题出在仅撤销了Token,但未终止IdentityServer的用户会话,且WebBrowser中保留了SSO登录Cookie,导致下次登录时IdentityServer直接复用现有会话,跳过凭证输入步骤。正确的登出流程需要同时处理以下三个环节:

完整登出流程步骤

  • 撤销Access Token和Refresh Token(你已实现这部分)
  • 向IdentityServer的end_session_endpoint发起请求,终止服务器端会话并清除SSO Cookie
  • 清除Expo WebBrowser的本地缓存与Cookie,确保客户端无残留登录状态

修改后的代码实现

1. 添加Id Token状态存储

因为终止会话需要id_token_hint参数,先在组件中添加Id Token的状态:

const [idToken, setIdToken] = useState()

2. 在换取Token时保存Id Token

修改exchangeCodeAsync后的逻辑,保存返回的Id Token:

const getCodeAsync = async () => {
  const codeResp = await AuthSession.exchangeCodeAsync(
    {
      code: response.params.code,
      clientId: clientId,
      redirectUri: redirectUrl,
      grantType: 'authorization_code',
      scopes: scopes,
      extraParams: { code_verifier: request?.codeVerifier },
    },
    discovery
  );
  console.log('codeResp: ', codeResp);

  setAccessToken(codeResp?.accessToken)
  setRefreshToken(codeResp?.refreshToken)
  setIdToken(codeResp?.idToken) // 新增:保存Id Token
};

3. 更新登出函数

修改handleLogout,加入会话终止和缓存清除逻辑:

const handleLogout = async (aToken, rToken, idTokenVal) => {
  try {
    // 1. 撤销Access和Refresh Token
    const revokeRespAccessToken = await AuthSession.revokeAsync(
      {
        token: aToken,
        tokenTypeHint: 'access_token',
        clientId: clientId
      },
      discovery
    );

    const revokeRespRefreshToken = await AuthSession.revokeAsync(
      {
        token: rToken,
        tokenTypeHint: 'refresh_token',
        clientId: clientId
      },
      discovery
    );

    // 2. 终止IdentityServer端会话
    if (discovery?.endSessionEndpoint && idTokenVal) {
      const postLogoutRedirectUri = AuthSession.makeRedirectUri({
        native: 'exp://127.0.0.1:19000',
        preferLocalhost: true,
      });
      // 构造登出URL,包含必要参数
      const endSessionUrl = `${discovery.endSessionEndpoint}?id_token_hint=${idTokenVal}&post_logout_redirect_uri=${encodeURIComponent(postLogoutRedirectUri)}&client_id=${clientId}`;
      
      // 打开登出页面完成会话终止
      await WebBrowser.openAuthSessionAsync(endSessionUrl, postLogoutRedirectUri);
      // 关闭浏览器窗口
      await WebBrowser.dismissBrowser();
    }

    // 3. 清除本地所有认证状态
    setAccessToken(null);
    setRefreshToken(null);
    setIdToken(null);

    // 4. 清除WebBrowser缓存与Cookie
    await WebBrowser.clearCacheAsync();
    await WebBrowser.clearCookiesAsync();

  } catch (e) {
    console.log('Error: ', e);
  }
};

4. 更新登出按钮调用

传递Id Token给登出函数:

<Button type='submit' onPress={() => handleLogout(accessToken, refreshToken, idToken)} title='Logout'>
  <Text>Logout</Text>
</Button>

关键配置注意事项

在IdentityServer4的客户端配置中,必须将你的post_logout_redirect_uri(即exp://127.0.0.1:19000)添加到PostLogoutRedirectUris列表中,否则IdentityServer会拒绝登出跳转请求:

new Client
{
    ClientId = "cee58f8c-8187-4cfd-9964-1948cd621939",
    // 其他配置...
    PostLogoutRedirectUris = new List<string>
    {
        "exp://127.0.0.1:19000"
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 19:54:53