Get to know Microsoft Office 365 APIs

Abstract

This article is about getting to know the Microsoft Office 365 APIs by using an ASP.NET Forms Application and VB.NET.

Objective

Retrieve E-Mails for the user accessing this 'Web App' by communicating with the Microsoft authentication service and the Office 365 REST APIs (w/o using Connected Services libraries).

Setup

Edit (2017-01-02): Version 1 login (login.windows.net) will redirect to Version 2 login (login.microsoftonline.com).
Login and Token processing should be made with V2, while V1 and V2 projects (depending on the scope) can be used for performing action, e.g. getting mail via the V1 (AAD) project is also working when login and receivcing tokens was made with V2.

The first step is to create a new project in Azure of the Office 365 hosted domain. A good reference on how to do this is found here.

The next steps describe how to authorize with Microsoft Online:

https://login.windows.net/common/oauth2/authorize?response_type=code&client_id=ae0020cb...&redirect_uri=https://consulity.de/content/Office365APIs.aspx&resource=https:%2f%2foutlook.office365.com%2f&state=5asdfadsffd60b-8457-4536-b20f-fcb658d1asdfasdfasd9458

Microsoft provides two different versions of projects: Azure Active Directory (AAD) projects and Microsoft Apps (v2.0 endpoint). The reference used to build the initial project follows AAD, below you can find the login buttons for Version 2 endpoint authentication. Please also confer the sources and links given at the end of the page for additional information concerning v2.0 apps.

https://login.microsoftonline.com/common/oauth2/v2.0/authorize?response_type=code&client_id=b99ac069...&redirect_uri= https://consulity.de/content/Office365APIs.aspx&scope=openid+offline_access+profile+https:%2f%2foutlook.office.com%2fmail.read&state=abc57446-73ed-4508-9e05-f3d6faa4939e&prompt=login

After a successful login and accepting the OAuth Scope, the login-page redirects to the given redirect_uri paramter that was passed in the login link and that is given in the Azure Project / Micrsofot App. Furthermore, a code and a state parameter are added to the URL (see ULR of your browser).

Note: The state parameter (arbitrary string that is hard to guess) should be generated and added to the link server side.

Please also note that in case the permission was not granted, an error is returned that has to be handeled (not implemented here).


Access Code

If the code parameter was retrieved (after successful login and trusting the app), is is displayed here. The code retrieved is called the Access Code:

results will be shown here...

With the access code retrieved after the successful login, it is possible to generate the Access Token and the Refresh Token.


Access Token / Refresh Token

In our example the retrieval of the access token and the refresh token is done with an xmlHttp request to the following URL (server side) passing the access code retrieved from the authorization step. Note: The real link is built server side and does not need a distinction between localhost and live version.

Please note: The response does not! deliver a refresh token ("refresh_token"), if the scope does not include "offline_access"!

Please note: While the version 1 posts a request to
https://login.windows.net/common/oauth2/token
version 2 communicates with https://login.microsoftonline.com/common/oauth2/v2.0/token

The base token URL is: POST https://login.windows.net/common/oauth2/token The body is form encoded: grant_type=authorization_code&code={code from the authorize request}&redirect_uri={reply url for your application}&client_id={your application’s client id in AAD}&client_secret={your application’s client secret}

For security reasons the response is truncated:

results will be shown here...

Get Mails

Now we want to retrieve the last two e-mails of the user accessing this service. In order to do that, we pass the Bearer token to the Outlook/Office 365 API.

results will be shown here...

Renew the Access Token

As an access token is only valid for a defined period of time, the access token can be refreshed. If the server persists the refresh token, it can receive new access tokens anytime unlesse the user revokes the given trus in the connected apps of his account profile.

https://login.microsoftonline.com/common/oauth2/token?grant_type=refresh_token&refresh_token=AAABAAAAiL9... &client_id=ae0020cb-2e0d-4e9e-ab8e-bceb50db5d30&client_secret=%2bPR...

