使用expo-auth-session登出后如何强制重新输入OAuth凭证?
解决方法
问题根源在于:虽然你成功撤销了OAuth Token,但Rails服务端的用户会话(由Devise或其他认证系统维护)仍保存在WebBrowser的Cookie中,导致授权页面自动跳过登录步骤。以下是具体解决步骤:
1. 让Doorkeeper响应prompt=login参数,强制重新认证
你已经在客户端设置了prompt: AuthSession.Prompt.Login,但Doorkeeper默认不会处理该参数,需要自定义授权控制器实现强制登出:
步骤1:创建自定义授权控制器
在app/controllers/doorkeeper/authorizations_controller.rb中添加代码:
module Doorkeeper class AuthorizationsController < Doorkeeper::ApplicationController def new # 当请求包含prompt=login时,清除当前用户会话 if params[:prompt] == 'login' && respond_to?(:sign_out) sign_out current_resource_owner end super end end end
步骤2:配置Doorkeeper使用自定义控制器
修改config/initializers/doorkeeper.rb,添加控制器配置:
Doorkeeper.configure do # 保留原有配置,新增以下内容 controllers do authorizations 'doorkeeper/authorizations' end end
2. 确保客户端登录请求的prompt参数正确传递
检查Rails日志中的授权请求,确认prompt=login参数已包含在请求中。如果未正常传递,可直接使用字符串参数:
const [request, response, promptForLogin] = AuthSession.useAuthRequest( { clientId: "J_OgOWUEjFNOwUrKtq58QEqQ0cPEQv4pqs1kuNtMOyk", redirectUri, scopes: ["public"], prompt: "login", // 直接使用字符串确保参数正确传递 usePKCE: true, }, { revocationEndpoint: "http://localhost:3000/oauth/revoke", authorizationEndpoint: "http://localhost:3000/oauth/authorize", tokenEndpoint: "http://localhost:3000/oauth/token", } );
3. 可选:登出时主动清除WebBrowser中的服务端会话
如果上述方法无效,可通过WebBrowser打开Rails的登出页面,销毁会话Cookie:
步骤1:配置Rails支持自动登出
修改config/routes.rb,添加GET路由:
devise_scope :user do get '/users/logout', to: 'devise/sessions#destroy', as: :logout end
创建app/views/devise/sessions/destroy.html.erb,自动提交DELETE请求销毁会话:
<!DOCTYPE html> <html> <head> <script> document.addEventListener('DOMContentLoaded', () => { const form = document.createElement('form'); form.method = 'POST'; form.action = '<%= destroy_user_session_path %>'; form.innerHTML = ` <input type="hidden" name="_method" value="DELETE"> <input type="hidden" name="authenticity_token" value="<%= form_authenticity_token %>"> `; document.body.appendChild(form); form.submit(); setTimeout(() => window.close(), 500); }); </script> </head> <body>正在登出...</body> </html>
步骤2:修改客户端登出方法
const handleLogout = async () => { if (token === "") return; // 撤销OAuth Token await AuthSession.revokeAsync( { clientId: "J_OgOWUEjFNOwUrKtq58QEqQ0cPEQv4pqs1kuNtMOyk", token }, { revocationEndpoint: "http://localhost:3000/oauth/revoke" } ); // 打开登出页面清除会话Cookie await AuthSession.startAsync({ authUrl: "http://localhost:3000/users/logout" }); setToken(""); };
内容的提问来源于stack exchange,提问作者João Fernandes
相关产品推荐
相关产品推荐

