Technology Blogs by SAP
Learn how to extend and personalize SAP applications. Follow the SAP technology blog for insights into SAP BTP, ABAP, SAP Analytics Cloud, SAP HANA, and more.
cancel
Showing results for 
Search instead for 
Did you mean: 
showkath_naseem
Product and Topic Expert
Product and Topic Expert
Note :  With this blogpost approach, You no need to  Add Azure AD as Identity Provider in the 
SAP BTP, Cloud Foundry environment account i.e Integrate Microsoft Azure AD with SAP BTP, Cloud Foundry environment

Introduction

In my previous blogpost I have demonstrated how to integrated your BTP application & call Microsoft Graph with not on behalf of a user i.e previous blogpost approach you use in a Scenario user authorization not required to access resources i.e it is like a technical user/service user approach without the presence of a signed-in user

But if your application has requirement of read and write resources on behalf of a user i.e Get the Microsoft objects (Sharepoint files that are allowed by the user, User Calendar, Email, read/update the profile of user in an organization via consent) . In this scenario your BTP application must authorize (via consent) user permissions of requested resources on Azure. At this point, the user will be asked to enter their credentials to authenticate with Microsoft. The Microsoft identity platform v2.0 endpoint will ensure that the user has consented to the permissions.

Refer best practise as mentioned  in Authentication and authorization basics for Microsoft Graph  https://docs.microsoft.com/en-us/graph/auth/auth-concepts  in “delegated permissions” section

In this blog post  I’m going to focus on using the Authorization Code Grant workflow

Solution

 

  1. Azure Admin responsibilities


 

Ask your organization Azure Account Admin to Register your application  & Configure permissions for Microsoft Graph on your application  in Azure portal 

Every app that wants to accept both personal and work or school accounts must be registered through the App registrations experience in the Azure portal before it can sign these users in using OAuth 2.0 or OpenID Connect. The app registration process will collect and assign a few values to your app:

  • An Application IDthat uniquely identifies your app

  • Redirect URI(optional) that can be used to direct responses back to your app

  • A few other scenario-specific values.


For more details, learn how to register an app.

 

Generally every company would have one Azure Admin & Azure process defined to take care of App Registration in Azure AD, Configure Graph API Scopes for your application Usecase so follow your organization process

 

Note :  With this blogpost approach, You no need to  Add Azure AD as Identity Provider in the
SAP BTP, Cloud Foundry environment account mentioned in Tutorial  Integrate Microsoft Azure AD with SAP BTP, Cloud Foundry environment No need to Integrate Microsoft Azure AD with SAP BTP, Cloud Foundry environment

 

  1. SAP BTP Application Developer responsibilities



Step 1  :  Get Authorization_code

Step 2  :  Get Access Token

Step 3  :  Use the access token to invoke Graph API

Step 4  :  Refresh the access token

Once app registered on Azure Portal , The first step to getting an access token for many OpenID Connect (OIDC) and OAuth 2.0 flows is to redirect the user to the Microsoft identity platform /authorize endpoint

Step 1  : Get Authorization_code

The authorization code flow begins with the client directing the user to the /authorize. It is a simple GET request

https://login.microsoftonline.com/{tenant}/oauth2/v2.0/authorize


 

 

// Line breaks for legibility only

 

https://login.microsoftonline.com/{tenant}/oauth2/v2.0/authorize?

client_id=Ask Azure Admin. The Client ID (Application ID) of the application we created in the previous step

&response_type=code

&redirect_uri=http%3A%2F%2Flocalhost%2Fmyapp%2F

&response_mode=query

&scope=https%3A%2F%2Fgraph.microsoft.com%2Fmail.read%20api%3A%2F%2F

&state=12345

&code_challenge=YTFjNjI1OWYzMzA3MTI4ZDY2Njg5M2RkNmVjNDE5YmEyZGRhOGYyM2IzNjdmZWFhMTQ1ODg3NDcxY2Nl

&code_challenge_method=S256

 

Instead of hardcoding value, you can move this properties to BTP destination or your project configuration file

If you would like to follow programming approach instead of dependent on Destination you can set value for this property programmatically

 


String azureLoginUri = "";//Ask your Azure portal admin

String client_id = "";//Ask your Azure portal admin.

String scope = "";//Ask your Azure portal admin

 

//Java Snippet to Build Azure User Login Url

 

public String getAzureLoginUrl() {

 

//Here you can log  UserID of SAP IDP of user authenticated with SAP SSO to trace AuthAttempt

UUID state = UUID.randomUUID();

UUID nonce = UUID.randomUUID();

 

UriComponentsBuilder urlBuilder =     UriComponentsBuilder.fromHttpUrl(azureloginUri);

urlBuilder.queryParam("client_id", appId);

urlBuilder.queryParam("redirect_uri", redirectUrl);

urlBuilder.queryParam("response_type", "code");

urlBuilder.queryParam("response_mode", "query");

urlBuilder.queryParam("scope", scopes);

urlBuilder.queryParam("state", state);

urlBuilder.queryParam("nonce", nonce);

 

return urlBuilder.toUriString();

}

 

