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

Rails 8中Devise+Omniauth实现微软SSO与独立离线授权的问题

Rails 8 中分离 Devise SSO 与独立微软 OAuth 授权的解决方案

核心思路

把Devise 集成的微软SSO和已登录用户的独立微软应用授权拆成两个完全独立的 Omniauth 策略实例——哪怕都是微软 OAuth,也要用不同的策略名称区分,彻底避免 Devise 初始化与单独 Omniauth 配置的冲突问题,同时实现权限隔离(SSO 只用基础权限,独立授权按需申请离线访问及高级 Graph 权限)。


步骤实现

1. 注册两个独立的微软应用

  • SSO专用应用:仅申请基础 OpenID 权限(openid, profile, email),不需要offline_access,用于用户快速登录。
  • 高级授权专用应用:申请业务所需的 Graph 权限 + offline_access,专门给已登录用户单独授权获取离线访问能力。

2. 配置双 Omniauth 策略

在config/initializers/omniauth.rb中分别配置两个策略,用自定义名称区分高级授权的策略:

Rails.application.config.middleware.use OmniAuth::Builder do
  # Devise SSO 用的微软策略
  provider :microsoft_graph, 
           ENV['MICROSOFT_SSO_CLIENT_ID'], 
           ENV['MICROSOFT_SSO_CLIENT_SECRET'],
           scope: 'openid profile email',
           redirect_uri: ENV['MICROSOFT_SSO_REDIRECT_URI']

  # 独立授权用的高级微软策略,自定义名称避免冲突
  provider :microsoft_advanced, 
           ENV['MICROSOFT_ADVANCED_CLIENT_ID'], 
           ENV['MICROSOFT_ADVANCED_CLIENT_SECRET'],
           scope: 'offline_access Calendars.ReadWrite Mail.Send', # 按需替换为你的权限
           redirect_uri: ENV['MICROSOFT_ADVANCED_REDIRECT_URI'],
           strategy_class: OmniAuth::Strategies::MicrosoftGraph # 复用原策略类
end

3. 调整 Devise 配置

在config/initializers/devise.rb中,只让 Devise 关联 SSO 专用的策略,不干涉独立授权流程:

config.omniauth :microsoft_graph, 
                ENV['MICROSOFT_SSO_CLIENT_ID'], 
                ENV['MICROSOFT_SSO_CLIENT_SECRET'],
                scope: 'openid profile email',
                redirect_uri: ENV['MICROSOFT_SSO_REDIRECT_URI']

4. 手动实现独立授权的路由与控制器

由于devise :omniauthable会拦截 Omniauth 默认路由,需要手动触发授权请求:

  • 路由配置(config/routes.rb):
get '/connect-microsoft', to: 'microsoft_advanced#initiate_auth'
get '/auth/microsoft_advanced/callback', to: 'microsoft_advanced#callback'
  • 控制器实现(app/controllers/microsoft_advanced_controller.rb):
class MicrosoftAdvancedController < ApplicationController
  before_action :authenticate_user! # 确保只有已登录用户能发起授权

  def initiate_auth
    # 手动构建策略实例,绕过 Devise 拦截
    auth_strategy = OmniAuth::Strategies::MicrosoftGraph.new(
      Rails.application.config.middleware,
      ENV['MICROSOFT_ADVANCED_CLIENT_ID'],
      ENV['MICROSOFT_ADVANCED_CLIENT_SECRET'],
      scope: 'offline_access Calendars.ReadWrite Mail.Send',
      redirect_uri: ENV['MICROSOFT_ADVANCED_REDIRECT_URI']
    )
    redirect_to auth_strategy.authorize_url
  end

  def callback
    auth_hash = request.env['omniauth.auth']
    # 将授权信息存储到用户关联的模型中(建议单独建表,不混在 User 表)
    current_user.create_or_update_user_microsoft_auth(
      refresh_token: auth_hash.credentials.refresh_token,
      access_token: auth_hash.credentials.token,
      expires_at: Time.at(auth_hash.credentials.expires_at)
    )
    redirect_to dashboard_path, notice: '微软账户授权成功'
  end
end

5. 数据存储优化

创建独立模型存储高级授权的 Token 信息,避免与 SSO 用户数据混淆:

# app/models/user.rb
has_one :user_microsoft_auth, dependent: :destroy

# app/models/user_microsoft_auth.rb
belongs_to :user
validates :user_id, uniqueness: true
attribute :refresh_token, :string
attribute :access_token, :string
attribute :expires_at, :datetime

关键注意事项

  • 两个微软应用的redirect_uri必须与配置完全一致(包括协议、域名、路径),否则微软会拒绝回调。
  • 独立授权流程必须通过before_action :authenticate_user!限制已登录用户访问,避免未注册用户发起授权。
  • 离线访问需妥善保存refresh_token,并在access_token过期时用其刷新,注意微软的 Refresh Token 轮换规则。
  • 严格遵循最小权限原则:SSO 只申请必要的登录权限,独立授权按需申请业务所需权限,降低用户授权门槛。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 20:55:12