Compare commits

...
3 changed files with 37 additions and 3 deletions
@@ -21,4 +21,11 @@ class Microsoft::CallbacksController < OauthCallbackController
def imap_login_identity
users_data['preferred_username'] || users_data['upn'] || super
end
# Prefer the actual `email` claim. Microsoft work/school accounts without a mailbox
# omit it, returning only `preferred_username`/`upn`; fall back to those so inbox
# creation does not fail on a null email.
def email_address
super || users_data['preferred_username'] || users_data['upn']
end
end
+10 -3
View File
@@ -41,7 +41,7 @@ class OauthCallbackController < ApplicationController
end
def find_channel_by_email
Channel::Email.find_by(email: users_data['email'], account: account)
Channel::Email.find_by(email: email_address, account: account)
end
def update_channel(channel_email)
@@ -64,6 +64,13 @@ class OauthCallbackController < ApplicationController
users_data['email']
end
# Email address stored on the Channel::Email record and used to match an existing
# channel. Defaults to the id_token's `email` claim; providers override when that
# claim can be absent and a different one carries the address.
def email_address
users_data['email']
end
def provider_name
raise NotImplementedError
end
@@ -74,7 +81,7 @@ class OauthCallbackController < ApplicationController
def create_channel_with_inbox
ActiveRecord::Base.transaction do
channel_email = Channel::Email.create!(email: users_data['email'], account: account)
channel_email = Channel::Email.create!(email: email_address, account: account)
account.inboxes.create!(
account: account,
@@ -119,7 +126,7 @@ class OauthCallbackController < ApplicationController
# Fallback name, for when name field is missing from users_data
def fallback_name
users_data['email'].split('@').first.parameterize.titleize
email_address.to_s.split('@').first.parameterize.titleize
end
def oauth_code
@@ -53,6 +53,26 @@ RSpec.describe 'Microsoft::CallbacksController', type: :request do
expect(channel.email).to eq mailbox
end
it 'falls back to preferred_username for the channel email when the id_token has no email claim' do
upn = 'testaccount@primary-domain.example'
response_body = {
id_token: JWT.encode({ preferred_username: upn, name: 'test' }, nil, 'none'),
access_token: SecureRandom.hex(10), token_type: 'Bearer', refresh_token: SecureRandom.hex(10)
}
stub_request(:post, 'https://login.microsoftonline.com/common/oauth2/v2.0/token')
.with(body: { 'code' => code, 'grant_type' => 'authorization_code',
'redirect_uri' => "#{ENV.fetch('FRONTEND_URL', 'http://localhost:3000')}/microsoft/callback" })
.to_return(status: 200, body: response_body.to_json, headers: { 'Content-Type' => 'application/json' })
get microsoft_callback_url, params: { code: code, state: state }
expect(response).to redirect_to app_email_inbox_agents_url(account_id: account.id, inbox_id: account.inboxes.last.id)
expect(account.inboxes.count).to be 1
channel = account.inboxes.last.channel
expect(channel.email).to eq upn
expect(channel.imap_login).to eq upn
end
it 'creates updates inbox channel config if inbox exists and authentication is successful' do
inbox = create(:channel_email, account: account, email: email)&.inbox
expect(inbox.channel.provider_config).to eq({})