More details on Parameters

Where the {tenant} can take one of four different values:
























ENDPOINTS
Value Description
common Allows users with both personal Microsoft accounts and work/school accounts from Azure AD to sign into the application.
organizations Allows only users with work/school accounts from Azure AD to sign into the application.
consumers Allows only users with personal Microsoft accounts (MSA) to sign into the application.

 














































































REQUEST AN AUTHORIZATION CODE
Parameter Required/optional Description
tenant required The {tenant} value in the path of the request can be used to control who can sign into the application. The allowed values are common, organizations, consumers, and tenant identifiers. For more detail, see protocol basics. Critically, for guest scenarios where you sign a user from one tenant into another tenant, you must provide the tenant identifier to correctly sign them into the resource tenant. Here The directory tenant that you want to request permission from. This can be in GUID or friendly name format. If you don't know which tenant the user belongs to and you want to let them sign in with any tenant, use common.
client_id required The Application (client) ID that the Azure portal – App registrations experience assigned to your app.
response_type required

The addition of id_token indicates to the server that the application would like an ID token in the response from the /authorize endpoint.

Must include code for the authorization code flow. Can also include id_token or token if using the hybrid flow.
redirect_uri required The redirect_uri of your app, where authentication responses can be sent and received by your app. It must exactly match one of the redirect_uris you registered in the portal, except it must be url encoded. For native & mobile apps, you should use one of the recommended values - https://login.microsoftonline.com/common/oauth2/nativeclient (for apps using embedded browsers) or http://localhost (for apps that use system browsers).
scope required

A space-separated list of scopes that you want the user to consent to. For the /authorize leg of the request, this can cover multiple resources, allowing your app to get consent for multiple web APIs you want to call.

The value passed for the scope parameter in this request should be the resource identifier (application ID URI) of the resource you want, affixed with the .default suffix. For the Microsoft Graph example, the value is https://graph.microsoft.com/.default.

This value tells the Microsoft identity platform that of all the direct application permissions you have configured for your app, the endpoint should issue a token for the ones associated with the resource you want to use. To learn more about the /.default scope, see the consent documentation.

A web API can give users the power to opt in or opt out of specific functionality or data by exposing permissions, also known as scopes. For a calling app to acquire permission to a scope, the user must consent to the scope during a flow. The Microsoft identity platform asks the user for permission, and then records permissions in all access tokens that the web API receives. The web API validates the access tokens it receives on each call and performs authorization checks.

 

Example = scope: openid offline_access profile User.Read Calendars.Read.Shared Calendars.ReadWrite MailboxSettings.Read Calendars.Read Calendars.ReadWrite Mail.ReadWrite
response_mode recommended

Specifies the method that should be used to send the resulting token back to your app. Can be one of the following:

- query
- fragment
- form_post

Defaults to query for just an authorization code, but fragment if the request includes an id_token response_type. However, apps are recommended to use form_post, especially when using http://localhost as a redirect URI.
query provides the code as a query string parameter on your redirect URI. If you're requesting an ID token using the implicit flow, you can't use query as specified in the OpenID spec. If you're requesting just the code, you can use query, fragment, or form_post. form_post executes a POST containing the code to your redirect URI.
state recommended

A value included in the request that will also be returned in the token response. It can be a string of any content that you wish. A randomly generated unique value is typically used for preventing cross-site request forgery attacks. The value can also encode information about the user's state in the app before the authentication request occurred, such as the page or view they were on.

Example

state = 12345
Nonce

A value included in the request, generated by the app, that will be included in the resulting id_token as a claim. The app can then verify this value to mitigate token replay attacks. The value is typically a randomized, unique string that can be used to identify the origin of the request.

Example

Nonce = abcde
prompt optional

Indicates the type of user interaction that is required. The only valid values at this time are login, none, consent, and select_account.

