基于Rails7、Devise和Doorkeeper的PKCE与OmniAuth回调重定向问题
解决Rails 7 + Devise + Doorkeeper + OmniAuth PKCE流程回调丢失应用信息的问题
核心思路是在PKCE授权流程启动时,将Doorkeeper客户端的关键信息存入Session,待OmniAuth回调完成后,从Session中取出信息完成正确的程序化重定向。
步骤1:修改Doorkeeper授权逻辑,存储客户端信息到Session
在Doorkeeper的resource_owner_authenticator中,检测当前请求是否为Doorkeeper的授权请求,若为PKCE流程,则将客户端的client_id和redirect_uri存入Session:
resource_owner_authenticator do # 捕获Doorkeeper客户端实例,存入Session供后续回调使用 if request.env['doorkeeper.client'] session[:doorkeeper_client] = { client_id: request.env['doorkeeper.client'].id, redirect_uri: request.env['doorkeeper.client'].redirect_uri, # 可选:存储PKCE相关参数,确保流程完整性 code_challenge: request.env['doorkeeper.params']['code_challenge'], code_challenge_method: request.env['doorkeeper.params']['code_challenge_method'], scope: request.env['doorkeeper.params']['scope'] } end current_user || warden.authenticate!(scope: :user) end
步骤2:在OmniAuth回调中读取Session,完成PKCE流程收尾
在OmniAuth成功回调方法中,取出Session中存储的客户端信息,手动生成Doorkeeper授权码并重定向回发起PKCE的应用:
def omniauth_successful_callback user = User.find_with_omniauth(request.env['omniauth.auth']) sign_in(user, scope: :user) # 确保用户已完成登录 # 取出并清除Session中的客户端信息(避免重复使用) unless (client_data = session.delete(:doorkeeper_client)).nil? client = Doorkeeper::Client.find(client_data[:client_id]) # 创建Doorkeeper授权记录,生成符合PKCE要求的授权码 authorization = Doorkeeper::Authorization.create( resource_owner_id: user.id, client_id: client.id, redirect_uri: client_data[:redirect_uri], scopes: client_data[:scope], code_challenge: client_data[:code_challenge], code_challenge_method: client_data[:code_challenge_method] ) # 重定向回发起PKCE的应用,携带授权码 redirect_to Doorkeeper::Authorization.redirect_uri(client_data[:redirect_uri], authorization) else # 非PKCE流程的常规登录重定向 redirect_to root_path end end
额外注意事项
- 确保Session跨OmniAuth请求可用:检查Devise的
session_store配置,避免Session在第三方OAuth跳转后丢失。 - 依赖Doorkeeper内置验证:Doorkeeper会自动校验
redirect_uri是否在客户端允许的列表中,无需额外手动验证。 - 添加错误处理:可针对客户端不存在、授权记录创建失败等情况添加异常捕获,避免500错误。
内容的提问来源于stack exchange,提问作者Scott White
相关产品推荐
相关产品推荐

