Sử dụng Outlook Mail API với Ruby on Rails

Điều kiện tiến hành :

  • Tài khoản Microsoft dùng để tạo App.
  • Ruby on Rails với gem oauth2faraday.

App đóng vai trò nhận Auth code, tokens giữa Client và Office 365.
Để hiểu thêm bạn cần tham khảo OAuth2.
Flow : IC740856.jpeg

Để tạo App, bạn có thể tạo qua :

  • Azure Active Directory ( Yêu cầu tài khoản Azure ) - mất phí nhưng sẽ có nhiều tùy chọn.
  • Tạo App miễn phí thông qua Application Registration Portal với những tùy chọn cơ bản.

Nếu mới bắt đầu làm quen thì nên sử dụng Application Registration Portal. Trong bài viết này mình sẽ dùng Application Registration Portal và sẽ hướng dẫn dùng Azure Active Directory trong bài viết khác.

  • Tại Application Registration Portal, bạn tạo một app mới, click vào Generate New Password để tạo Application Secrets, copy lại password vừa tạo cùng Application ID vào đâu đó để sử dụng sau này.
  • Tại Platforms bạn chọn là Web, ở mục Redirect URIs, bạn điền URI chỉ định nơi sẽ nhận Auth code trả về từ Office 365.

Đến đây cơ bản là xong phần App, thứ bạn cần quan tâm giờ là Application SecretsApplication ID vừa tạo ta.
Tạo một class Auth với nội dung như sau

module OutlookMail
  class Auth
    CLIENT_ID = # Application ID
    CLIENT_SECRET = # Application Secrets
    SITE = "https://login.microsoftonline.com".freeze
    AUTHORIZE_URL = "/common/oauth2/v2.0/authorize".freeze
    TOKEN_URL = "/common/oauth2/v2.0/token".freeze
    SCOPES = ["openid",
      "https://outlook.office.com/Mail.ReadWrite",
      "https://outlook.office.com/Mail.Send",
      "offline_access"]
    REFRESH_GRANT_TYPE = "refresh_token".freeze

    class << self
      def get_login_url redirect_uri
        auth_client.auth_code.authorize_url redirect_uri: redirect_uri, scope: SCOPES.join(" ")
      end

      def get_token_from_code auth_code, redirect_uri
        auth_client.auth_code.get_token auth_code, redirect_uri: redirect_uri,
          scope: SCOPES.join(" ")
      end

      def reset_token refresh_token
        conn = Faraday.new(url: SITE) do |faraday|
          faraday.response :logger
          faraday.adapter Faraday.default_adapter
        end
        response = conn.post do |request|
          request.url TOKEN_URL
          request.headers["Content-Type"] = "application/x-www-form-urlencoded"
          request.body = "grant_type=#{REFRESH_GRANT_TYPE}&
            refresh_token=#{refresh_token}&
            client_id=#{CLIENT_ID}&
            client_secret=#{CLIENT_SECRET}"
        end
        JSON.parse response.body
      end

      private
      def auth_client
        OAuth2::Client.new CLIENT_ID, CLIENT_SECRET, site: SITE,
          authorize_url: AUTHORIZE_URL, token_url: TOKEN_URL,
          raise_errors: false
      end
    end
  end
end
  • Hàm get_login_url sẽ tạo cho bạn một đường link để request Auth code từ Office 365 dựa trên Application IDApplication Secrets của bạn.
  • Hàm get_token_from_code sẽ hỗ trợ lấy Access TokenRefresh Token từ Auth code nhận được.
  • Hàm reset_token sẽ sử dụng Refresh Token giúp ta lấy được một cặp Access TokenRefresh Token mới khi Access Token hết hạn.

Về cơ bản Access Token sẽ dùng mỗi khi bạn gửi request và Refresh Token sẽ hỗ trợ khi Access Token hết hạn.
Để gửi request lên Office 365, ta sẽ sử dụng Faraday.
Khởi tạo :

def create_connect token
  connect = Faraday.new url: RESOURCE do |faraday|
  faraday.use CatchErrors
  faraday.adapter Faraday.default_adapter
  faraday.response :logger if LOG_REQUEST
  faraday.request :url_encoded if URL_ENCODE
  end
  connect.headers = {
  "Authorization" => "Bearer #{token}",
  "Accept" => "application/json"
  }
  connect
