基于Ruby on Rails适配Office 365 OAuth2的邮件发送代码改造求助
Let's walk through converting your existing SMTP login setup to OAuth2 authentication for Office 365. Since your stack is older (Ruby 2.2.1, Rails 4.2.5), we'll use compatible gems and configurations.
Step 1: Add Required Gem
First, add the oauth2 gem (compatible with Ruby 2.2.x) to your Gemfile:
gem 'oauth2', '~> 1.4'
Run bundle install to install it.
Step 2: Register an Azure AD Application
You need to create an app in Azure Active Directory to get OAuth2 credentials:
- Go to the Azure Portal → Azure Active Directory → App Registrations → New Registration.
- Name your app, select the appropriate account type (e.g., "Accounts in any organizational directory"), and set a redirect URI (can be
http://localhost:3000/auth/callbackfor testing). - After registration, copy your Client ID (from Overview) and create a Client Secret (under Certificates & Secrets).
- Grant the app the
SMTP.Senddelegated permission (under API Permissions → Add a permission → Microsoft Graph → Delegated Permissions → Search for "SMTP.Send"). - Get a Refresh Token: Use a tool like Postman to authenticate and retrieve a refresh token. The scope should include
offline_access https://outlook.office.com/SMTP.Send.
Step 3: Configure ActionMailer with OAuth2
Replace your old SMTP settings with this OAuth2 configuration. Create a new initializer at config/initializers/mailer_oauth2.rb:
require 'oauth2' # Load credentials from environment variables (never hardcode these!) AZURE_OAUTH2 = { client_id: ENV['AZURE_CLIENT_ID'], client_secret: ENV['AZURE_CLIENT_SECRET'], refresh_token: ENV['AZURE_REFRESH_TOKEN'], sender_email: ENV['OFFICE365_SENDER_EMAIL'], token_url: 'https://login.microsoftonline.com/common/oauth2/v2.0/token' } # Initialize OAuth2 client oauth_client = OAuth2::Client.new( AZURE_OAUTH2[:client_id], AZURE_OAUTH2[:client_secret], site: 'https://login.microsoftonline.com', token_url: AZURE_OAUTH2[:token_url] ) # Helper to fetch a fresh access token def fetch_oauth2_token # Start with expired token to force refresh existing_token = OAuth2::AccessToken.from_hash(oauth_client, { refresh_token: AZURE_OAUTH2[:refresh_token], expires_at: Time.now.to_i - 1 }) existing_token.refresh!.token end # Configure ActionMailer ActionMailer::Base.smtp_settings = { address: 'smtp.office365.com', port: 587, domain: 'your-domain.com', # Replace with your actual domain authentication: :oauth2, user_name: AZURE_OAUTH2[:sender_email], enable_starttls_auto: true, oauth2_token: fetch_oauth2_token } # Auto-refresh token before each email delivery (handles token expiration) class OAuth2TokenRefresher def self.delivering_email(message) message.smtp_settings[:oauth2_token] = fetch_oauth2_token end end ActionMailer::Base.register_interceptor(OAuth2TokenRefresher)
Step 4: Update Environment Variables
Add these to your .env file (use dotenv-rails gem if you don't already have it):
AZURE_CLIENT_ID=your-client-id-here AZURE_CLIENT_SECRET=your-client-secret-here AZURE_REFRESH_TOKEN=your-refresh-token-here OFFICE365_SENDER_EMAIL=your-sender-email@domain.com
Step 5: Verify Your Mailer Method
Your existing general_mail method should work as-is, but double-check attachment handling to ensure binary reading is correct:
def general_mail(to, subject, content, attachmts = []) attachmts.each do |attachment| attachments[File.basename(attachment)] = File.read(attachment, mode: 'rb') end mail(to: to, subject: subject) do |format| format.text { render text: content } end end
Step 6: Test the Setup
Run this in your Rails console to test sending an email:
NotificationMailer.general_mail('test@example.com', 'OAuth2 Test', 'This email uses OAuth2!').deliver_now
Key Notes
- Refresh Token Expiry: Delegated refresh tokens can expire. If you run into issues, re-authenticate to get a new refresh token. For long-term use, consider using an application permission flow (requires admin consent) which doesn't need a user-specific refresh token.
- Security: Never commit credentials to version control. Always use environment variables or a secrets manager.
- Compatibility: The
oauth2 ~>1.4gem is confirmed to work with Ruby 2.2.1 and Rails 4.2.5.
内容的提问来源于stack exchange,提问作者Leon

