{"id":373,"date":"2020-07-26T15:14:00","date_gmt":"2020-07-26T15:14:00","guid":{"rendered":"https:\/\/codingwithramin.com\/?p=373"},"modified":"2025-07-11T16:25:02","modified_gmt":"2025-07-11T16:25:02","slug":"pass-users-identity-and-authorization-from-an-spfx-web-part-to-an-azure-function-to-another-web-api-using-oauth-2-0-on-behalf-of-flow","status":"publish","type":"post","link":"https:\/\/codingwithramin.com\/?p=373","title":{"rendered":"Pass user\u2019s identity and authorization from an SPFx web part to an Azure Function to another web API using OAuth 2.0 On-Behalf-Of flow"},"content":{"rendered":"\n<p>There are many use cases which you need to call a service\/web API from another Web API and it requires to propagate the delegated user identity and permissions through the request chain. OAuth 2.0 On-Behalf-Of flow helps you to authorize access from the gateway to the downstream APIs without losing trace of the user.<\/p>\n\n\n\n<p>In this post I will show you how to use a SharePoint Framework web part as a client application communicates with an Azure Function which acts as a gateway that forwards the requests from to other downstream APIs (which is the Dynamics CRM in this sample).<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Azure Function<\/h2>\n\n\n\n<p>Let&#8217;s start with the Azure Function, go to <a href=\"https:\/\/portal.azure.com\/\">Azure portal<\/a> and create a new Function App with following settings.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"650\" height=\"323\" src=\"https:\/\/codingwithramin.com\/wp-content\/uploads\/2020\/07\/create-azure-function.jpg\" alt=\"\" class=\"wp-image-380\" srcset=\"https:\/\/codingwithramin.com\/wp-content\/uploads\/2020\/07\/create-azure-function.jpg 650w, https:\/\/codingwithramin.com\/wp-content\/uploads\/2020\/07\/create-azure-function-300x149.jpg 300w\" sizes=\"auto, (max-width: 650px) 100vw, 650px\" \/><\/figure>\n\n\n\n<p>When it&#8217;s ready, to secure the our function, go to Authentication\/Authorization settings, turn the App Srvice Authentication on and select &#8220;Log in with Azure Active Directory&#8221; for authentication.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"650\" height=\"507\" src=\"https:\/\/codingwithramin.com\/wp-content\/uploads\/2020\/07\/authentication.jpg\" alt=\"\" class=\"wp-image-381\" srcset=\"https:\/\/codingwithramin.com\/wp-content\/uploads\/2020\/07\/authentication.jpg 650w, https:\/\/codingwithramin.com\/wp-content\/uploads\/2020\/07\/authentication-300x234.jpg 300w\" sizes=\"auto, (max-width: 650px) 100vw, 650px\" \/><\/figure>\n\n\n\n<p>Under Authentication Providers select Azure Active Directory, select Express and create a new App Registration.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"650\" height=\"371\" src=\"https:\/\/codingwithramin.com\/wp-content\/uploads\/2020\/07\/authenticationprovider.jpg\" alt=\"\" class=\"wp-image-382\" srcset=\"https:\/\/codingwithramin.com\/wp-content\/uploads\/2020\/07\/authenticationprovider.jpg 650w, https:\/\/codingwithramin.com\/wp-content\/uploads\/2020\/07\/authenticationprovider-300x171.jpg 300w\" sizes=\"auto, (max-width: 650px) 100vw, 650px\" \/><\/figure>\n\n\n\n<p>Go to CORS settings and add your SharePoint url to make sure your are allowed to make cross-origin calls from your web part.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large is-resized\"><img decoding=\"async\" src=\"https:\/\/codingwithramin.com\/wp-content\/uploads\/2020\/07\/cors.jpg\" alt=\"\" class=\"wp-image-383\" width=\"400\" srcset=\"https:\/\/codingwithramin.com\/wp-content\/uploads\/2020\/07\/cors.jpg 600w, https:\/\/codingwithramin.com\/wp-content\/uploads\/2020\/07\/cors-278x300.jpg 278w\" sizes=\"(max-width: 600px) 100vw, 600px\" \/><\/figure>\n\n\n\n<p>Now go to Functions and create an HTTP Trigger function. Before explaining the code, let&#8217;s configure the App Registration and we will get back to the function later in this post.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">App Registration<\/h2>\n\n\n\n<p>Go to App Registrations and find the app you created previously when you configure the Authentication\/Authorization, go to API permissions and click on Add a permission. from the list, select the Dynamics CRM, for permission type select Delegated permissions and select user_impersonation as permission.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"650\" height=\"385\" src=\"https:\/\/codingwithramin.com\/wp-content\/uploads\/2020\/07\/apipermission.jpg\" alt=\"\" class=\"wp-image-384\" srcset=\"https:\/\/codingwithramin.com\/wp-content\/uploads\/2020\/07\/apipermission.jpg 650w, https:\/\/codingwithramin.com\/wp-content\/uploads\/2020\/07\/apipermission-300x178.jpg 300w\" sizes=\"auto, (max-width: 650px) 100vw, 650px\" \/><\/figure>\n\n\n\n<p>Grant admin consent fo the requested permissions by clicking on Grant admin consent button.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"650\" height=\"359\" src=\"https:\/\/codingwithramin.com\/wp-content\/uploads\/2020\/07\/grantadminconsent.jpg\" alt=\"\" class=\"wp-image-385\" srcset=\"https:\/\/codingwithramin.com\/wp-content\/uploads\/2020\/07\/grantadminconsent.jpg 650w, https:\/\/codingwithramin.com\/wp-content\/uploads\/2020\/07\/grantadminconsent-300x166.jpg 300w\" sizes=\"auto, (max-width: 650px) 100vw, 650px\" \/><\/figure>\n\n\n\n<p>Go to Certificates &amp; secrets page, and create a new client secret, copy and keep the value as you will need it later on, also go to Overview page and copy Application (client) ID and Directory (tenant) ID.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Obtain an access token via OBO flow<\/h2>\n\n\n\n<p>Let&#8217;s get back to our function app, and update the configurations by adding 3 entries as following table from the values you previously copied and saved from the app registration.<\/p>\n\n\n\n<figure class=\"wp-block-table is-style-stripes\"><table class=\"\"><tbody><tr><td>Name<\/td><td>Value<\/td><\/tr><tr><td>CLIENTID<\/td><td> Replace with client id of the app registration<\/td><\/tr><tr><td>CLIENTSECRET<\/td><td>  Replace with client secret of the app registration <\/td><\/tr><tr><td>TENANTID<\/td><td>  Replace with tenant id of the app registration <\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>In this <a href=\"https:\/\/docs.microsoft.com\/en-us\/azure\/active-directory\/develop\/v2-oauth2-on-behalf-of-flow#example\">document <\/a>you can find useful information on how to construct a HTTP POST request to obtain an access token via OBO flow. I will use MSAL.Net for this sample.<\/p>\n\n\n\n<p>Open Visual Studio and create a new Azure Function project, install &#8220;Microsoft.Identity.Client&#8221; NuGet package and update the code to:<\/p>\n\n\n\n<div style=\"height: 250px; position:relative; margin-bottom: 50px;\" class=\"wp-block-simple-code-block-ace\"><pre class=\"wp-block-simple-code-block-ace\" style=\"position:absolute;top:0;right:0;bottom:0;left:0\" data-mode=\"csharp\" data-theme=\"monokai\" data-fontsize=\"14\" data-lines=\"Infinity\" data-showlines=\"true\" data-copy=\"false\">public static class GetAccounts\n{\n    [FunctionName(\"GetAccounts\")]\n    public static async Task&lt;IActionResult> Run(\n        [HttpTrigger(AuthorizationLevel.Anonymous, \"get\", \"post\", Route = null)] HttpRequest req,\n        ILogger log)\n   {\n        log.LogInformation(\"C# HTTP trigger function processed a request.\");\n\n        try\n        {\n           var jwtToken = req.Headers.FirstOrDefault(x => x.Key == \"Authorization\").Value.FirstOrDefault();\n                var resource = req.Query[\"resource\"];\n                var token = GetDynamicsCrmAccessToken(jwtToken.Replace(\"Bearer \",\"\"),resource);\n                \n            using (HttpClient httpClient = new HttpClient())\n            {\n                httpClient.BaseAddress = new Uri(resource);\n                httpClient.Timeout = new TimeSpan(0, 2, 0);\n                httpClient.DefaultRequestHeaders.Add(\"OData-MaxVersion\", \"4.0\");\n                httpClient.DefaultRequestHeaders.Add(\"OData-Version\", \"4.0\");\n                httpClient.DefaultRequestHeaders.Accept.Add(\n                    new MediaTypeWithQualityHeaderValue(\"application\/json\"));\n                httpClient.DefaultRequestHeaders.Authorization =\n                    new AuthenticationHeaderValue(\"Bearer\", token);\n\n                HttpResponseMessage retrieveResponse =\n                            await httpClient.GetAsync(\"\/api\/data\/v9.1\/accounts?$select=accountid,name,emailaddress1&amp;$top=5\");\n                if (retrieveResponse.IsSuccessStatusCode)\n                {\n                    var results = await retrieveResponse.Content.ReadAsStringAsync();\n                    var jsonObject = JsonConvert.DeserializeObject(results);\n                    return new OkObjectResult(jsonObject);\n                }\n                else\n                    return new BadRequestErrorMessageResult(\"\");\n            }\n        }\n        catch(Exception ex)\n        {\n            return new BadRequestErrorMessageResult(ex.Message);\n        }\n        \n    }\n    private static string GetDynamicsCrmAccessToken(string jwtToken, string crmUri)\n    {\n        var crmResourceUri = $\"{crmUri}\/.default\";\n        var clientId = Environment.GetEnvironmentVariable(\"CLIENTID\");\n        var clientSecret = Environment.GetEnvironmentVariable(\"CLIENTSECRET\");\n        var tenantId = Environment.GetEnvironmentVariable(\"TENANTID\");\n        List&lt;string> scopes = new List&lt;string>();\n        scopes.Add(crmResourceUri);\n        var app = ConfidentialClientApplicationBuilder.Create(clientId)\n            .WithClientSecret(clientSecret)\n            .WithTenantId(tenantId)                \n            .Build();\n        var userAssertion = new UserAssertion(jwtToken);\n        var result = app.AcquireTokenOnBehalfOf(scopes, userAssertion).ExecuteAsync().GetAwaiter().GetResult();\n        return result.AccessToken;\n\n    }\n\n}<\/pre><\/div>\n\n\n\n<p>In line 12, we get the delegated access token from the Authorization header, and we use that as UserAssertion to obtain another delegated access token for Dynamics CRM. Then we can make a call to the Dynamics CRM endpoint and use the new access token in the Authorization header.<\/p>\n\n\n\n<p>In the above code we get top 5 accounts from Dynamics CRM and return the data to the client application (SPFx web part).<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">SharePoint Framework Web Part<\/h2>\n\n\n\n<p>Using the new <strong>AadHttpClient<\/strong>, you can easily connect to APIs secured with Azure AD without having to implement authentication and authorization yourself.  Internally, the&nbsp;<strong>AadHttpClient<\/strong>&nbsp;implements the Azure AD OAuth flow using ADAL JS by using the&nbsp;<strong>SharePoint Online Client Extensibility<\/strong>&nbsp;service principal to obtain a valid access token. <\/p>\n\n\n\n<p>Create a SharePoint Framework web part, to use the&nbsp;<strong>AadHttpClient<\/strong>&nbsp;in your SharePoint Framework solution, add the following&nbsp;<code>import<\/code>&nbsp;clause in your main web part file:<\/p>\n\n\n\n<div style=\"height: 250px; position:relative; margin-bottom: 50px;\" class=\"wp-block-simple-code-block-ace\"><pre class=\"wp-block-simple-code-block-ace\" style=\"position:absolute;top:0;right:0;bottom:0;left:0\" data-mode=\"typescript\" data-theme=\"monokai\" data-fontsize=\"14\" data-lines=\"Infinity\" data-showlines=\"true\" data-copy=\"false\">import { AadHttpClient } from \"@microsoft\/sp-http\";<\/pre><\/div>\n\n\n\n<p>Add OnInit method to the code, Get a new instance of the&nbsp;<strong>AadHttpClient<\/strong>, passing the resource to which you want to connect as parameters: <\/p>\n\n\n\n<div style=\"height: 250px; position:relative; margin-bottom: 50px;\" class=\"wp-block-simple-code-block-ace\"><pre class=\"wp-block-simple-code-block-ace\" style=\"position:absolute;top:0;right:0;bottom:0;left:0\" data-mode=\"typescript\" data-theme=\"monokai\" data-fontsize=\"14\" data-lines=\"Infinity\" data-showlines=\"true\" data-copy=\"false\">const httpClient: AadHttpClient = await this.context.aadHttpClientFactory.getClient(\n    this.properties.appRegistrationId\n);<\/pre><\/div>\n\n\n\n<p>AppRegistrationId is the app client ID you copied previously (alternatively you can also use either the application name or the azure function url).<\/p>\n\n\n\n<p>Now you can securely connect to the azure function and get the result back:<\/p>\n\n\n\n<div style=\"height: 250px; position:relative; margin-bottom: 50px;\" class=\"wp-block-simple-code-block-ace\"><pre class=\"wp-block-simple-code-block-ace\" style=\"position:absolute;top:0;right:0;bottom:0;left:0\" data-mode=\"typescript\" data-theme=\"monokai\" data-fontsize=\"14\" data-lines=\"Infinity\" data-showlines=\"true\" data-copy=\"false\">public async getAccounts() {\n    const headers: Headers = new Headers();\n    headers.append(\"Accept\", \"application\/json\");\n    const requestOptions:IHttpClientOptions  = {\n        headers          \n    };\n    const response = await this.aadHttpClient.get(\n        `${this.azureFunctionUri}?resource=${this.resourceUri}`,\n        AadHttpClient.configurations.v1,\n        requestOptions\n    );\n    const result = await response.text();\n    const accounts = JSON.parse(result);\n    return accounts.value;\n}<\/pre><\/div>\n\n\n\n<p>You can get the azure function Url from the function page by selecting Get Function Url:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"650\" height=\"189\" src=\"https:\/\/codingwithramin.com\/wp-content\/uploads\/2020\/07\/functionUrl.jpg\" alt=\"\" class=\"wp-image-389\" srcset=\"https:\/\/codingwithramin.com\/wp-content\/uploads\/2020\/07\/functionUrl.jpg 650w, https:\/\/codingwithramin.com\/wp-content\/uploads\/2020\/07\/functionUrl-300x87.jpg 300w\" sizes=\"auto, (max-width: 650px) 100vw, 650px\" \/><\/figure>\n\n\n\n<p>And the resource URI is your Dynamics CRM instance URL (e.x. https:\/\/contoso.crm11.dynamics.com).<\/p>\n\n\n\n<p>Finally, open the&nbsp;<strong>config\/package-solution.json<\/strong> file, to the&nbsp;<strong>solution<\/strong>&nbsp;property, add the&nbsp;<strong>webApiPermissionRequests<\/strong>&nbsp;property and update it with the permission for your app registration.<\/p>\n\n\n\n<div style=\"height: 250px; position:relative; margin-bottom: 50px;\" class=\"wp-block-simple-code-block-ace\"><pre class=\"wp-block-simple-code-block-ace\" style=\"position:absolute;top:0;right:0;bottom:0;left:0\" data-mode=\"json\" data-theme=\"monokai\" data-fontsize=\"14\" data-lines=\"Infinity\" data-showlines=\"true\" data-copy=\"false\">\"solution\": {\n    \"name\": \"react-dynamic-365-client-side-solution\",\n    \"id\": \"5bc3855d-9c59-4322-95e0-78e17dcd809e\",\n    \"version\": \"1.0.0.1\",\n    \"includeClientSideAssets\": true,\n    \"skipFeatureDeployment\": true,\n    \"isDomainIsolated\": false,\n    \"webApiPermissionRequests\": [\n      {\n        \"resource\": \"OBO-Flow-App\",\n        \"scope\": \"user_impersonation\"\n      }\n     ]\n}<\/pre><\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Summary<\/h2>\n\n\n\n<p>For middle-tier services which call another services\/web APIs it&#8217;s important to not lose trace of the current user if your application needs to use delegated permissions, above I explained how you can implement this flow using an Azure Function as middle-tier service and Dynamics CRM API as a downstream API.<\/p>\n\n\n\n<p>As always you can find the source code <a href=\"https:\/\/github.com\/AhmadiRamin\/azure-function-on-behalf-of-flow\">here<\/a>.<\/p>\n\n\n\n<p>Sharing is caring!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>There are many use cases which you need to call a service\/web API from another Web API and it requires to propagate the delegated user identity and permissions through the request chain. OAuth 2.0 On-Behalf-Of flow helps you to authorize access from the gateway to the downstream APIs without losing trace of the user. In this post I will show you how to use a SharePoint Framework web part as a client application communicates with&hellip;<\/p>\n","protected":false},"author":1,"featured_media":387,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[17,42,5,39,4,3],"tags":[10,43,11],"class_list":["post-373","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-azure-functions","category-dynamics-crm","category-microsoft-azure","category-oauth","category-sharepoint-framework","category-sharepoint-online","tag-azure-function","tag-oauth","tag-spfx"],"_links":{"self":[{"href":"https:\/\/codingwithramin.com\/index.php?rest_route=\/wp\/v2\/posts\/373","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/codingwithramin.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/codingwithramin.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/codingwithramin.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/codingwithramin.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=373"}],"version-history":[{"count":8,"href":"https:\/\/codingwithramin.com\/index.php?rest_route=\/wp\/v2\/posts\/373\/revisions"}],"predecessor-version":[{"id":392,"href":"https:\/\/codingwithramin.com\/index.php?rest_route=\/wp\/v2\/posts\/373\/revisions\/392"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/codingwithramin.com\/index.php?rest_route=\/wp\/v2\/media\/387"}],"wp:attachment":[{"href":"https:\/\/codingwithramin.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=373"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/codingwithramin.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=373"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/codingwithramin.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=373"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}