- prompt=login will force the user to enter their credentials on that request, negating single-sign on.
- prompt=none is the opposite - it will ensure that the user isn't presented with any interactive prompt whatsoever. If the request can't be completed silently via single-sign on, the Microsoft identity platform will return an interaction_required error.
- prompt=consent will trigger the OAuth consent dialog after the user signs in, asking the user to grant permissions to the app.
- prompt=select_account will interrupt single sign-on providing account selection experience listing all the accounts either in session or any remembered account or an option to choose to use a different account altogether.
login_hint Optional You can use this parameter to pre-fill the username and email address field of the sign-in page for the user, if you know the username ahead of time. Often, apps use this parameter during reauthentication, after already extracting the login_hint optional claim from an earlier sign-in.
domain_hint optional If included, it will skip the email-based discovery process that user goes through on the sign-in page, leading to a slightly more streamlined user experience - for example, sending them to their federated identity provider. Often apps will use this parameter during re-authentication, by extracting the tid from a previous sign-in.
code_challenge recommended / required Used to secure authorization code grants via Proof Key for Code Exchange (PKCE). Required if code_challenge_method is included. For more information, see the PKCE RFC. This is now recommended for all application types - both public and confidential clients - and required by the Microsoft identity platform for single page apps using the authorization code flow.
code_challenge_method recommended / required

The method used to encode the code_verifier for the code_challenge parameter. This SHOULD be S256, but the spec allows the use of plain if for some reason the client cannot support SHA256.

If excluded, code_challenge is assumed to be plaintext if code_challenge is included. The Microsoft identity platform supports both plain and S256. For more information, see the PKCE RFC. This is required for single page apps using the authorization code flow.

At this point, the user will be asked to enter their credentials and complete the authentication. The Microsoft identity platform will also ensure that the user has consented to the permissions indicated in the scope query parameter. If the user has not consented to any of those permissions, it will ask the user to consent to the required permissions. Details of permissions, consent, and multi-tenant apps are provided here.

Once the user authenticates and grants consent, the Microsoft identity platform will return a response to your app at the indicated redirect_uri, using the method specified in the response_mode parameter. Azure AD will sign the user in and ensure their consent for the permissions your app requests. In the authorization code grant flow, after consent is obtained, Azure AD will return an authorization_code to your app that it can redeem at the Microsoft identity platform /token endpoint for an access token.

Successful response

A successful response using response_mode=query looks like:

HTTP

GET http://localhost?code=AwABAAAAvPM1KaPlrEqdFSBzjqfTGBCmLdgfSTLEMPGYuNHSUYBrq...&state=12345




















SUCCESSFUL RESPONSE
Parameter Description
code The authorization_code that the app requested. The app can use the authorization code to request an access token for the target resource. Authorization_codes are short lived, typically they expire after about 10 minutes.
state If a state parameter is included in the request, the same value should appear in the response. The app should verify that the state values in the request and response are identical.

Follow wiki for more details: https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow

 

 

Instead of programmatically provide user consent login URL you can also try to configure details in BTP Destination

SAP BTP   provides the Destination Service, using which your application can reach to any other cloud service or system or SAP S/4HANA Cloud tenant. In our case we are going to use Destination Service to connect to  Microsoft 365  via Microsoft Graph

Ask your SAP BTP Subaccount Admin /Space Admin to create destination as described in this blog post

 

 

Destination:








Name= name of connection

Type = HTTP

URL = https://graph.microsoft.com/

Type = Internet

Authentication = OAuth2ClientCredentials

                      <Chose as per your business application architecture>

Client ID =<Ask your Azure AD Admin>

Client Secret = <Ask your Azure AD Admin>

Token Service URL  =  https://login.microsoftonline.com/{tenant}/oauth2/v2.0/authorize

Additional Properties

scope = https://graph.microsoft.com/.default

state = 12345

 

Nonce = abcde

 

response_mode = query

 

response_type = code

 

redirect_uri = <Enter redirectUrl , Azure Admin configured in Azure Potral >

 

 

 Step 2  : Get Access _code

 

Once you Get Authorization_code from Step 1 , you will need to  invoke HTTP  POST call to get Access Token  

https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token

 

POST /{tenant}/oauth2/v2.0/token HTTP/1.1

Host: https://login.microsoftonline.com

Content-Type

: application/x-www-form-urlencoded

 


















































Parameter Required/optional Description
tenant required

Ask your Azure portal Admin

The {tenant} value in the path of the request can be used to control who can sign into the application. The allowed values are common, organizations, consumers, and tenant identifiers. For more detail, see protocol basics.
client_id required Ask your Azure portal Admin.The Application (client) ID that the Azure portal – App registrations page assigned to your app.
scope optional A space-separated list of scopes. The scopes must all be from a single resource, along with OIDC scopes (profile, openid, email). For a more detailed explanation of scopes, refer to permissions, consent, and scopes. This is a Microsoft extension to the authorization code flow, intended to allow apps to declare the resource they want the token for during token redemption.
code required The authorization_code that you acquired in the first leg of the flow.
redirect_uri required The same redirect_uri value that was used to acquire the authorization_code.
grant_type required

Must be authorization_code for the authorization code flow.

grant_type = authorization_code
code_verifier recommended The same code_verifier that was used to obtain the authorization_code. Required if PKCE was used in the authorization code grant request. For more information, see the PKCE RFC.
client_secret required for confidential web apps

