feat: Support Azure single-tenant application using the Graph API (#6728) (#6878)

This commit is contained in:
Tejaswini Chile
2023-06-28 08:13:08 +05:30
committed by GitHub
parent 4a770fdea7
commit d05c953eef
14 changed files with 451 additions and 13 deletions

View File

@@ -0,0 +1,62 @@
# Simple HTTPS API helper class for interacting with MS Graph.
# Uses the standard ruby HTTP library for interacting with the API.
require 'uri'
require 'net/http'
class MicrosoftGraphApi
API_VERSION = 'v1.0'.freeze
API_PORT = 443
API_URL = "https://graph.microsoft.com/#{API_VERSION}".freeze
def initialize(token)
@token = token
end
# Simple get request to the endpoint
#
# 'queries' are the get variables after the main url
# eg. foo/bar?query=myquery
def get_from_api(endpoint, headers = {}, query = {})
uri = endpoint_to_uri(endpoint, query)
https = setup_https(uri.host)
request = Net::HTTP::Get.new(uri.request_uri)
# Assign each header to the request
headers.each { |key, value| request[key.to_s] = value.to_s }
request['Authorization'] = "Bearer #{@token}"
https.request(request)
end
# Simple post request to the endpoint
def post_to_api(endpoint, headers = {}, body = '')
uri = endpoint_to_uri(endpoint)
https = setup_https(uri.host)
request = Net::HTTP::Post.new(uri.path)
# Assign each header to the request
headers.each { |key, value| request[key.to_s] = value.to_s }
request['Authorization'] = "Bearer #{@token}"
request.body = body
https.request(request)
end
private
def setup_https(host)
https = Net::HTTP.new(host, API_PORT)
https.use_ssl = true
https
end
def endpoint_to_uri(endpoint, query = {})
endpoint.delete_prefix('/')
uri = URI("#{API_URL}/#{endpoint}")
return uri if query.empty?
uri.query = URI.encode_www_form(query)
uri
end
end

View File

@@ -9,6 +9,18 @@ require 'omniauth-oauth2'
# Implements an OmniAuth strategy to get a Microsoft Graph
# compatible token from Azure AD
class MicrosoftGraphAuth < OmniAuth::Strategies::OAuth2
# Microsoft Azure Tenant
# For single tenant applications, meant to be used by
# organisations for their own apps, the 'common' endpoint is not allowed.
# If the environment variable 'AZURE_TENANT_ID' is set,
# this will return it's value, otherwise, it will default to 'common'.
#
# The tenant id for your Azure organization can be obtained by
# by accessing 'Tenant properties' from the Azure portal.
def self.azure_tenant_id
ENV.fetch('AZURE_TENANT_ID', 'common')
end
option :name, :microsoft_graph_auth
DEFAULT_SCOPE = 'offline_access https://outlook.office.com/IMAP.AccessAsUser.All https://outlook.office.com/SMTP.Send'
@@ -16,8 +28,8 @@ class MicrosoftGraphAuth < OmniAuth::Strategies::OAuth2
# Configure the Microsoft identity platform endpoints
option :client_options,
site: 'https://login.microsoftonline.com',
authorize_url: '/common/oauth2/v2.0/authorize',
token_url: '/common/oauth2/v2.0/token'
authorize_url: "/#{azure_tenant_id}/oauth2/v2.0/authorize",
token_url: "/#{azure_tenant_id}/oauth2/v2.0/token"
option :pcke, true
# Send the scope parameter during authorize

View File

@@ -0,0 +1,26 @@
# Recently (around Feb/Mar 2023), Microsoft has made sending
# email through SMTP with Outlook near impossible, at least
# for single tenant applications.
#
# As such, adding a delivery method to use the Microsoft Graph
# API allows for emails to be sent again.
require 'base64'
class MicrosoftGraphDeliveryMethod
def initialize(config)
@config = config
end
def deliver!(mail)
# Create a new API connection, and post the mail to the `me/sendMail` endpoint.
# https://learn.microsoft.com/en-us/graph/api/user-sendmail#example-4-send-a-new-message-using-mime-format
headers = {
'Content-Type' => 'text/plain'
}
body = Base64.encode64(mail.to_s)
graph = MicrosoftGraphApi.new(@config[:token])
graph.post_to_api('me/sendMail', headers, body)
end
end