Edit (2017-01-02): The response will not deliver a new refresh token for Microsoft Accounts, only Office 365 accounts will respond a new refresh token!

results will be shown here...

Conclusion

The flow explained on the Authorization Code Grant Flow works fine and it was possible to access the Office 365 Outlook Mail API.

Please note: Some APIs, e.g. SharePoint Online, are not yet (2016-07-04) supported in V2 projects.


Alternative

Instead of using HTTP-Requests, it is also possible to use a Connected Service reference to Office 365 via Visual Studio. In this case .NET libraries can be used to perform the steps shown above.

Please note: the Connected Service libraries are ideal to use with Microsoft APS.NET MVC pattern due async calls, e.g. the browser window asks for user credentials and the process continues after successful submission asynchronously. Async Procedures can be achieved with .NET 4.5 but MVC seems to be the better pattern.


Source Code

 

Imports System.Collections.Generic

Imports System.IO

Imports System.Net

Imports System.Web.Script.Serialization

 

Partial Class _Default

    Inherits consulity.Web.Page

 

    Private Const CLIENT_ID As String = "...removed..."

    Private Const CLIENT_ID_V2 As String = "...removed..."

    Private Const CLIENT_SECRET As String = "...removed..."

    Private Const CLIENT_SECRET_V2 As String = "...removed..."

    ' if offline_access is not given, no refresh_token will be responded

    Private Const SCOPE_V2 As String = "openid+profile+https:%2f%2foutlook.office.com%2fmail.read"

    ' Private Const SCOPE_V2 As String = "openid+offline_access+profile+https%3A%2F%2Foutlook.office.com%2Fmail.read" ' colon not encoded?

 

    Private Sub _Default_Load(sender As Object, e As EventArgs) Handles Me.Load

 

        Dim strAccessCode As String = Request.Params("code")

        If strAccessCode <> "" Then

            Me.divCode.InnerHtml = strAccessCode

        End If

 

    End Sub

 

    Private Function GetMailsV2(ByVal strToken As String) As String

 

        Dim URL As String = "https://outlook.office.com/api/v2.0/me/mailfolders/inbox/messages?$top=2"

 

        Dim request As WebRequest = WebRequest.Create(URL)

 

        request.Method = "GET"

        request.Headers.Add("client-request-id", HttpContext.Current.Server.UrlEncode(CLIENT_ID_V2))

        request.Headers.Add("authorization", "Bearer " & HttpContext.Current.Server.UrlEncode(strToken))

 

        Dim dataStream As Stream

        Dim response As WebResponse = request.GetResponse()

        dataStream = response.GetResponseStream()

 

        Dim reader As New StreamReader(dataStream)

 

        Dim responseFromServer As String = reader.ReadToEnd()

 

        reader.Close()

        dataStream.Close()

        response.Close()

 

        Return responseFromServer

 

    End Function

 

    Private Function GetMailsV1(ByVal strToken As String) As String

 

        Dim URL As String = "https://outlook.office.com/api/v1.0/me/folders/inbox/messages?$top=2"

 

        Dim request As WebRequest = WebRequest.Create(URL)

 

        request.Method = "GET"

        request.Headers.Add("client-request-id", HttpContext.Current.Server.UrlEncode(CLIENT_ID))

        request.Headers.Add("authorization", "Bearer " & HttpContext.Current.Server.UrlEncode(strToken))

 

        Dim dataStream As Stream

        Dim response As WebResponse = request.GetResponse()

        dataStream = response.GetResponseStream()

 

        Dim reader As New StreamReader(dataStream)

 

        Dim responseFromServer As String = reader.ReadToEnd()

 

        reader.Close()

        dataStream.Close()

        response.Close()

 

        Return responseFromServer

 

    End Function

 

    'https://msdn.microsoft.com/en-us/library/debx8sh9(v=vs.110).aspx

    Private Function GetPOSTResult(ByVal strURL As String, ByVal strPostData As String) As String

 

        Dim request As WebRequest = WebRequest.Create(strURL)

        ' Set the Method property of the request to POST.

        request.Method = "POST"

        ' Create POST data and convert it to a byte array.

        Dim postData As String = strPostData

        Dim byteArray As Byte() = Encoding.UTF8.GetBytes(postData)

        ' Set the ContentType property of the WebRequest.

        request.ContentType = "application/x-www-form-urlencoded"

        ' Set the ContentLength property of the WebRequest.

        request.ContentLength = byteArray.Length

        ' Get the request stream.

        Dim dataStream As Stream = request.GetRequestStream()

        ' Write the data to the request stream.

        dataStream.Write(byteArray, 0, byteArray.Length)

        ' Close the Stream object.

        dataStream.Close()

        ' Get the response.

        Dim response As WebResponse = request.GetResponse()

        ' Display the status.

        Console.WriteLine(CType(response, HttpWebResponse).StatusDescription)

        ' Get the stream containing content returned by the server.

        dataStream = response.GetResponseStream()

        ' Open the stream using a StreamReader for easy access.

        Dim reader As New StreamReader(dataStream)

        ' Read the content.

        Dim responseFromServer As String = reader.ReadToEnd()

        ' Display the content.

        Console.WriteLine(responseFromServer)

        ' Clean up the streams.

        reader.Close()

        dataStream.Close()

        response.Close()

 

        Return responseFromServer

 

    End Function

 

    Private Sub btnGetTokensV1_Click(sender As Object, e As EventArgs) Handles btnGetTokensV1.Click

 

        Dim strAccessCode As String = Request.Params("code")

        Const BASE_URI As String = "https://login.windows.net/common/oauth2/token"

        Const GRANT_TYPE As String = "grant_type=authorization_code"

 

        Dim url As String = HttpContext.Current.Request.Url.AbsoluteUri

        Dim url_plain = url.Substring(0, url.IndexOf("?"))

 

        Dim strCode As String = "&code=" & HttpContext.Current.Server.UrlEncode(strAccessCode)

        Dim strRedirect As String = "&redirect_uri=" & HttpContext.Current.Server.UrlEncode(url_plain)

 

        Dim strScope As String = "&scope=openid+offline_access+profile+https%3A%2F%2Foutlook.office.com%2Fmail.read"

        Dim strClientId As String = "&client_id=" & HttpContext.Current.Server.UrlEncode(CLIENT_ID)

        Dim strClientSecret As String = "&client_secret=" & HttpContext.Current.Server.UrlEncode(CLIENT_SECRET)

 

        Dim strPostData As String

        strPostData = GRANT_TYPE & strCode & strRedirect & strScope & strClientId & strClientSecret

        Dim strResult As String = GetPOSTResult(BASE_URI, strPostData)

        Me.divTokens.InnerHtml = Left(strResult, 200)

 

        ' parse the result and retrieve the bearer access token

        Dim ss As New JavaScriptSerializer

        Dim co As Dictionary(Of String, Object) = ss.DeserializeObject(strResult)

        Dim strAccessToken As String = co("access_token")

        Dim strRefreshToken As String = co("refresh_token")

 

        ' store the access token in session

        Session("AT") = strAccessToken

 

        ' store the access token in session - (...persist in DB)

        Session("RT") = strRefreshToken

 

    End Sub

 

    Private Sub btnGetTokensV2_Click(sender As Object, e As EventArgs) Handles btnGetTokensV2.Click

 

        Dim strAccessCode As String = Request.Params("code")

        Const BASE_URI As String = "https://login.microsoftonline.com/common/oauth2/v2.0/token"

        Const GRANT_TYPE As String = "grant_type=authorization_code"

 

        Dim url As String = HttpContext.Current.Request.Url.AbsoluteUri

        Dim url_plain = url.Substring(0, url.IndexOf("?"))

 

        Dim strCode As String = "&code=" & HttpContext.Current.Server.UrlEncode(strAccessCode)

        Dim strRedirect As String = "&redirect_uri=" & HttpContext.Current.Server.UrlEncode(url_plain)

        Dim strScope As String = "&scope=" & SCOPE_V2

        Dim strClientId As String = "&client_id=" & HttpContext.Current.Server.UrlEncode(CLIENT_ID_V2)

        Dim strClientSecret As String = "&client_secret=" & HttpContext.Current.Server.UrlEncode(CLIENT_SECRET_V2)

 

        Dim strPostData As String

        strPostData = GRANT_TYPE & strCode & strRedirect & strScope & strClientId & strClientSecret

 

        Dim strResult As String = GetPOSTResult(BASE_URI, strPostData)

        Me.divTokens.InnerHtml = Left(strResult, 200)

 

        ' parse the result and retrieve the bearer access token

        'Dim strAccessToken As String = JObject.Parse(strResult)("access_token")

        'Dim strRefreshToken As String = JObject.Parse(strResult)("refresh_token")

 

        Dim ss As New JavaScriptSerializer

        Dim co As Dictionary(Of String, Object) = ss.DeserializeObject(strResult)

        Dim strAccessToken As String = co("access_token")

        Dim strRefreshToken As String = co("refresh_token")

 

        ' store the access token in session

        Session("AT") = strAccessToken

 

        ' store the access token in session - (...persist in DB)

        Session("RT") = strRefreshToken

 

    End Sub

 

    Private Sub btnGetMailsV1_Click(sender As Object, e As EventArgs) Handles btnGetMailsV1.Click

 

        ' call the mail API

        Me.divRMails.InnerHtml = Strings.Replace(HttpContext.Current.Server.HtmlEncode(GetMailsV1(Session("AT"))), ",", ",<br>")

 

    End Sub

 

    Private Sub btnGetMailsV2_Click(sender As Object, e As EventArgs) Handles btnGetMailsV2.Click

 

        ' call the mail API

        Me.divRMails.InnerHtml = Strings.Replace(HttpContext.Current.Server.HtmlEncode(GetMailsV2(Session("AT"))), ",", ",<br>")

 

    End Sub

 

    Private Sub btnGetR2AV1_Click(sender As Object, e As EventArgs) Handles btnGetR2AV1.Click

 

        ' refresh access token

        Dim strAccessCode As String = Request.Params("code")

        Const BASE_URI As String = "https://login.microsoftonline.com/common/oauth2/token"

        Const GRANT_TYPE As String = "grant_type=refresh_token"

 

        Dim url As String = HttpContext.Current.Request.Url.AbsoluteUri

        Dim url_plain = url.Substring(0, url.IndexOf("?"))

 

        Dim strCode As String = "&refresh_token=" & Session("RT")

        Dim strClientId As String = "&client_id=" & HttpContext.Current.Server.UrlEncode(CLIENT_ID)

        Dim strClientSecret As String = "&client_secret=" & HttpContext.Current.Server.UrlEncode(CLIENT_SECRET)

 

        Dim strPostData As String

        strPostData = GRANT_TYPE & strCode & strClientId & strClientSecret

        Dim strResult As String = GetPOSTResult(BASE_URI, strPostData)

        Me.divRefresh.InnerHtml = Left(strResult, 200)

 

        ' parse the result and retrieve the bearer access token

        Dim ss As New JavaScriptSerializer

        Dim co As Dictionary(Of String, Object) = ss.DeserializeObject(strResult)

        Dim strAccessToken As String = co("access_token")

        Dim strRefreshToken As String = co("refresh_token")

 

        ' store the access token in session

        Session("AT") = strAccessToken

 

        ' store the access token in session - (...persist in DB)

        Session("RT") = strRefreshToken

 

    End Sub

 

    Private Sub btnGetR2AV2_Click(sender As Object, e As EventArgs) Handles btnGetR2AV2.Click

 

        ' refresh access token

        Const BASE_URI As String = "https://login.microsoftonline.com/common/oauth2/v2.0/token"

        Const GRANT_TYPE As String = "&grant_type=refresh_token"

 

        Dim url As String = HttpContext.Current.Request.Url.AbsoluteUri

        Dim url_plain = url.Substring(0, url.IndexOf("?"))

 

        Dim strClientId As String = "client_id=" & HttpContext.Current.Server.UrlEncode(CLIENT_ID_V2)

        Dim strCode As String = "&refresh_token=" & HttpContext.Current.Server.UrlEncode(Session("RT"))

 

        Dim strScope As String = "&scope=" & SCOPE_V2

        Dim strRedirect As String = "&redirect_uri=" & HttpContext.Current.Server.UrlEncode(url_plain)

 

        Dim strClientSecret As String = "&client_secret=" & HttpContext.Current.Server.UrlEncode(CLIENT_SECRET_V2)

 

        Dim strPostData As String

        strPostData = strClientId & strScope & strCode & strRedirect & GRANT_TYPE & strClientSecret

 

        Dim strResult As String = GetPOSTResult(BASE_URI, strPostData)

        Me.divRefresh.InnerHtml = Left(strResult, 200)

 

        ' parse the result and retrieve the bearer access token

        Dim ss As New JavaScriptSerializer

        Dim co As Dictionary(Of String, Object) = ss.DeserializeObject(strResult)

        Dim strAccessToken As String = co("access_token")

        ' the response will not deliver a refresh token for Microsoft Accounts, only for Office 365 accounts

        ' the "refresh_token" is deliverd

        Try

            Dim strRefreshToken As String = co("refresh_token")

            ' store the access token in session - (...persist in DB)

            Session("RT") = strRefreshToken

        Catch ex As Exception

            Me.divRefresh.InnerHtml &= "<hr>" & ex.Message

        End Try

 

        ' store the access token in session

        Session("AT") = strAccessToken

 

    End Sub

 

