Product Information
Cloud Integration – Call Microsoft Graph API with OAuth 2.0 Authorization Code
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.
Prerequisites
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).
Steps
To set up the OAuth2 connection towards Microsoft Graph with SAP Cloud Integration, execute the following steps:
- Step 1: Determine Requests and Scopes
- Step 2: Determine Redirect URI
- Step 3: Create OAuth Client/App in Microsoft Azure Active Directory
- Step 4: Create OAuth2 Authorization Code Credential in your SAP Cloud Integration tenant
- Step 5: Create integration flow with script step and HTTP receiver adapter
- Step 6. Execute the integration flow
Step 1: Determine Requests and Necessary Scopes
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:
- list One Note sections
- list pages of a certain One Note section
- create a new One Note page
We have to find out which scopes/permissions are necessary to execute these requests.
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).”
Step 2 – 4: Redirect URI – OAuth Client – OAuth2 Authorization Code Credential
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
- Determine Redirect URI
- Create OAuth Client/App in Microsoft Azure Active Directory
- Create OAuth2 Authorization Code Credential in your SAP Cloud Integration tenant
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.)
Step 5: Create integration flow with script step and HTTP receiver adapter
In the following sub-chapters, you learn how the steps and adapters of the integration flow are configured.
Sender HTTPS Adapter
Groovy Script
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.
Receiver HTTP Adapter
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.
Remove Headers Content Modifier
Allow Headers “address”, “method”, and “Content-Type”
Step 6. Execute the integration flow
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.
You need this endpoint URL in the following client configurations.
List One Note Sections
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.
Create One Note Page
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.
List One Note Pages
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
Hello Franz, thanks for the nice example of using the new "access token in script" feature. This was a common scenario. I usually Alt-Tab between OneNote and CPI so it is an extra memorable example for me.
One question, are these access tokens cached for the time period of validity?
Regards,
Fatih
Hello Faith, the access tokens are cached for 50 minutes. If the access token has a life time of less then 50 minutes, and a request came after the life time of the access token but before the access token was removed from the cache, then still a new access token is fetched.
Regards Franz
Thanks for the clarification, Franz!
Regards,
Fatih
Hey Franz Forsthofer,
Thanks for this amazing article. I am trying to set destination to Microsoft graph API to use the API in SAP CLoud Platform Workflow. I am using OAuthJWTBearer Authentication type. However, It is not able to authorize me. Can you please help me with the same?
Thank you Franz!!!
Same method works with SAP DWH