Ask your Azure portal Admin

The application secret that you created in the app registration portal for your app. You shouldn't use the application secret in a native app or single page app because client_secrets can't be reliably stored on devices or web pages. It's required for web apps and web APIs, which have the ability to store the client_secret securely on the server side. Like all parameters discussed here, the client secret must be URL-encoded before being sent, a step usually performed by the SDK. For more information on uri encoding, see the URI Generic Syntax specification. The Basic auth pattern of instead providing credentials in the Authorization header, per RFC 6749 is also supported.

 

Other approach to get Access Token

 

You can also Request an access token with a certificate credential i.e instead of client_secret parameter is replaced by two parameters:  client_assertion_type and client_assertion.

 

Refer https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow

 

 

Step 3 : Use the access token

 

Now that you've successfully acquired an

access_token, you can use the token in requests to web APIs by including it in the Authorization header:

GET /v1.0/me/messages

Host: https://graph.microsoft.com

Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik5HVEZ2ZEstZnl0aEV1Q..

You can use the SAP Cloud SDK  which can reduce your effort when developing an application on SAP Business Technology Platform by building on best practices delivered by the SAP Cloud SDK. The SAP Cloud SDK provides Java librariesJavaScript libraries, project templates, and a continuous delivery toolkit.

SAP Cloud SDK comes with two variants—one for Java and one for JavaScript/Type-Script—and provides libraries, project templates, and a continuous delivery pipeline that you can use immediately.

Dependent on your application architecture choose either

Cloud SDK for Java

Cloud SDK for JavaScript (Node.js)

 

  • With the SAP Cloud SDK, you can get the result in a single line of code by using the Destination Accessor class of the SDK that gives you an instance of the SAP BTP destination service


 

HttpClient httpClient = HttpClientAccessor.getHttpClient(DestinationAccessor.getDestination(connectionName).asHttp());

 

Here connection Name is Destination Name you can get from your BTP CF Space Admin/Subaccount Admin

 

  • Then to call a Microsoft Graph API resource


Your resource URL will include the resource you are interacting with in the request, such as me, usergroupdrive, and site & to access additional resources, like me/messages or me/drive, me/sendMail

 

HttpDestination destination = DestinationAccessor.getDestination(connectionName).asHttp();

HttpUriRequest request = new HttpGet(destination.getUri()+”v1.0/Users/{id}/sendMail”);

 

 Step 4 : Refresh the access token

 

By default, Access/Bearer tokens have a lifetime of 1 hour. After this time, they are no longer valid.

Refresh Tokens are only returned when you include offline_access in your first scopes lis .In Step2 You would have requested  the scope offline_access. This tells the endpoint to provide a refresh_token alongside the access_token and associated metadata. Access tokens eventually expire & Short life span . You must refresh access token after they expire to get a new access token without requiring the user to be redirected & to continue accessing resources

 

There are two options you can either ask the user to re-authenticate or you can use a Refresh Token to get an new access token.Getting an Access Token from the Refresh Token is a simple process

 

POST

https://login.microsoftonline.com/common/oauth2/v2.0/tokenContent-Type: application/x-www-form-urlencoded grant_type=refresh_token&refresh_token=[REFRESH TOKEN]&client_id=[APPLICATION ID]&client_secret=[PASSWORD]&scope=[SCOPE]&redirect_uri=[REDIRECT URI]








































Parameter Required Description
client_id required The Application ID that the registration portal assigned your app.
grant_type required Must be refresh_token.
scope required A space-separated list of permissions (scopes). The permissions requested must be equivalent to or a subset of the permissions requested in the original authorization_code request.
refresh_token required The refresh_token that you acquired during the token request.
redirect_uri required The same redirect_uri value that was used to acquire the authorization_code.
client_secret required for web apps The application secret that you created in the app registration portal for your app. It should not be used in a native app, because client_secrets cannot be reliably stored on devices. It is required for web apps and web APIs, which have the ability to store the client_secret securely on the server side.

 

 

 

A successful token response will look similar to the following

{

"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik5HVEZ2ZEstZnl0aEV1Q...",

"token_type": "Bearer",

"expires_in": 3599,

"scope": "user.read%20mail.read",

"refresh_token": "AwABAAAAvPM1KaPlrEqdFSBzjqfTGAMxZGUTdM0t4B4...",

}





























Parameter Description
access_token The requested access token. The app can use this token in calls to Microsoft Graph.
token_type Indicates the token type value. The only type that Azure AD supports is Bearer
expires_in How long the access token is valid (in seconds).
scope The permissions (scopes) that the access_token is valid for.
refresh_token Refresh Tokens can also expire A new OAuth 2.0 refresh token. You should replace the old refresh token with this newly acquired refresh token to ensure your refresh tokens remain valid for as long as possible.