End Class

 

Sources

Version 1 Projects

  1. https://manage.windowsazure.com
  2. AAD Projects

Version 2 Projects

  1. https://apps.dev.microsoft.com/
  2. https://apps.dev.microsoft.com/#/application/b99ac069-d830-4e60-a89e-5b6388c6a8f5
  3. Overview
  4. Limitations
  5. App registration
  6. https://azure.microsoft.com/de-de/documentation/articles/active-directory-v2-devquickstarts-dotnet-web/
  7. https://azure.microsoft.com/en-us/documentation/articles/active-directory-v2-protocols-oauth-code/

Learning

  1. EdX
  2. https://github.com/GeekTrainer/Office365edX

Playground

  1. https://oauthplay.azurewebsites.net/
  2. https://graph.microsoft.io/

Links

  1. https://www.asp.net/web-forms/overview/performance-and-caching/using-asynchronous-methods-in-aspnet-45
  2. https://dev.office.com/
  3. https://dev.office.com/getting-started/office365apis
  4. https://graph.microsoft.io/de-de/code-samples-and-sdks
  5. https://msdn.microsoft.com/en-us/office/office365/api/api-catalog
  6. https://azure.microsoft.com/de-de/documentation/articles/active-directory-v2-protocols/#openid-connect-sign-in-flow
  7. https://github.com/AzureADQuickStarts/AppModelv2-WebApp-OpenIdConnect-DotNet
  8. https://blogs.msdn.microsoft.com/exchangedev/2014/03/25/using-oauth2-to-access-calendar-contact-and-mail-api-in-office-365-exchange-online/ !!!
  9. Write a ASP.NET MVC Web app to get Outlook mail
  10. https://msdn.microsoft.com/en-us/library/azure/dn645542.aspx

  11. https://apisandbox.msdn.microsoft.com/

  12. OAuth 2.0
  13. Managing User Authentication with OAuth
  14. MS account Developer Center - Enable your application to use Microsoft accounts
  15. Registrieren als App-Entwickler
  16. Example1
  17. Example 2
  18. OneDrive authentication and sign-in

Dieter Neumann