So MS disabled IMAP for basic auth as we all know.
I am trying to figure out how to get the OAUTH 2.0 working using ruby (not ruby on rails). I have Azure APP and everything needed (I think), but I can not find any code related to ruby and getting the access token.
First step is completed, but next step is to get the access token. https://learn.microsoft.com/en-us/exchange/client-developer/legacy-protocols/how-to-authenticate-an-imap-pop-smtp-application-by-using-oauth
I need to read different Outlook mailboxes.
Could someone please explain how to do this?
SOLUTION for me!
Steps I took.
Device Flow
" requests.Device Authorization Request
(you need a scope and client_id for this) I used https://outlook.office.com/IMAP.AccessAsUser.All
scope.Device Access Token Request
and use the "device_code" from the last request and put that under code
, under body.access_token
Connect using ruby
require 'gmail_xoauth' # MUST HAVE! otherwise XOAUTH2 auth wont work
require 'net/imap'
imap = Net::IMAP.new(HOST, PORT, true)
access_token = "XXXXX"
user_name = "email@outlook.com"
p imap.authenticate('XOAUTH2',"#{user_name}", "#{access_token}")
# example
imap.list('','*').each do |folders|
p folders
end
XOAUTH2 Returns
#<struct Net::IMAP::TaggedResponse tag="RUBY0001", name="OK", data=#<struct Net::IMAP::ResponseText code=nil, text="AUTHENTICATE completed.">, raw_data="RUBY0001 OK AUTHENTICATE completed.\r\n
Just to specify
HOST = 'outlook.office365.com'
PORT = 993
UPDATE 25.01.2023
class Oauth2
require 'selenium-webdriver'
require 'webdrivers'
require 'net/http'
# Use: Oauth2.new.get_access_code
# Grants access to Office 365 emails.
def get_access_code
p "### Access Request Started #{Time.now} ###"
begin
codes = device_auth_request
authorize_device_code(codes[:user_code])
access_code = device_access_token(codes[:device_code])
access_code
rescue => e
p e
p "Something went wrong with authorizing"
end
end
def device_auth_request # Returns user_code and device_code
url = URI('https://login.microsoftonline.com/organizations/oauth2/v2.0/devicecode')
https = Net::HTTP.new(url.host, url.port)
https.use_ssl = true
request = Net::HTTP::Post.new(url)
request.body = "client_id=YOUR_CLIENT_ID&scope=%09https%3A%2F%2Foutlook.office.com%2FIMAP.AccessAsUser.All"
response = https.request(request)
{
user_code: JSON.parse(response.read_body)["user_code"],
device_code: JSON.parse(response.read_body)["device_code"]
}
end
def device_access_token(device_code)
url = URI('https://login.microsoftonline.com/organizations/oauth2/v2.0/token')
https = Net::HTTP.new(url.host, url.port)
https.use_ssl = true
request = Net::HTTP::Post.new(url)
request.body = "grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Adevice_code&code=#{device_code}&client_id=YOUR_CLIENT_ID"
response = https.request(request)
JSON.parse(response.read_body)["access_token"]
end
def authorize_device_code(device_code)
# SELENIUM SETUP
driver = setup_selenium
driver.get "https://microsoft.com/devicelogin"
sleep(4)
# ------------------------------------------
# Give Access
element = driver.find_element(:class, "form-control")
element.send_keys(device_code)
sleep(2)
element = driver.find_element(:id, "idSIButton9")
element.submit
sleep(2)
element = driver.find_element(:id, "i0116")
element.send_keys("YOUR OUTLOOK ACCOUNT EMAIL")
sleep(2)
element = driver.find_element(:class, "button_primary")
element.click
sleep(2)
element = driver.find_element(:id, "i0118")
element.send_keys("YOUR OUTLOOK PASSWORD")
element = driver.find_element(:class, "button_primary")
element.click
sleep(2)
element = driver.find_element(:class, "button_primary")
element.click
sleep(2)
# ------------------------------------------
driver.quit
end
def setup_selenium
require 'selenium-webdriver'
# set up Selenium
options = Selenium::WebDriver::Chrome::Options.new(
prefs: {
download: {
prompt_for_download: false
},
plugins: {
'always_open_pdf_externally' => true
}
}
)
options.add_argument('--headless')
options.add_argument('--no-sandbox')
# options.add_argument('-incognito')
options.add_argument('disable-popup-blocking')
Selenium::WebDriver.for :chrome, options: options
end
end