end

Gửi một request :

def send_request url, params: nil
  conn.get do |request|
    request.url url, params
  end
 end
  • url phụ thuộc vào đối tượng bạn muốn request, ví dụ để nhận danh sách tất cả email trong thư mục Inbox, ta sẽ có :
    url = "https://outlook.office.com/api/v1.0/me/folders/inbox/messages" Để biết thêm, hãy tham khảo Outlook Mail REST API reference.
  • params sử dụng khi bạn muốn gửi email hoặc tác vụ liên quan tới việc gửi dữ liệu lên Office 365.

Ví dụ một reponse nhận về

{
  "@odata.context": "https://outlook.office365.com/api/v1.0/$metadata#Me/Folders('Inbox')/Messages",
  "value": [
    {
      "@odata.id": "https://outlook.office365.com/api/v1.0/Users('[email protected]')/Messages('AQMkADQyMzRkYTE3LWI0M2MtNDc0My1iNDA1LWMzZDE3OTZmNGQxYwBGAAADgR2nxNsv-kiMfj8LSgE8AQcA1jE7wJ_C3EqpppAp3Qe15gAAAgEMAAAA1jE7wJ_C3EqpppAp3Qe15gAAAgVTAAAA')",
      "@odata.etag": "W/\"CQAAABYAAADWMTvAn4LcSqmmkCndB7XmAAAAAAbw\"",
      "Id": "AQMkADQyMzRkYTE3LWI0M2MtNDc0My1iNDA1LWMzZDE3OTZmNGQxYwBGAAADgR2nxNsv-kiMfj8LSgE8AQcA1jE7wJ_C3EqpppAp3Qe15gAAAgEMAAAA1jE7wJ_C3EqpppAp3Qe15gAAAgVTAAAA",
      "ChangeKey": "CQAAABYAAADWMTvAn4LcSqmmkCndB7XmAAAAAAbw",
      "Categories": [],
      "DateTimeCreated": "2014-10-20T00:50:44Z",
      "DateTimeLastModified": "2014-10-20T00:50:44Z",
      "HasAttachments": false,
      "Subject": "Event tomorrow - atrium closed",
      "Body": {
        "ContentType": "Text",
        "Content": "Remember that the atrium will be closed tomorrow for the event."
      },
      "BodyPreview": "Remember that the atrium will be closed tomorrow for the event.",
      "Importance": "Normal",
      "ParentFolderId": "AQMkADQyMzRkYTE3LWI0M2MtNDc0My1iNDA1LWMzZDE3OTZmNGQxYwAuAAADgR2nxNsv-kiMfj8LSgE8AQEAANYxO8CfgtxKqaaQKd0HteYAAAIBDAAAAA==",
      "Sender": {
        "EmailAddress": {
          "Address": "[email protected]",
          "Name": "Katie Jordan"
        }
      },
      "From": {
        "EmailAddress": {
          "Address": "[email protected]",
          "Name": "Katie Jordan"
        }
      },
      "ToRecipients": [
        {
          "EmailAddress": {
            "Address": "[email protected]",
            "Name": "Garth Fort"
          }
        }
      ],
      "CcRecipients": [],
      "BccRecipients": [],
      "ReplyTo": [],
      "ConversationId": "AAQkADQyMzRkYTE3LWI0M2MtNDc0My1iNDA1LWMzZDE3OTZmNGQxYwAQACORL5p-BgdIvS98rIc4ZYQ=",
      "IsDeliveryReceiptRequested": false,
      "IsReadReceiptRequested": false,
      "IsRead": false,
      "IsDraft": false,
      "DateTimeReceived": "2014-10-20T00:50:44Z",
      "DateTimeSent": "2014-10-20T00:50:39Z",
      "WebLink": "https://outlook.office365.com/owa/?ItemID=AQMkADQyMzRkYTE3LWI0M2MtNDc0My1iNDA1LWMzZDE3OTZmNGQxYwBGAAADgR2nxNsv%2FkiMfj8LSgE8AQcA1jE7wJ%2BC3EqpppAp3Qe15gAAAgEMAAAA1jE7wJ%2BC3EqpppAp3Qe15gAAAgVTAAAA&exvsurl=1&viewmodel=ReadMessageItem"
    },
    {
      "@odata.id": "https://outlook.office365.com/api/v1.0/Users('[email protected]')/Messages('AQMkADQyMzRkYTE3LWI0M2MtNDc0My1iNDA1LWMzZDE3OTZmNGQxYwBGAAADgR2nxNsv-kiMfj8LSgE8AQcA1jE7wJ_C3EqpppAp3Qe15gAAAgEMAAAA1jE7wJ_C3EqpppAp3Qe15gAAAgVSAAAA')",
      "@odata.etag": "W/\"CQAAABYAAADWMTvAn4LcSqmmkCndB7XmAAAAAAbv\"",
      "Id": "AQMkADQyMzRkYTE3LWI0M2MtNDc0My1iNDA1LWMzZDE3OTZmNGQxYwBGAAADgR2nxNsv-kiMfj8LSgE8AQcA1jE7wJ_C3EqpppAp3Qe15gAAAgEMAAAA1jE7wJ_C3EqpppAp3Qe15gAAAgVSAAAA",
      "ChangeKey": "CQAAABYAAADWMTvAn4LcSqmmkCndB7XmAAAAAAbv",
      "Categories": [],
      "DateTimeCreated": "2014-10-20T00:36:29Z",
      "DateTimeLastModified": "2014-10-20T00:36:29Z",
      "HasAttachments": true,
      "Subject": "Re: Meeting Notes",
      "Body": {
        "ContentType": "Text",
        "Content": "\n________________________________________\nFrom: Alex D\nSent: Sunday, October 19, 2014 5:32 PM\nTo: Katie Jordan\nSubject: Meeting Notes\n\nPlease send me the meeting notes ASAP\n"
      },
      "BodyPreview": "________________________________________\nFrom: Alex D\nSent: Sunday, October 19, 2014 5:32 PM\nTo: Katie Jordan\nSubject: Meeting Notes\n\nPlease send me the meeting notes ASAP",
      "Importance": "Normal",
      "ParentFolderId": "AQMkADQyMzRkYTE3LWI0M2MtNDc0My1iNDA1LWMzZDE3OTZmNGQxYwAuAAADgR2nxNsv-kiMfj8LSgE8AQEAANYxO8CfgtxKqaaQKd0HteYAAAIBDAAAAA==",
      "Sender": {
        "EmailAddress": {
          "Address": "[email protected]",
          "Name": "Katie Jordan"
        }
      },
      "From": {
        "EmailAddress": {
          "Address": "[email protected]",
          "Name": "Katie Jordan"
        }
      },
      "ToRecipients": [
        {
          "EmailAddress": {
            "Address": "[email protected]",
            "Name": "Alex D"
          }
        }
      ],
      "CcRecipients": [],
      "BccRecipients": [],
      "ReplyTo": [],
      "ConversationId": "AAQkADQyMzRkYTE3LWI0M2MtNDc0My1iNDA1LWMzZDE3OTZmNGQxYwAQAJ6mQ1AJ6CVGnVVfBZfx47U=",
      "IsDeliveryReceiptRequested": false,
      "IsReadReceiptRequested": false,
      "IsRead": false,
      "IsDraft": false,
      "DateTimeReceived": "2014-10-20T00:36:29Z",
      "DateTimeSent": "2014-10-20T00:36:25Z",
      "WebLink": "https://outlook.office365.com/owa/?ItemID=AQMkADQyMzRkYTE3LWI0M2MtNDc0My1iNDA1LWMzZDE3OTZmNGQxYwBGAAADgR2nxNsv%2FkiMfj8LSgE8AQcA1jE7wJ%2BC3EqpppAp3Qe15gAAAgEMAAAA1jE7wJ%2BC3EqpppAp3Qe15gAAAgVSAAAA&exvsurl=1&viewmodel=ReadMessageItem"
    }
  ]
}

Reponse chứa khá đầy đủ các thông tin về email cũng như Web link, hỗ trợ ta tạo Mail Client đầy đủ mạnh mẽ không thua kém Web Mail.
Đến đây chắc các bạn cũng hiểu cơ bản cơ chế hoạt động cũng như cách thức tương tác với Outlook Mail REST API, với Outlook contacts, Outlook calendar, Files ... cũng sẽ tương tự, bạn có thể tham khảo tài liệu ở Office 365 API reference.
Nguồn tham khảo: