If you want to access the Microsoft Graph API, an OAuth2 with Authorization Code grant type is required. SAP Cloud Integration supports to fetch access tokens of an OAuth2 Authorization Code credential in a script step of an integration flow. With this new feature, you can call the Microsoft Graph API in an integration flow with the HTTP receiver adapter. Microsoft Graph offers many queries; in this blog we show how you can read and create pages in Microsoft One Note by using an integration flow as an example for calling the Microsoft Graph API from CPI.
This new feature is available from Cloud Integration NEO release 3.33 onwards or from Cloud Integration CLoud Foundry release 6.9 onwards.
To connect to the Microsoft Graph API, you need an organizational directory/tenant in Microsoft Azure Active Directory and a user in this directory which has sufficient roles assigned to execute the API queries you want to use. In our example, we use queries to Microsoft One Note; therefore, the user must have a subscription to Microsoft One Note (for example "Microsoft 365 Business Basic" license). For the configuration tasks in the Azure Active Directory, you also need an administrator user with the “Application administrator” and the “Application developer” role.
In addition, you need a SAP Cloud Integration tenant on which you have a user with the “Integration Developer” role and a user which can call the integration flow (user with role ESBMessaging.send).
To set up the OAuth2 connection towards Microsoft Graph with SAP Cloud Integration, execute the following steps:
First, you determine which requests you want to execute. You find an overview of the possible requests in the documentation of the Microsoft Graph API: https://docs.microsoft.com/en-us/graph/api/overview. Another good source to get an overview is the Microsoft Graph Explorer.
We want to execute three requests, which are related with the One Note application of a user:
We have to find out which scopes/permissions are necessary to execute these requests.
In the graph explorer, you can select the query you want to execute. Then, you can see the permissions (=scopes) you need (see Tab "Modify permissions"). Below, you see two screenshots of the Microsoft Graph Explorer tool which show the request URLs and the permissions for executing the first two requests (list One Note sections, list pages of a certain One Note section):
In the documentation of the Microsoft Graph API, we find the request for the One Note Page Creation:
We can see from this information that all tree requests can be executed with the permission Notes.ReadWrite.All. We now have to add the prefix https://graph.microsoft.com/ to the permission name to get the scope which we need for the creation of the Cloud Integration Credential: https://graph.microsoft.com/Notes.ReadWrite.all
Permission: Notes.ReadWrite.All -> Scope: https://graph.microsoft.com/Notes.ReadWrite.All
For more information why this prefix has to be added, have a look at https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-permissions-and-consent. There you find the following information: "Each permission is indicated by appending the permission value to the resource's identifier (the Application ID URI)."
Steps 2 - 4 are already outlined in detail in the blog https://blogs.sap.com/2020/08/20/cloud-intgration-connect-to-microsoft-365-mail-with-oauth2/. Please follow the description in that blog for these steps. See chapters
For step 4 (Create OAuth2 Authorization Code Credential in your SAP Cloud Integration tenant) you have to use different scopes than specified in that blog; you have to use the scope https://graph.microsoft.com/Notes.ReadWrite.all. (You can also specify several scopes in the "scope" field by separating them with the space character.)
The following screenshot shows the "OAuth2 Authorization Code" creation dialog with the correct scope.
We create an integration flow with a script step which uses the created OAuth2 Authorization Code credential and a HTTP receiver adapter which calls the Microsoft Graph API. The following screenshot shows the integration flow we want to build:
In the following sub-chapters, you learn how the steps and adapters of the integration flow are configured.
The HTTPS sender adapter is configured as follows:
The groovy script contains the new functionality to fetch an access token from the OAuth2 Authorization Code credential:
import com.sap.gateway.ip.core.customdev.util.Message; import com.sap.it.api.securestore.SecureStoreService; import com.sap.it.api.securestore.AccessTokenAndUser; import com.sap.it.api.securestore.exception.SecureStoreException; import com.sap.it.api.ITApiFactory; def Message processData(Message message) { SecureStoreService secureStoreService = ITApiFactory.getService(SecureStoreService.class, null); AccessTokenAndUser accessTokenAndUser = secureStoreService.getAccesTokenForOauth2AuthorizationCodeCredential("MICROSOFT_ONE_NOTE_ACCESS"); String token = accessTokenAndUser.getAccessToken(); message.setHeader("Authorization", "Bearer "+token); return message; }
By calling the method
getAccesTokenForOauth2AuthorizationCodeCredential("MICROSOFT_ONE_NOTE_ACCESS"),
you fetch the access token of the OAuth2 Authorization Code credential with name "MICROSOFT_ONE_NOTE_ACCESS".
Also the line
message.setHeader("Authorization", "Bearer "+token);
is important. With this line you fill the "Authorization" header with the access token. This header is sent by by the HTTP receiver adapter as HTTP header to the Microsoft Graph endpoint to establish the authentication.
To be able to make different kind of requests to Microsoft Graph, we parameterize the "Address" and "Method" field in the HTTP receiver adapter as follows:
The address is read from the header with the name "address" and the method is read from the header with the name "method".
Also, we set the "Authentication" to "None" because we've already set the "Authorization" header in the script step.
We call the HTTP receiver adapter in a request reply step because we want to clean-up headers after the call to Microsoft Graph. Therefore, we add the "Remove Headers" Content Modifier step after the "Request-Reply" step. Most importantly, we remove the "Authorization" header which is set by the script step and which contains the token. If we do not remove this header, the token will be part of the response to the sender and the sender could access this security relevant information. We also remove the other two headers "address" and "method" as shown in the following screenshot:
We want to use the headers "address", "method", and "Content-Type" to configure the HTTP Receiver adapter. The values for these headers shall be sent from the sender client. In the "Runtime Configuration" of the integration flow, you specify these headers in the "Allowed Headers" field. To get to the integration flow properties view, click on an empty space in the integration flow modeling area:
To be able to call the integration flow endpoint, you need a user with the ESBMessaging.send role assigned. We assume here that you are familiar with setting up the basic authentication in your http client (for example Postman); we do not explain the authentication part of the http client.
After you've deployed the integration flow, you find the integration flow endpoint in the Operations View in the "Manage Integration Content" tile:
You need this endpoint URL in the following client configurations.
In the first chapter, we've already found out that we have to execute the following call towards Microsoft Graph to list the one note sections.
GET https://graph.microsoft.com/v1.0/me/onenote/sections
Therefore, we call the integration flow endpoint with the "method" header value "GET" and the "address" header value "https://graph.microsoft.com/v1.0/me/onenote/sections". In the following block, you see the configuration details you have to enter in your http client:
GET https://<host from the iflow endpoint>/http/call_microsoft_graph_api Headers: Content-Type: text/plain address: https://graph.microsoft.com/v1.0/me/onenote/sections method: GET
After you've executed the call, you will get a response body similar to the following (if you have at least one section in One Note):
{ "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#users('a92e5487-af32-4283-93eb-dd47253c7856')/onenote/sections", "value": [ { "id": "1-ab9ffe21-abc1-56a9-1721-3b39f07a8f36", "self": "https://graph.microsoft.com/v1.0/users/a92e5487-af32-4283-93eb-dd47253c7856/onenote/sections/1-ab9ffe21-abc1-56a9-1721-3b39f07a8f36", "createdDateTime": "2020-11-19T14:16:31Z", "displayName": "Test Section", "lastModifiedDateTime": "2020-11-20T15:01:45Z", "isDefault": false, "pagesUrl": "https://graph.microsoft.com/v1.0/users/a92e5487-af32-4283-93eb-dd47253c7856/onenote/sections/1-ab9ffe21-abc1-56a9-1721-3b39f07a8f36/pages", "createdBy": { "user": { "id": "a92e5487-af32-4283-93eb-dd47253c7856", "displayName": "Test User" } }, "lastModifiedBy": { "user": { "id": "a92e5487-af32-4283-93eb-dd47253c7856", "displayName": "Test User" } }, "links": { "oneNoteClientUrl": { "href": "onenote:https://aintpomail-my.sharepoint.com/personal/test_user_test_com/Documents/Notebooks/My%20Notebook%20@%20Work/Research%20notes.one" }, "oneNoteWebUrl": { "href": "https://aintpomail-my.sharepoint.com/personal/test_user_test_com/Documents/Notebooks/My%20Notebook%20@%20Work?wd=target%28%2F%2FResearch%20notes.one%7C%2F%29" } }, "parentNotebook@odata.context": "https://graph.microsoft.com/v1.0/$metadata#users('a92e5487-af32-4283-93eb-dd47253c7856')/onenote/sections('1-ab9ffe21-abc1-56a9-1721-3b39f07a8f36')/parentNotebook/$entity", "parentNotebook": { "id": "1-72f96a39-135f-43a7-b907-3c29b90e6c63", "displayName": "My Notebook @ Work", "self": "https://graph.microsoft.com/v1.0/users/a92e5487-af32-4283-93eb-dd47253c7856/onenote/notebooks/1-72f96a39-135f-43a7-b907-3c29b90e6c63" }, "parentSectionGroup@odata.context": "https://graph.microsoft.com/v1.0/$metadata#users('a92e5487-af32-4283-93eb-dd47253c7856')/onenote/sections('1-ab9ffe21-abc1-56a9-1721-3b39f07a8f36')/parentSectionGroup/$entity", "parentSectionGroup": null } ] }
Remember the section ID "1-ab9ffe21-abc1-56a9-1721-3b39f07a8f36"; we need it for the next call.
To create a one Note Page in the section "1-ab9ffe21-abc1-56a9-1721-3b39f07a8f36", we have to make the following request to Microsoft Graph (here we use an example body, but you can adapt this body to your needs; for more information see the documentation of the Microsoft Graph API😞
POST https://graph.microsoft.com/v1.0/me/onenote/sections/1-ab9ffe21-abc1-56a9-1721-3b39f07a8f36/pages Headers: Content-Type: multipart/form-data; boundary=MyPartBoundary198374 Body: --MyPartBoundary198374 Content-Disposition:form-data; name="Presentation" Content-Type:text/html <!DOCTYPE html> <html> <head> <title>Note Page Demo</title> <meta name="created" content="2020-11-21T09:00:00-08:00" /> </head> <body> <p>Test creating Note Page via API</p> </html> --MyPartBoundary198374--
Hence, we use the following request towards the integration flow endpoint:
POST https://<host from the iflow endpoint>/http/call_microsoft_graph_api Headers: Content-Type: multipart/form-data; boundary=MyPartBoundary198374 address: https://graph.microsoft.com/v1.0/me/onenote/sections/1-ab9ffe21-abc1-56a9-1721-3b39f07a8f36/pages method: POST Body: --MyPartBoundary198374 Content-Disposition:form-data; name="Presentation" Content-Type:text/html <!DOCTYPE html> <html> <head> <title>Note Page Demo</title> <meta name="created" content="2020-11-21T09:00:00-08:00" /> </head> <body> <p>Test creating Note Page via API</p> </html> --MyPartBoundary198374--
If the creation is successful, you'll get as HTTP return status code 201.
Now you can check with the following call to the integration flow endpoint whether the page created can be queried.
GET https://<host from the iflow endpoint>/http/call_microsoft_graph_api Headers: Content-Type: text/plain address: https://graph.microsoft.com/v1.0/me/onenote/sections/1-ab9ffe21-abc1-56a9-1721-3b39f07a8f36/pages method: GET
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
User | Count |
---|---|
22 | |
18 | |
11 | |
10 | |
9 | |
9 | |
7 | |
7 | |
7 | |
5 |