如何在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
相关产品推荐
相关产品推荐

